聊天 • 更新日志 • 前提条件 • 开始上手 • 创建新 Bot • 生成 Xcode 项目 • API 概览 • 调试说明 • 示例 • 文档 • 支持 • 许可证
示例项目
购物清单 Bot。
单词反转 Bot。
琐碎 Bot
import TelegramBotSDK
let bot = TelegramBot(token: "my token")
let router = Router(bot: bot)
router["greet"] = { context in
guard let from = context.message?.from else { return false }
context.respondAsync("Hello, \(from.firstName)!")
return true
}
router[.newChatMembers] = { context in
guard let users = context.message?.newChatMembers else { return false }
for user in users {
guard user.id != bot.user.id else { continue }
context.respondAsync("Welcome, \(user.firstName)!")
}
return true
}
while let update = bot.nextUpdateSync() {
try router.process(update: update)
}
fatalError("Server stopped due to error: \(bot.lastError)")
加入我们在 Telegram 上的聊天:swiftsdkchat。
发行说明包含每个版本中的重大更改以及迁移说明。
在 OS X 上,使用最新的 Xcode 9 版本。
在 Linux 上,安装 Swift 4.2
或更高版本以及 libcurl4-openssl-dev
包。请注意,shopster-bot
示例无法在 Linux 上构建,因为 GRDB 尚不支持 Linux,但除此之外,该库应该是可用的。
请先熟悉 Telegram 网站上的文档
在 Telegram 中,添加 BotFather
。向他发送以下命令
/newbot
BotName
username_of_my_bot
BotFather 将返回一个令牌。
为您的 Bot 创建一个项目
mkdir hello-bot
cd hello-bot
swift package init --type executable
创建 Package.swift
// swift-tools-version:5.1
import PackageDescription
let package = Package(
name: "hello-bot",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.executable(
name: "hello-bot",
targets: ["hello-bot"]
),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(name: "TelegramBotSDK", url: "https://github.com/zmeyc/telegram-bot-swift.git", from: "2.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "hello-bot",
dependencies: ["TelegramBotSDK"]),
]
)
创建 Sources/main.swift
import Foundation
import TelegramBotSDK
let token = readToken(from: "HELLO_BOT_TOKEN")
let bot = TelegramBot(token: token)
while let update = bot.nextUpdateSync() {
if let message = update.message, let from = message.from, let text = message.text {
bot.sendMessageAsync(chatId: .chat(from.id),
text: "Hi \(from.firstName)! You said: \(text).\n")
}
}
fatalError("Server stopped due to error: \(String(describing: bot.lastError))")
不要将您的令牌提交到 git!
readToken
从环境变量或文件中读取令牌。因此,要么创建一个环境变量
export HELLO_BOT_TOKEN='token'
要么将令牌保存到一个文件并将其添加到 .gitignore
echo token > HELLO_BOT_TOKEN
构建您的 Bot
swift build
并运行它
./.build/x86_64-apple-macosx10.10/debug/hello-bot
更多详情请访问 Wiki:New Bot。
这很简单
swift package generate-xcodeproj
打开生成的 hello-bot.xcodeproj
并将活动 scheme 切换到底部的一个
不要忘记将您的令牌添加到 Xcode 中的环境变量(Scheme settings -> Run)。
按下 CMD-R 启动 Bot。
SDK 类型和请求名称与原始 Telegram 的名称非常相似。
在适当的地方添加了 Swift 类型和枚举
if entity.type == .botCommand { ... }
在大多数情况下,也提供了接受字符串的原始方法。如果所需的枚举 case 尚未添加,它们可以用作后备方案
if entity.typeString == "bot_command" { ... }
为了允许访问 SDK 中仍然缺失的字段,每个数据类型都有一个包含原始 json 结构的 json
成员
if entity.json["type"].stringValue == "bot_command" { ... }
所有类型都符合 JsonConvertible
协议,并且可以从 json 创建或序列化回 json。使用 debugDescription
方法获取人类可读的 json,或使用 description
获取可以发送到服务器的 json。
同步和异步
请求名称与 Telegram 的名称非常相似,但有两个版本:synchronous
和 asynchronous
,方法后缀分别为 Sync
和 Async
。
let fromId: ChatId = .chat(12345678) // your user id
bot.sendMessageSync(fromId, "Hello!") // blocks until the message is sent
bot.sendMessageSync(fromId, "Bye.")
这些方法返回服务器响应,如果发生错误,则返回 nil
。如果返回 nil
,可以通过查询 bot.lastError
获取详细信息。
guard let sentMessage = bot.sendMessageSync(fromId, "Hello") else {
fatalError("Unable to send message: \(bot.lastError.unwrapOptional)")
}
不要在实际应用中使用同步方法,因为它们很慢。在调试或在 REPL
中进行实验时使用它们。更多详情:使用 Swift REPL 调用 API 方法
默认情况下,完成处理程序在主线程上调用。
bot.sendMessageAsync(fromId, "Hello!") { result, error in
// message sent!
bot.sendMessageAsync(fromId, "Bye.")
}
// execution continues immediately
在完成处理程序中,result
包含服务器响应,如果发生错误,则为 nil
。可以通过查询 error
获取详细信息。
为了简单起见,可以同步处理消息,但异步响应以避免阻塞下一个消息的处理。因此,一个典型的 Bot 主循环可能如下所示
while let update = bot.nextUpdateSync() {
// process the message and call Async methods
}
请求参数
在大多数情况下,应显式指定参数名称
bot.sendLocationAsync(chat_id: chatId, latitude: 50.4501, longitude: 30.5234)
sendMessageSync/Async
和 respondSync/Async
函数是例外,它们经常被使用。在它们中可以省略参数名称
bot.sendMessageAsync(chatId: chatId, text: "Text")
bot.sendMessageAsync(chatId, "Text") // will also work
也可以传递 Optional
参数
let markup = ForceReply()
bot.sendMessageAsync(chatId: chatId, text: "Force reply",
reply_markup: markup, disable_notification: true)
如果您遇到某种情况,其中参数尚未添加到方法签名中,您可以在参数列表的末尾传递一个包含任何参数的字典
let markup = ForceReply()
bot.sendMessageAsync(chatId: chatId, text: "Force reply",
["reply_markup": markup, "disable_notification": true])
也可以为请求设置默认参数值
bot.defaultParameters["sendMessage"] = ["disable_notification": true]
在字典中,nil
值将被视为 无值
,并且不会发送到 Telegram 服务器。
可用请求
查看 TelegramBot/Requests
子目录以获取可用请求的列表。
如果您发现缺少某个请求,请创建一个 issue,我们会将其添加。在此之前,可以像这样调用任意不受支持的端点
let user: User? = requestSync("sendMessage", ["chat_id": chatId, "text": text])
或异步版本
requestAsync("sendMessage", ["chat_id": chatId, "text": text]) { (result: User?, error: DataTaskError?) -> () in
...
}
这些方法会自动反序列化 json 响应。
显式指定结果类型非常重要。结果类型应符合 JsonConvertible
协议。Bool
和 Int
已经符合 JsonConvertible
。
JSON 类本身也符合 JsonConvertible
,因此如果需要,您可以请求原始 json
let user: JSON? = requestSync("sendMessage", ["chat_id": chatId, "text": text])
Router 将文本命令和其他事件映射到其处理函数,并帮助解析命令参数。
let router = Router(bot)
router["command1"] = handler1
router["command2"] = handler2
router[.event] = handler3
...
router.process(update: update)
可以在单个规则中指定多个命令
router["Full Command Name", "command"] = handler
也支持多词命令
router["list add"] = onListAdd
router["list remove"] = onListRemove
路由器可以链式连接。这有助于创建上下文相关的路由器,并回退到全局路由器。
router1.unmatched = router2.handler
处理程序
处理程序接受 Context
参数并返回 Bool
。
true
,则命令匹配停止。false
,则将匹配其他路径。因此,在处理程序中检查先决条件,如果不满足,则返回 false
router["reboot"] = { context in
guard let fromId = context.fromId where isAdmin(fromId) else { return false }
context.respondAsync("I will now reboot the PC.") { _ in
reboot()
}
return true
}
处理函数可以标记为 throws
并抛出异常。路由器不会处理它们,只会将异常传递给调用者。
Context
是请求上下文,它包含
bot
- 对 Bot 的引用。update
- 当前 Update
结构。message
- 用于访问 update.message
的便捷方法。如果 update.message
为 nil,则回退到 update.edited_message
,然后回退到 update.callback_query?.message
。command
- 不带斜杠的命令。slash
- 如果命令以斜杠开头,则为 true。如果您想跳过群聊中不以斜杠开头的命令,这将很有用。args
- 命令参数扫描器。properties
- 上下文相关的属性。将它们传递给 process
方法var properties = [String: AnyObject]()
properties["myField"] = myValue
try router.process(update: update, properties: properties)
并在处理程序中使用它们
func myHandler(context: Context) -> Bool {
let myValue = context.properties["myField"] as? MyValueType
// ...
}
或者创建一个 Context
类别以便更轻松地访问您的属性,例如
extension Context {
var session: Session { return properties["session"] as! Session }
}
Context
还包含一些辅助方法和变量
privateChat
- 如果这是与 Bot 的私聊,则为 true;对于所有群聊类型,则为 false。chatId
- message?.chat.id 的快捷方式。如果 message 为 nil,则尝试从其他 Update
字段检索 chatId。fromId
- message?.from?.id 的快捷方式。如果 message 为 nil,则尝试从其他 Update
字段检索 fromId。respondAsync
, respondSync
- 作用类似于 sendMessage(chatId, ...)
respondPrivatelyAsync/Sync("text", groupText: "text")
- 私下回复用户,如果是群聊,则向群组发送一条简短消息。例如context.respondPrivatelyAsync("Command list: ...",
groupText: "Please find a list of commands in a private message.")
reportErrorAsync/Sync(text: "用户文本", errorDescription: "管理员的详细错误描述")
- 向用户发送一条简短消息,并将详细错误描述打印到控制台。可以省略 text
参数,在这种情况下,用户将收到一条通用错误消息。文本命令
路由器可以匹配文本命令
router["start"] = onStart
在私聊和群聊中,命令名称的处理方式有所不同
start
匹配 /start
以及 start
。/start
。这可以被覆盖。以下行即使在私聊中也需要斜杠
router["start", .slashRequired] = onStart
默认情况下,路由器不区分大小写。要使其区分大小写,请传递 .caseSensitive
选项
router["command", .caseSensitive] = handler
可以传递多个选项
router["command", [.slashRequired, .caseSensitive]] = handler
在 Telegram 群聊中,用户可以在命令后附加 Bot 名称,例如:/greet@hello_bot
。路由器会自动处理从命令名称中删除 @hello_bot
部分。
带参数的文本命令
可以使用 scanWord
方法捕获单词,然后进行处理。
router["two_words"] = { context in
let word1 = context.args.scanWord()
let word2 = context.args.scanWord()
}
可以使用 scanWords
捕获单词数组
router["words"] = { context in
let words = context.args.scanWords() // returns [String] array
}
可以使用 scanInt
、scanInt64
和 scanDouble
捕获数字。restOfString
将剩余部分捕获为单个字符串。
router["command"] = { context in
let value1 = context.args.scanInt()
let value2 = context.args.scanDouble()
let text = context.args.scanRestOfString()
}
也可以直接访问用于扫描参数的 NSScanner
:context.args.scanner
。
处理程序应读取所有参数,否则用户将看到警告:您的部分输入被忽略:文本
因此,例如,如果有一个命令 swap
期望两个参数,但用户输入:/swap aaa bbb ccc
,他将看到
bbb aaa
Part of your input was ignored: ccc
避免警告的一种可能方法是通过调用 context.args.skipRestOfString()
跳过不需要的参数。
此外,警告可以被覆盖
router.partialMatch = { context in
context.respondAsync("Part of your input was ignored: \(context.args.scanRestOfString())")
return true
}
其他事件
路由器也可以处理其他事件类型。例如,当新用户加入聊天时,将触发 .new_chat_member
路径
router[.new_chat_member] = { context in
guard let users = context.message?.newChatMembers else { return false }
for user in users {
guard user.id != bot.user.id else { return false }
context.respondAsync("Welcome, \(user.firstName)!")
}
return true
}
查看 TelegramBot/Router/ContentType.swift
文件以获取路由器支持的完整事件列表。
处理未匹配的路径
如果没有匹配的路径,路由器将调用其 unmatched
处理程序,默认情况下,该处理程序将打印“Command not found”。这可以通过设置显式处理程序来覆盖
router.unmatched = { context in
// Do something else with context.args
return true
}
在调试器中,您可能希望转储 json 结构的内容,但 debugDescription
会丢失其格式。
prettyPrint
辅助函数允许使用缩进打印任何 JsonConvertible
let user: User
user.prettyPrint()
bot.sendMessageSync(fromId, "Hello!")?.prettyPrint()
有 3 个示例项目可用
Examples/hello-bot
- 一个简单的 Bot,它响应 /greet
命令并问候加入聊天的用户。
Examples/word-reverse-bot
- 演示如何处理启动和停止请求、保持会话状态以及解析命令参数。在私聊和群聊中的行为有所不同。使用路由器和控制器。
Examples/shopster-bot
- 使用 sqlite3 数据库维护购物清单。允许在群聊中创建共享购物清单。GRDB 库用于处理数据库。
有关编译和运行 Bot 的详细信息,请访问 Wiki:构建和运行示例项目。
其他文档可在Telegram Bot Swift SDK Wiki上找到。
查看 Examples/
以获取示例 Bot 项目。
此 SDK 仍在开发中,预计 API 会经常更改。
请在 Github 上提交 issue。
如果您缺少特定功能,请创建一个 issue,我们将优先处理。也欢迎 Pull Request。
在我们的 Telegram 聊天中与其他开发者交流:swiftsdkchat。
祝您编程愉快!
Apache License Version 2.0 with Runtime Library Exception。有关更多信息,请参阅 LICENSE.txt。