glyde/message

Messages: what arrives, what to send, and every endpoint that acts on one.

A Draft builds the create body, an Edit the patch body. send, reply, edit, delete, react and pin take a whole Message where they can, so the ids come from the value you already hold; each has an _id variant for a caller who only has ids.

content, embeds, attachments and components arrive EMPTY, not absent, without the MESSAGE_CONTENT intent. Nothing tells that apart from a genuinely empty message.

MESSAGE_UPDATE does not carry a Message, whatever the docs say. Use update_decoder, never decoder, on that event.

Types

pub type ChannelMention {
  ChannelMention(
    id: id.Id(id.Channel),
    guild_id: id.Id(id.Guild),
    type_: channel.ChannelType,
    name: String,
  )
}

Constructors

POST /channels/{c}/messages. Discord needs at least one of content, embeds, sticker_ids, components, a file or a poll, unless it is a forward. Not checked here: Discord’s 400 names the bad fields.

pub type Draft {
  Draft(
    content: option.Option(String),
    embeds: List(embed.Embed),
    components: List(component.Component),
    sticker_ids: List(id.Id(id.Sticker)),
    files: List(attachment.File),
    allowed_mentions: option.Option(mentions.AllowedMentions),
    reference: option.Option(Reference),
    tts: Bool,
    nonce: NoncePolicy,
    flags: flags.Flags(MessageFlag),
  )
}

Constructors

  • Draft(
      content: option.Option(String),
      embeds: List(embed.Embed),
      components: List(component.Component),
      sticker_ids: List(id.Id(id.Sticker)),
      files: List(attachment.File),
      allowed_mentions: option.Option(mentions.AllowedMentions),
      reference: option.Option(Reference),
      tts: Bool,
      nonce: NoncePolicy,
      flags: flags.Flags(MessageFlag),
    )

    Arguments

    content

    Max 2000 characters.

    embeds

    Max 10, and 6000 characters across all of them.

    sticker_ids

    Max 3.

    files

    Drives both the attachments array and the files[n] parts.

    allowed_mentions

    None is Discord’s default: parse and deliver every mention in the content. A decision, not an absence.

    nonce

    Discord ignores enforce_nonce without a nonce, so the two are one value and the inert combination cannot be built.

    flags

    Only SUPPRESS_EMBEDS, SUPPRESS_NOTIFICATIONS, IS_VOICE_MESSAGE and IS_COMPONENTS_V2 can be set on a create.

PATCH /channels/{c}/messages/{m}. The attachments array has to name every attachment that survives the edit, so it and the uploads are one field.

pub type Edit {
  Edit(
    content: field.Field(String),
    embeds: field.Field(List(embed.Embed)),
    components: field.Field(List(component.Component)),
    flags: field.Field(flags.Flags(MessageFlag)),
    allowed_mentions: mentions.AllowedMentions,
    attachments: attachment.EditAttachments,
  )
}

Constructors

Discord splices member into each element of mentions.

pub type Mention {
  Mention(
    user: user.User,
    member: option.Option(member.GuildMember),
  )
}

Constructors

pub type Message {
  Message(
    id: id.Id(id.Message),
    channel_id: id.Id(id.Channel),
    guild_id: option.Option(id.Id(id.Guild)),
    author: user.User,
    member: option.Option(member.GuildMember),
    content: String,
    timestamp: String,
    edited_timestamp: option.Option(String),
    tts: Bool,
    mention_everyone: Bool,
    mentions: List(Mention),
    mention_roles: List(id.Id(id.Role)),
    mention_channels: List(ChannelMention),
    attachments: List(attachment.Attachment),
    embeds: List(embed.Embed),
    reactions: List(Reaction),
    nonce: option.Option(Nonce),
    pinned: Bool,
    webhook_id: option.Option(id.Id(id.Webhook)),
    type_: MessageType,
    application_id: option.Option(id.Id(id.Application)),
    flags: flags.Flags(MessageFlag),
    message_reference: option.Option(MessageReference),
    referenced_message: field.Field(Message),
    thread: option.Option(channel.Channel),
    components: List(component.Component),
    sticker_items: List(StickerItem),
    position: option.Option(Int),
  )
}

Constructors

Where in a channel’s history to read. One value, not three optional fields, because Discord rejects two cursors on one request.

