glyde/channel

Channels, threads and permission overwrites.

One flat record with a type_, not a sum type: twelve of the thirteen types carry name, so a sum type would force a twelve-arm case to read it.

Only id and type_ are required, so an interaction’s partial channel decodes through the same decoder as a full one.

Types

The body of GET /guilds/{id}/threads/active, which is every active thread at once. Discord sends no has_more here, so there is no page to ask for.

pub type ActiveThreads {
  ActiveThreads(
    threads: List(Channel),
    members: List(ThreadMember),
  )
}

Constructors

A channel of any kind, threads included.

pub type Channel {
  Channel(
    id: id.Id(id.Channel),
    type_: ChannelType,
    guild_id: option.Option(id.Id(id.Guild)),
    position: option.Option(Int),
    permission_overwrites: option.Option(
      List(PermissionOverwrite),
    ),
    name: option.Option(String),
    topic: option.Option(String),
    nsfw: option.Option(Bool),
    last_message_id: option.Option(id.Id(id.Message)),
    bitrate: option.Option(Int),
    user_limit: option.Option(Int),
    rate_limit_per_user: option.Option(Int),
    recipients: option.Option(List(user.User)),
    icon: option.Option(String),
    owner_id: option.Option(id.Id(id.User)),
    application_id: option.Option(id.Id(id.Application)),
    managed: option.Option(Bool),
    parent_id: option.Option(id.Id(id.Channel)),
    last_pin_timestamp: option.Option(String),
    rtc_region: option.Option(String),
    video_quality_mode: option.Option(VideoQualityMode),
    message_count: option.Option(Int),
    member_count: option.Option(Int),
    thread_metadata: option.Option(ThreadMetadata),
    member: option.Option(ThreadMember),
    default_auto_archive_duration: option.Option(
      ThreadAutoArchiveDuration,
    ),
    permissions: option.Option(permissions.Effective),
    flags: option.Option(flags.Flags(ChannelFlag)),
    total_message_sent: option.Option(Int),
    default_thread_rate_limit_per_user: option.Option(Int),
    newly_created: option.Option(Bool),
  )
}

Constructors

The only conversion PATCH /channels/{id} does: text to announcement and back, in a guild with the NEWS feature.

pub type ChannelConversion {
  ToText
  ToAnnouncement
}

Constructors

  • ToText
  • ToAnnouncement
pub type ChannelFlag {
  Pinned
  RequireTag
  HideMediaDownloadOptions
  IsSpoilerChannel
}

Constructors

  • Pinned

    A forum or media post pinned to the top of its parent.

  • RequireTag

    The parent forum requires a tag on every post.

  • HideMediaDownloadOptions
  • IsSpoilerChannel

    Every attachment in the channel is treated as a spoiler.

Live values are 0 to 5, 10 to 13, 15 and 16. 14 is hub-only, and 6 to 9 were withdrawn but can still turn up in cached data. A value this build has no name for fails the decode.

pub type ChannelType {
  GuildText
  Dm
  GuildVoice
  GroupDm
  GuildCategory
  GuildAnnouncement
  AnnouncementThread
  PublicThread
  PrivateThread
  GuildStageVoice
  GuildDirectory
  GuildForum
  GuildMedia
}

Constructors

  • GuildText
  • Dm
  • GuildVoice
  • GroupDm
  • GuildCategory
  • GuildAnnouncement
  • AnnouncementThread
  • PublicThread
  • PrivateThread
  • GuildStageVoice
  • GuildDirectory
  • GuildForum
  • GuildMedia

What POST /guilds/{g}/channels can create. Threads come from the thread routes and a DM is not a guild channel, so neither is in here.

pub type CreatableChannelType {
  TextChannel
  VoiceChannel
  CategoryChannel
  AnnouncementChannel
  StageChannel
  ForumChannel
  MediaChannel
}

Constructors

  • TextChannel
  • VoiceChannel
  • CategoryChannel
  • AnnouncementChannel
  • StageChannel
  • ForumChannel
  • MediaChannel

POST /guilds/{g}/channels. Only name is required.

