init: start bot write of swift
This commit is contained in:
commit
77672b5c2a
11 changed files with 465 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/configuration/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
7
LICENSE
Normal file
7
LICENSE
Normal file
|
@ -0,0 +1,7 @@
|
|||
ISC License
|
||||
|
||||
Copyright 2024 Oleh Nersh and Konrad Geletey
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
231
Package.resolved
Normal file
231
Package.resolved
Normal file
|
@ -0,0 +1,231 @@
|
|||
{
|
||||
"originHash" : "5b140fcd6a36165fb4c1302609ef5353bacb59ce0f82996aa0cf66ee021cbb17",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swift-server/async-http-client.git",
|
||||
"state" : {
|
||||
"revision" : "0a9b72369b9d87ab155ef585ef50700a34abf070",
|
||||
"version" : "1.23.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "async-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/async-kit.git",
|
||||
"state" : {
|
||||
"revision" : "e048c8ee94967e8d8a1c2ec0e1156d6f7fa34d31",
|
||||
"version" : "1.20.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "console-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/console-kit.git",
|
||||
"state" : {
|
||||
"revision" : "78c0dd739df8cb9ee14a8bbbf770facc4fc3402a",
|
||||
"version" : "4.15.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "multipart-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/multipart-kit.git",
|
||||
"state" : {
|
||||
"revision" : "a31236f24bfd2ea2f520a74575881f6731d7ae68",
|
||||
"version" : "4.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "routing-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/routing-kit.git",
|
||||
"state" : {
|
||||
"revision" : "8c9a227476555c55837e569be71944e02a056b72",
|
||||
"version" : "4.9.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-algorithms",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-algorithms.git",
|
||||
"state" : {
|
||||
"revision" : "f6919dfc309e7f1b56224378b11e28bab5bccc42",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-asn1",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-asn1.git",
|
||||
"state" : {
|
||||
"revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-atomics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-atomics.git",
|
||||
"state" : {
|
||||
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
|
||||
"version" : "1.1.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-crypto",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-crypto.git",
|
||||
"state" : {
|
||||
"revision" : "21f7878f2b39d46fd8ba2b06459ccb431cdf876c",
|
||||
"version" : "3.8.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-custom-logger",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/nerzh/swift-custom-logger",
|
||||
"state" : {
|
||||
"revision" : "b76cd97634c3d23348f041ca161187dac8bf6fe3",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-http-types",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-http-types",
|
||||
"state" : {
|
||||
"revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log",
|
||||
"state" : {
|
||||
"revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
|
||||
"version" : "1.6.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-metrics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-metrics.git",
|
||||
"state" : {
|
||||
"revision" : "e0165b53d49b413dd987526b641e05e246782685",
|
||||
"version" : "2.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio.git",
|
||||
"state" : {
|
||||
"revision" : "f7dc3f527576c398709b017584392fb58592e7f5",
|
||||
"version" : "2.75.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-extras",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-extras.git",
|
||||
"state" : {
|
||||
"revision" : "2e9746cfc57554f70b650b021b6ae4738abef3e6",
|
||||
"version" : "1.24.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-http2",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-http2.git",
|
||||
"state" : {
|
||||
"revision" : "eaa71bb6ae082eee5a07407b1ad0cbd8f48f9dca",
|
||||
"version" : "1.34.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-ssl",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-ssl.git",
|
||||
"state" : {
|
||||
"revision" : "d7ceaf0e4d8001cd35cdc12e42cdd281e9e564e8",
|
||||
"version" : "2.28.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-transport-services",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-transport-services.git",
|
||||
"state" : {
|
||||
"revision" : "dbace16f126fdcd80d58dc54526c561ca17327d7",
|
||||
"version" : "1.22.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-numerics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-numerics.git",
|
||||
"state" : {
|
||||
"revision" : "0a5bc04095a675662cf24757cc0640aa2204253b",
|
||||
"version" : "1.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-regular-expression",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/nerzh/swift-regular-expression",
|
||||
"state" : {
|
||||
"revision" : "19c3e569a8e81da6f7f4ee3b73028c25737d3706",
|
||||
"version" : "0.2.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-system",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system.git",
|
||||
"state" : {
|
||||
"revision" : "c8a44d836fe7913603e246acab7c528c2e780168",
|
||||
"version" : "1.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-telegram-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/nerzh/swift-telegram-sdk",
|
||||
"state" : {
|
||||
"revision" : "1be977abc50e9e6aed5a60f1d1d27e25e635da9b",
|
||||
"version" : "3.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "vapor",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/vapor.git",
|
||||
"state" : {
|
||||
"revision" : "1466c50e4ad39072143e2fcdf13b4ba11be375a0",
|
||||
"version" : "4.106.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "websocket-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/websocket-kit.git",
|
||||
"state" : {
|
||||
"revision" : "4232d34efa49f633ba61afde365d3896fc7f8740",
|
||||
"version" : "2.15.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
29
Package.swift
Normal file
29
Package.swift
Normal file
|
@ -0,0 +1,29 @@
|
|||
// swift-tools-version: 6.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
|
||||
var packageDependencies: [Package.Dependency] = [
|
||||
.package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.57.0")),
|
||||
]
|
||||
|
||||
var targetDependencies: [PackageDescription.Target.Dependency] = [
|
||||
.product(name: "Vapor", package: "vapor"),
|
||||
]
|
||||
|
||||
packageDependencies.append(.package(url: "https://github.com/nerzh/swift-telegram-sdk", .upToNextMajor(from: "3.0.3")))
|
||||
targetDependencies.append(.product(name: "SwiftTelegramSdk", package: "swift-telegram-sdk"))
|
||||
|
||||
let package = Package(
|
||||
name: "TelegramModeratorBot",
|
||||
dependencies: packageDependencies,
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||
// Targets can depend on other targets in this package and products from dependencies.
|
||||
.executableTarget(
|
||||
name: "TelegramModeratorBot",
|
||||
dependencies: targetDependencies
|
||||
),
|
||||
]
|
||||
)
|
18
Sources/TelegramModeratorBot/DefaultBotHandlers.swift
Normal file
18
Sources/TelegramModeratorBot/DefaultBotHandlers.swift
Normal 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)
|
||||
}))
|
||||
}
|
||||
}
|
13
Sources/TelegramModeratorBot/TGBotActor.swift
Normal file
13
Sources/TelegramModeratorBot/TGBotActor.swift
Normal 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
|
||||
}
|
||||
}
|
110
Sources/TelegramModeratorBot/VaporTGClient.swift
Normal file
110
Sources/TelegramModeratorBot/VaporTGClient.swift
Normal 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
|
||||
}
|
||||
}
|
20
Sources/TelegramModeratorBot/configure.swift
Normal file
20
Sources/TelegramModeratorBot/configure.swift
Normal 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)
|
||||
}
|
3
Sources/TelegramModeratorBot/errors.swift
Normal file
3
Sources/TelegramModeratorBot/errors.swift
Normal file
|
@ -0,0 +1,3 @@
|
|||
enum Errors: Error {
|
||||
case notVariable(String)
|
||||
}
|
18
Sources/TelegramModeratorBot/main.swift
Normal file
18
Sources/TelegramModeratorBot/main.swift
Normal 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()
|
8
Sources/TelegramModeratorBot/routes.swift
Normal file
8
Sources/TelegramModeratorBot/routes.swift
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
/*
|
||||
import Vapor
|
||||
|
||||
func routes(_ app: Application) throws {
|
||||
try app.register(collection: TelegramController())
|
||||
}
|
||||
*/
|
Loading…
Reference in a new issue