pub type MessageCursor {
  Around(id.Id(id.Message))
  Before(id.Id(id.Message))
  After(id.Id(id.Message))
}

Constructors

pub type MessageFlag {
  Crossposted
  IsCrosspost
  SuppressEmbeds
  SourceMessageDeleted
  Urgent
  HasThread
  Ephemeral
  Loading
  FailedToMentionSomeRolesInThread
  SuppressNotifications
  IsVoiceMessage
  HasSnapshot
  IsComponentsV2
}

Constructors

  • Crossposted
  • IsCrosspost
  • SuppressEmbeds
  • SourceMessageDeleted
  • Urgent
  • HasThread
  • Ephemeral
  • Loading
  • FailedToMentionSomeRolesInThread
  • SuppressNotifications
  • IsVoiceMessage
  • HasSnapshot
  • IsComponentsV2

    Once set on a message this can never be removed.

pub type MessageReference {
  MessageReference(
    type_: MessageReferenceType,
    message_id: option.Option(id.Id(id.Message)),
    channel_id: option.Option(id.Id(id.Channel)),
    guild_id: option.Option(id.Id(id.Guild)),
  )
}

Constructors

A value this build has no name for fails the decode.

pub type MessageReferenceType {
  DefaultReference
  ForwardReference
}

Constructors

  • DefaultReference

    Replies, crossposts, pins and thread starters. Populates referenced_message.

  • ForwardReference

    Forwards. Populates message_snapshots, which is not modelled.

0 to 46, with holes at 13, 30, 33 to 35, 40 to 43 and 45. Never index this by position. A value this build has no name for fails the decode.

pub type MessageType {
  DefaultMessage
  RecipientAdd
  RecipientRemove
  Call
  ChannelNameChange
  ChannelIconChange
  ChannelPinnedMessage
  UserJoin
  GuildBoost
  GuildBoostTier1
  GuildBoostTier2
  GuildBoostTier3
  ChannelFollowAdd
  GuildDiscoveryDisqualified
  GuildDiscoveryRequalified
  GuildDiscoveryGracePeriodInitialWarning
  GuildDiscoveryGracePeriodFinalWarning
  ThreadCreated
  ReplyMessage
  ChatInputCommand
  ThreadStarterMessage
  GuildInviteReminder
  ContextMenuCommand
  AutoModerationAction
  RoleSubscriptionPurchase
  InteractionPremiumUpsell
  StageStart
  StageEnd
  StageSpeaker
  StageTopic
  GuildApplicationPremiumSubscription
  GuildIncidentAlertModeEnabled
  GuildIncidentAlertModeDisabled
  GuildIncidentReportRaid
  GuildIncidentReportFalseAlarm
  PurchaseNotification
  PollResult
}

Constructors

  • DefaultMessage
  • RecipientAdd
  • RecipientRemove
  • Call
  • ChannelNameChange
  • ChannelIconChange
  • ChannelPinnedMessage
  • UserJoin
  • GuildBoost
  • GuildBoostTier1
  • GuildBoostTier2
  • GuildBoostTier3
  • ChannelFollowAdd
  • GuildDiscoveryDisqualified
  • GuildDiscoveryRequalified
  • GuildDiscoveryGracePeriodInitialWarning
  • GuildDiscoveryGracePeriodFinalWarning
  • ThreadCreated
  • ReplyMessage
  • ChatInputCommand
  • ThreadStarterMessage
  • GuildInviteReminder
  • ContextMenuCommand
  • AutoModerationAction
  • RoleSubscriptionPurchase
  • InteractionPremiumUpsell
  • StageStart
  • StageEnd
  • StageSpeaker
  • StageTopic
  • GuildApplicationPremiumSubscription
  • GuildIncidentAlertModeEnabled
  • GuildIncidentAlertModeDisabled
  • GuildIncidentReportRaid
  • GuildIncidentReportFalseAlarm
  • PurchaseNotification
  • PollResult

The MESSAGE_UPDATE payload, which is NOT a message object: an embed unfurl sends four keys. message is Some only when a full object arrived.

pub type MessageUpdate {
  MessageUpdate(
    id: id.Id(id.Message),
    channel_id: id.Id(id.Channel),
    guild_id: option.Option(id.Id(id.Guild)),
    message: option.Option(Message),
    raw: dynamic.Dynamic,
  )
}