pub type CreateChannel {
  CreateChannel(
    name: String,
    type_: option.Option(CreatableChannelType),
    topic: option.Option(String),
    bitrate: option.Option(Int),
    user_limit: option.Option(Int),
    rate_limit_per_user: option.Option(Int),
    position: option.Option(Int),
    permission_overwrites: option.Option(List(OverwriteBody)),
    parent_id: option.Option(id.Id(id.Channel)),
    nsfw: option.Option(Bool),
    rtc_region: option.Option(String),
    video_quality_mode: option.Option(VideoQualityMode),
    default_auto_archive_duration: option.Option(
      ThreadAutoArchiveDuration,
    ),
    default_thread_rate_limit_per_user: option.Option(Int),
    require_tag: option.Option(Bool),
    hide_media_download_options: option.Option(Bool),
  )
}

Constructors

POST /channels/{c}/threads. type_ is required rather than optional: leaving it out gets a private thread today, and Discord has said that default will change.

pub type CreateThread {
  CreateThread(
    name: String,
    type_: ThreadKind,
    auto_archive_duration: option.Option(
      ThreadAutoArchiveDuration,
    ),
    invitable: option.Option(Bool),
    rate_limit_per_user: option.Option(Int),
  )
}

Constructors

POST /channels/{c}/messages/{m}/threads. The thread takes the source message’s id, so a message can only ever have one.

pub type CreateThreadFromMessage {
  CreateThreadFromMessage(
    name: String,
    auto_archive_duration: option.Option(
      ThreadAutoArchiveDuration,
    ),
    rate_limit_per_user: option.Option(Int),
  )
}

Constructors

PATCH /channels/{id} for a guild channel. Group DM edits are out of v1.

pub type EditGuildChannel {
  EditGuildChannel(
    name: option.Option(String),
    type_: option.Option(ChannelConversion),
    position: field.Field(Int),
    topic: field.Field(String),
    nsfw: field.Field(Bool),
    rate_limit_per_user: field.Field(Int),
    bitrate: field.Field(Int),
    user_limit: field.Field(Int),
    permission_overwrites: field.Field(List(OverwriteBody)),
    parent_id: field.Field(id.Id(id.Channel)),
    rtc_region: field.Field(String),
    video_quality_mode: field.Field(VideoQualityMode),
    default_auto_archive_duration: field.Field(
      ThreadAutoArchiveDuration,
    ),
    default_thread_rate_limit_per_user: option.Option(Int),
    require_tag: option.Option(Bool),
    hide_media_download_options: option.Option(Bool),
  )
}

Constructors

PATCH /channels/{id} for a thread, which shares the URL with EditGuildChannel and nothing else.

pub type EditThread {
  EditThread(
    name: option.Option(String),
    archived: option.Option(Bool),
    auto_archive_duration: option.Option(
      ThreadAutoArchiveDuration,
    ),
    locked: option.Option(Bool),
    invitable: option.Option(Bool),
    rate_limit_per_user: field.Field(Int),
    pinned: option.Option(Bool),
  )
}

Constructors

One permission overwrite as it goes out.

Opaque because Discord reads type to decide what the id means: a role id under type: 1 is not an error, it applies the overwrite to whichever member happens to have that id. The two constructors are the only way in, so the pair cannot disagree.

pub opaque type OverwriteBody

Who an overwrite applies to. Two cases, because an overwrite whose type this build has no name for is dropped at decode time.

pub type OverwriteTarget {
  RoleTarget(id.Id(id.Role))
  MemberTarget(id.Id(id.User))
}

Constructors

A value this build has no name for is dropped from permission_overwrites, so it is never applied to the wrong kind of id.

pub type OverwriteType {
  RoleOverwrite
  MemberOverwrite
}

Constructors

  • RoleOverwrite
  • MemberOverwrite

id is a role id or a user id, and only type_ says which. Read it through overwrite_target.

pub type PermissionOverwrite {
  PermissionOverwrite(
    id: id.Id(id.Overwrite),
    type_: OverwriteType,
    allow: permissions.Permissions,
    deny: permissions.Permissions,
  )
}

Constructors

Minute values, not an ordinal: 60, 1440, 4320, 10080. A value this build has no name for fails the decode on ThreadMetadata, and yields None on Channel.default_auto_archive_duration where the field is optional.

pub type ThreadAutoArchiveDuration {
  OneHour
  OneDay
  ThreeDays
  OneWeek
}

