运行机器人¶

bot.run()¶
对大多数机器人来说,bot.run() 就是整个程序。它会在 stderr 上配置 INFO 级别的日志(除非根日志记录器已有处理器),启动一个事件循环,并运行机器人直到它停止。Ctrl+C,以及在主线程上收到的 SIGTERM,都会干净地关闭它。
令牌来自 BOSTIE_TOKEN 环境变量,或者来自 bot.run(token)。开头的 Bot 会被去掉,缺少令牌则引发 ValueError。run 还接受 loop_factory,一个创建事件循环的函数,会被传给 asyncio.run。
生命周期¶
bot.start() 完成 run() 背后的工作:
- 它发出
Starting事件,并依次 await 每个监听器。此时 REST 可用,网关不可用;抛出异常的监听器会中止启动。 - 它会同步命令,除非设置了
auto_sync=False。 - 它连接各个网关分片。当每个分片都已就绪、并且分片上的服务器都已到达时,它会发出一次
Started,启动后台任务,并让wait_until_ready()返回。 - 它持续处理事件,直到调用
close(),或者直到 Discord 永久关闭网关,此时会抛出异常,例如InvalidToken或PrivilegedIntentsRequired。
import logging
import bostie
from bostie.events import Started, Starting, Stopped, Stopping
log = logging.getLogger("my-bot")
bot = bostie.Bot()
@bot.listen
async def starting(event: Starting) -> None:
application = await bot.rest.application.fetch()
log.info("starting %s", application.name)
@bot.listen
async def started(event: Started) -> None:
log.info("ready in %d guilds, latency %s s", len(bot.cache.guilds()), bot.latency)
@bot.listen
async def stopping(event: Stopping) -> None:
log.info("saying goodbye while the gateway is still open")
@bot.listen
async def stopped(event: Stopped) -> None:
log.info("the gateway and REST are closed")
bot.run()
await bot.close() 按以下顺序关闭:
- 如果已经发出过
Started,它会发出Stopping,并在网关仍然打开时给其监听器最多 5 秒时间。 - 它离开每个语音频道,然后关闭每个分片,以关闭码 1000 结束其会话,或者保存会话以便之后恢复。
- 它取消后台任务。仍在运行的处理函数会有
shutdown_grace秒(默认为 0)的时间,之后同样会被取消。 - 它关闭 HTTP 客户端,并发出
Stopped,await 其监听器。此后bot.http、bot.rest和bot.gateway都不再可用。
close() 可以从任何地方调用,可以调用多次,也可以在 start() 运行时调用;之后机器人还可以再次启动。async with bot: 会在代码块结束时关闭它。
在其他程序中运行¶
如果你的程序已经运行着一个事件循环,就把机器人作为一个任务启动:
import asyncio
import logging
import bostie
from bostie import StateError
log = logging.getLogger("my-bot")
bot = bostie.Bot(shutdown_grace=10.0)
async def main() -> None:
logging.basicConfig(level=logging.INFO)
running = asyncio.create_task(bot.start())
try:
await bot.wait_until_ready()
except StateError:
await running
raise
log.info("%s is in %d guilds", bot.user, len(bot.cache.guilds()))
try:
await running
finally:
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
await bot.wait_until_ready() 会在发出 Started 后返回(若已发出则立即返回),如果机器人先停止了,则引发 StateError;此时 await start() 任务会抛出具体原因。bot.is_ready 无需等待即可得知同样的信息。Web 应用示例在 Web 服务器的 lifespan 中运行机器人,并从 Web 路由发送消息。
只通过 HTTP 回应交互的机器人不需要网关:参见 HTTP 交互。await bot.open() 以这种方式启动机器人并返回:它打开 HTTP 客户端,获取应用信息和机器人的用户,发出 Starting,同步命令,启动后台任务,并返回应用。ASGIApp 会调用它,close() 则结束它。只调用 REST API 的脚本使用 RESTClient。
运行期间¶
| 属性 | 值 |
|---|---|
bot.user |
机器人的 CurrentUser,连接后可用 |
bot.application_id |
应用的 ID,启动后可用 |
bot.latency |
各分片心跳往返时间的平均值(秒),或 None |
bot.is_running |
start() 或 open() 是否正在运行机器人,直到 close() 结束它 |
bot.is_ready |
自上次 start() 以来是否已发出 Started |
bot.rest、bot.http |
REST API,启动后可用 |
bot.gateway |
网关分片,连接后可用 |
bot.cache |
缓存 |
bot.voice |
语音连接 |
bot.auto_defer |
默认的自动延迟回应时间;随时可以修改 |
在 start() 之前访问 bot.rest、bot.http 和 bot.gateway 会引发 StateError。
日志¶
bostie 使用标准的 logging 模块,以下列名称记录日志:
| 日志记录器 | 报告内容 |
|---|---|
bostie.bot |
失败或未回应的处理函数、监听器需要的 Intent、未处理的交互 |
bostie.gateway |
连接、断开、恢复、背压、无效请求 |
bostie.rest |
来自 Rust HTTP 客户端的速率限制和重试 |
bostie.sync |
每次命令同步改变了什么 |
bostie.context |
无法生效的回应,例如编辑时设置的仅自己可见标志 |
bostie.events |
与其事件不匹配的载荷,每个事件名记录一次 |
bostie.voice |
语音连接、DAVE、sink、接收器和播放器 |
bostie.bridge |
Python 与 Rust 核心之间的连接 |
Rust 核心也通过这些日志记录器输出日志,并在机器人启动时读取它们的级别:在 run() 之前设置 logging.getLogger("bostie.gateway").setLevel(logging.DEBUG),就能看到网关的详细信息。
让 bostie 指向别处¶
Bot(api_url=...) 会把 REST 请求发送到另一个基础 URL,例如多个进程共享的速率限制代理,或测试中的模拟 Discord。如果省略,bostie 会在机器人启动时读取 BOSTIE_API_URL,若它也未设置,则使用 https://discord.com/api/v10。在速率限制代理后面时,请传入 http_settings=HTTPSettings(rate_limiter=False),让 bostie 把等待交给代理处理。其他 HTTP 设置请参见 REST 指南。
在一个进程中运行多个机器人¶
每个 Bot 都有自己与 Rust 核心的连接、自己的缓存和自己的 HTTP 客户端,因此一个进程可以在同一个事件循环中运行多个机器人,每个都可以单独启动和关闭。
保存状态¶
机器人就是一个普通对象:继承 Bot 来给它添加属性,就像生命周期示例那样,在 Starting 监听器中打开数据库连接,在 Stopped 监听器中关闭它。所有 Bot 选项都列在 Bot 选项参考中。