Constructors

Discord echoes back whatever was sent, so both exist for reading. Always SEND a StringNonce: it is a dedup key, and a number can be reinterpreted.

There is no variant for a nonce too big to hold, because this type is also what a create sends: a lost value has to read as None and omit the key, not encode as a null Discord would take for an instruction.

pub type Nonce {
  StringNonce(String)
  IntNonce(Int)
}

Constructors

  • StringNonce(String)
  • IntNonce(Int)

Whether the create carries a nonce, and what Discord should do with it.

pub type NoncePolicy {
  NoNonce
  UseNonce(value: Nonce, enforce: Bool)
}

Constructors

  • NoNonce
  • UseNonce(value: Nonce, enforce: Bool)

    Max 25 characters. Always send a StringNonce. enforce makes the create idempotent for a few minutes: a second POST with the same nonce returns the first message instead of posting twice.

pub type PinList {
  PinList(items: List(PinnedMessage), has_more: Bool)
}

Constructors

One entry from the pins API. The timestamp belongs to the pin, not to the message.

pub type PinnedMessage {
  PinnedMessage(pinned_at: String, message: Message)
}

Constructors

  • PinnedMessage(pinned_at: String, message: Message)
pub type Reaction {
  Reaction(
    count: Int,
    count_details: ReactionCountDetails,
    me: Bool,
    me_burst: Bool,
    emoji: emoji.Emoji,
    burst_colors: List(String),
  )
}

Constructors

  • Reaction(
      count: Int,
      count_details: ReactionCountDetails,
      me: Bool,
      me_burst: Bool,
      emoji: emoji.Emoji,
      burst_colors: List(String),
    )

    Arguments

    count

    Normal and super reactions added together.

    emoji

    A PARTIAL emoji, with a null id for a unicode reaction.

    burst_colors

    Hex colours for the super-reaction burst, with no leading #.

pub type ReactionCountDetails {
  ReactionCountDetails(burst: Int, normal: Int)
}

Constructors

  • ReactionCountDetails(burst: Int, normal: Int)

The {emoji} in a reaction path. Only ever decoded text, so a value that is already percent-encoded cannot be handed in and encoded twice.

pub opaque type ReactionEmoji

Discord’s two kinds of reaction, one type for both the gateway events and the type query parameter on the reactions route. A burst reaction is the animated “super reaction” a Nitro subscriber can send. A value this build has no name for fails the decode.

pub type ReactionType {
  NormalReaction
  BurstReaction
}

Constructors

  • NormalReaction
  • BurstReaction

A reply or a forward, sent as message_reference.

pub type Reference {
  Reply(
    message_id: id.Id(id.Message),
    channel_id: option.Option(id.Id(id.Channel)),
    guild_id: option.Option(id.Id(id.Guild)),
    fail_if_not_exists: Bool,
  )
  Forward(
    message_id: id.Id(id.Message),
    channel_id: id.Id(id.Channel),
    guild_id: option.Option(id.Id(id.Guild)),
  )
}

Constructors

  • Reply(
      message_id: id.Id(id.Message),
      channel_id: option.Option(id.Id(id.Channel)),
      guild_id: option.Option(id.Id(id.Guild)),
      fail_if_not_exists: Bool,
    )

    Arguments

    channel_id

    Discord infers the current channel when this is None.

    fail_if_not_exists

    False turns a reply to a deleted message into a plain message rather than a 400. Discord’s own default is true.

  • Forward(
      message_id: id.Id(id.Message),
      channel_id: id.Id(id.Channel),
      guild_id: option.Option(id.Id(id.Guild)),
    )

    Only DEFAULT, REPLY, CHAT_INPUT_COMMAND and CONTEXT_MENU_COMMAND messages can be forwarded, and the app has to be able to read the content or Discord answers error code 160014.

Discord’s sticker format table. A value this build has no name for fails the decode.

pub type StickerFormat {
  PngSticker
  ApngSticker
  LottieSticker
  GifSticker
}

Constructors

  • PngSticker
  • ApngSticker
  • LottieSticker
  • GifSticker
pub type StickerItem {
  StickerItem(
    id: id.Id(id.Sticker),
    name: String,
    format_type: StickerFormat,
  )
}

