From df11b2de7139704cd4cfa571143a9ccf3bf3a31c Mon Sep 17 00:00:00 2001 From: Filip Hoffmann Date: Tue, 11 Nov 2025 02:22:03 +0100 Subject: [PATCH] minor fixes, big heartbeat fix, the child is born --- examples/your_first_bot/gleam.toml | 2 ++ examples/your_first_bot/manifest.toml | 13 +++++++--- .../your_first_bot/src/your_first_bot.gleam | 10 ++++--- src/grom/application.gleam | 22 ++++++++++++---- src/grom/command.gleam | 8 ++++-- src/grom/gateway/heartbeat.gleam | 26 ++++++++++++------- src/grom/interaction.gleam | 22 +++++----------- src/grom/message/interaction_metadata.gleam | 5 +++- 8 files changed, 69 insertions(+), 39 deletions(-) diff --git a/examples/your_first_bot/gleam.toml b/examples/your_first_bot/gleam.toml index fe619a2..e8faa18 100644 --- a/examples/your_first_bot/gleam.toml +++ b/examples/your_first_bot/gleam.toml @@ -18,6 +18,8 @@ grom = { path = "../.." } logging = ">= 1.3.0 and < 2.0.0" gleam_otp = ">= 1.2.0 and < 2.0.0" gleam_erlang = ">= 1.3.0 and < 2.0.0" +envoy = ">= 1.0.2 and < 2.0.0" +dotenv_gleam = ">= 2.0.1 and < 3.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/examples/your_first_bot/manifest.toml b/examples/your_first_bot/manifest.toml index 49dbc17..fd577d4 100644 --- a/examples/your_first_bot/manifest.toml +++ b/examples/your_first_bot/manifest.toml @@ -3,7 +3,10 @@ packages = [ { name = "discord_gleam_stratus", version = "1.0.2", build_tools = ["gleam"], requirements = ["exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "gramps", "logging"], otp_app = "discord_gleam_stratus", source = "hex", outer_checksum = "30CABB6B27B6A98B28B9871BDC53B58AD011AB74E2193D981901CB70A48D206F" }, + { name = "dotenv_gleam", version = "2.0.1", build_tools = ["gleam"], requirements = ["envoy", "gleam_erlang", "gleam_stdlib", "simplifile"], otp_app = "dotenv_gleam", source = "hex", outer_checksum = "47391525F97AF2086B34A4F2E81C1A1102863ACA983540CD87A8D295B1636445" }, + { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" }, { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" }, @@ -14,18 +17,20 @@ packages = [ { name = "gleam_time", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "D560E672C7279C89908981E068DF07FD16D0C859DCA266F908B18F04DF0EB8E6" }, { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, { name = "gramps", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "8B7195978FBFD30B43DF791A8A272041B81E45D245314D7A41FC57237AA882A0" }, - { name = "grom", version = "0.0.0", build_tools = ["gleam"], requirements = ["discord_gleam_stratus", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_json", "gleam_otp", "gleam_stdlib", "gleam_time", "multipart_form", "operating_system", "repeatedly", "status_code"], source = "local", path = "../.." }, + { name = "grom", version = "0.0.0", build_tools = ["gleam"], requirements = ["discord_gleam_stratus", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_json", "gleam_otp", "gleam_stdlib", "gleam_time", "multipart_form", "operating_system", "status_code"], source = "local", path = "../.." }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, { name = "multipart_form", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "multipart_form", source = "hex", outer_checksum = "082C77A0C3BB1128FCD55491665E9B72BC943E849B67D02B08CFA6808AD8E47C" }, { name = "operating_system", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "operating_system", source = "hex", outer_checksum = "682D4D19496E607A6E6229AA51A38A1400ED5067C2C54D56843DBCB0C61FFA6D" }, - { name = "repeatedly", version = "2.1.2", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "93AE1938DDE0DC0F7034F32C1BF0D4E89ACEBA82198A1FE21F604E849DA5F589" }, + { name = "simplifile", version = "2.3.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "957E0E5B75927659F1D2A1B7B75D7B9BA96FAA8D0C53EA71C4AD9CD0C6B848F6" }, { name = "status_code", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "status_code", source = "hex", outer_checksum = "F6EC765345A634602AD030EB1B4D34A425873CBC223CEC595EBF062073CD70BC" }, ] [requirements] +dotenv_gleam = { version = ">= 2.0.1 and < 3.0.0" } +envoy = { version = ">= 1.0.2 and < 2.0.0" } +gleam_erlang = { version = ">= 1.3.0 and < 2.0.0" } +gleam_otp = { version = ">= 1.2.0 and < 2.0.0" } gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } grom = { path = "../.." } logging = { version = ">= 1.3.0 and < 2.0.0" } -gleam_otp = { version = ">= 1.2.0 and < 2.0.0" } -gleam_erlang = { version = ">= 1.3.0 and < 2.0.0" } diff --git a/examples/your_first_bot/src/your_first_bot.gleam b/examples/your_first_bot/src/your_first_bot.gleam index 680721f..9cda4ef 100644 --- a/examples/your_first_bot/src/your_first_bot.gleam +++ b/examples/your_first_bot/src/your_first_bot.gleam @@ -1,3 +1,5 @@ +import dotenv_gleam +import envoy import gleam/erlang/process import gleam/option.{Some} import gleam/otp/actor @@ -9,14 +11,16 @@ import grom/gateway/intent import grom/interaction.{type Interaction} import logging -const token = "ODI1NzUzOTgwNzE3MjM2Mjc0.GYOKqE.OmV7A8aP6bVUXPUy4ajrQH7Z7RgYHf5TDJoQlQ" - type State { State(client: grom.Client) } pub fn main() -> Nil { logging.configure() + + let assert Ok(_) = dotenv_gleam.config() + let assert Ok(token) = envoy.get("BOT_TOKEN") + let client = grom.Client(token:) let identify = @@ -67,7 +71,7 @@ fn create_actor( fn on_event(state: State, event: gateway.Event) { case event { gateway.ErrorEvent(error) -> { - logging.log(logging.Warning, "Error: " <> string.inspect(error)) + logging.log(logging.Warning, string.inspect(error)) actor.continue(state) } gateway.ReadyEvent(ready) -> on_ready(state, ready) diff --git a/src/grom/application.gleam b/src/grom/application.gleam index 0fda93d..006d092 100644 --- a/src/grom/application.gleam +++ b/src/grom/application.gleam @@ -241,7 +241,7 @@ pub fn decoder() -> decode.Decoder(Application) { "integration_types_config", None, decode.optional(decode.dict( - installation_context_decoder(), + installation_context_string_decoder(), installation_context_config_decoder(), )), ) @@ -292,7 +292,19 @@ pub fn install_params_decoder() -> decode.Decoder(InstallParams) { } @internal -pub fn installation_context_decoder() -> decode.Decoder(InstallationContext) { +pub fn installation_context_string_decoder() -> decode.Decoder( + InstallationContext, +) { + use variant <- decode.then(decode.string) + case variant { + "0" -> decode.success(GuildInstall) + "1" -> decode.success(UserInstall) + _ -> decode.failure(GuildInstall, "IntegrationType") + } +} + +@internal +pub fn installation_context_int_decoder() -> decode.Decoder(InstallationContext) { use variant <- decode.then(decode.int) case variant { 0 -> decode.success(GuildInstall) @@ -347,10 +359,10 @@ pub fn installation_context_to_string(context: InstallationContext) -> String { @internal pub fn installation_context_to_json(context: InstallationContext) -> Json { case context { - GuildInstall -> 0 - UserInstall -> 1 + GuildInstall -> "0" + UserInstall -> "1" } - |> json.int + |> json.string } @internal diff --git a/src/grom/command.gleam b/src/grom/command.gleam index f4249de..892f819 100644 --- a/src/grom/command.gleam +++ b/src/grom/command.gleam @@ -470,7 +470,9 @@ pub fn slash_command_decoder() -> decode.Decoder(SlashCommand) { use integration_types <- decode.optional_field( "integration_types", None, - decode.optional(decode.list(of: application.installation_context_decoder())), + decode.optional( + decode.list(of: application.installation_context_int_decoder()), + ), ) use contexts <- decode.optional_field( "contexts", @@ -518,7 +520,9 @@ pub fn user_command_decoder() -> decode.Decoder(UserCommand) { use integration_types <- decode.optional_field( "integration_types", None, - decode.optional(decode.list(of: application.installation_context_decoder())), + decode.optional( + decode.list(of: application.installation_context_int_decoder()), + ), ) use contexts <- decode.optional_field( "contexts", diff --git a/src/grom/gateway/heartbeat.gleam b/src/grom/gateway/heartbeat.gleam index af965a9..f8ecd11 100644 --- a/src/grom/gateway/heartbeat.gleam +++ b/src/grom/gateway/heartbeat.gleam @@ -18,7 +18,7 @@ pub type Message { } pub type State { - State(callback: fn() -> Nil, interval: Duration) + State(callback: fn() -> Nil, interval: Duration, subject: Subject(Message)) } pub type Counter { @@ -60,20 +60,28 @@ pub fn start( every interval: Duration, after initial_wait: Duration, ) { - actor.new(State(callback:, interval:)) - |> actor.on_message(on_message) - |> actor.start - |> result.map_error(grom.CouldNotStartActor) - |> result.map(fn(actor) { - process.send_after( - actor.data, + actor.new_with_initialiser(20, fn(subject) { + let selector = + process.new_selector() + |> process.select(subject) + + subject + |> process.send_after( initial_wait |> duration.to_seconds |> float.multiply(1000.0) |> float.round, Tick, ) + + actor.initialised(State(callback:, interval:, subject:)) + |> actor.selecting(selector) + |> actor.returning(subject) + |> Ok }) + |> actor.on_message(on_message) + |> actor.start + |> result.map_error(grom.CouldNotStartActor) } fn on_message(state: State, message: Message) { @@ -82,7 +90,7 @@ fn on_message(state: State, message: Message) { state.callback() process.send_after( - process.new_subject(), + state.subject, state.interval |> duration.to_seconds |> float.multiply(1000.0) diff --git a/src/grom/interaction.gleam b/src/grom/interaction.gleam index c035bc5..3a8c84d 100644 --- a/src/grom/interaction.gleam +++ b/src/grom/interaction.gleam @@ -10,11 +10,9 @@ import gleam/option.{type Option, None, Some} import gleam/result import grom import grom/application -import grom/channel.{type Channel} import grom/channel/thread import grom/entitlement.{type Entitlement} import grom/file.{type File} -import grom/guild.{type Guild} import grom/guild/role.{type Role} import grom/guild_member.{type GuildMember} import grom/internal/flags @@ -37,7 +35,6 @@ pub type Interaction { application_id: String, data: Data, invokement_info: InvokementInfo, - channel: Channel, channel_id: String, token: String, /// For modals/component interactions, the message it was triggered from. @@ -274,12 +271,7 @@ pub type Context { } pub type InvokementInfo { - InvokedInGuild( - guild: Guild, - guild_id: String, - member: GuildMember, - guild_locale: String, - ) + InvokedInGuild(guild_id: String, member: GuildMember, guild_locale: String) InvokedInDm(user: User) } @@ -498,7 +490,6 @@ pub fn decoder() -> decode.Decoder(Interaction) { use application_id <- decode.field("application_id", decode.string) use type_ <- decode.field("type", decode.int) use invokement_info <- decode.then(invokement_info_decoder()) - use channel <- decode.field("channel", channel.decoder()) use channel_id <- decode.field("channel_id", decode.string) use token <- decode.field("token", decode.string) use data <- decode.field("data", data_decoder(type_)) @@ -519,7 +510,10 @@ pub fn decoder() -> decode.Decoder(Interaction) { ) use authorizing_integration_owners_ids <- decode.field( "authorizing_integration_owners", - decode.dict(application.installation_context_decoder(), decode.string), + decode.dict( + application.installation_context_string_decoder(), + decode.string, + ), ) use context <- decode.optional_field( "context", @@ -536,7 +530,6 @@ pub fn decoder() -> decode.Decoder(Interaction) { application_id:, data:, invokement_info:, - channel:, channel_id:, token:, message:, @@ -683,7 +676,7 @@ pub fn submitted_label_component_decoder() -> decode.Decoder( @internal pub fn command_execution_decoder() -> decode.Decoder(CommandExecution) { - use command_type <- decode.subfield(["data", "type"], decode.int) + use command_type <- decode.field("type", decode.int) case command_type { 1 -> decode.map(slash_command_execution_decoder(), SlashCommandExecuted) 2 -> decode.map(user_command_execution_decoder(), UserCommandExecuted) @@ -859,12 +852,11 @@ pub fn context_decoder() -> decode.Decoder(Context) { @internal pub fn invokement_info_decoder() -> decode.Decoder(InvokementInfo) { let in_guild_decoder = { - use guild <- decode.field("guild", guild.decoder()) use guild_id <- decode.field("guild_id", decode.string) use member <- decode.field("member", guild_member.decoder()) use guild_locale <- decode.field("guild_locale", decode.string) - decode.success(InvokedInGuild(guild:, guild_id:, member:, guild_locale:)) + decode.success(InvokedInGuild(guild_id:, member:, guild_locale:)) } let in_dm_decoder = { use user <- decode.field("user", user.decoder()) diff --git a/src/grom/message/interaction_metadata.gleam b/src/grom/message/interaction_metadata.gleam index a91a380..c8591ff 100644 --- a/src/grom/message/interaction_metadata.gleam +++ b/src/grom/message/interaction_metadata.gleam @@ -38,7 +38,10 @@ pub fn decoder() -> decode.Decoder(InteractionMetadata) { use user <- decode.field("user", user.decoder()) use authorizing_integration_owners <- decode.field( "authorizing_integration_owners", - decode.dict(application.installation_context_decoder(), decode.string), + decode.dict( + application.installation_context_string_decoder(), + decode.string, + ), ) use original_response_message_id <- decode.optional_field( "original_response_message_id", -- 2.51.2