init: start bot write of swift

This commit is contained in:
Konrad Geletey 2024-10-19 17:39:04 +03:00
commit 77672b5c2a
Signed by: kglt
GPG key ID: 386DEE24B60BD996
11 changed files with 465 additions and 0 deletions

View file

@ -0,0 +1,18 @@
import Vapor
import SwiftTelegramSdk
final class DefaultBotHandlers {
static func addHandlers() async {
await defaultBaseHandler()
}
private static func defaultBaseHandler() async {
await botActor.bot.dispatcher.add(TGBaseHandler({ update in
guard let message = update.message else { return }
let params: TGSendMessageParams = .init(chatId: .chat(message.chat.id), text: """
Help message
""")
try await botActor.bot.sendMessage(params: params)
}))
}
}

View file

@ -0,0 +1,13 @@
import SwiftTelegramSdk
actor TGBotActor {
private var _bot: TGBot!
var bot: TGBot {
self._bot
}
func setBot(_ bot: TGBot) {
self._bot = bot
}
}

View file

@ -0,0 +1,110 @@
//
// VaporTGClient.swift
// Vapor-telegram-bot-example
//
// Created by Oleh Hudeichuk on 01.07.2024.
//
import Foundation
import Vapor
import SwiftTelegramSdk
public enum TGHTTPMediaType: String, Equatable {
case formData
case json
}
private struct TGEmptyParams: Encodable {}
public final class VaporTGClient: TGClientPrtcl {
public typealias HTTPMediaType = SwiftTelegramSdk.HTTPMediaType
public var log: Logging.Logger = .init(label: "VaporTGClient")
private let client: Vapor.Client
public init(client: Vapor.Client) {
self.client = client
}
@discardableResult
public func post<Params: Encodable, Response: Decodable>
(
_ url: URL,
params: Params? = nil,
as mediaType: HTTPMediaType? = nil
) async throws -> Response {
let clientResponse: ClientResponse = try await client.post(URI(string: url.absoluteString), headers: HTTPHeaders()) { clientRequest in
if mediaType == .formData || mediaType == nil {
#warning("THIS CODE FOR FAST FIX, BECAUSE https://github.com/vapor/multipart-kit/issues/63 not accepted yet")
var rawMultipart: (body: NSMutableData, boundary: String)!
do {
/// Content-Disposition: form-data; name="nested_object"
///
/// { json string }
if let currentParams: Params = params {
rawMultipart = try currentParams.toMultiPartFormData(log: log)
} else {
rawMultipart = try TGEmptyParams().toMultiPartFormData(log: log)
}
} catch {
log.critical("Post request error: \(error.logMessage)")
}
clientRequest.headers.add(name: "Content-Type", value: "multipart/form-data; boundary=\(rawMultipart.boundary)")
let buffer = ByteBuffer.init(data: rawMultipart.body as Data)
clientRequest.body = buffer
/// Debug
// TGBot.log.critical("url: \(url)\n\(String(decoding: rawMultipart.body, as: UTF8.self))")
} else {
let mediaType: Vapor.HTTPMediaType = if let mediaType {
.init(type: mediaType.type, subType: mediaType.subType, parameters: mediaType.parameters)
} else {
.json
}
try clientRequest.content.encode(params ?? (TGEmptyParams() as! Params), as: mediaType)
}
}
let telegramContainer: TGTelegramContainer = try clientResponse.content.decode(TGTelegramContainer<Response>.self)
return try processContainer(telegramContainer)
}
@discardableResult
public func post<Response: Decodable>(_ url: URL) async throws -> Response {
try await post(url, params: TGEmptyParams(), as: nil)
}
private func processContainer<T: Decodable>(_ container: TGTelegramContainer<T>) throws -> T {
guard container.ok else {
let desc = """
Response marked as `not Ok`, it seems something wrong with request
Code: \(container.errorCode ?? -1)
\(container.description ?? "Empty")
"""
let error = BotError(
type: .server,
description: desc
)
log.error(error.logMessage)
throw error
}
guard let result = container.result else {
let error = BotError(
type: .server,
reason: "Response marked as `Ok`, but doesn't contain `result` field."
)
log.error(error.logMessage)
throw error
}
let logString = """
Response:
Code: \(container.errorCode ?? 0)
Status OK: \(container.ok)
Description: \(container.description ?? "Empty")
"""
log.trace(logString.logMessage)
return result
}
}

View file

@ -0,0 +1,20 @@
import Foundation
import Vapor
import SwiftTelegramSdk
public func configure(_ app: Application) async throws {
guard let tgApi: String = Environment.get("TG_BOT_API") else { throw Errors.notVariable("Telegram key is not defined")}
app.logger.logLevel = .debug
let bot: TGBot = try await .init(connectionType: .longpolling(limit: nil,
timeout: nil,
allowedUpdates: nil),
dispatcher: nil,
tgClient: VaporTGClient(client: app.client),
tgURI: TGBot.standardTGURL,
botId: tgApi,
log: app.logger)
await botActor.setBot(bot)
await DefaultBotHandlers.addHandlers()
try await botActor.bot.start()
// try routes(app)
}

View file

@ -0,0 +1,3 @@
enum Errors: Error {
case notVariable(String)
}

View file

@ -0,0 +1,18 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book
import Vapor
import SwiftTelegramSdk
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)
let eventLoop: EventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount * 4)
let app: Application = try await Application.make(env, Application.EventLoopGroupProvider.shared(eventLoop))
let botActor: TGBotActor = .init()
defer { app.shutdown() }
try await configure(app)
try await app.execute()

View file

@ -0,0 +1,8 @@
/*
import Vapor
func routes(_ app: Application) throws {
try app.register(collection: TelegramController())
}
*/