Constructors

Values

pub fn allowed_mentions(
  draft: Draft,
  policy: mentions.AllowedMentions,
) -> Draft

Without this Discord pings every mention in the content.

pub fn attach(draft: Draft, file: attachment.File) -> Draft

to_body writes the matching attachments entry, so an embed can point at it with attachment://{filename}.

pub fn bulk_delete(
  api: api.Api,
  channel: id.Id(id.Channel),
  messages: List(id.Id(id.Message)),
) -> Result(Nil, api.CallFailure)

POST /channels/{channel.id}/messages/bulk-delete, as Bulk Delete Messages. Discord takes 2 to 100 ids and rejects the whole request if any message is over two weeks old.

pub fn bulk_delete_body(
  messages: List(id.Id(id.Message)),
) -> body.Body

POST /channels/{c}/messages/bulk-delete. Two to 100 ids, none older than 14 days, or Discord rejects the whole batch.

pub fn bulk_delete_call(
  channel: id.Id(id.Channel),
  messages: List(id.Id(id.Message)),
) -> rest.Call(Nil)

The Call for [bulk_delete], for building the request without sending it.

pub fn clear_emoji_reactions(
  api: api.Api,
  msg: Message,
  emoji: ReactionEmoji,
) -> Result(Nil, api.CallFailure)

DELETE .../reactions/{emoji}, as Delete All Reactions For Emoji.

pub fn clear_emoji_reactions_call(
  msg: Message,
  emoji: ReactionEmoji,
) -> rest.Call(Nil)

The Call for [clear_emoji_reactions], for building the request without sending it.

pub fn clear_emoji_reactions_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
) -> Result(Nil, api.CallFailure)

clear_emoji_reactions for a caller who only has the ids.

pub fn clear_emoji_reactions_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
) -> rest.Call(Nil)

The Call for [clear_emoji_reactions_id], for building the request without sending it.

pub fn clear_reactions(
  api: api.Api,
  msg: Message,
) -> Result(Nil, api.CallFailure)

DELETE .../reactions, as Delete All Reactions.

pub fn clear_reactions_call(msg: Message) -> rest.Call(Nil)

The Call for [clear_reactions], for building the request without sending it.

pub fn clear_reactions_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> Result(Nil, api.CallFailure)

clear_reactions for a caller who only has the ids. Its own bucket, having no emoji to collapse at.

pub fn clear_reactions_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> rest.Call(Nil)

The Call for [clear_reactions_id], for building the request without sending it.

pub fn component(
  draft: Draft,
  component: component.Component,
) -> Draft

One top-level component, so an action row at a time.

pub fn content(draft: Draft, content: String) -> Draft
pub fn custom_emoji(
  emoji: id.Id(id.Emoji),
  name: String,
) -> ReactionEmoji

A custom guild emoji, which Discord wants as name:id. The colon is percent-encoded on the way out, which Discord accepts.

pub fn custom_emoji_by_id(
  emoji: id.Id(id.Emoji),
) -> ReactionEmoji

A custom emoji whose name you do not have. Discord reads only the id, so the name goes out as the literal e.

pub fn delete(
  api: api.Api,
  msg: Message,
) -> Result(Nil, api.CallFailure)

DELETE /channels/{channel.id}/messages/{message.id}, as Delete Message.

pub fn delete_call(msg: Message) -> rest.Call(Nil)

The Call for [delete], for building the request without sending it.

pub fn delete_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> Result(Nil, api.CallFailure)

delete for a caller who only has the ids. Discord splits this route into three buckets by the target’s age and reports none of it in the headers (discord-api-docs#1092, #1295), so route.Aged carries the age.

pub fn delete_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> rest.Call(Nil)

The Call for [delete_id], for building the request without sending it.

pub fn edit(
  api: api.Api,
  msg: Message,
  edit: Edit,
) -> Result(Message, api.CallFailure)

PATCH /channels/{channel.id}/messages/{message.id}, as Edit Message.

pub fn edit_body(edit: Edit) -> body.Body

A ready-to-send body for a PATCH, files included.

pub fn edit_call(msg: Message, edit: Edit) -> rest.Call(Message)

The Call for edit, for building the request without sending it.

pub fn edit_entries(
  edit: Edit,
) -> List(#(String, field.Field(json.Json)))

The edit keys before the absent ones are dropped. Public because the interaction callback nests the same six keys under data.

pub fn edit_files(edit: Edit) -> List(attachment.File)

The files the multipart body has to carry, in files[n] order.

pub fn edit_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  edit: Edit,
) -> Result(Message, api.CallFailure)

edit for a caller who only has the ids.

pub fn edit_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  edit: Edit,
) -> rest.Call(Message)

