tadpole/model/message
The Discord message object for MESSAGE_CREATE, plus the partial object MESSAGE_UPDATE sends. Attachments are kept to a skeleton: bots that need them fetch the message again anyway. message_type is Discord’s raw integer; naming a sum type now would freeze it against Discord’s growing list.
Two records, two payloads:
Message— the full object MESSAGE_CREATE carries. Consumed bytadpole/gateway/events’ MessageCreate and bytadpole/rest/endpoints’s send/reply responses.MessageUpdate— the PARTIAL object MESSAGE_UPDATE carries. Only the ids are guaranteed; every other field may be absent, and absent reads asNonerather than falling back to a cached message — merging old and new state is the caller’s job.
Why fields are Option
An Option means Discord omits the key or nulls it in the payloads
this record models: guild_id in DMs, edited_timestamp before the
first edit, webhook_id for non-webhook authors. Defaults differ by
field: content arrives as "" for embed-only posts, tts /
mention_everyone / pinned default to False, message_type to 0
— those are documented Discord semantics, not guesses. Unknown
fields are ignored.
Why some fields are String, not IDs
Attachment.id is a plain String: attachments are rare in this
milestone and snowflake validation would only add a failure mode
without buying type safety anyone uses yet. Every field the library
itself dereferences — id, channel_id, guild_id, webhook_id,
mention role ids — is a typed ID that fails the decode if it is not
a snowflake.
Types
pub type Attachment {
Attachment(
id: String,
filename: String,
size: Int,
url: String,
)
}
Constructors
-
Attachment(id: String, filename: String, size: Int, url: String)Arguments
- id
-
Deliberately String, not a typed ID: attachments are rare in this milestone and the snowflake validation buys nothing here yet.
pub type Message {
Message(
id: ids.MessageId,
channel_id: ids.ChannelId,
guild_id: option.Option(ids.GuildId),
author: user.User,
content: String,
timestamp: String,
edited_timestamp: option.Option(String),
tts: Bool,
mention_everyone: Bool,
mentions: List(user.User),
mention_role_ids: List(ids.RoleId),
attachments: List(Attachment),
pinned: Bool,
webhook_id: option.Option(ids.WebhookId),
message_type: Int,
)
}
Constructors
-
Message( id: ids.MessageId, channel_id: ids.ChannelId, guild_id: option.Option(ids.GuildId), author: user.User, content: String, timestamp: String, edited_timestamp: option.Option(String), tts: Bool, mention_everyone: Bool, mentions: List(user.User), mention_role_ids: List(ids.RoleId), attachments: List(Attachment), pinned: Bool, webhook_id: option.Option(ids.WebhookId), message_type: Int, )Arguments
- guild_id
-
None in DMs.
- content
-
Empty when the message is an embed-only or attachment-only post.
- timestamp
-
ISO8601 timestamp, passed through exactly as Discord sent it (always UTC, e.g. 2026-09-07T12:00:00.000000+00:00).
- edited_timestamp
-
Stays None until the first edit; then it is Discord’s ISO8601 edit time.
- webhook_id
-
Set when a webhook sent the message.
- message_type
-
Discord’s raw MESSAGE type integer: 0 DEFAULT, 1 RECIPIENT_ADD, 19 REPLY, 20 APPLICATION_COMMAND, and so on. Kept raw because Discord adds types faster than libraries track them.
Decoder for the partial message object MESSAGE_UPDATE carries: the IDs are always present, every other field may be absent. Absent fields read as None rather than falling back to a cached message — merging old and new state is the caller’s job, not the decoder’s.
pub type MessageUpdate {
MessageUpdate(
id: ids.MessageId,
channel_id: ids.ChannelId,
content: option.Option(String),
author: option.Option(user.User),
edited_timestamp: option.Option(String),
pinned: option.Option(Bool),
)
}
Constructors
-
MessageUpdate( id: ids.MessageId, channel_id: ids.ChannelId, content: option.Option(String), author: option.Option(user.User), edited_timestamp: option.Option(String), pinned: option.Option(Bool), )
Values
pub fn attachment_decoder() -> decode.Decoder(Attachment)
Attachment decoder, exposed for later modules that meet attachments outside a message payload.
pub fn decoder() -> decode.Decoder(Message)
Decoder for the full message object MESSAGE_CREATE carries.
Discord sends IDs as strings; a non-snowflake string in id,
channel_id, guild_id, webhook_id, or a mention role fails the
decode. Fields Discord reserves for rarely used features are optional
here and default sensibly: tts False, mention_everyone False,
pinned False. Unknown fields are ignored.
pub fn from_json(
payload: String,
) -> Result(Message, error.TadpoleError)
Decode a MESSAGE_CREATE payload: the message object, not a gateway
frame. Fails with error.DecodeFailed when the payload is not valid
JSON, a required field is missing or mistyped, or an ID is not a
snowflake. The error’s event field stays None; wiring that knows
the event name fills it in.
pub fn update_decoder() -> decode.Decoder(MessageUpdate)
Decoder for MESSAGE_UPDATE’s d. Fails the same way as the full
message decoder when the payload is not JSON or an ID is not a
snowflake.
pub fn update_from_json(
payload: String,
) -> Result(MessageUpdate, error.TadpoleError)
Decode a MESSAGE_UPDATE payload: the partial message object, not a gateway frame. Fails with error.DecodeFailed when the payload is not valid JSON or one of the always-present IDs is not a snowflake.