Constructors

  • OneHour
  • OneDay
  • ThreeDays
  • OneWeek

The three thread types Discord’s start-thread endpoints accept. Any other ChannelType sent as type is a 400.

pub type ThreadKind {
  AsPublicThread
  AsPrivateThread
  AsAnnouncementThread
}

Constructors

  • AsPublicThread
  • AsPrivateThread
  • AsAnnouncementThread

The body of the two archived thread listings, which page. members holds only the bot’s own membership, and only of the threads it has joined.

pub type ThreadList {
  ThreadList(
    threads: List(Channel),
    members: List(ThreadMember),
    has_more: Bool,
  )
}

Constructors

  • ThreadList(
      threads: List(Channel),
      members: List(ThreadMember),
      has_more: Bool,
    )

    Arguments

    has_more

    Another page exists before the oldest thread returned.

One user’s membership of one thread. Mind the collision: Channel.member is a ThreadMember, and ThreadMember.member is a GuildMember.

pub type ThreadMember {
  ThreadMember(
    id: option.Option(id.Id(id.Channel)),
    user_id: option.Option(id.Id(id.User)),
    join_timestamp: String,
    flags: Int,
    member: option.Option(member.GuildMember),
  )
}

Constructors

pub type ThreadMetadata {
  ThreadMetadata(
    archived: Bool,
    auto_archive_duration: ThreadAutoArchiveDuration,
    archive_timestamp: String,
    locked: Bool,
    invitable: option.Option(Bool),
    create_timestamp: option.Option(String),
  )
}

Constructors

  • ThreadMetadata(
      archived: Bool,
      auto_archive_duration: ThreadAutoArchiveDuration,
      archive_timestamp: String,
      locked: Bool,
      invitable: option.Option(Bool),
      create_timestamp: option.Option(String),
    )

    Arguments

    auto_archive_duration

    MINUTES.

    archive_timestamp

    ISO-8601, when the archive flag last changed.

    invitable

    Private threads only.

    create_timestamp

    Only threads created after 2022-01-09.

A point in time to page back from. Three routes here order by an instant instead of by snowflake, and a snowflake in one of their before parameters is accepted and answers the wrong window, so this is not a bare string.

pub opaque type TimeCursor

A value this build has no name for decodes as None.

pub type VideoQualityMode {
  AutoQuality
  FullQuality
}

Constructors

  • AutoQuality
  • FullQuality

Values

pub fn add_thread_member(
  api: api.Api,
  thread: id.Id(id.Channel),
  user: id.Id(id.User),
) -> Result(Nil, api.CallFailure)

PUT /channels/{thread.id}/thread-members/{user.id}, as Add Thread Member.

pub fn add_thread_member_call(
  thread: id.Id(id.Channel),
  user: id.Id(id.User),
) -> rest.Call(Nil)

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

pub fn allowing(
  body: OverwriteBody,
  allow: permissions.Permissions,
) -> OverwriteBody

The complete allowed set, not an addition to what is already there.

pub fn archived_at(thread: ThreadMetadata) -> TimeCursor

When a thread was archived, which is the order the archived listings come in. Page on with the oldest thread you just read.

pub fn at_time(iso8601: String) -> TimeCursor

An instant of your own, as ISO-8601. Every timestamp Discord sends is already in this shape.

pub fn auto_archive_duration_from_int(
  value: Int,
) -> option.Option(ThreadAutoArchiveDuration)
pub fn auto_archive_duration_to_int(
  value: ThreadAutoArchiveDuration,
) -> Int
pub fn auto_archive_duration_to_json(
  value: ThreadAutoArchiveDuration,
) -> json.Json
pub fn channel_flags(
  of chosen: List(ChannelFlag),
) -> flags.Flags(ChannelFlag)
pub fn channel_type_from_int(
  value: Int,
) -> option.Option(ChannelType)
pub fn channel_type_to_int(value: ChannelType) -> Int
pub fn channel_type_to_json(value: ChannelType) -> json.Json
pub fn clear_permission(
  api: api.Api,
  channel: id.Id(id.Channel),
  overwrite: id.Id(id.Overwrite),
) -> Result(Nil, api.CallFailure)

DELETE /channels/{channel.id}/permissions/{overwrite.id}, as Delete Channel Permission.

