Giter Club home page Giter Club logo

nerzh / telegram-vapor-bot Goto Github PK

View Code? Open in Web Editor NEW
184.0 6.0 25.0 801 KB

🤖 The wrapper for the Telegram Bot API written in Swift with Vapor. It's not a framework. There is no special syntax here. This is a library that implements all Telegram Bot API methods, which is available to you to work with Vapor.

Home Page: https://core.telegram.org/bots/api#available-methods

License: MIT License

Swift 96.21% Ruby 3.79%
telegram telegram-bot telegram-bot-api bot swift server-side-swift vapor async await await-async

telegram-vapor-bot's Introduction

Telegram Vapor Bot (SDK for creating Telegram Bots in Swift)

Please, support with ⭐️

🤖 The wrapper for the Telegram Bot API written in Swift with Vapor. It's not a framework. There is no special syntax here. This is a library that implements all Telegram Bot API methods, which is available to you to work with Vapor.

Example Telegram Bot based on Swift Telegram Vapor Bot - Here

Telegram-bot-example

Swift Server Side Community

Swift Server Side Community - Ukraine / Russian / CIS Telegram Chat

Usage

create folder with your handlers TGHandlers/DefaultBotHandlers.swift

import Vapor
import TelegramVaporBot

final class DefaultBotHandlers {

    static func addHandlers(app: Vapor.Application, connection: TGConnectionPrtcl) async {
        await defaultBaseHandler(app: app, connection: connection)
        await messageHandler(app: app, connection: connection)
        await commandPingHandler(app: app, connection: connection)
        await commandShowButtonsHandler(app: app, connection: connection)
        await buttonsActionHandler(app: app, connection: connection)
    }
    
    /// Handler for all updates
    private static func defaultBaseHandler(app: Vapor.Application, connection: TGConnectionPrtcl) async {
        await connection.dispatcher.add(TGBaseHandler({ update, bot in
            guard let message = update.message else { return }
            let params: TGSendMessageParams = .init(chatId: .chat(message.chat.id), text: "TGBaseHandler")
            try await connection.bot.sendMessage(params: params)
        }))
    }

    /// Handler for Messages
    private static func messageHandler(app: Vapor.Application, connection: TGConnectionPrtcl) async {
        await connection.dispatcher.add(TGMessageHandler(filters: (.all && !.command.names(["/ping", "/show_buttons"]))) { update, bot in
            let params: TGSendMessageParams = .init(chatId: .chat(update.message!.chat.id), text: "Success")
            try await connection.bot.sendMessage(params: params)
        })
    }

    /// Handler for Commands
    private static func commandPingHandler(app: Vapor.Application, connection: TGConnectionPrtcl) async {
        await connection.dispatcher.add(TGCommandHandler(commands: ["/ping"]) { update, bot in
            try await update.message?.reply(text: "pong", bot: bot)
        })
    }

    /// Show buttons
    private static func commandShowButtonsHandler(app: Vapor.Application, connection: TGConnectionPrtcl) async {
        await connection.dispatcher.add(TGCommandHandler(commands: ["/show_buttons"]) { update, bot in
            guard let userId = update.message?.from?.id else { fatalError("user id not found") }
            let buttons: [[TGInlineKeyboardButton]] = [
                [.init(text: "Button 1", callbackData: "press 1"), .init(text: "Button 2", callbackData: "press 2")]
            ]
            let keyboard: TGInlineKeyboardMarkup = .init(inlineKeyboard: buttons)
            let params: TGSendMessageParams = .init(chatId: .chat(userId),
                                                    text: "Keyboard active",
                                                    replyMarkup: .inlineKeyboardMarkup(keyboard))
            try await connection.bot.sendMessage(params: params)
        })
    }

    /// Handler for buttons callbacks
    private static func buttonsActionHandler(app: Vapor.Application, connection: TGConnectionPrtcl) async {
        await connection.dispatcher.add(TGCallbackQueryHandler(pattern: "press 1") { update, bot in
            let params: TGAnswerCallbackQueryParams = .init(callbackQueryId: update.callbackQuery?.id ?? "0",
                                                            text: update.callbackQuery?.data  ?? "data not exist",
                                                            showAlert: nil,
                                                            url: nil,
                                                            cacheTime: nil)
            try await bot.answerCallbackQuery(params: params)
        })
        
        await connection.dispatcher.add(TGCallbackQueryHandler(pattern: "press 2") { update, bot in
            let params: TGAnswerCallbackQueryParams = .init(callbackQueryId: update.callbackQuery?.id ?? "0",
                                                            text: update.callbackQuery?.data  ?? "data not exist",
                                                            showAlert: nil,
                                                            url: nil,
                                                            cacheTime: nil)
            try await bot.answerCallbackQuery(params: params)
        })
    }
}

