모델과 열거형¶
bostie가 Discord에서 디코딩하는 모든 것은 모델입니다. Message, Guild, Member, Interaction을 비롯해 200개 남짓한 모델이 있으며, 모두 bostie에서 가져올 수 있습니다. 모델은 고정(frozen)된 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이 나오는 대신 타입 에러가 됩니다.
하나의 페이로드가 두 가지 모양을 갖는 세 곳에는 클래스가 두 개씩 있습니다.
- 웹후크가 보낸 메시지는 인터랙션 응답을 포함해
WebhookMessage이며, 그author는PartialUser입니다. 그 밖의 메시지는 작성자가 완전한User인Message입니다. 둘 다 받는 필드의 타입은Message | WebhookMessage입니다. - 옵션, 선택 메뉴, 명령어 대상에서 해석된 멤버는
ResolvedMember이며,user를 담고 있고deaf와mute는 없습니다. 게이트웨이나 REST에서 받은Member에는 둘 다 있습니다. message.mentions에 있는 사용자는MentionedUser입니다. 메시지가 서버에서 보내졌을 때 Discord가 덧붙이는member를 가진User입니다.
인터랙션은 타입에 따라 CommandInteraction, ComponentInteraction, ModalSubmitInteraction, AutocompleteInteraction 중 하나이며, 각각 타입이 지정된 data를 가집니다.
변경 내성¶
Discord는 필드, 열거형 값, 컴포넌트 타입을 예고 없이 추가하며, 그때 봇이 깨지면 안 됩니다.
- bostie가 모르는 필드는 디코딩할 때 무시합니다.
- 열거형은 알 수 없는 값을
UNKNOWN_<value>라는 이름의 고정된 멤버에 대응시키며, 이 멤버는 그 값과 같다고 비교됩니다.MessageType(99).name == "UNKNOWN_99". - 플래그는 bostie가 모르는 비트를
~를 포함한 모든 연산자에서 그대로 유지합니다. - 알 수 없는 컴포넌트 타입은
UnknownComponent로 디코딩됩니다. 이 객체는 원시 JSON을 보관하며,components에 넘기면 바뀌지 않은 채로 다시 전송됩니다. 알 수 없는 인터랙션 타입은UnknownInteraction으로 디코딩되며,InteractionCreate리스너에는 전달되지만 명령어나 라우트에는 전달되지 않습니다. message.referenced_message나 컴포넌트 인터랙션의 메시지처럼 중첩된 메시지는 속성을 읽을 때 비로소 디코딩되므로, 깨진 메시지가 있어도 그것을 읽는 곳에서만 실패합니다.
필수 필드가 빠진 경우처럼 이벤트 페이로드가 해당 클래스에 전혀 맞지 않으면, 한 번 로그에 남기고 RawEvent 리스너에만 전달합니다. 자세한 내용은 이벤트에서 설명합니다.
원시 페이로드¶
페이로드 전체에서 디코딩한 모델은 그 페이로드를 보관합니다. model.raw_json에는 바이트가 들어 있고, model.raw는 이를 새 dict로 디코딩합니다. 메시지 작성자처럼 중첩된 모델은 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로 표현한 작성자 |
role.permissions, overwrite.allow, overwrite.deny |
Permissions 플래그 |
member.permissions |
Discord가 보냈을 때, 인터랙션이 일어난 채널에서 멤버가 가진 권한 |
webhook.url |
웹후크에 토큰이 있을 때, 웹후크의 URL |
ID로 만드는 멘션, 타임스탬프, CDN URL은 서식 가이드에서 다룹니다.
스노플레이크¶
Discord ID에는 객체가 만들어진 시점이 담겨 있습니다. 스노플레이크 ID를 가진 모든 모델에는 그 시점을 UTC datetime으로 나타내는 created_at이 있습니다. message.created_at, member.created_at(계정이 만들어진 시점), emoji.created_at(유니코드 이모지는 None)이 그 예입니다. ID만 있을 때는 bostie.snowflake_time(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가 해당 타입의 메시지 삭제를 허용하는지 알려 줍니다. 그룹 메시지와 통화 알림, 채널 이름과 아이콘 변경 알림, 스레드의 시작 메시지는 삭제할 수 없습니다. JSONErrorCode는 HTTPException.code가 보고하는 Discord 에러 코드에 이름을 붙입니다.