pub fn clear_permission_call(
  channel: id.Id(id.Channel),
  overwrite: id.Id(id.Overwrite),
) -> rest.Call(Nil)

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

pub fn create_channel(name: String) -> CreateChannel
pub fn create_channel_body(payload: CreateChannel) -> body.Body
pub fn create_thread(
  name: String,
  type_: ThreadKind,
) -> CreateThread
pub fn create_thread_body(payload: CreateThread) -> body.Body
pub fn create_thread_from_message(
  name: String,
) -> CreateThreadFromMessage
pub fn create_thread_from_message_body(
  payload: CreateThreadFromMessage,
) -> body.Body
pub fn delete(
  api: api.Api,
  channel: id.Id(id.Channel),
) -> Result(Channel, api.CallFailure)

DELETE /channels/{channel.id}, as Delete/Close Channel. Closes a DM and deletes anything else. Answers with the channel it removed, not 204.

pub fn delete_call(
  channel: id.Id(id.Channel),
) -> rest.Call(Channel)

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

pub fn denying(
  body: OverwriteBody,
  deny: permissions.Permissions,
) -> OverwriteBody

The complete denied set. Anything in neither set is inherited.

pub fn edit(
  api: api.Api,
  channel: id.Id(id.Channel),
  edit: EditGuildChannel,
) -> Result(Channel, api.CallFailure)

PATCH /channels/{channel.id}, as Modify Channel, for a guild channel. Name and topic edits carry an undocumented limit of two per ten minutes and glyde cannot see inside the body, so edits_name_or_topic on the payload puts the call in that sublimit rather than letting it spend the channel’s whole PATCH budget.

pub fn edit_call(
  channel: id.Id(id.Channel),
  edit: EditGuildChannel,
) -> rest.Call(Channel)

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

pub fn edit_guild_channel() -> EditGuildChannel
pub fn edit_guild_channel_body(
  payload: EditGuildChannel,
) -> body.Body
pub fn edit_thread(
  api: api.Api,
  thread: id.Id(id.Channel),
  edit: EditThread,
) -> Result(Channel, api.CallFailure)

PATCH /channels/{channel.id}, as Modify Channel, for a thread. Same route as edit, different body.

pub fn edit_thread_body(payload: EditThread) -> body.Body
pub fn edit_thread_call(
  thread: id.Id(id.Channel),
  edit: EditThread,
) -> rest.Call(Channel)

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

pub fn edits_name_or_topic(payload: EditGuildChannel) -> Bool

Whether this edit touches name or topic. Discord meters those two at two per ten minutes and says nothing about it in the headers, so edit asks for the answer and the payload is what holds it. Clearing the topic counts: it is still a topic edit.

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

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

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

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

pub fn has_flag(
  bits: flags.Flags(ChannelFlag),
  flag: ChannelFlag,
) -> Bool
pub fn is_dm(type_: ChannelType) -> Bool
pub fn is_textable(type_: ChannelType) -> Bool

Voice and stage channels have a text chat too.

pub fn is_thread(type_: ChannelType) -> Bool
pub fn is_thread_only(type_: ChannelType) -> Bool

Forums and media channels hold threads only, so they are not textable.

pub fn is_voice(type_: ChannelType) -> Bool
pub fn join_thread(
  api: api.Api,
  thread: id.Id(id.Channel),
) -> Result(Nil, api.CallFailure)

PUT /channels/{thread.id}/thread-members/@me, as Join Thread.

pub fn join_thread_call(
  thread: id.Id(id.Channel),
) -> rest.Call(Nil)

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

pub fn leave_thread(
  api: api.Api,
  thread: id.Id(id.Channel),
) -> Result(Nil, api.CallFailure)

DELETE /channels/{thread.id}/thread-members/@me, as Leave Thread.

pub fn leave_thread_call(
  thread: id.Id(id.Channel),
) -> rest.Call(Nil)

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

pub fn member_overwrite(user: id.Id(id.User)) -> OverwriteBody
pub fn new_edit_thread() -> EditThread
pub const no_channel_flags: flags.Flags(ChannelFlag)
pub fn open_dm(
  api: api.Api,
  recipient: id.Id(id.User),
) -> Result(Channel, api.CallFailure)

