diff --git a/src/grom.gleam b/src/grom.gleam index d529cac..93f4106 100644 --- a/src/grom.gleam +++ b/src/grom.gleam @@ -24,6 +24,7 @@ import status_code const version: String = "v6.0.0" /// A snowflake is another name for an ID. +/// /// It is possible to retrieve an object's creation date & time from its snowflake. pub opaque type Snowflake(a) { Snowflake(id: Int) @@ -39,7 +40,7 @@ pub opaque type Token { /// Creates a token object for a bot. pub fn bot(token: String) -> Token { - BotToken(token) + BotToken(token:) } /// An error that is returned if something goes wrong using REST (HTTP) API calls. @@ -56,13 +57,13 @@ pub type RestError { } /// An image hash is used to download images from Discord. +/// /// Every non-attachment image (avatars, banners, even guild tag badges) is hashed by Discord and is able to be downloaded through a link. pub opaque type ImageHash { ImageHash(hash: String) } /// Returns the image hash in string form. -/// Generally not needed. pub fn image_hash_to_string(image_hash: ImageHash) -> String { image_hash.hash } @@ -90,12 +91,14 @@ fn snowflake_decoder() -> Decoder(Snowflake(a)) { use id <- decode.then(decode.string) case int.parse(id) { Ok(id) -> decode.success(Snowflake(id)) - Error(_) -> decode.failure(Snowflake(0), "Snowflake") + Error(_) -> decode.failure(Snowflake(id: 0), expected: "Snowflake") } } /// Used for creating arbitary snowflakes. +/// /// This should only be used for hardcoding values or retrieving IDs from a database. +/// /// Don't use this function to change `Snowflake(a)` to a `Snowflake(b)`. /// /// Example usage: @@ -103,7 +106,13 @@ fn snowflake_decoder() -> Decoder(Snowflake(a)) { /// let guild_id: Snowflake(Guild) = new_snowflake(768594524158427167) /// ``` pub fn new_snowflake(from id: Int) -> Snowflake(a) { - Snowflake(id) + Snowflake(id:) +} + +fn list_to_json_object(list: List(Result(#(String, Json), Nil))) -> Json { + list + |> list.filter_map(function.identity) + |> json.object } fn flags_decoder(bits_flags: List(#(Int, flag))) -> Decoder(List(flag)) { @@ -135,7 +144,7 @@ fn permissions_decoder() -> Decoder(List(Permission)) { }) |> decode.success } - Error(_) -> decode.failure([], "Permission") + Error(_) -> decode.failure([], expected: "Permission") } } @@ -258,10 +267,6 @@ pub type User { /// /// Discord hasn't disclosed when this field could be `None`. locale: Option(Locale), - /// The user's banner accent color in RGB hexadecimal format. - /// - /// Is `None` when the user uses a default color based on avatar. - accent_color: Option(Int), flags: List(UserFlag), /// Is `None` if the user's premium type isn't known. premium_type: Option(UserPremiumType), @@ -269,9 +274,7 @@ pub type User { /// /// Visible in the top right corner of a person's profile in the apps. public_flags: List(UserFlag), - /// Is `None` when the user has no avatar decorations. avatar_decoration: Option(UserAvatarDecoration), - /// Is `None` when the user has no collectibles. collectibles: Option(UserCollectibles), /// Is `None` if the user never had a primary guild. primary_guild: Option(UserPrimaryGuild), @@ -283,7 +286,7 @@ fn hex_colour_decoder() -> Decoder(Colour) { case colour.from_rgb_hex(hex) { Ok(colour) -> decode.success(colour) - Error(_) -> decode.failure(colour.black, "Colour") + Error(_) -> decode.failure(colour.black, expected: "Colour") } } @@ -313,11 +316,6 @@ fn user_decoder() -> Decoder(User) { None, decode.optional(hex_colour_decoder()), ) - use accent_color <- decode.optional_field( - "accent_color", - None, - decode.optional(decode.int), - ) use locale <- decode.optional_field( "locale", None, @@ -364,7 +362,6 @@ fn user_decoder() -> Decoder(User) { has_mfa_enabled:, banner_hash:, accent_colour:, - accent_color:, locale:, flags:, premium_type:, @@ -406,7 +403,7 @@ fn user_premium_type_decoder() -> Decoder(UserPremiumType) { 1 -> decode.success(UserHasNitroClassic) 2 -> decode.success(UserHasNitro) 3 -> decode.success(UserHasNitroBasic) - _ -> decode.failure(UserHasNoPremiumSubscription, "PremiumType") + _ -> decode.failure(UserHasNoPremiumSubscription, expected: "PremiumType") } } @@ -554,11 +551,12 @@ fn user_nameplate_palette_decoder() -> Decoder(UserNameplatePalette) { "clover" -> decode.success(CloverUserNameplate) "lemon" -> decode.success(LemonUserNameplate) "white" -> decode.success(WhiteUserNameplate) - _ -> decode.failure(CrimsonUserNameplate, "UserNameplatePalette") + _ -> decode.failure(CrimsonUserNameplate, expected: "UserNameplatePalette") } } /// Used to get the default user avatar. +/// /// If it returns an error - you got lucky - the user still has a discriminator and it somehow isn't an integer. pub fn get_user_index(of user: User) -> Result(Int, Nil) { case user.discriminator { @@ -839,7 +837,7 @@ pub fn modify_current_user_response( } pub fn new_modify_current_user() -> ModifyCurrentUser { - ModifyCurrentUser(None, Skip, Skip) + ModifyCurrentUser(username: None, avatar: Skip, banner: Skip) } /// May cause the discriminator to randomize if changed (mostly applies to bots). @@ -882,8 +880,7 @@ fn modify_current_user_to_json(modify: ModifyCurrentUser) -> Json { modification_to_json(modify.avatar, "avatar", image_data_to_json), modification_to_json(modify.banner, "banner", image_data_to_json), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } fn modification_to_json( @@ -1206,7 +1203,8 @@ fn rfc3339_decoder() -> Decoder(Timestamp) { use rfc3339 <- decode.then(decode.string) case timestamp.parse_rfc3339(rfc3339) { Ok(ts) -> decode.success(ts) - Error(_) -> decode.failure(timestamp.from_unix_seconds(0), "Timestamp") + Error(_) -> + decode.failure(timestamp.from_unix_seconds(0), expected: "Timestamp") } } @@ -1487,7 +1485,7 @@ fn thread_type_decoder() -> Decoder(ThreadType) { ) decode.success(PrivateThread(is_invitable:)) } - _ -> decode.failure(AnnouncementThread, "ThreadType") + _ -> decode.failure(AnnouncementThread, expected: "ThreadType") } } @@ -1576,15 +1574,15 @@ fn guild_channel_decoder() -> Decoder(GuildChannel) { _ -> decode.failure( GuildChannelCategory(CategoryChannel( - Snowflake(0), - Snowflake(0), - Snowflake(0), - [], - None, - 0, - "", + id: Snowflake(0), + channel_id: Snowflake(0), + guild_channel_id: Snowflake(0), + permission_overwrites: [], + guild_id: None, + position: 0, + name: "", )), - "GuildChannel", + expected: "GuildChannel", ) } } @@ -1825,7 +1823,7 @@ fn video_quality_mode_decoder() -> Decoder(VideoQualityMode) { case variant { 1 -> decode.success(AutomaticVideoQuality) 2 -> decode.success(HdVideoQuality) - _ -> decode.failure(AutomaticVideoQuality, "VideoQualityMode") + _ -> decode.failure(AutomaticVideoQuality, expected: "VideoQualityMode") } } @@ -1854,7 +1852,11 @@ fn thread_auto_archive_duration_decoder() -> Decoder(ThreadAutoArchiveDuration) 1440 -> decode.success(ArchiveThreadAfter1Day) 4320 -> decode.success(ArchiveThreadAfter3Days) 10_080 -> decode.success(ArchiveThreadAfter7Days) - _ -> decode.failure(ArchiveThreadAfter1Hour, "ThreadAutoArchiveDuration") + _ -> + decode.failure( + ArchiveThreadAfter1Hour, + expected: "ThreadAutoArchiveDuration", + ) } } @@ -2319,7 +2321,7 @@ fn forum_layout_decoder() -> Decoder(ForumLayout) { 0 -> decode.success(ForumLayoutUnspecified) 1 -> decode.success(ListForumLayout) 2 -> decode.success(GalleryForumLayout) - _ -> decode.failure(ForumLayoutUnspecified, "ForumLayout") + _ -> decode.failure(ForumLayoutUnspecified, expected: "ForumLayout") } } @@ -2336,7 +2338,7 @@ fn forum_sort_order_decoder() -> Decoder(ForumSortOrder) { Some(0) -> decode.success(SortForumPostsByActivity) Some(1) -> decode.success(SortForumPostsByCreationTime) None -> decode.success(ForumSortOrderUnspecified) - _ -> decode.failure(ForumSortOrderUnspecified, "ForumSortOrder") + _ -> decode.failure(ForumSortOrderUnspecified, expected: "ForumSortOrder") } } @@ -2475,8 +2477,8 @@ fn permission_overwrite_decoder() -> Decoder(PermissionOverwrite) { } _ -> decode.failure( - RolePermissionOverwrite(Snowflake(0), [], []), - "PermissionOverwrite", + RolePermissionOverwrite(role_id: Snowflake(id: 0), allow: [], deny: []), + expected: "PermissionOverwrite", ) } } @@ -2875,7 +2877,7 @@ fn sticker_format_type_decoder() -> Decoder(StickerFormatType) { 2 -> decode.success(ApngSticker) 3 -> decode.success(LottieSticker) 4 -> decode.success(GifSticker) - _ -> decode.failure(PngSticker, "StickerFormatType") + _ -> decode.failure(PngSticker, expected: "StickerFormatType") } } @@ -2899,7 +2901,7 @@ fn guild_nsfw_level_decoder() -> Decoder(GuildNsfwLevel) { 1 -> decode.success(ExplicitGuild) 2 -> decode.success(SafeGuild) 3 -> decode.success(AgeRestrictedGuild) - _ -> decode.failure(DefaultGuildNsfwLevel, "GuildNsfwLevel") + _ -> decode.failure(DefaultGuildNsfwLevel, expected: "GuildNsfwLevel") } } @@ -3022,7 +3024,7 @@ fn guild_premium_tier_decoder() -> Decoder(GuildPremiumTier) { 1 -> decode.success(GuildPremiumTier1) 2 -> decode.success(GuildPremiumTier2) 3 -> decode.success(GuildPremiumTier3) - _ -> decode.failure(GuildWithoutPremium, "GuildPremiumTier") + _ -> decode.failure(GuildWithoutPremium, expected: "GuildPremiumTier") } } @@ -3344,7 +3346,7 @@ fn application_webhook_event_status_decoder() -> Decoder( _ -> decode.failure( ApplicationWebhookEventsDisabled, - "ApplicationWebhookEventStatus", + expected: "ApplicationWebhookEventStatus", ) } } @@ -3458,7 +3460,7 @@ fn developer_team_member_role_decoder() -> Decoder(DeveloperTeamMemberRole) { "admin" -> decode.success(DeveloperTeamAdmin) "developer" -> decode.success(DeveloperTeamDeveloper) "read_only" -> decode.success(DeveloperTeamReadOnlyAccess) - _ -> decode.failure(DeveloperTeamAdmin, "DeveloperTeamMemberRole") + _ -> decode.failure(DeveloperTeamAdmin, expected: "DeveloperTeamMemberRole") } } @@ -3476,7 +3478,11 @@ fn developer_team_membership_state_decoder() -> Decoder( case variant { 1 -> decode.success(DeveloperIsInvited) 2 -> decode.success(DeveloperIsAccepted) - _ -> decode.failure(DeveloperIsAccepted, "DeveloperTeamMembershipState") + _ -> + decode.failure( + DeveloperIsAccepted, + expected: "DeveloperTeamMembershipState", + ) } } @@ -3490,7 +3496,8 @@ fn guild_required_mfa_level_decoder() -> Decoder(GuildRequiredMfaLevel) { case variant { 0 -> decode.success(GuildDoesNotRequireMfa) 1 -> decode.success(GuildRequiresMfaForModerationActions) - _ -> decode.failure(GuildDoesNotRequireMfa, "GuildRequiredMfaLevel") + _ -> + decode.failure(GuildDoesNotRequireMfa, expected: "GuildRequiredMfaLevel") } } @@ -3731,7 +3738,10 @@ fn guild_member_verification_level_decoder() -> Decoder( 3 -> decode.success(HighGuildMemberVerification) 4 -> decode.success(VeryHighGuildMemberVerification) _ -> - decode.failure(NoGuildMemberVerification, "GuildMemberVerificationLevel") + decode.failure( + NoGuildMemberVerification, + expected: "GuildMemberVerificationLevel", + ) } } @@ -3752,7 +3762,7 @@ fn guild_default_message_notification_setting_decoder() -> Decoder( _ -> decode.failure( NotifyForAllMessages, - "GuildDefaultMessageNotificationSetting", + expected: "GuildDefaultMessageNotificationSetting", ) } } @@ -3777,7 +3787,7 @@ fn guild_explicit_content_filter_setting_decoder() -> Decoder( _ -> decode.failure( GuildExplicitContentFilterDisabled, - "GuildExplicitContentFilterSetting", + expected: "GuildExplicitContentFilterSetting", ) } } @@ -3949,25 +3959,25 @@ pub opaque type ModifyGuild { pub fn new_modify_guild() -> ModifyGuild { ModifyGuild( - None, - Skip, - Skip, - Skip, - Skip, - None, - Skip, - Skip, - Skip, - Skip, - Skip, - None, - Skip, - Skip, - Skip, - None, - Skip, - None, - Skip, + name: None, + member_verification_level: Skip, + default_message_notification_setting: Skip, + explicit_content_filter_setting: Skip, + afk_channel_id: Skip, + afk_timeout: None, + icon: Skip, + splash: Skip, + discovery_splash: Skip, + banner: Skip, + system_channel_id: Skip, + system_channel_flags: None, + rules_channel_id: Skip, + public_updates_channel_id: Skip, + preferred_locale: Skip, + features: None, + description: Skip, + is_premium_progress_bar_enabled: None, + safety_alerts_channel_id: Skip, ) } @@ -4053,7 +4063,7 @@ fn afk_timeout_decoder() -> Decoder(AfkTimeout) { 900 -> decode.success(AfkTimeout900Seconds) 1800 -> decode.success(AfkTimeout1800Seconds) 3600 -> decode.success(AfkTimeout3600Seconds) - _ -> decode.failure(AfkTimeout60Seconds, "AfkTimeout") + _ -> decode.failure(AfkTimeout60Seconds, expected: "AfkTimeout") } } @@ -4280,7 +4290,7 @@ fn locale_decoder() -> Decoder(Locale) { "ja" -> decode.success(JapaneseLocale) "zh-TW" -> decode.success(ChineseTaiwanLocale) "ko" -> decode.success(KoreanLocale) - _ -> decode.failure(IndonesianLocale, "Locale") + _ -> decode.failure(IndonesianLocale, expected: "Locale") } } @@ -4424,8 +4434,7 @@ fn modify_guild_to_json(modify: ModifyGuild) -> Json { snowflake_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } fn guild_feature_to_json(feature: GuildFeature) -> Json { @@ -4628,8 +4637,7 @@ fn create_text_channel_to_json(create: CreateTextChannel) -> Json { rate_limit_per_user_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub opaque type CreateAnnouncementChannel { @@ -4671,8 +4679,7 @@ fn create_announcement_channel_to_json( rate_limit_per_user_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -4703,7 +4710,16 @@ pub fn new_announcement_channel_response( pub fn new_announcement_channel( named name: String, ) -> CreateAnnouncementChannel { - CreateAnnouncementChannel(name, None, None, None, None, None, None, None) + CreateAnnouncementChannel( + name:, + topic: None, + position: None, + permission_overwrites: None, + parent_id: None, + is_nsfw: None, + default_thread_auto_archive_duration: None, + default_thread_rate_limit_per_user: None, + ) } pub fn new_announcement_channel_with_topic( @@ -4802,7 +4818,17 @@ pub fn new_text_channel_response( /// Requires the `AllowManagingChannels` permission. pub fn new_text_channel(named name: String) -> CreateTextChannel { - CreateTextChannel(name, None, None, None, None, None, None, None, None) + CreateTextChannel( + name:, + topic: None, + rate_limit_per_user: None, + position: None, + permission_overwrites: None, + parent_id: None, + is_nsfw: None, + default_thread_auto_archive_duration: None, + default_thread_rate_limit_per_user: None, + ) } pub fn new_text_channel_with_topic( @@ -4915,8 +4941,7 @@ fn create_voice_channel_to_json(create: CreateVoiceChannel) -> Json { video_quality_mode_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -4944,7 +4969,18 @@ pub fn new_voice_channel_response( } pub fn new_voice_channel(named name: String) -> CreateVoiceChannel { - CreateVoiceChannel(name, None, None, None, None, None, None, None, None, None) + CreateVoiceChannel( + name:, + rate_limit_per_user: None, + bitrate: None, + user_limit: None, + position: None, + permission_overwrites: None, + parent_id: None, + is_nsfw: None, + rtc_region_id: None, + video_quality_mode: None, + ) } /// The rate limit per user amount of time that a user has to wait between sending messages in the voice-channel attached text channel. @@ -5046,8 +5082,7 @@ fn create_category_channel_to_json(create: CreateCategoryChannel) -> Json { json.array(_, permission_overwrite_to_json), ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -5075,7 +5110,7 @@ pub fn new_category_channel_response( } pub fn new_category_channel(named name: String) -> CreateCategoryChannel { - CreateCategoryChannel(name, None, None) + CreateCategoryChannel(name:, position: None, permission_overwrites: None) } /// Channels without a specified position will automatically be assigned one at the bottom of their channel list. @@ -5138,8 +5173,7 @@ fn create_stage_channel_to_json(create: CreateStageChannel) -> Json { video_quality_mode_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -5167,7 +5201,18 @@ pub fn new_stage_channel_response( } pub fn new_stage_channel(named name: String) -> CreateStageChannel { - CreateStageChannel(name, None, None, None, None, None, None, None, None, None) + CreateStageChannel( + name:, + rate_limit_per_user: None, + bitrate: None, + user_limit: None, + position: None, + permission_overwrites: None, + parent_id: None, + is_nsfw: None, + rtc_region_id: None, + video_quality_mode: None, + ) } /// The rate limit per user amount of time that a user has to wait between sending messages in the stage-channel attached text channel. @@ -5316,8 +5361,7 @@ fn create_forum_channel_to_json(create: CreateForumChannel) -> Json { rate_limit_per_user_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -5346,19 +5390,19 @@ pub fn new_forum_channel_response( pub fn new_forum_channel(named name: String) -> CreateForumChannel { CreateForumChannel( - name, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, + name:, + topic: None, + rate_limit_per_user: None, + position: None, + permission_overwrites: None, + parent_id: None, + is_nsfw: None, + default_thread_auto_archive_duration: None, + default_reaction: None, + available_tags: None, + default_sort_order: None, + default_layout: None, + default_thread_rate_limit_per_user: None, ) } @@ -5514,8 +5558,7 @@ fn create_media_channel_to_json(create: CreateMediaChannel) -> Json { rate_limit_per_user_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -5544,18 +5587,18 @@ pub fn new_media_channel_response( pub fn new_media_channel(named name: String) -> CreateMediaChannel { CreateMediaChannel( - name, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, + name:, + topic: None, + rate_limit_per_user: None, + position: None, + permission_overwrites: None, + parent_id: None, + is_nsfw: None, + default_thread_auto_archive_duration: None, + default_reaction: None, + available_tags: None, + default_sort_order: None, + default_thread_rate_limit_per_user: None, ) } @@ -5655,7 +5698,12 @@ pub opaque type ModifyGuildChannelPosition { pub fn new_modify_guild_channel_position( of_channel_with_id id: Snowflake(GuildChannel), ) -> ModifyGuildChannelPosition { - ModifyGuildChannelPosition(id, Skip, None, Skip) + ModifyGuildChannelPosition( + id:, + position: Skip, + sync_permissions: None, + parent_id: Skip, + ) } /// Channels with the same position are sorted by ID (newer channel will be lower) @@ -5702,8 +5750,7 @@ fn modify_guild_channel_position_to_json( optional_to_json(modify.sync_permissions, "lock_permissions", json.bool), modification_to_json(modify.parent_id, "parent_id", snowflake_to_json), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -5921,7 +5968,15 @@ pub opaque type ModifyGuildMember { pub type MovableChannel pub fn new_modify_guild_member() -> ModifyGuildMember { - ModifyGuildMember(Skip, Skip, Skip, Skip, Skip, Skip, Skip) + ModifyGuildMember( + nick: Skip, + roles: Skip, + is_mute: Skip, + is_deaf: Skip, + channel_id: Skip, + communication_disabled_until: Skip, + flags: Skip, + ) } /// Requires the `AllowManagingNicknames` permission. @@ -6062,8 +6117,7 @@ fn modify_guild_member_to_json(modify: ModifyGuildMember) -> Json { bits_guild_member_flags(), )), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } fn timestamp_to_json(timestamp: Timestamp) -> Json { @@ -6099,8 +6153,8 @@ pub fn modify_guild_member_response( handle_response(response, decode_with: guild_member_decoder()) } -pub opaque type ModifyCurrentMember { - ModifyCurrentMember( +pub opaque type ModifyCurrentGuildMember { + ModifyCurrentGuildMember( nick: Modification(String), banner: Modification(ImageData), avatar: Modification(ImageData), @@ -6108,88 +6162,89 @@ pub opaque type ModifyCurrentMember { ) } -pub fn new_modify_current_member() -> ModifyCurrentMember { - ModifyCurrentMember(Skip, Skip, Skip, Skip) +pub fn new_modify_current_guild_member() -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(nick: Skip, banner: Skip, avatar: Skip, bio: Skip) } /// Requires the `AllowChangingOwnNickname` permission. -pub fn modify_current_member_nick( - modify: ModifyCurrentMember, +pub fn modify_current_guild_member_nick( + modify: ModifyCurrentGuildMember, new nick: String, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, nick: Modify(nick)) +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, nick: Modify(nick)) } /// Requires the `AllowChangingOwnNickname` permission. -pub fn delete_current_member_nick( - modify: ModifyCurrentMember, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, nick: Delete) +pub fn delete_current_guild_member_nick( + modify: ModifyCurrentGuildMember, +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, nick: Delete) } /// Modifies your per-guild banner. -pub fn modify_current_member_banner( - modify: ModifyCurrentMember, +pub fn modify_current_guild_member_banner( + modify: ModifyCurrentGuildMember, new banner: ImageData, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, banner: Modify(banner)) +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, banner: Modify(banner)) } /// Modifies your per-guild banner. -pub fn delete_current_member_banner( - modify: ModifyCurrentMember, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, banner: Delete) +pub fn delete_current_guild_member_banner( + modify: ModifyCurrentGuildMember, +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, banner: Delete) } /// Modifies your per-guild avatar. -pub fn modify_current_member_avatar( - modify: ModifyCurrentMember, +pub fn modify_current_guild_member_avatar( + modify: ModifyCurrentGuildMember, new avatar: ImageData, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, avatar: Modify(avatar)) +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, avatar: Modify(avatar)) } /// Modifies your per-guild avatar. -pub fn delete_current_member_avatar( - modify: ModifyCurrentMember, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, avatar: Delete) +pub fn delete_current_guild_member_avatar( + modify: ModifyCurrentGuildMember, +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, avatar: Delete) } /// Modifies your per-guild bio. -pub fn modify_current_member_bio( - modify: ModifyCurrentMember, +pub fn modify_current_guild_member_bio( + modify: ModifyCurrentGuildMember, new bio: String, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, bio: Modify(bio)) +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, bio: Modify(bio)) } /// Deletes your per-guild bio. -pub fn delete_current_member_bio( - modify: ModifyCurrentMember, -) -> ModifyCurrentMember { - ModifyCurrentMember(..modify, bio: Delete) +pub fn delete_current_guild_member_bio( + modify: ModifyCurrentGuildMember, +) -> ModifyCurrentGuildMember { + ModifyCurrentGuildMember(..modify, bio: Delete) } -fn modify_current_member_to_json(modify: ModifyCurrentMember) -> Json { +fn modify_current_guild_member_to_json( + modify: ModifyCurrentGuildMember, +) -> Json { [ modification_to_json(modify.nick, "nick", json.string), modification_to_json(modify.banner, "banner", image_data_to_json), modification_to_json(modify.avatar, "avatar", image_data_to_json), modification_to_json(modify.bio, "bio", json.string), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } -pub fn modify_current_member_request( +pub fn modify_current_guild_member_request( token token: Token, in_guild_with_id guild_id: Snowflake(Guild), - using modify: ModifyCurrentMember, + using modify: ModifyCurrentGuildMember, reason reason: Option(String), ) -> Request(String) { - let body = modify |> modify_current_member_to_json |> json.to_string + let body = modify |> modify_current_guild_member_to_json |> json.to_string new_request( token:, @@ -6200,7 +6255,7 @@ pub fn modify_current_member_request( |> request.set_body(body) } -pub fn modify_current_member_response( +pub fn modify_current_guild_member_response( response: Response(String), ) -> Result(GuildMember, RestError) { handle_response(response, decode_with: guild_member_decoder()) @@ -6303,7 +6358,7 @@ pub opaque type GetGuildBansOptions { } pub fn new_get_guild_bans_options() -> GetGuildBansOptions { - GetGuildBansOptions(None, None, None) + GetGuildBansOptions(limit: None, before: None, after: None) } /// Between 1-1000 bans. @@ -6485,8 +6540,7 @@ pub fn bulk_guild_ban_request( duration_to_json_seconds, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object |> json.to_string new_request( @@ -6585,8 +6639,7 @@ fn create_role_to_json(create: CreateRole) -> Json { optional_to_json(create.unicode_emoji, "unicode_emoji", json.string), optional_to_json(create.is_mentionable, "mentionable", json.bool), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Defaults: @@ -6598,7 +6651,15 @@ fn create_role_to_json(create: CreateRole) -> Json { /// * unicode_emoji: null /// * is_mentionable: false pub fn new_role() -> CreateRole { - CreateRole(None, None, None, None, None, None, None) + CreateRole( + name: None, + permissions: None, + colours: None, + is_hoisted: None, + icon: None, + unicode_emoji: None, + is_mentionable: None, + ) } pub fn new_role_with_name(create: CreateRole, name: String) -> CreateRole { @@ -6621,7 +6682,11 @@ pub fn new_role_with_colours( } pub fn new_role_colours() -> RoleColours { - RoleColours(colour.black, None, None) + RoleColours( + primary_colour: colour.black, + secondary_colour: None, + tertiary_colour: None, + ) } pub fn role_colours_with_primary_colour( @@ -6704,8 +6769,7 @@ fn modify_role_position_to_json(modify: ModifyRolePosition) -> Json { Ok(#("id", snowflake_to_json(modify.id))), modification_to_json(modify.position, "position", json.int), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Roles with the same position are sorted by ID. @@ -6776,12 +6840,19 @@ fn modify_role_to_json(modify: ModifyRole) -> Json { modification_to_json(modify.unicode_emoji, "unicode_emoji", json.string), modification_to_json(modify.is_mentionable, "mentionable", json.bool), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_role() -> ModifyRole { - ModifyRole(Skip, Skip, Skip, Skip, Skip, Skip, Skip) + ModifyRole( + name: Skip, + permissions: Skip, + colours: Skip, + is_hoisted: Skip, + icon: Skip, + unicode_emoji: Skip, + is_mentionable: Skip, + ) } pub fn modify_role_name(modify: ModifyRole, new name: String) -> ModifyRole { @@ -6965,15 +7036,14 @@ fn prune_guild_to_json(prune: PruneGuild) -> Json { snowflake_to_json, )), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Defaults: /// * days: 7 /// * include_roles: None pub fn new_prune_guild() -> PruneGuild { - PruneGuild(None, False, None) + PruneGuild(days: None, compute_prune_count: False, include_roles: None) } /// The minimum number of days is 1 and the maximum number of days is 30. Numbers not within these bounds will kick the closest amount of time (1 or 30 days). @@ -7386,8 +7456,11 @@ fn scheduled_event_location_decoder() -> Decoder(ScheduledEventLocation) { } _ -> decode.failure( - ScheduledEventInStageChannel(Snowflake(0), None), - "ScheduledEventLocation", + ScheduledEventInStageChannel( + channel_id: Snowflake(0), + scheduled_end_time: None, + ), + expected: "ScheduledEventLocation", ) } } @@ -7410,7 +7483,8 @@ fn scheduled_event_status_decoder() -> Decoder(ScheduledEventStatus) { 2 -> decode.success(ScheduledEventIsActive) 3 -> decode.success(ScheduledEventIsCompleted) 4 -> decode.success(ScheduledEventIsCancelled) - _ -> decode.failure(ScheduledEventIsPlanned, "ScheduledEventStatus") + _ -> + decode.failure(ScheduledEventIsPlanned, expected: "ScheduledEventStatus") } } @@ -7455,28 +7529,27 @@ fn invite_target_decoder() -> Decoder(Option(InviteTarget)) { Some(_) -> decode.failure( Some( - StreamInvite(User( - Snowflake(0), - "", - "", - None, - None, - False, - False, - None, - None, - None, - None, - None, - [], - None, - [], - None, - None, - None, + StreamInvite(streaming_user: User( + id: Snowflake(0), + username: "", + discriminator: "", + global_name: None, + avatar_hash: None, + is_bot: False, + is_system: False, + has_mfa_enabled: None, + banner_hash: None, + accent_colour: None, + locale: None, + flags: [], + premium_type: None, + public_flags: [], + avatar_decoration: None, + collectibles: None, + primary_guild: None, )), ), - "InviteTarget", + expected: "InviteTarget", ) None -> decode.success(None) } @@ -7569,7 +7642,7 @@ fn weekday_decoder() -> Decoder(Weekday) { 4 -> decode.success(weekday.Friday) 5 -> decode.success(weekday.Saturday) 6 -> decode.success(weekday.Sunday) - _ -> decode.failure(weekday.Monday, "Weekday") + _ -> decode.failure(weekday.Monday, expected: "Weekday") } } @@ -7588,7 +7661,7 @@ fn month_decoder() -> Decoder(calendar.Month) { 10 -> decode.success(calendar.October) 11 -> decode.success(calendar.November) 12 -> decode.success(calendar.December) - _ -> decode.failure(calendar.January, "Month") + _ -> decode.failure(calendar.January, expected: "Month") } } @@ -7615,8 +7688,8 @@ fn scheduled_event_recurrence_rule_frequency_decoder() -> Decoder( 3 -> decode.success(ScheduledEventRecursDaily(interval:)) _ -> decode.failure( - ScheduledEventRecursYearly(0), - "ScheduledEventRecurrenceRuleFrequency", + ScheduledEventRecursYearly(interval: 0), + expected: "ScheduledEventRecurrenceRuleFrequency", ) } } @@ -7708,7 +7781,10 @@ fn guild_integration_data_decoder() -> Decoder(GuildIntegrationData) { ) "guild_subscription" -> decode.success(GuildIntegrationGuildSubscription) _ -> - decode.failure(GuildIntegrationGuildSubscription, "GuildIntegrationData") + decode.failure( + GuildIntegrationGuildSubscription, + expected: "GuildIntegrationData", + ) } } @@ -7856,7 +7932,11 @@ fn guild_social_integration_data_decoder() -> Decoder( use is_syncing_emoticons <- decode.field("enable_emoticons", decode.bool) decode.success(TwitchGuildIntegration(is_syncing_emoticons:)) } - _ -> decode.failure(YoutubeGuildIntegration, "GuildSocialIntegrationData") + _ -> + decode.failure( + YoutubeGuildIntegration, + expected: "GuildSocialIntegrationData", + ) } } @@ -7877,7 +7957,7 @@ fn guild_social_integration_subscription_expiration_behavior_decoder() -> Decode _ -> decode.failure( RemoveRoleOnSubscriptionExpiration, - "GuildSocialIntegrationSubscriptionExpirationBehavior", + expected: "GuildSocialIntegrationSubscriptionExpirationBehavior", ) } } @@ -7971,7 +8051,7 @@ pub opaque type ModifyGuildWidgetSettings { } pub fn new_modify_guild_widget_settings() -> ModifyGuildWidgetSettings { - ModifyGuildWidgetSettings(None, Skip) + ModifyGuildWidgetSettings(is_enabled: None, channel_id: Skip) } pub fn enable_guild_widget( @@ -8006,8 +8086,7 @@ fn modify_guild_widget_settings_to_json( optional_to_json(modify.is_enabled, "enabled", json.bool), modification_to_json(modify.channel_id, "channel_id", snowflake_to_json), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingGuild` permission. @@ -8158,7 +8237,7 @@ fn user_status_decoder() -> Decoder(UserStatus) { "idle" -> decode.success(UserIsIdle) "invisible" -> decode.success(UserIsInvisible) "offline" -> decode.success(UserIsOffline) - _ -> decode.failure(UserIsOnline, "UserStatus") + _ -> decode.failure(UserIsOnline, expected: "UserStatus") } } @@ -8261,12 +8340,15 @@ fn modify_guild_welcome_screen_to_json( ), modification_to_json(modify.description, "description", json.string), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_guild_welcome_screen() -> ModifyGuildWelcomeScreen { - ModifyGuildWelcomeScreen(Skip, Skip, Skip) + ModifyGuildWelcomeScreen( + is_enabled: Skip, + welcome_channels: Skip, + description: Skip, + ) } pub fn enable_guild_welcome_screen( @@ -8369,7 +8451,7 @@ fn guild_onboarding_mode_decoder() -> Decoder(GuildOnboardingMode) { case variant { 0 -> decode.success(DefaultOnboardingMode) 1 -> decode.success(AdvancedOnboardingMode) - _ -> decode.failure(DefaultOnboardingMode, "GuildOnboardingMode") + _ -> decode.failure(DefaultOnboardingMode, expected: "GuildOnboardingMode") } } @@ -8424,7 +8506,7 @@ fn guild_onboarding_prompt_type_decoder() -> Decoder(GuildOnboardingPromptType) _ -> decode.failure( MultipleChoiceGuildOnboardingPrompt, - "GuildOnboardingPromptType", + expected: "GuildOnboardingPromptType", ) } } @@ -8512,12 +8594,14 @@ fn modify_guild_incidents_data_to_json( timestamp_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_guild_incidents_data() -> ModifyGuildIncidentsData { - ModifyGuildIncidentsData(Skip, Skip) + ModifyGuildIncidentsData( + invites_disabled_until: Skip, + dms_disabled_until: Skip, + ) } /// Disable invites in response to an incident. @@ -8632,15 +8716,15 @@ fn channel_decoder() -> Decoder(Channel) { _ -> decode.failure( ChannelCategory(CategoryChannel( - Snowflake(0), - Snowflake(0), - Snowflake(0), - [], - None, - 0, - "", + id: Snowflake(0), + channel_id: Snowflake(0), + guild_channel_id: Snowflake(0), + permission_overwrites: [], + guild_id: None, + position: 0, + name: "", )), - "Channel", + expected: "Channel", ) } } @@ -8709,12 +8793,22 @@ fn modify_text_channel_to_json(modify: ModifyTextChannel) -> Json { rate_limit_per_user_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_text_channel() -> ModifyTextChannel { - ModifyTextChannel(None, None, Skip, Skip, None, Skip, None, Skip, Skip, None) + ModifyTextChannel( + name: None, + switch_to_announcement: None, + position: Skip, + topic: Skip, + is_nsfw: None, + rate_limit_per_user: Skip, + permission_overwrites: None, + parent_id: Skip, + default_thread_auto_archive_duration: Skip, + default_thread_rate_limit_per_user: None, + ) } /// The modified value will only apply to new threads - existing threads won't auto-update. @@ -8873,7 +8967,18 @@ pub opaque type ModifyVoiceChannel { } pub fn new_modify_voice_channel() -> ModifyVoiceChannel { - ModifyVoiceChannel(None, Skip, None, Skip, Skip, Skip, None, Skip, Skip, Skip) + ModifyVoiceChannel( + name: None, + position: Skip, + is_nsfw: None, + rate_limit_per_user: Skip, + bitrate: Skip, + user_limit: Skip, + permission_overwrites: None, + parent_id: Skip, + rtc_region_id: Skip, + video_quality_mode: Skip, + ) } pub fn modify_voice_channel_name( @@ -9016,8 +9121,7 @@ fn modify_voice_channel_to_json(modify: ModifyVoiceChannel) -> Json { video_quality_mode_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -9053,7 +9157,7 @@ pub opaque type ModifyCategoryChannel { } pub fn new_modify_category_channel() -> ModifyCategoryChannel { - ModifyCategoryChannel(None, Skip, None) + ModifyCategoryChannel(name: None, position: Skip, permission_overwrites: None) } pub fn modify_category_channel_name( @@ -9097,8 +9201,7 @@ fn modify_category_channel_to_json(modify: ModifyCategoryChannel) -> Json { json.array(_, permission_overwrite_to_json), ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -9164,8 +9267,7 @@ fn modify_announcement_channel_to_json( thread_auto_archive_duration_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn modify_announcement_channel_request( @@ -9201,7 +9303,16 @@ pub fn modify_announcement_channel_with_conversion_response( } pub fn new_modify_announcement_channel() -> ModifyAnnouncementChannel { - ModifyAnnouncementChannel(None, None, Skip, Skip, None, None, Skip, Skip) + ModifyAnnouncementChannel( + name: None, + switch_to_text: None, + position: Skip, + topic: Skip, + is_nsfw: None, + permission_overwrites: None, + parent_id: Skip, + default_thread_auto_archive_duration: Skip, + ) } pub fn modify_announcement_channel_name( @@ -9314,7 +9425,18 @@ pub opaque type ModifyStageChannel { } pub fn new_modify_stage_channel() -> ModifyStageChannel { - ModifyStageChannel(None, Skip, None, Skip, Skip, Skip, None, Skip, Skip, Skip) + ModifyStageChannel( + name: None, + position: Skip, + is_nsfw: None, + rate_limit_per_user: Skip, + bitrate: Skip, + user_limit: Skip, + permission_overwrites: None, + parent_id: Skip, + rtc_region_id: Skip, + video_quality_mode: Skip, + ) } pub fn modify_stage_channel_name( @@ -9457,8 +9579,7 @@ fn modify_stage_channel_to_json(modify: ModifyStageChannel) -> Json { video_quality_mode_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Requires the `AllowManagingChannels` permission. @@ -9557,26 +9678,25 @@ fn modify_forum_channel_to_json(modify: ModifyForumChannel) -> Json { forum_layout_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_forum_channel() -> ModifyForumChannel { ModifyForumChannel( - None, - Skip, - Skip, - None, - Skip, - None, - Skip, - Skip, - None, - None, - Skip, - None, - Skip, - None, + name: None, + position: Skip, + topic: Skip, + is_nsfw: None, + rate_limit_per_user: Skip, + permission_overwrites: None, + parent_id: Skip, + default_thread_auto_archive_duration: Skip, + flags: None, + available_tags: None, + default_reaction: Skip, + default_thread_rate_limit_per_user: None, + default_sort_order: Skip, + default_layout: None, ) } @@ -9820,25 +9940,24 @@ fn modify_media_channel_to_json(modify: ModifyMediaChannel) -> Json { forum_sort_order_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_media_channel() -> ModifyMediaChannel { ModifyMediaChannel( - None, - Skip, - Skip, - None, - Skip, - None, - Skip, - Skip, - None, - None, - Skip, - None, - Skip, + name: None, + position: Skip, + topic: Skip, + is_nsfw: None, + rate_limit_per_user: Skip, + permission_overwrites: None, + parent_id: Skip, + default_thread_auto_archive_duration: Skip, + flags: None, + available_tags: None, + default_reaction: Skip, + default_thread_rate_limit_per_user: None, + default_sort_order: Skip, ) } @@ -10047,12 +10166,20 @@ fn modify_thread_to_json(modify: ModifyThread) -> Json { snowflake_to_json, )), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_modify_thread() -> ModifyThread { - ModifyThread(None, None, None, None, None, Skip, None, None) + ModifyThread( + name: None, + is_archived: None, + auto_archive_duration: None, + is_locked: None, + is_invitable: None, + rate_limit_per_user: Skip, + flags: None, + applied_tags_ids: None, + ) } pub fn modify_thread_name( @@ -10287,12 +10414,19 @@ fn create_guild_channel_invite_to_json( Some(target) -> invite_target_to_json(target) None -> [] }) - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_guild_channel_invite() -> CreateGuildChannelInvite { - CreateGuildChannelInvite(None, None, None, None, None, None, None) + CreateGuildChannelInvite( + max_age: None, + max_uses: None, + is_temporary: None, + is_unique: None, + target: None, + allowlist: None, + automatically_awarded_roles_ids: None, + ) } /// Defaults to 24 hours. @@ -10484,7 +10618,11 @@ pub opaque type CreateThreadFromMessage { } pub fn new_thread_from_message(named name: String) -> CreateThreadFromMessage { - CreateThreadFromMessage(name, None, None) + CreateThreadFromMessage( + name:, + auto_archive_duration: None, + rate_limit_per_user: None, + ) } pub fn new_thread_from_message_with_auto_archive_duration( @@ -10515,8 +10653,7 @@ fn create_thread_from_message_to_json(create: CreateThreadFromMessage) -> Json { rate_limit_per_user_to_json, ), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } /// Behavior: @@ -10565,7 +10702,12 @@ pub fn new_thread( named name: String, of_type type_: ThreadType, ) -> CreateThread { - CreateThread(name, None, type_, None) + CreateThread( + name:, + auto_archive_duration: None, + type_:, + rate_limit_per_user: None, + ) } pub fn new_thread_with_auto_archive_duration( @@ -10597,8 +10739,7 @@ fn create_thread_to_json(create: CreateThread) -> Json { ), ] |> list.append(thread_type_to_json(create.type_)) - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_thread_request( @@ -10628,23 +10769,313 @@ pub opaque type CreateForumLikeThread { ) } +pub fn new_forum_like_thread_request( + token token: Token, + in_channel_with_id channel_id: Snowflake(ForumLikeChannel), + using create: CreateForumLikeThread, + reason reason: Option(String), +) -> Request(BitArray) { + let json = create |> create_forum_like_thread_to_json |> json.to_string + + let request = + new_request( + token:, + to: "/channels/" <> snowflake_to_string(channel_id) <> "/threads", + method: http.Post, + ) + |> request.set_body(json) + |> request_with_reason(reason) + |> request.map(bit_array.from_string) + + case create.message.attachments { + Some(attachments) -> { + let body = + attachments + |> list.index_map(fn(attachment, idx) { + #( + "files[" <> int.to_string(idx) <> "]", + field.File( + name: attachment.file.name, + content_type: attachment.file.content_type, + content: attachment.file.content, + ), + ) + }) + |> list.prepend(#( + "payload_json", + field.StringWithType(content: json, content_type: "application/json"), + )) + + request + |> multipart_form.to_request(body) + } + None -> request + } +} + +fn create_forum_like_thread_to_json(create: CreateForumLikeThread) -> Json { + [ + Ok(#("name", json.string(create.name))), + optional_to_json( + create.auto_archive_duration, + "auto_archive_duration", + thread_auto_archive_duration_to_json, + ), + optional_to_json( + create.rate_limit_per_user, + "rate_limit_per_user", + rate_limit_per_user_to_json, + ), + Ok(#("message", create_forum_like_thread_message_to_json(create.message))), + optional_to_json(create.applied_tags_ids, "applied_tags", json.array( + _, + snowflake_to_json, + )), + ] + |> list_to_json_object +} + +pub fn new_forum_like_thread( + named name: String, + using message: CreateForumLikeThreadMessage, +) -> CreateForumLikeThread { + CreateForumLikeThread( + name:, + auto_archive_duration: None, + rate_limit_per_user: None, + message:, + applied_tags_ids: None, + ) +} + +pub fn new_forum_like_thread_with_auto_archive_duration( + create: CreateForumLikeThread, + duration: ThreadAutoArchiveDuration, +) -> CreateForumLikeThread { + CreateForumLikeThread(..create, auto_archive_duration: Some(duration)) +} + +pub fn new_forum_like_thread_with_rate_limit_per_user( + create: CreateForumLikeThread, + limit: RateLimitPerUser, +) -> CreateForumLikeThread { + CreateForumLikeThread(..create, rate_limit_per_user: Some(limit)) +} + +pub fn new_forum_like_thread_with_applied_tags( + create: CreateForumLikeThread, + ids ids: List(Snowflake(ForumTag)), +) -> CreateForumLikeThread { + CreateForumLikeThread(..create, applied_tags_ids: Some(ids)) +} + pub opaque type CreateForumLikeThreadMessage { CreateForumLikeThreadMessage( content: Option(String), embeds: Option(List(CreateForumLikeThreadMessageEmbed)), allowed_mentions: List(AllowedMessageMention), + components: Option(List(CreateMessageComponent)), + sticker_ids: Option(List(Snowflake(Sticker))), + attachments: Option(List(CreateMessageAttachment)), + flags: Option(List(CreateForumLikeThreadMessageFlag)), ) } -/// This type controls which persons get notified after mentioning them in a message. -/// -/// This type will always be used as a special unique list - you cannot reuse the same variant. -/// -/// That means that, i.e. +fn create_forum_like_thread_message_to_json( + create: CreateForumLikeThreadMessage, +) -> Json { + let attachments = case create.attachments { + Some(attachments) -> + Ok(#( + "attachments", + attachments + |> list.index_map(fn(attachment, idx) { + create_message_attachment_to_json(idx, attachment) + }) + |> json.preprocessed_array, + )) + None -> Error(Nil) + } + [ + optional_to_json(create.content, "content", json.string), + optional_to_json(create.embeds, "embeds", json.array( + _, + create_forum_like_thread_message_embed_to_json, + )), + Ok(#( + "allowed_mentions", + allowed_message_mentions_to_json(create.allowed_mentions), + )), + optional_to_json(create.components, "components", json.array( + _, + create_message_component_to_json, + )), + attachments, + optional_to_json(create.flags, "flags", flags_to_json( + _, + bits_create_forum_like_thread_message_flags(), + )), + ] + |> list_to_json_object +} + +/// You need to set one of `content`, `embeds`, `sticker_ids`, `components`, or `attachments`. /// -/// ```gleam -/// [ -/// AllowMentioningUsers(AllowMentioningAllUsers), +/// You have to use the `CreateForumLikeThreadMessageUsingComponentsV2` flag if you use Components V2. +pub fn new_forum_like_thread_message() -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage( + content: None, + embeds: None, + allowed_mentions: [], + components: None, + sticker_ids: None, + attachments: None, + flags: None, + ) +} + +pub fn new_forum_like_thread_message_with_content( + create: CreateForumLikeThreadMessage, + content: String, +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, content: Some(content)) +} + +pub fn new_forum_like_thread_message_with_embeds( + create: CreateForumLikeThreadMessage, + embeds: List(CreateForumLikeThreadMessageEmbed), +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, embeds: Some(embeds)) +} + +pub fn new_forum_like_thread_message_with_allowed_mentions( + create: CreateForumLikeThreadMessage, + allowed_mentions: List(AllowedMessageMention), +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, allowed_mentions:) +} + +/// If sending a V2 component, remember to set the `CreateForumLikeThreadMessageUsingComponentsV2` flag. +pub fn new_forum_like_thread_message_with_components( + create: CreateForumLikeThreadMessage, + components: List(CreateMessageComponent), +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, components: Some(components)) +} + +pub fn new_forum_like_thread_message_with_stickers( + create: CreateForumLikeThreadMessage, + ids sticker_ids: List(Snowflake(Sticker)), +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, sticker_ids: Some(sticker_ids)) +} + +pub fn new_forum_like_thread_message_with_attachments( + create: CreateForumLikeThreadMessage, + attachments: List(CreateMessageAttachment), +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, attachments: Some(attachments)) +} + +pub fn new_forum_like_thread_message_with_flags( + create: CreateForumLikeThreadMessage, + flags: List(CreateForumLikeThreadMessageFlag), +) -> CreateForumLikeThreadMessage { + CreateForumLikeThreadMessage(..create, flags: Some(flags)) +} + +pub type CreateForumLikeThreadMessageFlag { + CreateForumLikeThreadMessageWithSuppressedEmbeds + CreateForumLikeThreadMessageWithSuppressedNotifications + CreateForumLikeThreadMessageUsingComponentsV2 +} + +fn bits_create_forum_like_thread_message_flags() -> List( + #(Int, CreateForumLikeThreadMessageFlag), +) { + [ + #( + int.bitwise_shift_left(1, 2), + CreateForumLikeThreadMessageWithSuppressedEmbeds, + ), + #( + int.bitwise_shift_left(1, 12), + CreateForumLikeThreadMessageWithSuppressedNotifications, + ), + #( + int.bitwise_shift_left(1, 15), + CreateForumLikeThreadMessageUsingComponentsV2, + ), + ] +} + +pub opaque type CreateMessageAttachment { + CreateMessageAttachment( + file: File, + title: Option(String), + description: Option(String), + is_spoiler: Bool, + ) +} + +fn create_message_attachment_to_json( + idx: Int, + create: CreateMessageAttachment, +) -> Json { + [ + Ok(#("id", json.int(idx))), + Ok(#("filename", json.string(create.file.name))), + optional_to_json(create.title, "title", json.string), + optional_to_json(create.description, "description", json.string), + // YES, this is `is_`, NO, I don't know why + Ok(#("is_spoiler", json.bool(create.is_spoiler))), + ] + |> list_to_json_object +} + +pub fn new_message_attachment(file file: File) -> CreateMessageAttachment { + CreateMessageAttachment( + file:, + title: None, + description: None, + is_spoiler: False, + ) +} + +pub fn new_message_attachment_with_title( + create: CreateMessageAttachment, + title: String, +) -> CreateMessageAttachment { + CreateMessageAttachment(..create, title: Some(title)) +} + +/// Maximum 1024 characters. +pub fn new_message_attachment_with_description( + create: CreateMessageAttachment, + description: String, +) -> CreateMessageAttachment { + CreateMessageAttachment(..create, description: Some(description)) +} + +pub fn new_spoiler_message_attachment( + create: CreateMessageAttachment, +) -> CreateMessageAttachment { + CreateMessageAttachment(..create, is_spoiler: True) +} + +pub type File { + File(name: String, content_type: String, content: BitArray) +} + +/// This type controls which persons get notified after mentioning them in a message. +/// +/// This type will always be used as a special unique list - you cannot reuse the same variant. +/// +/// That means that, i.e. +/// +/// ```gleam +/// [ +/// AllowMentioningUsers(AllowMentioningAllUsers), /// AllowMentioningUsers(AllowMentioningSpecificUsers([some_id])) /// ] /// ``` @@ -10847,22 +11278,21 @@ fn create_forum_like_thread_message_embed_to_json( create_embed_field_to_json, )), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_forum_like_thread_message_embed() { CreateForumLikeThreadMessageEmbed( - None, - None, - None, - None, - None, - None, - None, - None, - None, - None, + title: None, + description: None, + url: None, + timestamp: None, + colour: None, + footer: None, + image: None, + thumbnail: None, + author: None, + fields: None, ) } @@ -10938,12 +11368,11 @@ fn create_embed_footer_to_json(create: CreateEmbedFooter) -> Json { Ok(#("text", json.string(create.text))), optional_to_json(create.icon_url, "icon_url", json.string), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_embed_footer(text text: String) -> CreateEmbedFooter { - CreateEmbedFooter(text, None) + CreateEmbedFooter(text:, icon_url: None) } /// URL must be[attachment](#get_url_to_attachment) or HTTPS. @@ -10968,8 +11397,7 @@ fn create_embed_image_to_json(create: CreateEmbedImage) -> Json { Ok(#("url", json.string(create.url))), optional_to_json(create.description, "description", json.string), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } fn create_embed_video_to_json(create: CreateEmbedVideo) -> Json { @@ -10977,12 +11405,11 @@ fn create_embed_video_to_json(create: CreateEmbedVideo) -> Json { Ok(#("url", json.string(create.url))), optional_to_json(create.description, "description", json.string), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_embed_image(url url: String) -> CreateEmbedImage { - CreateEmbedImage(url, None) + CreateEmbedImage(url:, description: None) } pub fn new_embed_image_with_description( @@ -10997,7 +11424,7 @@ pub opaque type CreateEmbedVideo { } pub fn new_embed_video(url url: String) -> CreateEmbedVideo { - CreateEmbedVideo(url, None) + CreateEmbedVideo(url:, description: None) } pub fn new_embed_video_with_description( @@ -11017,12 +11444,11 @@ fn create_embed_author_to_json(create: CreateEmbedAuthor) -> Json { optional_to_json(create.url, "url", json.string), optional_to_json(create.icon_url, "icon_url", json.string), ] - |> list.filter_map(function.identity) - |> json.object + |> list_to_json_object } pub fn new_embed_author(named name: String) -> CreateEmbedAuthor { - CreateEmbedAuthor(name, None, None) + CreateEmbedAuthor(name:, url: None, icon_url: None) } /// URL must be HTTPS. @@ -11033,7 +11459,7 @@ pub fn new_embed_author_with_url( CreateEmbedAuthor(..create, url: Some(url)) } -/// URL must be[attachment](#get_url_to_attachment) or HTTPS. +/// URL must be [attachment](#get_url_to_attachment) or HTTPS. pub fn new_embed_author_with_icon( create: CreateEmbedAuthor, url icon_url: String, @@ -11058,7 +11484,7 @@ pub fn new_embed_field( named name: String, value value: String, ) -> CreateEmbedField { - CreateEmbedField(name, value, False) + CreateEmbedField(name:, value:, is_inline: False) } pub fn new_inline_embed_field(create: CreateEmbedField) -> CreateEmbedField { @@ -11067,7 +11493,36 @@ pub fn new_inline_embed_field(create: CreateEmbedField) -> CreateEmbedField { pub type CreateMessageComponent { CreateMessageActionRowComponent(CreateMessageActionRow) + /// Component V2 - requires the appropriate flag. CreateMessageSectionComponent(CreateMessageSection) + /// Component V2 - requires the appropriate flag. + CreateMessageTextDisplayComponent(CreateMessageTextDisplay) + /// Component V2 - requires the appropriate flag. + CreateMessageMediaGalleryComponent(CreateMessageMediaGallery) + /// Component V2 - requires the appropriate flag. + CreateMessageFileComponent(CreateMessageFile) + /// Component V2 - requires the appropriate flag. + CreateMessageSeparatorComponent(CreateMessageSeparator) + /// Component V2 - requires the appropriate flag. + CreateMessageContainerComponent(CreateMessageContainer) +} + +fn create_message_component_to_json(component: CreateMessageComponent) -> Json { + case component { + CreateMessageActionRowComponent(action_row) -> + create_message_action_row_to_json(action_row) + CreateMessageSectionComponent(section) -> + create_message_section_to_json(section) + CreateMessageTextDisplayComponent(text_display) -> + create_message_text_display_to_json(text_display) + CreateMessageMediaGalleryComponent(media_gallery) -> + create_message_media_gallery_to_json(media_gallery) + CreateMessageFileComponent(file) -> create_message_file_to_json(file) + CreateMessageSeparatorComponent(separator) -> + create_message_separator_to_json(separator) + CreateMessageContainerComponent(container) -> + create_message_container_to_json(container) + } } pub opaque type CreateMessageActionRow { @@ -11077,10 +11532,23 @@ pub opaque type CreateMessageActionRow { ) } +fn create_message_action_row_to_json(create: CreateMessageActionRow) -> Json { + [ + Ok(#("type", json.int(1))), + optional_to_json(create.id, "id", json.int), + Ok(#( + "components", + json.array(create.components, create_action_row_component_to_json), + )), + ] + |> list_to_json_object +} + +/// Components must be either up to 5 buttons or a single select component. pub fn new_message_action_row( - with components: List(CreateActionRowComponent), + components components: List(CreateActionRowComponent), ) -> CreateMessageActionRow { - CreateMessageActionRow(None, components:) + CreateMessageActionRow(id: None, components:) } pub fn new_message_action_row_with_id( @@ -11099,10 +11567,39 @@ pub type CreateActionRowComponent { CreateActionRowChannelSelect(CreateMessageChannelSelect) } +fn create_action_row_component_to_json( + component: CreateActionRowComponent, +) -> Json { + case component { + CreateActionRowButton(button) -> create_message_button_to_json(button) + CreateActionRowStringSelect(select) -> + create_message_string_select_to_json(select) + CreateActionRowUserSelect(select) -> + create_message_user_select_to_json(select) + CreateActionRowRoleSelect(select) -> + create_message_role_select_to_json(select) + CreateActionRowMentionableSelect(select) -> + create_message_mentionable_select_to_json(select) + CreateActionRowChannelSelect(select) -> + create_message_channel_select_to_json(select) + } +} + pub type CreateMessageButton { - MessageButtonRegular(CreateRegularMessageButton) - MessageButtonLink(CreateLinkMessageButton) - MessageButtonPremium(CreatePremiumMessageButton) + CreateMessageButtonRegular(CreateRegularMessageButton) + CreateMessageButtonLink(CreateLinkMessageButton) + CreateMessageButtonPremium(CreatePremiumMessageButton) +} + +fn create_message_button_to_json(button: CreateMessageButton) -> Json { + case button { + CreateMessageButtonRegular(button) -> + create_regular_message_button_to_json(button) + CreateMessageButtonLink(button) -> + create_link_message_button_to_json(button) + CreateMessageButtonPremium(button) -> + create_premium_message_button_to_json(button) + } } pub opaque type CreateLinkMessageButton { @@ -11115,6 +11612,57 @@ pub opaque type CreateLinkMessageButton { ) } +fn create_link_message_button_to_json(create: CreateLinkMessageButton) -> Json { + [ + Ok(#("type", json.int(2))), + optional_to_json(create.id, "id", json.int), + Ok(#("style", json.int(5))), + optional_to_json(create.label, "label", json.string), + optional_to_json(create.emoji, "emoji", component_emoji_to_json), + Ok(#("url", json.string(create.url))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +pub fn new_link_message_button(url url: String) -> CreateLinkMessageButton { + CreateLinkMessageButton( + id: None, + label: None, + emoji: None, + url:, + is_disabled: False, + ) +} + +pub fn new_link_message_button_with_id( + create: CreateLinkMessageButton, + id: Int, +) -> CreateLinkMessageButton { + CreateLinkMessageButton(..create, id: Some(id)) +} + +/// Label must be max 80 characters. +pub fn new_link_message_button_with_label( + create: CreateLinkMessageButton, + label: String, +) -> CreateLinkMessageButton { + CreateLinkMessageButton(..create, label: Some(label)) +} + +pub fn new_link_message_button_with_emoji( + create: CreateLinkMessageButton, + emoji: ComponentEmoji, +) -> CreateLinkMessageButton { + CreateLinkMessageButton(..create, emoji: Some(emoji)) +} + +pub fn new_disabled_link_message_button( + create: CreateLinkMessageButton, +) -> CreateLinkMessageButton { + CreateLinkMessageButton(..create, is_disabled: True) +} + pub opaque type CreatePremiumMessageButton { CreatePremiumMessageButton( id: Option(Int), @@ -11123,6 +11671,38 @@ pub opaque type CreatePremiumMessageButton { ) } +fn create_premium_message_button_to_json( + create: CreatePremiumMessageButton, +) -> Json { + [ + Ok(#("type", json.int(2))), + optional_to_json(create.id, "id", json.int), + Ok(#("style", json.int(6))), + Ok(#("sku_id", snowflake_to_json(create.sku_id))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +pub fn new_premium_message_button( + for_sku_with_id sku_id: Snowflake(Sku), +) -> CreatePremiumMessageButton { + CreatePremiumMessageButton(None, sku_id, False) +} + +pub fn new_premium_message_button_with_id( + create: CreatePremiumMessageButton, + id: Int, +) -> CreatePremiumMessageButton { + CreatePremiumMessageButton(..create, id: Some(id)) +} + +pub fn new_disabled_premium_message_button( + create: CreatePremiumMessageButton, +) -> CreatePremiumMessageButton { + CreatePremiumMessageButton(..create, is_disabled: True) +} + pub opaque type CreateRegularMessageButton { CreateRegularMessageButton( id: Option(Int), @@ -11134,11 +11714,73 @@ pub opaque type CreateRegularMessageButton { ) } +fn create_regular_message_button_to_json( + create: CreateRegularMessageButton, +) -> Json { + [ + Ok(#("type", json.int(2))), + optional_to_json(create.id, "id", json.int), + Ok(#("style", regular_button_style_to_json(create.style))), + optional_to_json(create.label, "label", json.string), + optional_to_json(create.emoji, "emoji", component_emoji_to_json), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +/// Custom ID must be between 1 and 100 characters. +pub fn new_regular_message_button( + custom_id custom_id: String, + style style: RegularButtonStyle, +) -> CreateRegularMessageButton { + CreateRegularMessageButton(None, style, None, None, custom_id, False) +} + +pub fn new_regular_message_button_with_id( + create: CreateRegularMessageButton, + id: Int, +) -> CreateRegularMessageButton { + CreateRegularMessageButton(..create, id: Some(id)) +} + +/// Label must be max 80 characters. +pub fn new_regular_message_button_with_label( + create: CreateRegularMessageButton, + label: String, +) -> CreateRegularMessageButton { + CreateRegularMessageButton(..create, label: Some(label)) +} + +pub fn new_regular_message_button_with_emoji( + create: CreateRegularMessageButton, + emoji: ComponentEmoji, +) -> CreateRegularMessageButton { + CreateRegularMessageButton(..create, emoji: Some(emoji)) +} + +pub fn new_disabled_regular_message_button( + create: CreateRegularMessageButton, +) -> CreateRegularMessageButton { + CreateRegularMessageButton(..create, is_disabled: True) +} + pub type ComponentEmoji { UnicodeComponentEmoji(character: String) CustomComponentEmoji(id: Snowflake(Emoji), name: String, is_animated: Bool) } +fn component_emoji_to_json(emoji: ComponentEmoji) -> Json { + case emoji { + UnicodeComponentEmoji(character:) -> [#("name", json.string(character))] + CustomComponentEmoji(..) -> [ + #("name", json.string(emoji.name)), + #("id", snowflake_to_json(emoji.id)), + #("animated", json.bool(emoji.is_animated)), + ] + } + |> json.object +} + pub type RegularButtonStyle { PrimaryRegularButton SecondaryRegularButton @@ -11146,11 +11788,21 @@ pub type RegularButtonStyle { DangerRegularButton } +fn regular_button_style_to_json(style: RegularButtonStyle) -> Json { + case style { + PrimaryRegularButton -> 1 + SecondaryRegularButton -> 2 + SuccessRegularButton -> 3 + DangerRegularButton -> 4 + } + |> json.int +} + pub opaque type CreateMessageStringSelect { CreateMessageStringSelect( id: Option(Int), custom_id: String, - options: List(CreateStringSelectOption), + options: List(StringSelectOption), /// Shown when no value is selected. Maximum 150 characters. placeholder: Option(String), /// Minimum amount of selected values, default 1. @@ -11166,8 +11818,80 @@ pub opaque type CreateMessageStringSelect { ) } -pub opaque type CreateStringSelectOption { - CreateStringSelectOption( +fn create_message_string_select_to_json( + create: CreateMessageStringSelect, +) -> Json { + [ + Ok(#("type", json.int(3))), + optional_to_json(create.id, "id", json.int), + Ok(#("custom_id", json.string(create.custom_id))), + Ok(#("options", json.array(create.options, string_select_option_to_json))), + optional_to_json(create.placeholder, "placeholder", json.string), + Ok(#("min_values", json.int(create.min_values))), + Ok(#("max_values", json.int(create.max_values))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +pub fn new_message_string_select( + custom_id custom_id: String, + options options: List(StringSelectOption), +) -> CreateMessageStringSelect { + CreateMessageStringSelect( + id: None, + custom_id:, + options:, + placeholder: None, + min_values: 1, + max_values: 1, + is_disabled: False, + ) +} + +pub fn new_message_string_select_with_id( + create: CreateMessageStringSelect, + id: Int, +) -> CreateMessageStringSelect { + CreateMessageStringSelect(..create, id: Some(id)) +} + +/// Shown when no value is selected. Maximum 150 characters. +pub fn new_message_string_select_with_placeholder( + create: CreateMessageStringSelect, + placeholder: String, +) -> CreateMessageStringSelect { + CreateMessageStringSelect(..create, placeholder: Some(placeholder)) +} + +/// Minimum amount of selected values, default 1. +/// +/// Must be between 0 and 25. +pub fn new_message_string_select_with_min_values( + create: CreateMessageStringSelect, + min_values: Int, +) -> CreateMessageStringSelect { + CreateMessageStringSelect(..create, min_values:) +} + +/// Maximum amount of selected values, default 1. +/// +/// Must be between 1 and 25. +pub fn new_message_string_select_with_max_values( + create: CreateMessageStringSelect, + max_values: Int, +) -> CreateMessageStringSelect { + CreateMessageStringSelect(..create, max_values:) +} + +pub fn new_disabled_message_string_select( + create: CreateMessageStringSelect, +) -> CreateMessageStringSelect { + CreateMessageStringSelect(..create, is_disabled: True) +} + +pub type StringSelectOption { + StringSelectOption( /// User-facing name. label: String, /// Dev-facing value. @@ -11175,11 +11899,51 @@ pub opaque type CreateStringSelectOption { /// Max 100 characters. description: Option(String), emoji: Option(ComponentEmoji), - /// Whether the option is selected by default. Defaults to False. + /// Whether the option is selected by default. is_default_selected: Bool, ) } +fn string_select_option_to_json(option: StringSelectOption) -> Json { + [ + Ok(#("label", json.string(option.label))), + Ok(#("value", json.string(option.value))), + optional_to_json(option.description, "description", json.string), + optional_to_json(option.emoji, "emoji", component_emoji_to_json), + Ok(#("default", json.bool(option.is_default_selected))), + ] + |> list_to_json_object +} + +/// Label is user-facing, value is dev-facing. +pub fn new_string_select_option( + label label: String, + value value: String, +) -> StringSelectOption { + StringSelectOption( + label:, + value:, + description: None, + emoji: None, + is_default_selected: False, + ) +} + +/// Description must be max 100 characters. +pub fn new_string_select_option_with_description( + option: StringSelectOption, + description: String, +) -> StringSelectOption { + StringSelectOption(..option, description: Some(description)) +} + +/// Whether the option is selected by default. +pub fn new_default_selected_string_select_option( + option: StringSelectOption, +) -> StringSelectOption { + StringSelectOption(..option, is_default_selected: True) +} + pub opaque type CreateMessageUserSelect { CreateMessageUserSelect( id: Option(Int), @@ -11201,6 +11965,91 @@ pub opaque type CreateMessageUserSelect { ) } +fn create_message_user_select_to_json(create: CreateMessageUserSelect) -> Json { + [ + Ok(#("type", json.int(5))), + optional_to_json(create.id, "id", json.int), + Ok(#("custom_id", json.string(create.custom_id))), + optional_to_json(create.placeholder, "placeholder", json.string), + Ok(#( + "default_values", + json.array(create.default_users, user_select_default_value_to_json), + )), + Ok(#("min_values", json.int(create.min_values))), + Ok(#("max_values", json.int(create.max_values))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +fn user_select_default_value_to_json(id: Snowflake(User)) -> Json { + [#("id", snowflake_to_json(id)), #("type", json.string("user"))] + |> json.object +} + +pub fn new_message_user_select( + custom_id custom_id: String, +) -> CreateMessageUserSelect { + CreateMessageUserSelect( + id: None, + custom_id:, + default_users: [], + placeholder: None, + min_values: 1, + max_values: 1, + is_disabled: False, + ) +} + +pub fn new_message_user_select_with_id( + create: CreateMessageUserSelect, + id: Int, +) -> CreateMessageUserSelect { + CreateMessageUserSelect(..create, id: Some(id)) +} + +/// Shown when no value is selected. Maximum 150 characters. +pub fn new_message_user_select_with_placeholder( + create: CreateMessageUserSelect, + placeholder: String, +) -> CreateMessageUserSelect { + CreateMessageUserSelect(..create, placeholder: Some(placeholder)) +} + +/// Which users are to be selected by default. +pub fn new_message_user_select_with_default_users( + create: CreateMessageUserSelect, + default_users: List(Snowflake(User)), +) -> CreateMessageUserSelect { + CreateMessageUserSelect(..create, default_users:) +} + +/// Minimum amount of selected values, default 1. +/// +/// Must be between 0 and 25. +pub fn new_message_user_select_with_min_values( + create: CreateMessageUserSelect, + min_values: Int, +) -> CreateMessageUserSelect { + CreateMessageUserSelect(..create, min_values:) +} + +/// Maximum amount of selected values, default 1. +/// +/// Must be between 1 and 25. +pub fn new_message_user_select_with_max_values( + create: CreateMessageUserSelect, + max_values: Int, +) -> CreateMessageUserSelect { + CreateMessageUserSelect(..create, max_values:) +} + +pub fn new_disabled_message_user_select( + create: CreateMessageUserSelect, +) -> CreateMessageUserSelect { + CreateMessageUserSelect(..create, is_disabled: True) +} + pub opaque type CreateMessageRoleSelect { CreateMessageRoleSelect( id: Option(Int), @@ -11222,6 +12071,91 @@ pub opaque type CreateMessageRoleSelect { ) } +fn create_message_role_select_to_json(create: CreateMessageRoleSelect) -> Json { + [ + Ok(#("type", json.int(6))), + optional_to_json(create.id, "id", json.int), + Ok(#("custom_id", json.string(create.custom_id))), + optional_to_json(create.placeholder, "placeholder", json.string), + Ok(#( + "default_values", + json.array(create.default_roles, role_select_default_value_to_json), + )), + Ok(#("min_values", json.int(create.min_values))), + Ok(#("max_values", json.int(create.max_values))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +fn role_select_default_value_to_json(id: Snowflake(Role)) -> Json { + [#("id", snowflake_to_json(id)), #("type", json.string("role"))] + |> json.object +} + +pub fn new_message_role_select( + custom_id custom_id: String, +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect( + id: None, + custom_id:, + default_roles: [], + placeholder: None, + min_values: 1, + max_values: 1, + is_disabled: False, + ) +} + +pub fn new_message_role_select_with_id( + create: CreateMessageRoleSelect, + id: Int, +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect(..create, id: Some(id)) +} + +/// Shown when no value is selected. Maximum 150 characters. +pub fn new_message_role_select_with_placeholder( + create: CreateMessageRoleSelect, + placeholder: String, +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect(..create, placeholder: Some(placeholder)) +} + +/// Which roles are to be selected by default. +pub fn new_message_role_select_with_default_roles( + create: CreateMessageRoleSelect, + default_roles: List(Snowflake(Role)), +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect(..create, default_roles:) +} + +/// Minimum amount of selected values, default 1. +/// +/// Must be between 0 and 25. +pub fn new_message_role_select_with_min_values( + create: CreateMessageRoleSelect, + min_values: Int, +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect(..create, min_values:) +} + +/// Maximum amount of selected values, default 1. +/// +/// Must be between 1 and 25. +pub fn new_message_role_select_with_max_values( + create: CreateMessageRoleSelect, + max_values: Int, +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect(..create, max_values:) +} + +pub fn new_disabled_message_role_select( + create: CreateMessageRoleSelect, +) -> CreateMessageRoleSelect { + CreateMessageRoleSelect(..create, is_disabled: True) +} + pub opaque type CreateMessageMentionableSelect { CreateMessageMentionableSelect( id: Option(Int), @@ -11231,7 +12165,7 @@ pub opaque type CreateMessageMentionableSelect { /// Which users will be selected by default. default_users: List(Snowflake(User)), /// Which roles will be selected by default. - default_roles: List(Snowflake(User)), + default_roles: List(Snowflake(Role)), /// Minimum amount of selected values, default 1. /// /// Must be between 0 and 25. @@ -11245,6 +12179,106 @@ pub opaque type CreateMessageMentionableSelect { ) } +fn create_message_mentionable_select_to_json( + create: CreateMessageMentionableSelect, +) -> Json { + let default_users = + create.default_users + |> list.map(user_select_default_value_to_json) + + let default_roles = + create.default_roles + |> list.map(role_select_default_value_to_json) + + let default_values = + default_users + |> list.append(default_roles) + + [ + Ok(#("type", json.int(7))), + optional_to_json(create.id, "id", json.int), + Ok(#("custom_id", json.string(create.custom_id))), + optional_to_json(create.placeholder, "placeholder", json.string), + Ok(#("default_values", json.preprocessed_array(default_values))), + Ok(#("min_values", json.int(create.min_values))), + Ok(#("max_values", json.int(create.max_values))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +pub fn new_message_mentionable_select( + custom_id custom_id: String, +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect( + id: None, + custom_id:, + default_users: [], + default_roles: [], + placeholder: None, + min_values: 1, + max_values: 1, + is_disabled: False, + ) +} + +pub fn new_message_mentionable_select_with_id( + create: CreateMessageMentionableSelect, + id: Int, +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, id: Some(id)) +} + +/// Shown when no value is selected. Maximum 150 characters. +pub fn new_message_mentionable_select_with_placeholder( + create: CreateMessageMentionableSelect, + placeholder: String, +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, placeholder: Some(placeholder)) +} + +/// Which roles are to be selected by default. +pub fn new_message_mentionable_select_with_default_roles( + create: CreateMessageMentionableSelect, + default_roles: List(Snowflake(Role)), +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, default_roles:) +} + +/// Which users are to be selected by default. +pub fn new_message_mentionable_select_with_default_users( + create: CreateMessageMentionableSelect, + default_users: List(Snowflake(User)), +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, default_users:) +} + +/// Minimum amount of selected values, default 1. +/// +/// Must be between 0 and 25. +pub fn new_message_mentionable_select_with_min_values( + create: CreateMessageMentionableSelect, + min_values: Int, +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, min_values:) +} + +/// Maximum amount of selected values, default 1. +/// +/// Must be between 1 and 25. +pub fn new_message_mentionable_select_with_max_values( + create: CreateMessageMentionableSelect, + max_values: Int, +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, max_values:) +} + +pub fn new_disabled_message_mentionable_select( + create: CreateMessageMentionableSelect, +) -> CreateMessageMentionableSelect { + CreateMessageMentionableSelect(..create, is_disabled: True) +} + pub opaque type CreateMessageChannelSelect { CreateMessageChannelSelect( id: Option(Int), @@ -11254,7 +12288,7 @@ pub opaque type CreateMessageChannelSelect { /// Which channels will be selected by default. default_channels: List(Snowflake(Channel)), /// Which channel types to include in the list. - channel_types: List(ChannelSelectType), + channel_types: Option(List(ChannelSelectType)), /// Minimum amount of selected values, default 1. /// /// Must be between 0 and 25. @@ -11268,6 +12302,106 @@ pub opaque type CreateMessageChannelSelect { ) } +fn create_message_channel_select_to_json( + create: CreateMessageChannelSelect, +) -> Json { + [ + Ok(#("type", json.int(8))), + optional_to_json(create.id, "id", json.int), + Ok(#("custom_id", json.string(create.custom_id))), + optional_to_json(create.placeholder, "placeholder", json.string), + Ok(#( + "default_values", + json.array(create.default_channels, channel_select_default_value_to_json), + )), + optional_to_json(create.channel_types, "channel_types", json.array( + _, + channel_select_type_to_json, + )), + Ok(#("min_values", json.int(create.min_values))), + Ok(#("max_values", json.int(create.max_values))), + Ok(#("disabled", json.bool(create.is_disabled))), + ] + |> list_to_json_object +} + +fn channel_select_default_value_to_json(id: Snowflake(Channel)) -> Json { + [#("id", snowflake_to_json(id)), #("type", json.string("channel"))] + |> json.object +} + +pub fn new_message_channel_select( + custom_id custom_id: String, +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect( + id: None, + custom_id:, + default_channels: [], + channel_types: None, + placeholder: None, + min_values: 1, + max_values: 1, + is_disabled: False, + ) +} + +pub fn new_message_channel_select_with_id( + create: CreateMessageChannelSelect, + id: Int, +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, id: Some(id)) +} + +/// Shown when no value is selected. Maximum 150 characters. +pub fn new_message_channel_select_with_placeholder( + create: CreateMessageChannelSelect, + placeholder: String, +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, placeholder: Some(placeholder)) +} + +/// Which channels are to be selected by default. +pub fn new_message_channel_select_with_default_channels( + create: CreateMessageChannelSelect, + default_channels: List(Snowflake(Channel)), +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, default_channels:) +} + +/// Which channel types are to be included in the select window. +pub fn new_message_channel_select_with_channel_types( + create: CreateMessageChannelSelect, + channel_types: List(ChannelSelectType), +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, channel_types: Some(channel_types)) +} + +/// Minimum amount of selected values, default 1. +/// +/// Must be between 0 and 25. +pub fn new_message_channel_select_with_min_values( + create: CreateMessageChannelSelect, + min_values: Int, +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, min_values:) +} + +/// Maximum amount of selected values, default 1. +/// +/// Must be between 1 and 25. +pub fn new_message_channel_select_with_max_values( + create: CreateMessageChannelSelect, + max_values: Int, +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, max_values:) +} + +pub fn new_disabled_message_channel_select( + create: CreateMessageChannelSelect, +) -> CreateMessageChannelSelect { + CreateMessageChannelSelect(..create, is_disabled: True) +} + pub type ChannelSelectType { SelectTextChannels SelectDmChannels @@ -11282,6 +12416,23 @@ pub type ChannelSelectType { SelectMediaChannels } +fn channel_select_type_to_json(type_: ChannelSelectType) -> Json { + case type_ { + SelectTextChannels -> 0 + SelectDmChannels -> 1 + SelectVoiceChannels -> 2 + SelectCategoryChannels -> 4 + SelectAnnouncementChannels -> 5 + SelectAnnouncementThreads -> 10 + SelectPublicThreads -> 11 + SelectPrivateThreads -> 12 + SelectStageChannels -> 13 + SelectForumChannels -> 15 + SelectMediaChannels -> 16 + } + |> json.int +} + pub opaque type CreateMessageSection { CreateMessageSection( id: Option(Int), @@ -11291,19 +12442,86 @@ pub opaque type CreateMessageSection { ) } +fn create_message_section_to_json(create: CreateMessageSection) -> Json { + [ + Ok(#("type", json.int(9))), + optional_to_json(create.id, "id", json.int), + Ok(#( + "components", + json.array(create.components, create_section_component_to_json), + )), + Ok(#("accessory", create_section_accessory_to_json(create.accessory))), + ] + |> list_to_json_object +} + +/// A section must contain between 1 and 3 children. +pub fn new_message_section( + children components: List(CreateSectionComponent), + accessory accessory: CreateSectionAccessory, +) -> CreateMessageSection { + CreateMessageSection(id: None, components:, accessory:) +} + +pub fn new_message_section_with_id( + create: CreateMessageSection, + id: Int, +) -> CreateMessageSection { + CreateMessageSection(..create, id: Some(id)) +} + pub type CreateSectionComponent { CreateSectionTextDisplay(CreateMessageTextDisplay) } +fn create_section_component_to_json(component: CreateSectionComponent) -> Json { + case component { + CreateSectionTextDisplay(text_display) -> + create_message_text_display_to_json(text_display) + } +} + pub type CreateSectionAccessory { CreateSectionButton(CreateMessageButton) CreateSectionThumbnail(CreateMessageThumbnail) } +fn create_section_accessory_to_json(accessory: CreateSectionAccessory) -> Json { + case accessory { + CreateSectionButton(button) -> create_message_button_to_json(button) + CreateSectionThumbnail(thumbnail) -> + create_message_thumbnail_to_json(thumbnail) + } +} + pub opaque type CreateMessageTextDisplay { CreateMessageTextDisplay(id: Option(Int), content: String) } +fn create_message_text_display_to_json( + create: CreateMessageTextDisplay, +) -> Json { + [ + Ok(#("type", json.int(10))), + optional_to_json(create.id, "id", json.int), + Ok(#("content", json.string(create.content))), + ] + |> list_to_json_object +} + +pub fn new_message_text_display( + content content: String, +) -> CreateMessageTextDisplay { + CreateMessageTextDisplay(id: None, content:) +} + +pub fn new_message_text_display_with_id( + create: CreateMessageTextDisplay, + id: Int, +) -> CreateMessageTextDisplay { + CreateMessageTextDisplay(..create, id: Some(id)) +} + pub opaque type CreateMessageThumbnail { CreateMessageThumbnail( id: Option(Int), @@ -11315,6 +12533,304 @@ pub opaque type CreateMessageThumbnail { ) } +fn create_message_thumbnail_to_json(create: CreateMessageThumbnail) -> Json { + [ + Ok(#("type", json.int(11))), + optional_to_json(create.id, "id", json.int), + Ok(#("media", create_unfurled_media_item_to_json(create.media))), + optional_to_json(create.description, "description", json.string), + Ok(#("spoiler", json.bool(create.is_spoiler))), + ] + |> list_to_json_object +} + +pub fn new_message_thumbnail( + media media: CreateUnfurledMediaItem, +) -> CreateMessageThumbnail { + CreateMessageThumbnail(id: None, media:, description: None, is_spoiler: False) +} + +pub fn new_message_thumbnail_with_id( + create: CreateMessageThumbnail, + id: Int, +) -> CreateMessageThumbnail { + CreateMessageThumbnail(..create, id: Some(id)) +} + +/// The description must be max 1024 characters. +pub fn new_message_thumbnail_with_description( + create: CreateMessageThumbnail, + description: String, +) -> CreateMessageThumbnail { + CreateMessageThumbnail(..create, description: Some(description)) +} + +pub fn new_spoiler_message_thumbnail( + create: CreateMessageThumbnail, +) -> CreateMessageThumbnail { + CreateMessageThumbnail(..create, is_spoiler: True) +} + pub opaque type CreateUnfurledMediaItem { CreateUnfurledMediaItem(url: String) } + +fn create_unfurled_media_item_to_json(create: CreateUnfurledMediaItem) -> Json { + json.object([#("url", json.string(create.url))]) +} + +/// Supports HTTPS and [attachment](#get_url_to_attachment) URLs. +pub fn new_unfurled_media_item(url url: String) -> CreateUnfurledMediaItem { + CreateUnfurledMediaItem(url:) +} + +pub opaque type CreateMessageMediaGallery { + CreateMessageMediaGallery( + id: Option(Int), + items: List(CreateMediaGalleryItem), + ) +} + +fn create_message_media_gallery_to_json( + create: CreateMessageMediaGallery, +) -> Json { + [ + Ok(#("type", json.int(12))), + optional_to_json(create.id, "id", json.int), + Ok(#("items", json.array(create.items, create_media_gallery_item_to_json))), + ] + |> list_to_json_object +} + +pub opaque type CreateMediaGalleryItem { + CreateMediaGalleryItem( + media: CreateUnfurledMediaItem, + description: Option(String), + is_spoiler: Bool, + ) +} + +fn create_media_gallery_item_to_json(create: CreateMediaGalleryItem) -> Json { + [ + Ok(#("media", create_unfurled_media_item_to_json(create.media))), + optional_to_json(create.description, "description", json.string), + Ok(#("spoiler", json.bool(create.is_spoiler))), + ] + |> list_to_json_object +} + +pub fn new_media_gallery_item( + media media: CreateUnfurledMediaItem, +) -> CreateMediaGalleryItem { + CreateMediaGalleryItem(media:, description: None, is_spoiler: False) +} + +/// Description must be max 1024 characters. +pub fn new_media_gallery_item_with_description( + create: CreateMediaGalleryItem, + description: String, +) -> CreateMediaGalleryItem { + CreateMediaGalleryItem(..create, description: Some(description)) +} + +pub fn new_spoiler_media_gallery_item( + create: CreateMediaGalleryItem, +) -> CreateMediaGalleryItem { + CreateMediaGalleryItem(..create, is_spoiler: True) +} + +/// A media gallery must contain between 1 and 10 items. +pub fn new_message_media_gallery( + items items: List(CreateMediaGalleryItem), +) -> CreateMessageMediaGallery { + CreateMessageMediaGallery(id: None, items:) +} + +pub fn new_message_media_gallery_with_id( + create: CreateMessageMediaGallery, + id: Int, +) -> CreateMessageMediaGallery { + CreateMessageMediaGallery(..create, id: Some(id)) +} + +pub opaque type CreateMessageFile { + CreateMessageFile( + id: Option(Int), + file: CreateUnfurledMediaItem, + is_spoiler: Bool, + ) +} + +fn create_message_file_to_json(create: CreateMessageFile) -> Json { + [ + Ok(#("type", json.int(13))), + optional_to_json(create.id, "id", json.int), + Ok(#("file", create_unfurled_media_item_to_json(create.file))), + Ok(#("spoiler", json.bool(create.is_spoiler))), + ] + |> list_to_json_object +} + +/// The unfurled media item MUST contain an [attachment](#get_url_for_attachment) URL. +pub fn new_message_file( + file file: CreateUnfurledMediaItem, +) -> CreateMessageFile { + CreateMessageFile(id: None, file:, is_spoiler: False) +} + +pub fn new_message_file_with_id( + create: CreateMessageFile, + id: Int, +) -> CreateMessageFile { + CreateMessageFile(..create, id: Some(id)) +} + +pub fn new_spoiler_message_file( + create: CreateMessageFile, +) -> CreateMessageFile { + CreateMessageFile(..create, is_spoiler: True) +} + +pub opaque type CreateMessageSeparator { + CreateMessageSeparator( + id: Option(Int), + is_divider_visible: Bool, + spacing: SeparatorSpacing, + ) +} + +fn create_message_separator_to_json(create: CreateMessageSeparator) -> Json { + [ + Ok(#("type", json.int(14))), + optional_to_json(create.id, "id", json.int), + Ok(#("divider", json.bool(create.is_divider_visible))), + Ok(#("spacing", separator_spacing_to_json(create.spacing))), + ] + |> list_to_json_object +} + +pub type SeparatorSpacing { + SmallSeparatorSpacing + LargeSeparatorSpacing +} + +fn separator_spacing_to_json(spacing: SeparatorSpacing) -> Json { + case spacing { + SmallSeparatorSpacing -> 1 + LargeSeparatorSpacing -> 2 + } + |> json.int +} + +/// Defaults: visible divider, small spacing. +pub fn new_message_separator() -> CreateMessageSeparator { + CreateMessageSeparator( + id: None, + is_divider_visible: True, + spacing: SmallSeparatorSpacing, + ) +} + +pub fn new_message_separator_with_id( + create: CreateMessageSeparator, + id: Int, +) -> CreateMessageSeparator { + CreateMessageSeparator(..create, id: Some(id)) +} + +pub fn new_message_separator_without_visible_divider( + create: CreateMessageSeparator, +) -> CreateMessageSeparator { + CreateMessageSeparator(..create, is_divider_visible: False) +} + +pub fn new_message_separator_with_large_spacing( + create: CreateMessageSeparator, +) -> CreateMessageSeparator { + CreateMessageSeparator(..create, spacing: LargeSeparatorSpacing) +} + +pub opaque type CreateMessageContainer { + CreateMessageContainer( + id: Option(Int), + components: List(CreateContainerComponent), + accent_colour: Option(Colour), + is_spoiler: Bool, + ) +} + +fn create_message_container_to_json(create: CreateMessageContainer) -> Json { + [ + Ok(#("type", json.int(17))), + optional_to_json(create.id, "id", json.int), + Ok(#( + "components", + json.array(create.components, create_container_component_to_json), + )), + optional_to_json(create.accent_colour, "accent_colour", colour_to_json), + Ok(#("spoiler", json.bool(create.is_spoiler))), + ] + |> list_to_json_object +} + +pub fn new_message_container() -> CreateMessageContainer { + CreateMessageContainer( + id: None, + components: [], + accent_colour: None, + is_spoiler: False, + ) +} + +pub fn new_message_container_with_id( + create: CreateMessageContainer, + id: Int, +) -> CreateMessageContainer { + CreateMessageContainer(..create, id: Some(id)) +} + +pub fn new_message_container_with_components( + create: CreateMessageContainer, + components: List(CreateContainerComponent), +) -> CreateMessageContainer { + CreateMessageContainer(..create, components:) +} + +pub fn new_message_container_with_accent_colour( + create: CreateMessageContainer, + accent_colour: Colour, +) -> CreateMessageContainer { + CreateMessageContainer(..create, accent_colour: Some(accent_colour)) +} + +pub fn new_spoiler_message_container( + create: CreateMessageContainer, +) -> CreateMessageContainer { + CreateMessageContainer(..create, is_spoiler: True) +} + +pub type CreateContainerComponent { + CreateContainerActionRow(CreateMessageActionRow) + CreateContainerTextDisplay(CreateMessageTextDisplay) + CreateContainerSection(CreateMessageSection) + CreateContainerMediaGallery(CreateMessageMediaGallery) + CreateContainerSeparator(CreateMessageSeparator) + CreateContainerFile(CreateMessageFile) +} + +fn create_container_component_to_json( + component: CreateContainerComponent, +) -> Json { + case component { + CreateContainerActionRow(action_row) -> + create_message_action_row_to_json(action_row) + CreateContainerTextDisplay(text_display) -> + create_message_text_display_to_json(text_display) + CreateContainerSection(section) -> create_message_section_to_json(section) + CreateContainerMediaGallery(media_gallery) -> + create_message_media_gallery_to_json(media_gallery) + CreateContainerSeparator(separator) -> + create_message_separator_to_json(separator) + CreateContainerFile(file) -> create_message_file_to_json(file) + } +}