インタラクションへの応答¶
スラッシュコマンド、コンテキストメニューのコマンド、ボタン、セレクトメニュー、モーダルの送信は、どれもインタラクションです。Discord はアプリがそれに応答するのを待ちます。コマンドのハンドラーは Context を、コンポーネントのハンドラーは ComponentContext を、モーダルのハンドラーは ModalContext を受け取り、いずれも以下の応答用メソッドを共通で持ちます。
コンテキストが持つ情報¶
| 属性 | 値 |
|---|---|
ctx.user |
操作した User(サーバー内でも DM でも) |
ctx.member |
サーバー内ではその人の Member、それ以外では None |
ctx.guild_id、ctx.channel_id |
操作が行われた場所。DM では guild_id は None です |
ctx.channel |
インタラクションに含まれる PartialChannel |
ctx.locale |
ユーザーのクライアントの言語 |
ctx.interaction |
インタラクションのモデル全体。app_permissions や guild_locale など、その他の情報に使います |
ctx.bot |
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= を渡すと、送ったメッセージがその秒数後に削除されます。削除は Bot がバックグラウンドで行い、Bot の終了時に取り消されるので、Bot の停止後に削除予定だったメッセージは残ります。インタラクションのメッセージはそのトークンを使って削除されます。この方法はエフェメラルなメッセージにも使えますが、インタラクションから 15 分で使えなくなるため、それより後に予定された削除は失敗し、警告がログに出力されます。すでに誰かが削除したメッセージは、何も出力せずにスキップされます。
エフェメラルな応答¶
呼び出しに ephemeral=True を渡すと、そのメッセージはそのユーザーにだけ表示されます。コマンド、コンポーネントのルート、モーダルのルート、ルーターに ephemeral=True を設定すると、それらの応答の既定値になり、個々の呼び出しで ephemeral=False を渡すこともできます。既存のメッセージをエフェメラルと公開の間で切り替えることはできません。そうした編集を求めると、警告が 1 回ログに出力され、メッセージはそのまま残ります。
遅延応答¶
最初の応答は 3 秒以内に Discord に届く必要があります。await ctx.defer() を呼ぶと、応答の代わりに「考え中…」が表示され、ハンドラーはその後 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 は Bot、ルーター、コマンド、ルートに設定でき、None を指定するとそこで無効になります。タイマーはゲートウェイがインタラクションを届けた時点から数えるので、イベントループがブロックされるとハンドラーの開始は遅れますが、期限は延びません。ハンドラーの開始が 1 秒以上遅れると、bostie は警告をログに出力します。
モーダルとアクティビティ¶
await ctx.modal(ui.modal(...)) はポップアップのフォームで応答します。詳しくはモーダルを参照してください。await ctx.launch_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 を送出します。
応答せずに戻ったハンドラーでは、ユーザーには「アプリケーションが応答しませんでした」と表示され、bostie はそのハンドラーの名前を示した警告をログに出力します。遅延応答をしたまま応答しなかったハンドラーでは、トークンの期限が切れるまで「考え中…」が表示されたままになり、この場合も警告が出ます。ハンドラーが応答前に例外を送出すると、エラーフックが応答しない限り、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 を指定すると、返信先のメッセージの送信者に通知が届かなくなります。