Modelle und Enums¶
Alles, was bostie von Discord dekodiert, ist ein Modell: Message, Guild, Member, Interaction und rund zweihundert weitere, alle aus bostie importierbar. Modelle sind eingefrorene msgspec-Structs, die direkt aus den JSON-Bytes dekodiert werden.
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)
Typen folgen Discords Dokumentation¶
Jedes Feld ist so typisiert, wie Discords Dokumentation es beschreibt:
- Ein Feld, das Discord immer sendet, hat keinen Standardwert und fehlt nie. Sagt die Dokumentation, dass es
nullsein kann, ist esT | None. - Ein Feld, das die Dokumentation als optional kennzeichnet, hat einen Standardwert:
()für eine Liste, eine leere Flag-Menge für Flags,Falsefür einen Boolean, dessen Fehlen falsch bedeutet, und sonstNone. - IDs sind
int, Zeitstempel sind zeitzonenbewusstedatetime-Objekte in UTC, und Farben sindint. - Wo Discord ein partielles Objekt sendet, hat das Feld ein partielles Modell:
PartialUser,PartialMember,PartialChannel,PartialMessage,PartialEmoji,PartialApplication,PartialIntegration,PartialEntitlement,UnavailableGuild. Ein partielles Modell hat nur die Felder, die der partielle Payload hat; ein fehlendes Feld ist also ein Typfehler und keinNonezur Laufzeit.
An drei Stellen, an denen ein Payload zwei Formen hat, gibt es zwei Klassen:
- Eine Nachricht, die ein Webhook gesendet hat, Antworten auf Interaktionen eingeschlossen, ist eine
WebhookMessage, derenauthoreinPartialUserist. Jede andere Nachricht ist eineMessagemit einem vollständigenUserals Autor. Felder, die beides akzeptieren, sind alsMessage | WebhookMessagetypisiert. - Ein Mitglied, das aus einer Option, einem Auswahlmenü oder einem Befehlsziel aufgelöst wird, ist ein
ResolvedMember, das seinenusermitbringt und wederdeafnochmutehat. EinMemberaus dem Gateway oder aus REST hat beides. - Ein Benutzer in
message.mentionsist einMentionedUser: einUsermit demmember, das Discord hinzufügt, wenn die Nachricht auf einem Server gesendet wurde.
Interaktionen sind je nach Typ CommandInteraction, ComponentInteraction, ModalSubmitInteraction oder AutocompleteInteraction, jeweils mit typisiertem data.
Robustheit gegenüber Änderungen¶
Discord fügt ohne Ankündigung Felder, Enum-Werte und Komponententypen hinzu, und ein Bot darf dann nicht kaputtgehen:
- Felder, die bostie nicht kennt, werden beim Dekodieren ignoriert.
- Enums bilden einen unbekannten Wert auf ein stabiles Mitglied namens
UNKNOWN_<value>ab, das gleich dem Wert ist:MessageType(99).name == "UNKNOWN_99". - Flags behalten Bits, die bostie nicht kennt, bei jedem Operator,
~eingeschlossen. - Ein unbekannter Komponententyp wird zu
UnknownComponentdekodiert, das sein rohes JSON behält und unverändert zurückgesendet wird, wenn du es incomponentsübergibst; ein unbekannter Interaktionstyp wird zuUnknownInteractiondekodiert, dasInteractionCreate-Listener erreicht, aber keinen Befehl und keine Route. - Eine verschachtelte Nachricht, etwa
message.referenced_messageoder die Nachricht einer Komponenten-Interaktion, wird erst dekodiert, wenn du die Eigenschaft liest; eine defekte schlägt also nur dort fehl, wo sie gelesen wird.
Ein Event-Payload, der überhaupt nicht zu seiner Klasse passt, etwa weil ein erforderliches Feld fehlt, wird einmal geloggt und nur an RawEvent-Listener zugestellt, wie Events erklärt.
Rohe Payloads¶
Ein Modell, das aus einem vollständigen Payload dekodiert wurde, behält ihn: model.raw_json enthält die Bytes, und model.raw dekodiert sie in ein neues dict. Verschachtelte Modelle, etwa der Autor einer Nachricht, haben raw_json = None. Jedes Event hat ebenfalls raw und raw_json. Damit erreichst du jedes Feld, das Discord sendet, bevor bostie es modelliert.
Die repr eines Modells lässt raw_json und Geheimnisse weg: token, access_token, refresh_token und id_token.
Modelle bauen und kopieren¶
Modelle sind unveränderlich und werden mit Schlüsselwortargumenten gebaut. msgspec.structs.replace(model, field=value) gibt eine Kopie mit einigen geänderten Feldern zurück. Die Modelle, die du an Discord sendest, etwa Embed, AllowedMentions, PollCreateRequest, MessageReference, ForumTag, RecurrenceRule und AutoModerationAction, lassen die Felder weg, die du nicht gesetzt hast, sodass ein dekodiertes Modell so zurückgesendet wird, wie es kam.
Helfer an Modellen¶
| Helfer | Wert |
|---|---|
user.mention, member.mention, role.mention, channel.mention |
der Erwähnungstext, etwa <@123> |
user.display_name, member.display_name |
der globale Name oder Benutzername; bei einem Mitglied zuerst der Nickname |
message.jump_url |
der Link zur Nachricht |
message.author_member |
der Autor als ResolvedMember, wenn die Nachricht das Mitglied mitbringt |
role.permissions, overwrite.allow, overwrite.deny |
Permissions-Flags |
member.permissions |
die Berechtigungen eines Mitglieds im Kanal der Interaktion, wenn Discord sie gesendet hat |
webhook.url |
die URL des Webhooks, wenn er ein Token hat |
Die Anleitung zur Formatierung behandelt Erwähnungen, Zeitstempel und CDN-URLs, die aus IDs gebaut werden.
Snowflakes¶
Discord-IDs kodieren, wann das Objekt erstellt wurde. Jedes Modell mit einer Snowflake-ID hat created_at, diesen Zeitpunkt als datetime in UTC: message.created_at, member.created_at (der des Kontos), emoji.created_at (None bei einem Unicode-Emoji). bostie.snowflake_time(id) tut dasselbe für eine bloße ID, und bostie.time_snowflake(when) liefert die niedrigste ID, die zu einem Zeitpunkt erstellt wurde, oder mit high=True die höchste, zum Blättern nach Datum. Ein naives datetime wird als Ortszeit gelesen.
Enums¶
bostie exportiert Discords Enums und Flags: ChannelType, MessageType, Permissions, Intents, MessageFlags, Locale, ButtonStyle und die übrigen aus der Enum-Referenz. Flags kombinierst du mit | und prüfst sie mit in: Permissions.SEND_MESSAGES in member_permissions. message.type.deletable sagt, ob Discord eine Nachricht dieses Typs löschen lässt: Hinweise zu Gruppen-DMs und Anrufen, Änderungen von Kanalname und -symbol sowie die Startnachricht eines Threads lassen sich nicht löschen. JSONErrorCode benennt Discords Fehlercodes, so wie HTTPException.code sie meldet.