diff --git a/src/flybycord/application/current_application.gleam b/src/flybycord/application/current_application.gleam index 588b3fc..754f7ff 100644 --- a/src/flybycord/application/current_application.gleam +++ b/src/flybycord/application/current_application.gleam @@ -4,7 +4,7 @@ import flybycord/error import flybycord/image import flybycord/internal/flags import flybycord/internal/rest -import flybycord/modification.{type Modification, Delete, New, Skip} +import flybycord/modification.{type Modification, Skip} import flybycord/webhook_event import gleam/dict.{type Dict} import gleam/http diff --git a/src/flybycord/channel.gleam b/src/flybycord/channel.gleam index 1978f41..3130076 100644 --- a/src/flybycord/channel.gleam +++ b/src/flybycord/channel.gleam @@ -1,3 +1,4 @@ +import flybycord/channel/guild/announcement import flybycord/channel/guild/category import flybycord/channel/guild/forum import flybycord/channel/guild/media @@ -11,7 +12,7 @@ import flybycord/internal/rest import gleam/dynamic/decode import gleam/http import gleam/http/request -import gleam/json +import gleam/json.{type Json} import gleam/result // TYPES ----------------------------------------------------------------------- @@ -19,6 +20,7 @@ import gleam/result pub type Channel { GuildText(text.Channel) Dm(dm.Channel) + GuildAnnouncement(announcement.Channel) GuildVoice(voice.Channel) GuildCategory(category.Channel) Thread(thread.Thread) @@ -55,7 +57,7 @@ pub type Mention { pub fn decoder() -> decode.Decoder(Channel) { use variant <- decode.field("type", type_decoder()) case variant { - GuildTextChannel | GuildAnnouncementChannel -> { + GuildTextChannel -> { use channel <- decode.then(text.channel_decoder()) decode.success(GuildText(channel)) } @@ -63,6 +65,10 @@ pub fn decoder() -> decode.Decoder(Channel) { use channel <- decode.then(dm.channel_decoder()) decode.success(Dm(channel)) } + GuildAnnouncementChannel -> { + use channel <- decode.then(announcement.channel_decoder()) + decode.success(GuildAnnouncement(channel)) + } GuildVoiceChannel | GuildStageVoiceChannel -> { use channel <- decode.then(voice.channel_decoder()) decode.success(GuildVoice(channel)) @@ -114,11 +120,43 @@ pub fn mention_decoder() -> decode.Decoder(Mention) { decode.success(Mention(id:, guild_id:, channel_type:, channel_name:)) } +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn type_encode(type_: Type) -> Json { + case type_ { + GuildTextChannel -> 0 + DmChannel -> 1 + GuildVoiceChannel -> 2 + GuildCategoryChannel -> 4 + GuildAnnouncementChannel -> 5 + AnnouncementThreadChannel -> 10 + PublicThreadChannel -> 11 + PrivateThreadChannel -> 12 + GuildStageVoiceChannel -> 13 + GuildForumChannel -> 15 + GuildMediaChannel -> 16 + } + |> json.int +} + // PUBLIC API FUNCTIONS -------------------------------------------------------- +pub fn get(client: Client, id channel_id: String) { + use response <- result.try( + client + |> rest.new_request(http.Get, "/channels/" <> channel_id) + |> rest.execute, + ) + + response.body + |> json.parse(using: decoder()) + |> result.map_error(error.DecodeError) +} + pub fn create_dm( client: Client, - recipient_id: String, + to recipient_id: String, ) -> Result(Channel, error.FlybycordError) { let json = json.object([#("recipient_id", json.string(recipient_id))]) diff --git a/src/flybycord/channel/guild/announcement.gleam b/src/flybycord/channel/guild/announcement.gleam new file mode 100644 index 0000000..942aa62 --- /dev/null +++ b/src/flybycord/channel/guild/announcement.gleam @@ -0,0 +1,184 @@ +import flybycord/channel/permission_overwrite.{type PermissionOverwrite} +import flybycord/internal/time_duration +import flybycord/internal/time_rfc3339 +import flybycord/modification.{type Modification} +import flybycord/permission.{type Permission} +import gleam/dynamic/decode +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} +import gleam/time/duration.{type Duration} +import gleam/time/timestamp.{type Timestamp} + +// TYPES ----------------------------------------------------------------------- + +pub type Channel { + Channel( + id: String, + guild_id: Option(String), + position: Int, + permission_overwrites: List(PermissionOverwrite), + name: String, + topic: Option(String), + is_nsfw: Bool, + last_message_id: Option(String), + rate_limit_per_user: Duration, + parent_id: Option(String), + last_pin_timestamp: Option(Timestamp), + current_user_permissions: Option(List(Permission)), + default_auto_archive_duration: Duration, + ) +} + +pub type Modify { + Modify( + name: Option(String), + to_text: Option(Bool), + position: Modification(Int), + topic: Modification(String), + is_nsfw: Option(Bool), + rate_limit_per_user: Modification(Duration), + permission_overwrites: Modification(List(permission_overwrite.Create)), + parent_id: Modification(String), + default_auto_archive_duration: Modification(Duration), + default_thread_rate_limit_per_user: Option(Duration), + ) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn channel_decoder() -> decode.Decoder(Channel) { + use id <- decode.field("id", decode.string) + use guild_id <- decode.optional_field( + "guild_id", + None, + decode.optional(decode.string), + ) + use position <- decode.field("position", decode.int) + use permission_overwrites <- decode.field( + "permission_overwrites", + decode.list(permission_overwrite.decoder()), + ) + use name <- decode.field("name", decode.string) + use topic <- decode.field("topic", decode.optional(decode.string)) + use is_nsfw <- decode.field("nsfw", decode.bool) + use last_message_id <- decode.field( + "last_message_id", + decode.optional(decode.string), + ) + use rate_limit_per_user <- decode.field( + "rate_limit_per_user", + time_duration.from_minutes_decoder(), + ) + use parent_id <- decode.field("parent_id", decode.optional(decode.string)) + use last_pin_timestamp <- decode.field( + "last_pin_timestamp", + decode.optional(time_rfc3339.decoder()), + ) + use current_user_permissions <- decode.optional_field( + "permissions", + None, + decode.optional(permission.decoder()), + ) + use default_auto_archive_duration <- decode.field( + "default_auto_archive_duration", + time_duration.from_int_seconds_decoder(), + ) + decode.success(Channel( + id:, + guild_id:, + position:, + permission_overwrites:, + name:, + topic:, + is_nsfw:, + last_message_id:, + rate_limit_per_user:, + parent_id:, + last_pin_timestamp:, + current_user_permissions:, + default_auto_archive_duration:, + )) +} + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let type_ = case modify.to_text { + Some(True) -> [#("type", json.int(0))] + Some(False) -> [] + None -> [] + } + + let position = + modify.position + |> modification.encode("position", json.int) + + let topic = + modify.topic + |> modification.encode("topic", json.string) + + let is_nsfw = case modify.is_nsfw { + Some(nsfw) -> [#("nsfw", json.bool(nsfw))] + None -> [] + } + + let rate_limit_per_user = + modify.rate_limit_per_user + |> modification.encode( + "rate_limit_per_user", + time_duration.to_int_seconds_encode, + ) + + let permission_overwrites = + modify.permission_overwrites + |> modification.encode("permission_overwrites", fn(permissions) { + permissions + |> json.array(permission_overwrite.create_encode) + }) + + let parent_id = + modify.parent_id + |> modification.encode("parent_id", json.string) + + let default_auto_archive_duration = + modify.default_auto_archive_duration + |> modification.encode( + "default_auto_archive_duration", + time_duration.to_int_seconds_encode, + ) + + let default_thread_rate_limit_per_user = case + modify.default_thread_rate_limit_per_user + { + Some(limit) -> [ + #( + "default_thread_rate_limit_per_user", + time_duration.to_int_seconds_encode(limit), + ), + ] + None -> [] + } + + [ + name, + type_, + position, + topic, + is_nsfw, + rate_limit_per_user, + permission_overwrites, + parent_id, + default_auto_archive_duration, + default_thread_rate_limit_per_user, + ] + |> list.flatten + |> json.object +} diff --git a/src/flybycord/channel/guild/category.gleam b/src/flybycord/channel/guild/category.gleam index 5d34a1d..0979e5f 100644 --- a/src/flybycord/channel/guild/category.gleam +++ b/src/flybycord/channel/guild/category.gleam @@ -1,7 +1,10 @@ import flybycord/channel/permission_overwrite.{type PermissionOverwrite} +import flybycord/modification.{type Modification} import flybycord/permission.{type Permission} import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} // TYPES ----------------------------------------------------------------------- @@ -16,6 +19,14 @@ pub type Channel { ) } +pub type Modify { + Modify( + name: Option(String), + position: Modification(Int), + permission_overwrites: Modification(List(permission_overwrite.Create)), + ) +} + // DECODERS -------------------------------------------------------------------- @internal @@ -46,3 +57,28 @@ pub fn channel_decoder() -> decode.Decoder(Channel) { current_user_permissions:, )) } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let position = + modify.position + |> modification.encode("position", json.int) + + let permission_overwrites = + modify.permission_overwrites + |> modification.encode("permission_overwrites", fn(overwrites) { + overwrites + |> json.array(permission_overwrite.create_encode) + }) + + [name, position, permission_overwrites] + |> list.flatten + |> json.object +} diff --git a/src/flybycord/channel/guild/forum.gleam b/src/flybycord/channel/guild/forum.gleam index c500172..ce2c69f 100644 --- a/src/flybycord/channel/guild/forum.gleam +++ b/src/flybycord/channel/guild/forum.gleam @@ -1,10 +1,14 @@ +import flybycord/channel/guild/forum/tag.{type Tag} import flybycord/channel/permission_overwrite.{type PermissionOverwrite} import flybycord/internal/flags import flybycord/internal/time_duration +import flybycord/modification.{type Modification} import flybycord/permission.{type Permission} import gleam/dynamic/decode import gleam/int -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import gleam/time/duration.{type Duration} // TYPES ----------------------------------------------------------------------- @@ -32,13 +36,22 @@ pub type Channel { ) } -pub type Tag { - Tag( - id: String, - name: String, - is_moderated: Bool, - emoji_id: Option(String), - emoji_name: Option(String), +pub type Modify { + Modify( + name: Option(String), + position: Modification(Int), + topic: Modification(String), + is_nsfw: Option(Bool), + rate_limit_per_user: Modification(Duration), + permission_overwrites: Modification(List(permission_overwrite.Create)), + parent_id: Modification(String), + default_auto_archive_duration: Modification(Duration), + flags: Option(List(Flag)), + available_tags: Option(List(tag.Create)), + default_reaction_emoji: Modification(DefaultReaction), + default_thread_rate_limit_per_user: Option(Duration), + default_sort_order: Modification(SortOrderType), + default_layout: Option(LayoutType), ) } @@ -108,7 +121,7 @@ pub fn channel_decoder() -> decode.Decoder(Channel) { use flags <- decode.field("flags", flags.decoder(bits_flags())) use available_tags <- decode.field( "available_tags", - decode.list(tag_decoder()), + decode.list(tag.decoder()), ) use default_reaction_emoji <- decode.field( "default_reaction_emoji", @@ -145,16 +158,6 @@ pub fn channel_decoder() -> decode.Decoder(Channel) { )) } -@internal -pub fn tag_decoder() -> decode.Decoder(Tag) { - use id <- decode.field("id", decode.string) - use name <- decode.field("name", decode.string) - use is_moderated <- decode.field("moderated", decode.bool) - use emoji_id <- decode.field("emoji_id", decode.optional(decode.string)) - use emoji_name <- decode.field("emoji_name", decode.optional(decode.string)) - decode.success(Tag(id:, name:, is_moderated:, emoji_id:, emoji_name:)) -} - @internal pub fn default_reaction_decoder() -> decode.Decoder(DefaultReaction) { use emoji_id <- decode.field("emoji_id", decode.optional(decode.string)) @@ -182,3 +185,139 @@ pub fn layout_type_decoder() -> decode.Decoder(LayoutType) { _ -> decode.failure(NotSet, "LayoutType") } } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let position = + modify.position + |> modification.encode("position", json.int) + + let topic = + modify.topic + |> modification.encode("topic", json.string) + + let is_nsfw = case modify.is_nsfw { + Some(nsfw) -> [#("nsfw", json.bool(nsfw))] + None -> [] + } + + let rate_limit_per_user = + modify.rate_limit_per_user + |> modification.encode( + "rate_limit_per_user", + time_duration.to_int_seconds_encode, + ) + + let permission_overwrites = + modify.permission_overwrites + |> modification.encode("permission_overwrites", fn(overwrites) { + overwrites + |> json.array(permission_overwrite.create_encode) + }) + + let parent_id = + modify.parent_id + |> modification.encode("parent_id", json.string) + + let default_auto_archive_duration = + modify.default_auto_archive_duration + |> modification.encode( + "default_auto_archive_duration", + time_duration.to_int_seconds_encode, + ) + + let flags = case modify.flags { + Some(flags) -> [#("flags", flags.encode(flags, bits_flags()))] + None -> [] + } + + let available_tags = case modify.available_tags { + Some(tags) -> [#("available_tags", json.array(tags, tag.create_encode))] + None -> [] + } + + let default_reaction_emoji = + modify.default_reaction_emoji + |> modification.encode("default_reaction_emoji", default_reaction_encode) + + let default_thread_rate_limit_per_user = case + modify.default_thread_rate_limit_per_user + { + Some(limit) -> [ + #( + "default_thread_rate_limit_per_user", + time_duration.to_int_seconds_encode(limit), + ), + ] + None -> [] + } + + let default_sort_order = + modify.default_sort_order + |> modification.encode("default_sort_order", sort_order_type_encode) + + let default_layout = case modify.default_layout { + Some(layout) -> [#("default_forum_layout", layout_type_encode(layout))] + None -> [] + } + + [ + name, + position, + topic, + is_nsfw, + rate_limit_per_user, + permission_overwrites, + parent_id, + default_auto_archive_duration, + flags, + available_tags, + default_reaction_emoji, + default_thread_rate_limit_per_user, + default_sort_order, + default_layout, + ] + |> list.flatten + |> json.object +} + +@internal +pub fn default_reaction_encode(default_reaction: DefaultReaction) -> Json { + let DefaultReaction(emoji_id:, emoji_name:) = default_reaction + json.object([ + #("emoji_id", case emoji_id { + None -> json.null() + Some(value) -> json.string(value) + }), + #("emoji_name", case emoji_name { + None -> json.null() + Some(value) -> json.string(value) + }), + ]) +} + +@internal +pub fn sort_order_type_encode(sort_order_type: SortOrderType) -> Json { + case sort_order_type { + LatestActivity -> 0 + CreationDate -> 1 + } + |> json.int +} + +@internal +pub fn layout_type_encode(layout_type: LayoutType) -> Json { + case layout_type { + NotSet -> 0 + ListView -> 1 + GalleryView -> 2 + } + |> json.int +} diff --git a/src/flybycord/channel/guild/forum/tag.gleam b/src/flybycord/channel/guild/forum/tag.gleam new file mode 100644 index 0000000..4ac0b68 --- /dev/null +++ b/src/flybycord/channel/guild/forum/tag.gleam @@ -0,0 +1,55 @@ +import gleam/dynamic/decode +import gleam/json.{type Json} +import gleam/option.{type Option, None, Some} + +// TYPES ----------------------------------------------------------------------- + +pub type Tag { + Tag( + id: String, + name: String, + is_moderated: Bool, + emoji_id: Option(String), + emoji_name: Option(String), + ) +} + +pub type Create { + Create( + name: String, + is_moderated: Bool, + emoji_id: Option(String), + emoji_name: Option(String), + ) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn decoder() -> decode.Decoder(Tag) { + use id <- decode.field("id", decode.string) + use name <- decode.field("name", decode.string) + use is_moderated <- decode.field("moderated", decode.bool) + use emoji_id <- decode.field("emoji_id", decode.optional(decode.string)) + use emoji_name <- decode.field("emoji_name", decode.optional(decode.string)) + decode.success(Tag(id:, name:, is_moderated:, emoji_id:, emoji_name:)) +} + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn create_encode(create: Create) -> Json { + let Create(name:, is_moderated:, emoji_id:, emoji_name:) = create + json.object([ + #("name", json.string(name)), + #("is_moderated", json.bool(is_moderated)), + #("emoji_id", case emoji_id { + None -> json.null() + Some(value) -> json.string(value) + }), + #("emoji_name", case emoji_name { + None -> json.null() + Some(value) -> json.string(value) + }), + ]) +} diff --git a/src/flybycord/channel/guild/media.gleam b/src/flybycord/channel/guild/media.gleam index 5afb75a..eef4bd4 100644 --- a/src/flybycord/channel/guild/media.gleam +++ b/src/flybycord/channel/guild/media.gleam @@ -1,11 +1,15 @@ import flybycord/channel/guild/forum +import flybycord/channel/guild/forum/tag.{type Tag} import flybycord/channel/permission_overwrite.{type PermissionOverwrite} import flybycord/internal/flags import flybycord/internal/time_duration +import flybycord/modification.{type Modification} import flybycord/permission.{type Permission} import gleam/dynamic/decode import gleam/int -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import gleam/time/duration.{type Duration} // TYPES ----------------------------------------------------------------------- @@ -25,13 +29,31 @@ pub type Channel { default_auto_archive_duration: Option(Duration), current_user_permissions: Option(List(Permission)), flags: List(Flag), - available_tags: List(forum.Tag), + available_tags: List(Tag), default_reaction_emoji: Option(forum.DefaultReaction), default_thread_rate_limit_per_user: Option(Duration), default_sort_order: Option(forum.SortOrderType), ) } +pub type Modify { + Modify( + name: Option(String), + position: Modification(Int), + topic: Modification(String), + is_nsfw: Option(Bool), + rate_limit_per_user: Modification(Duration), + permission_overwrites: Modification(List(permission_overwrite.Create)), + parent_id: Modification(String), + default_auto_archive_duration: Modification(Duration), + flags: Option(List(Flag)), + available_tags: Option(List(tag.Create)), + default_reaction_emoji: Modification(forum.DefaultReaction), + default_thread_rate_limit_per_user: Option(Duration), + default_sort_order: Modification(forum.SortOrderType), + ) +} + pub type Flag { RequiresTag HideMediaDownloadOptions @@ -87,7 +109,7 @@ pub fn channel_decoder() { use flags <- decode.field("flags", flags.decoder(bits_flags())) use available_tags <- decode.field( "available_tags", - decode.list(forum.tag_decoder()), + decode.list(tag.decoder()), ) use default_reaction_emoji <- decode.field( "default_reaction_emoji", @@ -122,3 +144,101 @@ pub fn channel_decoder() { default_sort_order:, )) } + +// ENCODERS -------------------------------------------------------------------- + +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let position = + modify.position + |> modification.encode("position", json.int) + + let topic = + modify.topic + |> modification.encode("topic", json.string) + + let is_nsfw = case modify.is_nsfw { + Some(nsfw) -> [#("nsfw", json.bool(nsfw))] + None -> [] + } + + let rate_limit_per_user = + modify.rate_limit_per_user + |> modification.encode( + "rate_limit_per_user", + time_duration.to_int_seconds_encode, + ) + + let permission_overwrites = + modify.permission_overwrites + |> modification.encode("permission_overwrites", fn(overwrites) { + overwrites + |> json.array(permission_overwrite.create_encode) + }) + + let parent_id = + modify.parent_id + |> modification.encode("parent_id", json.string) + + let default_auto_archive_duration = + modify.default_auto_archive_duration + |> modification.encode( + "default_auto_archive_duration", + time_duration.to_int_seconds_encode, + ) + + let flags = case modify.flags { + Some(flags) -> [#("flags", flags.encode(flags, bits_flags()))] + None -> [] + } + + let available_tags = case modify.available_tags { + Some(tags) -> [#("available_tags", json.array(tags, tag.create_encode))] + None -> [] + } + + let default_reaction_emoji = + modify.default_reaction_emoji + |> modification.encode( + "default_reaction_emoji", + forum.default_reaction_encode, + ) + + let default_thread_rate_limit_per_user = case + modify.default_thread_rate_limit_per_user + { + Some(limit) -> [ + #( + "default_thread_rate_limit_per_user", + time_duration.to_int_seconds_encode(limit), + ), + ] + None -> [] + } + + let default_sort_order = + modify.default_sort_order + |> modification.encode("default_sort_order", forum.sort_order_type_encode) + + [ + name, + position, + topic, + is_nsfw, + rate_limit_per_user, + permission_overwrites, + parent_id, + default_auto_archive_duration, + flags, + available_tags, + default_reaction_emoji, + default_thread_rate_limit_per_user, + default_sort_order, + ] + |> list.flatten + |> json.object +} diff --git a/src/flybycord/channel/guild/text.gleam b/src/flybycord/channel/guild/text.gleam index 6401e65..fdaa433 100644 --- a/src/flybycord/channel/guild/text.gleam +++ b/src/flybycord/channel/guild/text.gleam @@ -1,9 +1,12 @@ import flybycord/channel/permission_overwrite.{type PermissionOverwrite} import flybycord/internal/time_duration import flybycord/internal/time_rfc3339 +import flybycord/modification.{type Modification} import flybycord/permission.{type Permission} import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import gleam/time/duration.{type Duration} import gleam/time/timestamp.{type Timestamp} @@ -27,6 +30,21 @@ pub type Channel { ) } +pub type Modify { + Modify( + name: Option(String), + to_announcement: Option(Bool), + position: Modification(Int), + topic: Modification(String), + is_nsfw: Option(Bool), + rate_limit_per_user: Modification(Duration), + permission_overwrites: Modification(List(permission_overwrite.Create)), + parent_id: Modification(String), + default_auto_archive_duration: Modification(Duration), + default_thread_rate_limit_per_user: Option(Duration), + ) +} + // DECODERS -------------------------------------------------------------------- @internal @@ -83,3 +101,84 @@ pub fn channel_decoder() -> decode.Decoder(Channel) { default_auto_archive_duration:, )) } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let type_ = case modify.to_announcement { + Some(True) -> [#("type", json.int(5))] + Some(False) -> [] + None -> [] + } + + let position = + modify.position + |> modification.encode("position", json.int) + + let topic = + modify.topic + |> modification.encode("topic", json.string) + + let is_nsfw = case modify.is_nsfw { + Some(nsfw) -> [#("nsfw", json.bool(nsfw))] + None -> [] + } + + let rate_limit_per_user = + modify.rate_limit_per_user + |> modification.encode( + "rate_limit_per_user", + time_duration.to_int_seconds_encode, + ) + + let permission_overwrites = + modify.permission_overwrites + |> modification.encode("permission_overwrites", fn(permissions) { + permissions + |> json.array(permission_overwrite.create_encode) + }) + + let parent_id = + modify.parent_id + |> modification.encode("parent_id", json.string) + + let default_auto_archive_duration = + modify.default_auto_archive_duration + |> modification.encode( + "default_auto_archive_duration", + time_duration.to_int_seconds_encode, + ) + + let default_thread_rate_limit_per_user = case + modify.default_thread_rate_limit_per_user + { + Some(limit) -> [ + #( + "default_thread_rate_limit_per_user", + time_duration.to_int_seconds_encode(limit), + ), + ] + None -> [] + } + + [ + name, + type_, + position, + topic, + is_nsfw, + rate_limit_per_user, + permission_overwrites, + parent_id, + default_auto_archive_duration, + default_thread_rate_limit_per_user, + ] + |> list.flatten + |> json.object +} diff --git a/src/flybycord/channel/guild/thread.gleam b/src/flybycord/channel/guild/thread.gleam index ba76099..499bebf 100644 --- a/src/flybycord/channel/guild/thread.gleam +++ b/src/flybycord/channel/guild/thread.gleam @@ -2,10 +2,13 @@ import flybycord/guild/member.{type Member as GuildMember} import flybycord/internal/flags import flybycord/internal/time_duration import flybycord/internal/time_rfc3339 +import flybycord/modification.{type Modification} import flybycord/permission.{type Permission} import gleam/dynamic/decode import gleam/int -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import gleam/time/duration.{type Duration} import gleam/time/timestamp.{type Timestamp} @@ -31,6 +34,19 @@ pub type Thread { ) } +pub type Modify { + Modify( + name: Option(String), + is_archived: Option(Bool), + auto_archive_duration: Option(Duration), + is_locked: Option(Bool), + is_invitable: Option(Bool), + rate_limit_per_user: Modification(Duration), + flags: Modification(List(Flag)), + applied_tags_ids: Modification(List(String)), + ) +} + pub type Metadata { Metadata( is_archived: Bool, @@ -170,3 +186,67 @@ pub fn member_decoder() -> decode.Decoder(Member) { use guild_member <- decode.field("member", member.decoder()) decode.success(Member(thread_id:, user_id:, join_timestamp:, guild_member:)) } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let is_archived = case modify.is_archived { + Some(archived) -> [#("archived", json.bool(archived))] + None -> [] + } + + let auto_archive_duration = case modify.auto_archive_duration { + Some(duration) -> [ + #("auto_archive_duration", time_duration.to_int_seconds_encode(duration)), + ] + None -> [] + } + + let is_locked = case modify.is_locked { + Some(locked) -> [#("locked", json.bool(locked))] + None -> [] + } + + let is_invitable = case modify.is_invitable { + Some(invitable) -> [#("invitable", json.bool(invitable))] + None -> [] + } + + let rate_limit_per_user = + modify.rate_limit_per_user + |> modification.encode( + "rate_limit_per_user", + time_duration.to_int_seconds_encode, + ) + + let flags = + modify.flags + |> modification.encode("flags", fn(flags) { + flags.encode(flags, bits_flags()) + }) + + let applied_tags_ids = + modify.applied_tags_ids + |> modification.encode("applied_tags", fn(tags) { + json.array(tags, json.string) + }) + + [ + name, + is_archived, + auto_archive_duration, + is_locked, + is_invitable, + rate_limit_per_user, + flags, + applied_tags_ids, + ] + |> list.flatten + |> json.object +} diff --git a/src/flybycord/channel/guild/voice.gleam b/src/flybycord/channel/guild/voice.gleam index 246908a..a631af2 100644 --- a/src/flybycord/channel/guild/voice.gleam +++ b/src/flybycord/channel/guild/voice.gleam @@ -1,7 +1,12 @@ import flybycord/channel/permission_overwrite.{type PermissionOverwrite} +import flybycord/internal/time_duration +import flybycord/modification.{type Modification} import flybycord/permission.{type Permission} import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} +import gleam/time/duration.{type Duration} // TYPES ----------------------------------------------------------------------- @@ -15,6 +20,7 @@ pub type Channel { is_nsfw: Bool, bitrate: Int, user_limit: Int, + rate_limit_per_user: Duration, parent_id: Option(String), rtc_region: Option(String), video_quality_mode: VideoQualityMode, @@ -22,6 +28,21 @@ pub type Channel { ) } +pub type Modify { + Modify( + name: Option(String), + position: Modification(Int), + is_nsfw: Option(Bool), + rate_limit_per_user: Modification(Duration), + bitrate: Modification(Int), + user_limit: Modification(Int), + permission_overwrites: Modification(List(permission_overwrite.Create)), + parent_id: Modification(String), + rtc_region: Modification(String), + video_quality_mode: Modification(VideoQualityMode), + ) +} + pub type VideoQualityMode { Auto Full @@ -52,6 +73,10 @@ pub fn channel_decoder() -> decode.Decoder(Channel) { use is_nsfw <- decode.field("nsfw", decode.bool) use bitrate <- decode.field("bitrate", decode.int) use user_limit <- decode.field("user_limit", decode.int) + use rate_limit_per_user <- decode.field( + "rate_limit_per_user", + time_duration.from_int_seconds_decoder(), + ) use parent_id <- decode.field("parent_id", decode.optional(decode.string)) use rtc_region <- decode.field("rtc_region", decode.optional(decode.string)) use video_quality_mode <- decode.optional_field( @@ -73,9 +98,87 @@ pub fn channel_decoder() -> decode.Decoder(Channel) { is_nsfw:, bitrate:, user_limit:, + rate_limit_per_user:, parent_id:, rtc_region:, video_quality_mode:, current_user_permissions:, )) } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn modify_encode(modify: Modify) -> Json { + let name = case modify.name { + Some(name) -> [#("name", json.string(name))] + None -> [] + } + + let position = + modify.position + |> modification.encode("position", json.int) + + let is_nsfw = case modify.is_nsfw { + Some(nsfw) -> [#("nsfw", json.bool(nsfw))] + None -> [] + } + + let rate_limit_per_user = + modify.rate_limit_per_user + |> modification.encode( + "rate_limit_per_user", + time_duration.to_int_seconds_encode, + ) + + let bitrate = + modify.bitrate + |> modification.encode("bitrate", json.int) + + let user_limit = + modify.user_limit + |> modification.encode("user_limit", json.int) + + let permission_overwrites = + modify.permission_overwrites + |> modification.encode("permission_overwrites", fn(overwrites) { + overwrites + |> json.array(permission_overwrite.create_encode) + }) + + let parent_id = + modify.parent_id + |> modification.encode("parent_id", json.string) + + let rtc_region = + modify.rtc_region + |> modification.encode("rtc_region", json.string) + + let video_quality_mode = + modify.video_quality_mode + |> modification.encode("video_quality_mode", video_quality_mode_encode) + + [ + name, + position, + is_nsfw, + rate_limit_per_user, + bitrate, + user_limit, + permission_overwrites, + parent_id, + rtc_region, + video_quality_mode, + ] + |> list.flatten + |> json.object +} + +@internal +pub fn video_quality_mode_encode(video_quality_mode: VideoQualityMode) -> Json { + case video_quality_mode { + Auto -> 1 + Full -> 2 + } + |> json.int +} diff --git a/src/flybycord/channel/permission_overwrite.gleam b/src/flybycord/channel/permission_overwrite.gleam index a5be83d..0c573b1 100644 --- a/src/flybycord/channel/permission_overwrite.gleam +++ b/src/flybycord/channel/permission_overwrite.gleam @@ -1,5 +1,7 @@ import flybycord/permission.{type Permission} import gleam/dynamic/decode +import gleam/json.{type Json} +import gleam/option.{type Option} // TYPES ----------------------------------------------------------------------- @@ -12,6 +14,14 @@ pub type PermissionOverwrite { ) } +pub type Create { + Create( + type_: Type, + allow: Option(List(Permission)), + deny: Option(List(Permission)), + ) +} + pub type Type { Role Member @@ -37,3 +47,23 @@ pub fn type_decoder() -> decode.Decoder(Type) { _ -> decode.failure(Role, "Type") } } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn create_encode(create: Create) -> Json { + json.object([ + #("type", type_encode(create.type_)), + #("allow", json.nullable(create.allow, permission.encode)), + #("deny", json.nullable(create.deny, permission.encode)), + ]) +} + +@internal +pub fn type_encode(type_: Type) -> Json { + case type_ { + Role -> 0 + Member -> 1 + } + |> json.int +} diff --git a/src/flybycord/internal/time_duration.gleam b/src/flybycord/internal/time_duration.gleam index 72708f3..dd9f689 100644 --- a/src/flybycord/internal/time_duration.gleam +++ b/src/flybycord/internal/time_duration.gleam @@ -1,5 +1,6 @@ import gleam/dynamic/decode import gleam/float +import gleam/json.{type Json} import gleam/time/duration.{type Duration} // DECODERS -------------------------------------------------------------------- @@ -31,6 +32,16 @@ pub fn from_int_seconds_decoder() -> decode.Decoder(Duration) { // ENCODERS -------------------------------------------------------------------- +@internal +pub fn to_int_seconds_encode(duration: Duration) -> Json { + duration + |> to_int_seconds + |> json.int +} + +// INTERNAL FUNCTIONS ---------------------------------------------------------- + +@internal pub fn to_int_seconds(duration: Duration) -> Int { duration |> duration.to_seconds