The bare Call, for driving glyde/rest yourself.

pub fn embed(draft: Draft, embed: embed.Embed) -> Draft
pub fn flags_field(
  value: flags.Flags(MessageFlag),
) -> field.Field(json.Json)

No flags set is an absence, not a zero. Public because a create and an interaction callback both write the key this way.

pub fn forward_of(
  draft: Draft,
  message_id: id.Id(id.Message),
  from channel_id: id.Id(id.Channel),
) -> Draft

Make it a forward. Discord accepts new() |> forward_of(..) on its own: a forward needs no content.

pub fn get(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> Result(Message, api.CallFailure)

GET /channels/{channel.id}/messages/{message.id}, as Get Channel Message.

pub fn get_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> rest.Call(Message)

The Call for [get], for building the request without sending it.

pub fn has_flag(
  bits: flags.Flags(MessageFlag),
  flag: MessageFlag,
) -> Bool
pub fn is_deletable(value: MessageType) -> Bool

Whether DELETE /channels/{id}/messages/{id} accepts this message.

pub fn is_from_webhook(message: Message) -> Bool

author.bot is true of ordinary bots too, so it is a different question.

pub fn list(
  api: api.Api,
  channel: id.Id(id.Channel),
  cursor cursor: option.Option(MessageCursor),
  limit limit: option.Option(Int),
) -> Result(List(Message), api.CallFailure)

GET /channels/{channel.id}/messages, as Get Channel Messages. None is no cursor, which Discord reads as the newest messages. Discord caps limit at 100 and defaults it to 50.

pub fn list_call(
  channel: id.Id(id.Channel),
  cursor cursor: option.Option(MessageCursor),
  limit limit: option.Option(Int),
) -> rest.Call(List(Message))

The Call for [list], for building the request without sending it.

pub fn mention_decoder() -> decode.Decoder(Mention)

The user object is not nested: the spliced member sits beside its keys.

pub fn mentioned_users(message: Message) -> List(user.User)
pub fn message_flags(
  of chosen: List(MessageFlag),
) -> flags.Flags(MessageFlag)
pub fn message_reference_decoder() -> decode.Decoder(
  MessageReference,
)
pub fn message_reference_type_decoder() -> decode.Decoder(
  MessageReferenceType,
)
pub fn message_reference_type_from_int(
  value: Int,
) -> option.Option(MessageReferenceType)
pub fn message_reference_type_to_int(
  value: MessageReferenceType,
) -> Int
pub fn message_reference_type_to_json(
  value: MessageReferenceType,
) -> json.Json
pub fn message_type_decoder() -> decode.Decoder(MessageType)
pub fn message_type_from_int(
  value: Int,
) -> option.Option(MessageType)
pub fn message_type_to_int(value: MessageType) -> Int
pub fn message_type_to_json(value: MessageType) -> json.Json
pub fn new() -> Draft

An empty draft, to pipe setters onto.

pub fn new_edit(mentions: mentions.AllowedMentions) -> Edit

The only Edit constructor: the mention policy is not optional.

pub fn nonce_decoder() -> decode.Decoder(option.Option(Nonce))

Total on purpose. wire.integer rejects a number too big to hold exactly, and decode.optional_field passes that failure up, so without the last alternative an oversized nonce would sink the whole message. Past 2^53-1 the low digits are already gone, so None is all there is to hand back.

pub fn nonce_to_json(value: Nonce) -> json.Json
pub fn pin(
  api: api.Api,
  msg: Message,
) -> Result(Nil, api.CallFailure)

PUT /channels/{channel.id}/messages/pins/{message.id}, as Pin Message.

pub fn pin_call(msg: Message) -> rest.Call(Nil)

The Call for [pin], for building the request without sending it.

pub fn pin_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> Result(Nil, api.CallFailure)

pin for a caller who only has the ids.

pub fn pin_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> rest.Call(Nil)

The Call for [pin_id], for building the request without sending it.

pub fn pin_list_decoder() -> decode.Decoder(PinList)
pub fn pinned_at(pin: PinnedMessage) -> channel.TimeCursor

When a message was pinned. Page on with the oldest entry you just read.

pub fn pins(
  api: api.Api,
  channel: id.Id(id.Channel),
  before before: option.Option(channel.TimeCursor),
  limit limit: option.Option(Int),
) -> Result(PinList, api.CallFailure)

GET /channels/{channel.id}/messages/pins, as Get Channel Pins. Each message wrapped in its pin time; pages backwards through the order they were pinned in.

pub fn pins_call(
  channel: id.Id(id.Channel),
  before before: option.Option(channel.TimeCursor),
  limit limit: option.Option(Int),
) -> rest.Call(PinList)

The Call for [pins], for building the request without sending it.

pub fn react(
  api: api.Api,
  msg: Message,
  emoji: ReactionEmoji,
) -> Result(Nil, api.CallFailure)

PUT .../reactions/{emoji}/@me, as Create Reaction.

pub fn react_call(
  msg: Message,
  emoji: ReactionEmoji,
) -> rest.Call(Nil)

The Call for [react], for building the request without sending it.

pub fn react_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
) -> Result(Nil, api.CallFailure)

