モデルと列挙型¶
bostie が Discord からデコードするものはすべてモデルです。Message、Guild、Member、Interaction をはじめ約 200 種類あり、すべて bostie からインポートできます。モデルは凍結された msgspec の構造体で、JSON のバイト列から直接デコードされます。
import msgspec
import bostie
from bostie import (
Context,
Embed,
EmbedField,
Message,
MessageType,
WebhookMessage,
fmt,
)
bot = bostie.Bot()
@bot.message_command("Inspect")
async def inspect(ctx: Context, message: Message | WebhookMessage) -> None:
if isinstance(message, WebhookMessage):
sender = f"the webhook {message.author.username}"
else:
sender = message.author.mention
kind = message.type.name
if message.type.name.startswith("UNKNOWN_"):
kind = f"a type bostie does not know yet ({message.type.value})"
embed = Embed(
title="Message",
url=message.jump_url,
fields=(
EmbedField(name="Sent by", value=sender),
EmbedField(name="Type", value=kind),
EmbedField(name="Deletable", value=str(message.type.deletable)),
EmbedField(name="Created", value=fmt.timestamp(message.created_at)),
EmbedField(name="Raw keys", value=", ".join(sorted(message.raw or {}))),
),
)
if message.type is MessageType.REPLY:
embed = msgspec.structs.replace(embed, description="A reply.")
await ctx.respond(embeds=[embed], ephemeral=True)
型は Discord のドキュメントに従う¶
各フィールドの型は、Discord のドキュメントの記述どおりです。
- Discord が必ず送るフィールドには既定値がなく、欠けることもありません。ドキュメントで
nullになりうるとされているものはT | Noneです。 - ドキュメントで省略可能とされているフィールドには既定値があります。リストなら
()、フラグなら空のフラグセット、省略が false を意味する真偽値ならFalse、それ以外はNoneです。 - ID は
int、タイムスタンプは UTC のタイムゾーン付きdatetimeオブジェクト、色はintです。 - Discord が部分オブジェクトを送る箇所では、フィールドの型は部分モデルになります。
PartialUser、PartialMember、PartialChannel、PartialMessage、PartialEmoji、PartialApplication、PartialIntegration、PartialEntitlement、UnavailableGuildです。部分モデルは部分ペイロードにあるフィールドしか持たないため、存在しないフィールドへのアクセスは実行時のNoneではなく型エラーになります。
1 つのペイロードが 2 つの形を取る次の 3 か所では、クラスが 2 つに分かれています。
- ウェブフックが送ったメッセージ(インタラクションへの応答を含む)は
WebhookMessageで、そのauthorはPartialUserです。それ以外のメッセージは、完全なUserを author に持つMessageです。両方を受け付けるフィールドの型はMessage | WebhookMessageです。 - オプション、セレクト、コマンドの対象から解決されたメンバーは
ResolvedMemberで、userを持ち、deafとmuteを持ちません。ゲートウェイや REST から得たMemberは両方を持ちます。 message.mentions内のユーザーはMentionedUserです。これは、メッセージがサーバーで送信された場合に Discord が付け加えるmemberを持つUserです。
インタラクションは種類に応じて CommandInteraction、ComponentInteraction、ModalSubmitInteraction、AutocompleteInteraction のいずれかになり、それぞれ型付きの data を持ちます。
変更への耐性¶
Discord は予告なくフィールド、列挙値、コンポーネントの種類を追加します。そのときも Bot が壊れてはいけません。
- bostie が知らないフィールドは、デコード時に無視されます。
- 列挙型は未知の値を
UNKNOWN_<value>という名前の安定したメンバーに対応付けます。このメンバーは値と等しいものとして比較されます(MessageType(99).name == "UNKNOWN_99")。 - フラグは、bostie が知らないビットを
~を含むすべての演算子を通して保持します。 - 未知のコンポーネントの種類は
UnknownComponentにデコードされます。これは生の JSON を保持し、componentsに渡せば変更されずにそのまま送り返されます。未知のインタラクションの種類はUnknownInteractionにデコードされ、InteractionCreateリスナーには届きますが、コマンドやルートには届きません。 message.referenced_messageやコンポーネントインタラクションのメッセージのような入れ子のメッセージは、プロパティを読んだときに初めてデコードされます。そのため、壊れたメッセージが失敗するのは、それを読んだ箇所だけです。
必須フィールドが欠けているなど、クラスにまったく合わないイベントのペイロードは、一度だけログに記録され、RawEvent リスナーにのみ配信されます。詳しくはイベントを参照してください。
生のペイロード¶
ペイロード全体からデコードされたモデルは、そのペイロードを保持します。model.raw_json がバイト列を持ち、model.raw はそれを新しい dict にデコードします。メッセージの author のような入れ子のモデルでは raw_json = None です。すべてのイベントも raw と raw_json を持ちます。これらを使えば、bostie がモデル化する前に Discord が送ってくるどのフィールドにもアクセスできます。
モデルの repr には、raw_json と秘密情報(token、access_token、refresh_token、id_token)は含まれません。
モデルの作成とコピー¶
モデルはイミュータブルで、キーワード引数で作成します。msgspec.structs.replace(model, field=value) は、一部のフィールドを変更したコピーを返します。Embed、AllowedMentions、PollCreateRequest、MessageReference、ForumTag、RecurrenceRule、AutoModerationAction など、Discord に送るモデルは設定していないフィールドを省いて送信するので、デコードしたモデルは受け取ったときのまま送り返されます。
モデルのヘルパー¶
| ヘルパー | 値 |
|---|---|
user.mention、member.mention、role.mention、channel.mention |
<@123> のようなメンションのテキスト |
user.display_name、member.display_name |
グローバル名またはユーザー名。メンバーの場合はニックネームが優先されます |
message.jump_url |
メッセージへのリンク |
message.author_member |
メッセージがメンバーを含む場合、ResolvedMember としての author |
role.permissions、overwrite.allow、overwrite.deny |
Permissions フラグ |
member.permissions |
Discord が送ってきた場合、インタラクションのチャンネルにおけるメンバーの権限 |
webhook.url |
ウェブフックがトークンを持つ場合、その URL |
メンション、タイムスタンプ、ID から作る CDN の URL については書式のガイドで扱います。
Snowflake¶
Discord の ID には、オブジェクトが作成された日時が埋め込まれています。Snowflake の ID を持つすべてのモデルには created_at があり、その日時を UTC の datetime で表します。message.created_at、member.created_at(アカウントの作成日時)、emoji.created_at(Unicode 絵文字では None)などです。bostie.snowflake_time(id) は単独の ID に対して同じことを行います。bostie.time_snowflake(when) は、ある時点で作成されうる最小の ID を返し、high=True を指定すると最大の ID を返します。日付によるページングに使えます。タイムゾーンを持たない datetime はローカル時刻として扱われます。
列挙型¶
bostie は Discord の列挙型とフラグをエクスポートしています。ChannelType、MessageType、Permissions、Intents、MessageFlags、Locale、ButtonStyle、そのほか列挙型のリファレンスに載っているものです。フラグは | で組み合わせ、in で判定します(Permissions.SEND_MESSAGES in member_permissions)。message.type.deletable は、その種類のメッセージを Discord が削除させるかどうかを示します。グループ DM と通話の通知、チャンネル名とアイコンの変更、スレッドの最初のメッセージは削除できません。JSONErrorCode は、HTTPException.code が報告する Discord のエラーコードに名前を付けたものです。