POST /users/@me/channels, as Create DM. Safe to call again: Discord returns the existing channel when there is one. Discord also blocks a bot that opens a lot of DM channels quickly from opening any more, so keep the channel id rather than reopening before every message.

pub fn open_dm_call(
  recipient: id.Id(id.User),
) -> rest.Call(Channel)

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

pub fn overwrite_body_to_json(
  payload: OverwriteBody,
) -> json.Json

An entry in a permission_overwrites array, id included.

pub fn overwrite_from(
  overwrite: PermissionOverwrite,
) -> OverwriteBody

An overwrite read off a channel, ready to send back. permission_overwrites replaces the whole array, so an edit that changes one entry has to send the others back with it, and they arrive as PermissionOverwrite.

pub fn overwrite_id(body: OverwriteBody) -> id.Id(id.Overwrite)

The id for the path of PUT /channels/{c}/permissions/{o}, so the path and the body come from the same value.

pub fn overwrite_permissions_body(
  payload: OverwriteBody,
) -> body.Body

PUT /channels/{c}/permissions/{o}, which carries the id in the path instead. Replaces the whole overwrite.

pub fn overwrite_target(
  overwrite: PermissionOverwrite,
) -> OverwriteTarget
pub fn overwrite_type_from_int(
  value: Int,
) -> option.Option(OverwriteType)
pub fn overwrite_type_to_int(value: OverwriteType) -> Int
pub fn overwrite_type_to_json(value: OverwriteType) -> json.Json
pub fn permission_overwrite_decoder() -> decode.Decoder(
  option.Option(PermissionOverwrite),
)

An overwrite whose type this build has no name for yields None, so permission_overwrites holds only entries it can classify.

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

GET /channels/{channel.id}/threads/archived/private, as List Private Archived Threads. Needs MANAGE_THREADS.

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

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

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

GET /channels/{channel.id}/threads/archived/public, as List Public Archived Threads. Newest archive first.

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

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

pub fn remove_thread_member(
  api: api.Api,
  thread: id.Id(id.Channel),
  user: id.Id(id.User),
) -> Result(Nil, api.CallFailure)

DELETE /channels/{thread.id}/thread-members/{user.id}, as Remove Thread Member.

pub fn remove_thread_member_call(
  thread: id.Id(id.Channel),
  user: id.Id(id.User),
) -> rest.Call(Nil)

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

pub fn role_overwrite(role: id.Id(id.Role)) -> OverwriteBody
pub fn set_permission(
  api: api.Api,
  channel: id.Id(id.Channel),
  overwrite: OverwriteBody,
) -> Result(Nil, api.CallFailure)

PUT /channels/{channel.id}/permissions/{overwrite.id}, as Edit Channel Permissions. The path id and the body’s type come from the same OverwriteBody, so a role id cannot go out under type: 1.

pub fn set_permission_call(
  channel: id.Id(id.Channel),
  overwrite: OverwriteBody,
) -> rest.Call(Nil)

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

pub fn start_thread(
  api: api.Api,
  channel: id.Id(id.Channel),
  thread: CreateThread,
) -> Result(Channel, api.CallFailure)

POST /channels/{channel.id}/threads, as Start Thread Without Message. The same path creates a forum or media post, with a different body.

pub fn start_thread_call(
  channel: id.Id(id.Channel),
  thread: CreateThread,
) -> rest.Call(Channel)

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

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

POST /channels/{channel.id}/messages/{message.id}/threads, as Start Thread From Message.

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

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

pub fn thread_list_decoder() -> decode.Decoder(ThreadList)
pub fn thread_member_decoder() -> decode.Decoder(ThreadMember)
pub fn time_cursor_value(cursor: TimeCursor) -> String

The wrapped ISO-8601, for building ?before= where the type lives in another module.

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

POST /channels/{channel.id}/typing, as Trigger Typing Indicator. Lasts ten seconds or until the bot posts.

pub fn typing_call(channel: id.Id(id.Channel)) -> rest.Call(Nil)

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

pub fn video_quality_mode_from_int(
  value: Int,
) -> option.Option(VideoQualityMode)
pub fn video_quality_mode_to_int(value: VideoQualityMode) -> Int
pub fn video_quality_mode_to_json(
  value: VideoQualityMode,
) -> json.Json
Search Document