react for a caller who only has the ids.

pub fn react_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
) -> rest.Call(Nil)

The Call for [react_id], for building the request without sending it.

pub fn reaction_count_details_decoder() -> decode.Decoder(
  ReactionCountDetails,
)
pub fn reaction_decoder() -> decode.Decoder(Reaction)

The super-reaction fields are documented as required and are missing from cached or replayed payloads, so they default.

pub fn reaction_emoji(emoji: emoji.Emoji) -> ReactionEmoji

An emoji that came off the wire, from a reaction event or a component.

pub fn reaction_type_decoder() -> decode.Decoder(ReactionType)
pub fn reaction_type_from_int(
  value: Int,
) -> option.Option(ReactionType)
pub fn reaction_type_to_int(value: ReactionType) -> Int
pub fn reactions(
  api: api.Api,
  msg: Message,
  emoji: ReactionEmoji,
  type_ type_: option.Option(ReactionType),
  after after: option.Option(id.Id(id.User)),
  limit limit: option.Option(Int),
) -> Result(List(user.User), api.CallFailure)

GET .../reactions/{emoji}, as Get Reactions: the users who reacted.

pub fn reactions_call(
  msg: Message,
  emoji: ReactionEmoji,
  type_ type_: option.Option(ReactionType),
  after after: option.Option(id.Id(id.User)),
  limit limit: option.Option(Int),
) -> rest.Call(List(user.User))

The Call for [reactions], for building the request without sending it.

pub fn reactions_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
  type_ type_: option.Option(ReactionType),
  after after: option.Option(id.Id(id.User)),
  limit limit: option.Option(Int),
) -> Result(List(user.User), api.CallFailure)

reactions for a caller who only has the ids.

pub fn reactions_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
  type_ type_: option.Option(ReactionType),
  after after: option.Option(id.Id(id.User)),
  limit limit: option.Option(Int),
) -> rest.Call(List(user.User))

The Call for [reactions_id], for building the request without sending it.

pub fn redraft(message: Message) -> Draft

A draft with this message’s content, embeds, components, stickers and the flags a create may set. Attachments do not carry: a received one is a URL, a draft’s is bytes. The receive-only parts of an embed (type, provider, video, proxy urls) come along and are dropped by embed.to_json.

pub fn reference_to_json(value: Reference) -> json.Json
pub fn referenced(message: Message) -> option.Option(Message)

referenced_message collapsed to an Option.

pub fn remove_reaction(
  api: api.Api,
  msg: Message,
  emoji: ReactionEmoji,
  user: id.Id(id.User),
) -> Result(Nil, api.CallFailure)

DELETE .../reactions/{emoji}/{user.id}, as Delete User Reaction.

pub fn remove_reaction_call(
  msg: Message,
  emoji: ReactionEmoji,
  user: id.Id(id.User),
) -> rest.Call(Nil)