Setup

Create file TGBotConnectionActor.swift

Add Actor for TGConnection

import Foundation
import TelegramVaporBot

actor TGBotConnection {
    private var _connection: TGConnectionPrtcl!

    var connection: TGConnectionPrtcl {
        self._connection
    }
    
    func setConnection(_ conn: TGConnectionPrtcl) {
        self._connection = conn
    }
}

vapor main.swift

make strong reference to TGBotConnection instance and add "await" to configure

import Vapor
import TelegramVaporBot

let TGBOT: TGBotConnection = .init()

try await configure(app)

Use with LongPolling

for longpolling you should only configure vapor configure.swift

import TelegramVaporBot

public func configure(_ app: Application) async throws {
    let tgApi: String = "XXXXXXXXXX:YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
    /// set level of debug if you needed
    TGBot.log.logLevel = app.logger.logLevel
    let bot: TGBot = .init(app: app, botId: tgApi)
    await TGBOT.setConnection(try await TGLongPollingConnection(bot: bot))
    await DefaultBotHandlers.addHandlers(app: app, connection: TGBOT.connection)
    try await TGBOT.connection.start()

    try routes(app)
}

Use with Webhooks

vapor configure.swift

import TelegramVaporBot

public func configure(_ app: Application) async throws {
    let tgApi: String = "XXXXXXXXXX:YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
    /// set level of debug if you needed
    TGBot.log.logLevel = app.logger.logLevel
    let bot: TGBot = .init(app: app, botId: tgApi)
    await TGBOT.setConnection(try await TGWebHookConnection(bot: bot, webHookURL: "https://your_domain/telegramWebHook"))
    await DefaultBotHandlers.addHandlers(app: app, connection: TGBOT.connection)
    try await TGBOT.connection.start()

    try routes(app)
}

vapor routes.swift

import Vapor
import TelegramVaporBot


func routes(_ app: Application) throws {
    try app.register(collection: TelegramController())
}

vapor TelegramController.swift

import Vapor
import TelegramVaporBot

final class TelegramController: RouteCollection {
    
    func boot(routes: Vapor.RoutesBuilder) throws {
        routes.post("telegramWebHook", use: telegramWebHook)
    }
}

extension TelegramController {
    
    func telegramWebHook(_ req: Request) async throws -> Bool {
        let update: TGUpdate = try req.content.decode(TGUpdate.self)
        return try await TGBOT.connection.dispatcher.process([update])
    }
}

Add to your Vapor project with Swift Package Manager

add to yor Package.json

// swift-tools-version:5.8

import PackageDescription

var packageDependencies: [Package.Dependency] = [
    .package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.57.0")),
]

packageDependencies.append(.package(url: "https://github.com/nerzh/telegram-vapor-bot", .upToNextMajor(from: "2.1.0")))


let package = Package(
    name: "Telegram-bot-example",
    platforms: [
        .macOS(.v12)
    ],
    dependencies: packageDependencies,
    targets: [
        .executableTarget(
            name: "Telegram-bot-example",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
                .product(name: "TelegramVaporBot", package: "telegram-vapor-bot"),
            ]
        )
    ]
)

Acknowledgments

Inspired by Telegrammer

telegram-vapor-bot's People

Contributors

crescentheaded avatar flyer2001 avatar higher08 avatar hsharghi avatar mihaelisaev avatar nerzh avatar valeriyvan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

telegram-vapor-bot's Issues

Confusing error message

[ com.tgbot ] [ ERROR ] 
>>>Type: server
>>>Description: Response marked as `not Ok`, it seems something wrong with request
Code: 404
Not Found
>>>Reason: 
 (TelegramVaporBot/TGClient.swift:145)
