回应交互¶
每条斜杠命令、上下文菜单命令、按钮、选择菜单和模态框提交都是一次交互,Discord 会等待应用对它作出回应。命令处理函数得到一个 Context,组件处理函数得到 ComponentContext,模态框处理函数得到 ModalContext;它们共享下面介绍的回应方法。
上下文里有什么¶
| 属性 | 值 |
|---|---|
ctx.user |
触发交互的 User,无论在服务器还是私信中 |
ctx.member |
在服务器中是其 Member,否则为 None |
ctx.guild_id、ctx.channel_id |
发生的位置;在私信中 guild_id 为 None |
ctx.channel |
交互携带的 PartialChannel |
ctx.locale |
用户客户端的语言 |
ctx.interaction |
完整的交互模型,用于其他所有信息,例如 app_permissions 或 guild_locale |
ctx.bot |
机器人 |
ctx.voice |
该服务器和该用户的语音 |
ctx.command_name |
在 Context 上,是带子命令的完整命令名,例如 "roles color set" |
回应、编辑、后续消息¶
import asyncio
import bostie
from bostie import Context
bot = bostie.Bot()
@bot.command
async def countdown(ctx: Context, seconds: int = 3) -> None:
"""Count down in one message."""
await ctx.respond(f"{seconds}…")
for left in range(seconds - 1, 0, -1):
await asyncio.sleep(1)
await ctx.edit(content=f"{left}…")
await asyncio.sleep(1)
await ctx.edit(content="Go!")
note = await ctx.followup("Only you see this one.", ephemeral=True)
await asyncio.sleep(5)
await ctx.delete(note.id)
ctx.respond(...) 发送第一次回应。再次调用时,它会改为发送一条后续消息,因此处理函数可以按需多次调用它。它返回 Discord 创建的消息,一个 WebhookMessage。
ctx.edit(**parts)编辑第一次回应,ctx.edit(message_id, **parts)编辑某条后续消息。ctx.followup(...)总是发送一条新消息,唯一的例外是紧跟在defer()之后,此时它会填充被延迟的那次回应。ctx.fetch()和ctx.fetch(message_id)返回 Discord 上当前的回应或后续消息。ctx.delete()和ctx.delete(message_id)删除它们。
respond 和 followup 接受 content 以及消息内容中列出的各部分:embeds、components、files、allowed_mentions、flags、tts、poll,再加上 ephemeral。编辑以关键字参数接受同样的部分:省略的部分保持不变,传入 None 则清除该部分。
自动删除的消息¶
import bostie
from bostie import Context
bot = bostie.Bot()
@bot.command
async def nudge(ctx: Context) -> None:
"""Remind the channel to stay on topic."""
await ctx.respond("Please keep this channel on topic.", delete_after=30)
respond、followup 和 bot.rest.messages.create 上的 delete_after= 会在指定秒数之后删除所发送的消息。机器人在后台执行删除,并在关闭时取消它,因此计划在机器人停止之后才删除的消息会保留下来。交互的消息通过其令牌删除,这对仅自己可见的消息同样有效,但令牌在交互发生 15 分钟后失效;计划在那之后的删除会失败,并记录一条警告。已经被别人删除的消息会被悄悄跳过。
仅自己可见的回应¶
在某次调用上传入 ephemeral=True,会让那条消息仅用户自己可见。在命令、组件路由、模态框路由或路由器上设置 ephemeral=True,会让它成为这些对象所有回应的默认行为,而某次调用仍然可以传入 ephemeral=False。已有的消息不能在仅自己可见和公开之间切换:要求这样做的编辑会记录一次警告,并保持消息原样。
延迟回应¶
第一次回应必须在 3 秒内送达 Discord。await ctx.defer() 会在原处显示“thinking…”,之后处理函数有 15 分钟时间调用 respond;defer(ephemeral=True) 会让随后的回应仅自己可见。
import asyncio
import bostie
from bostie import Context
bot = bostie.Bot(auto_defer=2.0)
@bot.command
async def report(ctx: Context) -> None:
"""Build a slow report."""
await ctx.defer(ephemeral=True)
await asyncio.sleep(10)
await ctx.respond("The report is ready.")
@bot.command(auto_defer=None)
async def instant(ctx: Context) -> None:
"""Never deferred automatically."""
await ctx.respond("Done.")
bostie 也会自动延迟回应:交互到达 auto_defer 秒后仍未回应的,会被延迟回应,默认是 2.5 秒。从消息上打开的组件或模态框会用 defer_update 延迟,它会保持消息原样,直到 update 编辑它。auto_defer 可以设置在机器人、路由器、命令或路由上,设为 None 则在该处关闭。计时从网关送达交互时开始,因此被阻塞的事件循环会推迟处理函数,却不会推迟截止时间;当处理函数晚于一秒以上才开始时,bostie 会记录一条警告。
模态框与 Activity¶
await ctx.modal(ui.modal(...)) 以一个弹出式表单作为回应;参见模态框。await ctx.launch_activity() 打开应用的 Activity,并返回其实例 ID。两者都必须是第一次回应,并且在模态框之后,任何东西都无法再回应这次交互:模态框的提交是一次新的交互。
规则,以及违反规则时的错误¶
每次调用都会先检查交互的状态:
| 调用 | 尚未回应 | defer() 之后 |
defer_update() 之后 |
已回应之后 |
|---|---|---|---|---|
respond |
回应 | 填充回应 | 发送后续消息 | 发送后续消息 |
defer |
延迟回应 | 无操作 | 无操作 | InteractionResponded |
edit、delete |
InteractionNotAnswered |
编辑、删除回应 | 编辑、删除消息 | 编辑、删除回应 |
followup |
InteractionNotAnswered |
填充回应 | 发送后续消息 | 发送后续消息 |
update |
编辑消息 | InteractionResponded |
编辑消息 | InteractionResponded |
modal、launch_activity |
回应 | InteractionResponded |
InteractionResponded |
InteractionResponded |
update 和 defer_update 只存在于组件和模态框的上下文中。在模态框之后进行的任何调用都会引发 InteractionResponded。
交互令牌的有效期是 15 分钟。在那之后,或者在 Discord 表示令牌无效之后,任何调用都会引发 InteractionExpired,不会发送任何内容。
处理函数没有回应就返回时,用户会看到“The application did not respond”,bostie 会记录一条指明该处理函数的警告。处理函数延迟了回应却始终没有回应时,“thinking…”会一直显示到令牌过期,同样会有一条警告。处理函数在回应之前抛出异常时,如果没有错误钩子作出回应,bostie 会回应“Something went wrong.”;这条回应仅自己可见,除非处理函数此前已延迟回应。
对于只安装到用户账号的应用,Discord 允许每次交互发送 5 条后续消息,bostie 会在第 6 条时发出警告。
允许的提及¶
默认情况下,回应可以通知用户和身份组,但不能通知 @everyone 或 @here:Bot(allowed_mentions=...) 的默认值是 AllowedMentions(parse=("users", "roles"))。随消息传入的 allowed_mentions 只会改变它所设置的那几类提及,因此 AllowedMentions(users=(123,)) 只限制对用户的通知,身份组的设置保持不变。AllowedMentions(parse=()) 让所有提及都不发出通知,replied_user=False 则让回复不通知被回复消息的作者。