From acb99bb34aa237f40e8d85777de337c40aeb25e0 Mon Sep 17 00:00:00 2001 From: Filip Hoffmann Date: Wed, 22 Jul 2026 01:44:57 +0200 Subject: [PATCH] modify message endpoint --- src/grom.gleam | 265 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 208 insertions(+), 57 deletions(-) diff --git a/src/grom.gleam b/src/grom.gleam index 28be632..5d51695 100644 --- a/src/grom.gleam +++ b/src/grom.gleam @@ -12745,6 +12745,32 @@ pub opaque type CreateForumLikeThread { ) } +fn multipart_request( + request: Request(_), + json json: String, + attachments attachments: List(CreateMessageAttachment), +) -> Request(BitArray) { + 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) +} + /// Behavior: /// - the created thread will be a `PublicThread` /// - requires the `AllowSendingMessages` permission in the forum-like channel @@ -12767,27 +12793,10 @@ pub fn new_forum_like_thread_request( |> 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"), - )) - + Some(attachments) -> request - |> multipart_form.to_request(body) - } + |> multipart_request(json:, attachments:) + None -> request } } @@ -12866,10 +12875,10 @@ pub opaque type CreateForumLikeThreadMessage { ) } -fn create_forum_like_thread_message_to_json( - create: CreateForumLikeThreadMessage, -) -> Json { - let attachments = case create.attachments { +fn create_message_attachments_to_json( + attachments: Option(List(CreateMessageAttachment)), +) -> Result(#(String, Json), Nil) { + case attachments { Some(attachments) -> Ok(#( "attachments", @@ -12879,6 +12888,11 @@ fn create_forum_like_thread_message_to_json( )) None -> Error(Nil) } +} + +fn create_forum_like_thread_message_to_json( + create: CreateForumLikeThreadMessage, +) -> Json { [ optional_to_json(create.content, "content", json.string), optional_to_json(create.embeds, "embeds", json.array( @@ -12893,7 +12907,7 @@ fn create_forum_like_thread_message_to_json( _, create_message_component_to_json, )), - attachments, + create_message_attachments_to_json(create.attachments), optional_to_json(create.flags, "flags", flags_to_json( _, bits_create_forum_like_thread_message_flags(), @@ -15074,17 +15088,6 @@ pub fn new_message() -> CreateMessage { } fn create_message_to_json(create: CreateMessage) -> Json { - let attachments = case create.attachments { - Some(attachments) -> - Ok(#( - "attachments", - attachments - |> list.index_map(create_message_attachment_to_json) - |> json.preprocessed_array, - )) - None -> Error(Nil) - } - [ optional_to_json(create.content, "content", json.string), optional_to_json(create.nonce, "nonce", message_nonce_to_json), @@ -15106,7 +15109,7 @@ fn create_message_to_json(create: CreateMessage) -> Json { _, create_message_component_to_json, )), - attachments, + create_message_attachments_to_json(create.attachments), optional_to_json(create.enforce_nonce, "enforce_nonce", json.bool), optional_to_json(create.poll, "poll", create_poll_to_json), ] @@ -15512,27 +15515,9 @@ pub fn new_message_request( |> request.map(bit_array.from_string) case create.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"), - )) - + Some(attachments) -> request - |> multipart_form.to_request(body) - } + |> multipart_request(json:, attachments:) None -> request } } @@ -15796,3 +15781,169 @@ pub fn delete_message_emoji_reactions_response( ) -> Result(Nil, RestError) { handle_no_content_response(response) } + +pub opaque type ModifyMessage { + ModifyMessage( + content: Option(String), + embeds: Option(List(CreateMessageEmbed)), + // Yes, this is an integer. + // + // The builder functions use a type, but also take the regular message flags from the old message, + // then they get ORed and put straight into JSON. + flags: Option(Int), + allowed_mentions: Option(List(AllowedMessageMention)), + components: Option(List(CreateMessageComponent)), + attachments: Option(List(CreateMessageAttachment)), + ) +} + +pub fn new_modify_message() -> ModifyMessage { + ModifyMessage( + content: None, + embeds: None, + flags: None, + allowed_mentions: None, + components: None, + attachments: None, + ) +} + +pub fn modify_message_content( + modify: ModifyMessage, + new content: String, +) -> ModifyMessage { + ModifyMessage(..modify, content: Some(content)) +} + +pub fn modify_message_embeds( + modify: ModifyMessage, + new embeds: List(CreateMessageEmbed), +) -> ModifyMessage { + ModifyMessage(..modify, embeds: Some(embeds)) +} + +/// You need the message's present flags to modify them. +/// +/// Removal takes prescedence over addition. +/// +/// Trying to add an already present flag or remove an already absent one is a no-op. +pub fn modify_message_flags( + modify: ModifyMessage, + old old: List(MessageFlag), + new_added added: List(ModifyMessageAddFlag), + new_removed removed: List(ModifyMessageRemoveFlag), +) -> ModifyMessage { + let old = flags_to_int(old, bits_message_flags()) + let added = flags_to_int(added, bits_modify_message_add_flags()) + let removed = flags_to_int(removed, bits_modify_message_remove_flags()) + + let flags = + old + |> int.bitwise_or(added) + |> int.bitwise_and(int.bitwise_not(removed)) + + ModifyMessage(..modify, flags: Some(flags)) +} + +/// Modifying a message's allowed mentions doesn't notify anyone. +/// +/// Please read through the [`AllowedMessageMention`](#AllowedMessageMention) documentation before going forward. +pub fn modify_message_allowed_mentions( + modify: ModifyMessage, + allowed_mentions: List(AllowedMessageMention), +) -> ModifyMessage { + ModifyMessage(..modify, allowed_mentions: Some(allowed_mentions)) +} + +/// All the components you plan on having in the new version of the message must be provided. +/// +/// Make sure to set the `ModifyMessageToUseComponentsV2` flag if using components V2. +/// +/// Clear out the `content` and `embeds` fields if using components V2. +pub fn modify_message_components( + modify: ModifyMessage, + components: List(CreateMessageComponent), +) -> ModifyMessage { + ModifyMessage(..modify, components: Some(components)) +} + +/// All the attachments you plan on having in the new version of the message must be provided. +pub fn modify_message_attachments( + modify: ModifyMessage, + attachments: List(CreateMessageAttachment), +) -> ModifyMessage { + ModifyMessage(..modify, attachments: Some(attachments)) +} + +fn modify_message_to_json(modify: ModifyMessage) -> Json { + [ + optional_to_json(modify.content, "content", json.string), + optional_to_json(modify.embeds, "embeds", json.array( + _, + create_message_embed_to_json, + )), + optional_to_json(modify.flags, "flags", json.int), + optional_to_json( + modify.allowed_mentions, + "allowed_mentions", + allowed_message_mentions_to_json, + ), + optional_to_json(modify.components, "components", json.array( + _, + create_message_component_to_json, + )), + create_message_attachments_to_json(modify.attachments), + ] + |> list_to_json_object +} + +pub type ModifyMessageAddFlag { + ModifyMessageToSuppressEmbeds + ModifyMessageToUseComponentsV2 +} + +pub type ModifyMessageRemoveFlag { + ModifyMessageToNoLongerSuppressEmbeds +} + +fn bits_modify_message_add_flags() -> List(#(Int, ModifyMessageAddFlag)) { + [ + #(int.bitwise_shift_left(1, 2), ModifyMessageToSuppressEmbeds), + #(int.bitwise_shift_left(1, 15), ModifyMessageToUseComponentsV2), + ] +} + +fn bits_modify_message_remove_flags() -> List(#(Int, ModifyMessageRemoveFlag)) { + [#(int.bitwise_shift_left(1, 2), ModifyMessageToNoLongerSuppressEmbeds)] +} + +/// Modifying someone else's message: +/// - you can only add the `ModifyMessageToSuppressEmbeds` flag +/// - requires the `AllowManagingMessages` permission. +pub fn modify_message_request( + token token: Token, + in_channel_with_id channel_id: Snowflake(Channel), + message_with_id message_id: Snowflake(Message), + using modify: ModifyMessage, +) -> Request(BitArray) { + let json = modify |> modify_message_to_json |> json.to_string + + let request = + new_request( + token:, + to: "/channels/" + <> snowflake_to_string(channel_id) + <> "/messages/" + <> snowflake_to_string(message_id), + method: http.Patch, + ) + |> request.set_body(json) + |> request.map(bit_array.from_string) + + case modify.attachments { + Some(attachments) -> + request + |> multipart_request(json:, attachments:) + None -> request + } +} -- 2.51.2