diff --git a/.env.example b/.env.example index c9ed30f..4fd3d74 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ TEST_BOT_TOKEN= -TEST_BOT_CLIENT_ID= \ No newline at end of file +TEST_BOT_CLIENT_ID= +TEST_BOT_GUILD_ID= \ No newline at end of file diff --git a/README.md b/README.md index 7985de9..1f18dd9 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,37 @@ ```sh gleam add discord_gleam ``` + ```gleam import discord_gleam +import discord_gleam/event_handler +import discord_gleam/types/message +import gleam/list +import gleam/string +import logging pub fn main() { - todo + logging.configure() + logging.set_level(logging.Info) + + let bot = discord_gleam.bot("YOUR TOKEN") + + discord_gleam.run(bot, [event_handler]) +} + +fn event_handler(bot, packet: event_handler.Packet) { + case packet { + event_handler.MessagePacket(message) -> { + logging.log(logging.Info, "Message: " <> message.d.content) + case message.d.content { + "!ping" -> { + discord_gleam.send_message(bot, message.d.channel_id, "Pong!", []) + } + _ -> Nil + } + } + _ -> Nil + } } ``` @@ -23,19 +49,21 @@ gleam test # Run the tests ``` ## Features: -| Feature | Status | -| --- | --- | -| Basic events | ✅ | -| More events | 🔨 | -| Sending messages | ✅ | -| Ban/kick | ✅ | -| Deleting messages | ✅ | -| Embeds | ✅ | -| Basic Slash commands | ✅ | + +| Feature | Status | +| -------------------- | ------ | +| Basic events | ✅ | +| More events | 🔨 | +| Sending messages | ✅ | +| Ban/kick | ✅ | +| Deleting messages | ✅ | +| Embeds | ✅ | +| Basic Slash commands | ✅ | ✅ - Done | 🔨 - In Progress | 📆 - Planned | ❌ - Not Planned ## Supported events: + - READY - MESSAGE_CREATE - MESSAGE_DELETE diff --git a/examples/slash_commands.gleam b/examples/slash_commands.gleam index 1aca72f..dabc414 100644 --- a/examples/slash_commands.gleam +++ b/examples/slash_commands.gleam @@ -36,7 +36,11 @@ pub fn main() { options: [], ) - discord_gleam.register_commands(bot, "YOUR BOT ID", [test_cmd, test_cmd2]) + discord_gleam.register_global_commands(bot, "YOUR BOT ID", [test_cmd]) + + discord_gleam.register_guild_commands(bot, "YOUR BOT ID", "YOUR GUILD ID", [ + test_cmd2, + ]) discord_gleam.run(bot, [event_handler]) } @@ -53,12 +57,12 @@ fn event_handler(bot, packet: event_handler.Packet) { case interaction.d.data.name { "ping" -> { - discord_gleam.interaction_reply_message(interaction, "pong") + discord_gleam.interaction_reply_message(interaction, "pong", False) Nil } "pong" -> { - discord_gleam.interaction_reply_message(interaction, "ping") + discord_gleam.interaction_reply_message(interaction, "ping", False) Nil } diff --git a/gleam.toml b/gleam.toml index 4f6a958..5996ce1 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,5 +1,5 @@ name = "discord_gleam" -version = "0.0.4" +version = "0.0.5" # Fill out these fields if you intend to generate HTML documentation or publish # your project to the Hex package manager. diff --git a/src/discord_gleam.gleam b/src/discord_gleam.gleam index c669954..dabc0f6 100644 --- a/src/discord_gleam.gleam +++ b/src/discord_gleam.gleam @@ -70,23 +70,46 @@ pub fn delete_message( endpoints.delete_message(bot.token, channel_id, message_id, reason) } -pub fn wipe_slash_commands(bot: bot.Bot, client_id: String) -> #(String, String) { - endpoints.wipe_slash_commands(bot.token, client_id) +pub fn wipe_global_commands( + bot: bot.Bot, + client_id: String, +) -> #(String, String) { + endpoints.wipe_global_commands(bot.token, client_id) +} + +pub fn wipe_guild_commands( + bot: bot.Bot, + client_id: String, + guild_id: String, +) -> #(String, String) { + endpoints.wipe_guild_commands(bot.token, client_id, guild_id) } -pub fn register_commands( +pub fn register_global_commands( bot: bot.Bot, client_id: String, commands: List(slash_command.SlashCommand), ) { list.each(commands, fn(command) { - endpoints.register_slash_command(bot.token, client_id, command) + endpoints.register_global_command(bot.token, client_id, command) + }) +} + +pub fn register_guild_commands( + bot: bot.Bot, + client_id: String, + guild_id: String, + commands: List(slash_command.SlashCommand), +) { + list.each(commands, fn(command) { + endpoints.register_guild_command(bot.token, client_id, guild_id, command) }) } pub fn interaction_reply_message( interaction: interaction_create.InteractionCreate, message: String, + ephemeral: Bool, ) -> #(String, String) { - endpoints.interaction_send_text(interaction, message) + endpoints.interaction_send_text(interaction, message, ephemeral) } diff --git a/src/discord_gleam/http/endpoints.gleam b/src/discord_gleam/http/endpoints.gleam index 1637efe..522292b 100644 --- a/src/discord_gleam/http/endpoints.gleam +++ b/src/discord_gleam/http/endpoints.gleam @@ -233,7 +233,7 @@ pub fn delete_message( } } -pub fn wipe_slash_commands( +pub fn wipe_global_commands( token: String, client_id: String, ) -> #(String, String) { @@ -249,11 +249,11 @@ pub fn wipe_slash_commands( Ok(resp) -> { case resp.status { 200 -> { - logging.log(logging.Debug, "Wiped Slash Commands") + logging.log(logging.Debug, "Wiped global commands") #("OK", resp.body) } _ -> { - logging.log(logging.Error, "Failed to wipe Slash Commands") + logging.log(logging.Error, "Failed to wipe global commands") io.debug(resp.body) #("FAILED", resp.body) @@ -261,7 +261,7 @@ pub fn wipe_slash_commands( } } Error(err) -> { - logging.log(logging.Error, "Failed to wipe Slash Commands") + logging.log(logging.Error, "Failed to wipe global commands") io.debug(err) #("FAILED", "ERROR") @@ -269,7 +269,44 @@ pub fn wipe_slash_commands( } } -pub fn register_slash_command( +pub fn wipe_guild_commands( + token: String, + client_id: String, + guild_id: String, +) -> #(String, String) { + let request = + request.new_auth_post( + http.Put, + "/applications/" <> client_id <> "/guilds/" <> guild_id <> "/commands", + token, + "{}", + ) + + case hackney.send(request) { + Ok(resp) -> { + case resp.status { + 200 -> { + logging.log(logging.Debug, "Wiped guild commands") + #("OK", resp.body) + } + _ -> { + logging.log(logging.Error, "Failed to wipe guild commands") + io.debug(resp.body) + + #("FAILED", resp.body) + } + } + } + Error(err) -> { + logging.log(logging.Error, "Failed to wipe guild commands") + io.debug(err) + + #("FAILED", "ERROR") + } + } +} + +pub fn register_global_command( token: String, client_id: String, command: slash_command.SlashCommand, @@ -286,17 +323,62 @@ pub fn register_slash_command( Ok(resp) -> { case resp.status { 201 -> { - logging.log(logging.Debug, "Added Slash Command " <> command.name) + logging.log(logging.Debug, "Added global command " <> command.name) + #("OK", resp.body) + } + 200 -> { + logging.log(logging.Debug, "Updated global command " <> command.name) + #("OK", resp.body) + } + _ -> { + logging.log( + logging.Error, + "Failed to add global command" <> command.name, + ) + io.debug(resp.body) + + #("FAILED", resp.body) + } + } + } + Error(err) -> { + logging.log(logging.Error, "Failed to add global command" <> command.name) + io.debug(err) + + #("FAILED", "ERROR") + } + } +} + +pub fn register_guild_command( + token: String, + client_id: String, + guild_id: String, + command: slash_command.SlashCommand, +) -> #(String, String) { + let request = + request.new_auth_post( + http.Post, + "/applications/" <> client_id <> "/guilds/" <> guild_id <> "/commands", + token, + slash_command.command_to_string(command), + ) + + case hackney.send(request) { + Ok(resp) -> { + case resp.status { + 201 -> { + logging.log(logging.Debug, "Added guild command " <> command.name) #("OK", resp.body) } 200 -> { - logging.log(logging.Debug, "Updated Slash Command " <> command.name) + logging.log(logging.Debug, "Updated guild command " <> command.name) #("OK", resp.body) } _ -> { logging.log( logging.Error, - "Failed to add Slash Command" <> command.name, + "Failed to add guild command" <> command.name, ) io.debug(resp.body) @@ -305,7 +387,7 @@ pub fn register_slash_command( } } Error(err) -> { - logging.log(logging.Error, "Failed to add Slash Command" <> command.name) + logging.log(logging.Error, "Failed to add guild command" <> command.name) io.debug(err) #("FAILED", "ERROR") @@ -316,6 +398,7 @@ pub fn register_slash_command( pub fn interaction_send_text( interaction: interaction_create.InteractionCreate, message: String, + ephemeral: Bool, ) -> #(String, String) { let request = request.new_post( @@ -325,7 +408,7 @@ pub fn interaction_send_text( <> "/" <> interaction.d.token <> "/callback", - slash_command.make_basic_text_reply(message), + slash_command.make_basic_text_reply(message, ephemeral), ) case hackney.send(request) { diff --git a/src/discord_gleam/http/request.gleam b/src/discord_gleam/http/request.gleam index 1ff2636..fc99203 100644 --- a/src/discord_gleam/http/request.gleam +++ b/src/discord_gleam/http/request.gleam @@ -10,7 +10,7 @@ pub fn new(method: http.Method, path: String) -> request.Request(String) { |> request.prepend_header("accept", "application/json") |> request.prepend_header( "User-Agent", - "DiscordBot (https://github.com/cyteon/discord_gleam, 0.0.4)", + "DiscordBot (https://github.com/cyteon/discord_gleam, 0.0.5)", ) } diff --git a/src/discord_gleam/types/slash_command.gleam b/src/discord_gleam/types/slash_command.gleam index 4cfc6cc..e38c6c5 100644 --- a/src/discord_gleam/types/slash_command.gleam +++ b/src/discord_gleam/types/slash_command.gleam @@ -43,13 +43,18 @@ type BasicResponse { BasicResponse(type_: Int, data: BasicResponseData) } -pub fn make_basic_text_reply(message: String) -> String { +pub fn make_basic_text_reply(message: String, ephemeral: Bool) -> String { let data = BasicResponseData(content: message) let response = BasicResponse(type_: 4, data: data) + let callback_data = case ephemeral { + True -> [#("content", json.string(data.content)), #("flags", json.int(64))] + False -> [#("content", json.string(data.content))] + } + json.object([ #("type", json.int(response.type_)), - #("data", json.object([#("content", json.string(data.content))])), + #("data", json.object(callback_data)), ]) |> json.to_string } diff --git a/src/discord_gleam/ws/event_loop.gleam b/src/discord_gleam/ws/event_loop.gleam index 8793c86..6227f08 100644 --- a/src/discord_gleam/ws/event_loop.gleam +++ b/src/discord_gleam/ws/event_loop.gleam @@ -34,7 +34,7 @@ pub fn main(bot: bot.Bot, event_handlers: List(event_handler.EventHandler)) { |> request.set_path("/?v=10&encoding=json") |> request.set_header( "User-Agent", - "DiscordBot (https://github.com/cyteon/discord_gleam, 0.0.4)", + "DiscordBot (https://github.com/cyteon/discord_gleam, 0.0.5)", ) |> request.set_header("Host", "gateway.discord.gg") |> request.set_header("Connection", "Upgrade") diff --git a/test/discord_gleam_test.gleam b/test/discord_gleam_test.gleam index fe605e4..68e4de4 100644 --- a/test/discord_gleam_test.gleam +++ b/test/discord_gleam_test.gleam @@ -8,8 +8,9 @@ pub fn main() { { use token <- result.try(get_env("TEST_BOT_TOKEN")) use client_id <- result.try(get_env("TEST_BOT_CLIENT_ID")) + use guild_id <- result.try(get_env("TEST_BOT_GUILD_ID")) - Ok(example_bot.main(token, client_id)) + Ok(example_bot.main(token, client_id, guild_id)) } { Ok(_) -> Nil diff --git a/test/example_bot.gleam b/test/example_bot.gleam index b398f26..1108904 100644 --- a/test/example_bot.gleam +++ b/test/example_bot.gleam @@ -2,12 +2,11 @@ import discord_gleam import discord_gleam/event_handler import discord_gleam/types/message import discord_gleam/types/slash_command -import discord_gleam/ws/packets/message_delete import gleam/list import gleam/string import logging -pub fn main(token: String, client_id: String) { +pub fn main(token: String, client_id: String, guild_id: String) { logging.configure() logging.set_level(logging.Debug) @@ -28,8 +27,26 @@ pub fn main(token: String, client_id: String) { ], ) - discord_gleam.wipe_slash_commands(bot, client_id) - discord_gleam.register_commands(bot, client_id, [test_cmd]) + let test_cmd2 = + slash_command.SlashCommand( + name: "test2", + type_: 1, + description: "Test command", + options: [ + slash_command.CommandOption( + name: "test", + description: "Test option", + type_: 3, + required: False, + ), + ], + ) + + discord_gleam.wipe_global_commands(bot, client_id) + discord_gleam.register_global_commands(bot, client_id, [test_cmd]) + + discord_gleam.wipe_guild_commands(bot, client_id, guild_id) + discord_gleam.register_guild_commands(bot, client_id, guild_id, [test_cmd2]) discord_gleam.run(bot, [event_handler]) } @@ -177,7 +194,12 @@ fn event_handler(bot, packet: event_handler.Packet) { case interaction.d.data.name { "test" -> { - discord_gleam.interaction_reply_message(interaction, "test") + discord_gleam.interaction_reply_message(interaction, "test", True) + + Nil + } + "test2" -> { + discord_gleam.interaction_reply_message(interaction, "test2", False) Nil }