[ codes.vapor.application ] [ DEBUG ] Application shutting down (Vapor/Application.swift:235)
[ codes.vapor.application ] [ TRACE ] Shutting down providers (Vapor/Application.swift:237)
[ codes.vapor.application ] [ TRACE ] Clearing Application storage (Vapor/Application.swift:241)
[ codes.vapor.application ] [ DEBUG ] Shutting down connection pool [ahc-pool-key: https://api.telegram.org:443 TLS-hash: 3820552592687539142 ] (AsyncHTTPClient/HTTPConnectionPool.swift:84)
[ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/Application.swift:247)
[ codes.vapor.application ] [ TRACE ] Application shutdown complete (Vapor/Application.swift:258)
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: TelegramVaporBot.BotError
zs

Is what follows Reason real reason? Or there's no reason here?

Thread 76: Fatal error: String index is out of bounds

Когда кто-то из пользователей пересылает в чат длинное сообщение, или перепост, бот падает с ошибкой Fatal error: String index is out of bounds со ссылкой на строку 70 в файле CommandHandler

Снимок экрана 2022-01-12 в 12 04 08

Бот перестает реагировать

Привет! Есть ли у бота какой-то таймаут? Такое чувство, что, если не пользоваться им 3-4 часа, он просто перестает реагировать. Причем в консоль ничего не выводится, процесс Vapor запущен и работает, а бот не отвечает. Делал на основе примера.

Предупреждения в файле TelegramController.swift

Постоянно в Xcode висят предупреждения:

  1. "Converting non-sendable function value to '@sendable (Request) async throws -> Bool' may introduce data races".
  2. "Non-sendable type 'any TGConnectionPrtcl' in implicitly asynchronous access to actor-isolated property 'connection' cannot cross actor boundary" - исправляеься заменой "import TelegramVaporBot" на "@preconcurrency import TelegramVaporBot"
    Xcode v.15.3, Swift v.5.10

Consider Declarative Syntax

В конце файла Readme.md можно увидеть следующий отрывок кода:

// swift-tools-version:5.8

import PackageDescription

var packageDependencies: [Package.Dependency] = [
    .package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.57.0")),
]

packageDependencies.append(.package(url: "https://github.com/nerzh/telegram-vapor-bot", .upToNextMajor(from: "2.1.0")))


let package = Package(
    name: "Telegram-bot-example",
    platforms: [
        .macOS(.v12)
    ],
    dependencies: packageDependencies,
    targets: [
        .executableTarget(
            name: "Telegram-bot-example",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
                .product(name: "TelegramVaporBot", package: "telegram-vapor-bot"),
            ]
        )
    ]
)

Слайд 58 материала для разработчиков от Apple говорит нам о том, что лучше использовать декларативный синтаксис подразумевая, что вместо отрывка кода выше будет отрывок кода ниже:

// swift-tools-version:5.8

import PackageDescription

let package = Package(
    name: "Telegram-bot-example",
    platforms: [
        .macOS(.v12)
    ],
    dependencies: [
        .package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.57.0")),
        .package(url: "https://github.com/nerzh/telegram-vapor-bot", .upToNextMajor(from: "2.1.0"))
    ],
    targets: [
        .executableTarget(
            name: "Telegram-bot-example",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
                .product(name: "TelegramVaporBot", package: "telegram-vapor-bot"),
            ]
        )
    ]
)

Честно сказать, не знаю как это в теории может повлиять на что-либо, но почему нет :)

Ну и по мелочи можно убрать лишние пробельные символы после всего листинга.

Buttons in place of the keyboard

Hi! Thank you for your work!
Please, show me tan example how to make buttons not in chat, but in place of the keyboard.
Is there any documentation?

Connecting to database.

Can you make a quick explanation on connecting to fluent database? Really appreciate that.

buttonsActionHandler does not work

When I trying to use buttons callback as it showed in example, I'm facing problem when bot can't reply to user. It seems because bot somehow forgets chat id. Example does not work too, actually.

Request support for Concurrency and Web App.

Your project is a great library! It has a lot of fun features when combined with Vapor. The combination with Vapor is also great for making Telegram Web App for Bot.

Request support for these features:

  • async/await makes it easier to read and is now supported by most of the libraries still being updated.
  • Telegram is supporting Web App for Bot this year and we hope to add these features.
    https://core.telegram.org/bots/webapps

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.