The Call for [remove_reaction], for building the request without sending it.

pub fn remove_reaction_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
  user: id.Id(id.User),
) -> Result(Nil, api.CallFailure)

remove_reaction for a caller who only has the ids.

pub fn remove_reaction_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
  user: id.Id(id.User),
) -> rest.Call(Nil)

The Call for [remove_reaction_id], for building the request without sending it.

pub fn reply(
  api: api.Api,
  to: Message,
  draft: Draft,
) -> Result(Message, api.CallFailure)

send(api, to.channel_id, draft |> reply_to(to.id)), so Discord shows it attached to the message rather than as a bare post.

pub fn reply_call(
  to: Message,
  draft: Draft,
) -> rest.Call(Message)

The Call for reply, for building the request without sending it.

pub fn reply_to(
  draft: Draft,
  message_id: id.Id(id.Message),
) -> Draft

Make it a reply. fail_if_not_exists stays at Discord’s default, so replying to a message that has since been deleted is a 400.

pub fn send(
  api: api.Api,
  channel: id.Id(id.Channel),
  draft: Draft,
) -> Result(Message, api.CallFailure)

POST /channels/{channel.id}/messages, as Create Message.

pub fn send_call(
  channel: id.Id(id.Channel),
  draft: Draft,
) -> rest.Call(Message)

The bare Call, for driving glyde/rest yourself.

pub fn silent(draft: Draft) -> Draft

Delivered without a push or desktop notification, mentions included.

pub fn sticker(draft: Draft, sticker: id.Id(id.Sticker)) -> Draft

Discord takes at most 3.

pub fn sticker_format_from_int(
  value: Int,
) -> option.Option(StickerFormat)
pub fn sticker_format_to_int(value: StickerFormat) -> Int
pub fn sticker_item_decoder() -> decode.Decoder(StickerItem)
pub fn suppress_embeds(draft: Draft) -> Draft

No link previews. The other create-time flags are IS_VOICE_MESSAGE and IS_COMPONENTS_V2; set those on flags directly.

pub fn text(content: String) -> Draft

new() |> content(content), since most messages start with words.

pub fn to_body(draft: Draft) -> body.Body

A ready-to-send body, files already paired to their attachments entries.

pub fn tts(draft: Draft) -> Draft

Read aloud by the client to anyone with the channel open.

pub fn unicode_emoji(text: String) -> ReactionEmoji

A standard emoji, written as the characters themselves: "🔥".

pub fn unpin(
  api: api.Api,
  msg: Message,
) -> Result(Nil, api.CallFailure)

DELETE /channels/{channel.id}/messages/pins/{message.id}, as Unpin Message.

pub fn unpin_call(msg: Message) -> rest.Call(Nil)

The Call for [unpin], for building the request without sending it.

pub fn unpin_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> Result(Nil, api.CallFailure)

unpin for a caller who only has the ids.

pub fn unpin_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
) -> rest.Call(Nil)

The Call for [unpin_id], for building the request without sending it.

pub fn unreact(
  api: api.Api,
  msg: Message,
  emoji: ReactionEmoji,
) -> Result(Nil, api.CallFailure)

DELETE .../reactions/{emoji}/@me, as Delete Own Reaction.

pub fn unreact_call(
  msg: Message,
  emoji: ReactionEmoji,
) -> rest.Call(Nil)

The Call for [unreact], for building the request without sending it.

pub fn unreact_id(
  api: api.Api,
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
) -> Result(Nil, api.CallFailure)

unreact for a caller who only has the ids.

pub fn unreact_id_call(
  channel: id.Id(id.Channel),
  message: id.Id(id.Message),
  emoji: ReactionEmoji,
) -> rest.Call(Nil)

The Call for [unreact_id], for building the request without sending it.

pub fn with_correlation_nonce(
  draft: Draft,
  value: Nonce,
) -> Draft

A nonce only for correlating MESSAGE_CREATE back to the create. Discord does not dedupe on it, so a retried POST posts a second message.

pub fn with_nonce(draft: Draft, value: Nonce) -> Draft

A nonce Discord echoes back on MESSAGE_CREATE, enforced, so a retried POST returns the first message rather than posting twice. The safe one is the short name on purpose: a retry is the reason to reach for a nonce at all.

Search Document