From 056502d68b27d57eae931049ddc869ac901a5238 Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Sat, 23 Mar 2024 11:10:15 -0300 Subject: [PATCH 1/8] fix(rest_request): memory leak when using `reason` field This commit fixes a memory leak that happened when a reason was set for some request. --- src/discord-rest_request.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 48850136..8105be93 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -256,7 +256,8 @@ discord_request_cancel(struct discord_requestor *rqtor, if (NOT_EMPTY_STR(req->reason)) { ua_conn_remove_header(req->conn, "X-Audit-Log-Reason"); - *req->reason = '\0'; + free(req->reason); + req->reason = NULL; } if (req->conn) { ua_conn_stop(req->conn); From c226e7b39909fb8ca7e488b1d2248dad72a6e470 Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Tue, 27 Feb 2024 18:10:06 -0300 Subject: [PATCH 2/8] feat: zombie connection check This commit adds the check that verifies if the connection is dead, AKA zombie. Without this, once internet was unavailable for a certain time, Discord would stop responding to the heartbeats and wouldn't send any events, causing to the bot be offline, while the process would be still alive. --- include/discord-internal.h | 5 +++++ src/discord-gateway.c | 4 ++++ src/discord-gateway_dispatch.c | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/include/discord-internal.h b/include/discord-internal.h index 17eb2a59..7f37fb8b 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -749,6 +749,11 @@ struct discord_gateway { * @note obtained at `HELLO` */ int64_t hbeat_interval; + /** + * boolean that indicates if the last heartbeat was answered + * @note used to detect zombie connections + */ + bool hbeat_acknowledged; /** * Gateway's concept of "now" * @note updated at discord_gateway_perform() diff --git a/src/discord-gateway.c b/src/discord-gateway.c index dd272c83..c2b47174 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -319,6 +319,7 @@ _discord_on_heartbeat_ack(struct discord_gateway *gw) /* get request / response interval in milliseconds */ pthread_rwlock_wrlock(&gw->timer->rwlock); gw->timer->ping_ms = (int)(gw->timer->now - gw->timer->hbeat_last); + gw->timer->hbeat_acknowledged = true; pthread_rwlock_unlock(&gw->timer->rwlock); logconf_trace(&gw->conf, "PING: %d ms", gw->timer->ping_ms); @@ -551,6 +552,9 @@ discord_gateway_init(struct discord_gateway *gw, ASSERT_S(!pthread_rwlock_init(&gw->timer->rwlock, NULL), "Couldn't initialize Gateway's rwlock"); + /* mark true to not get reconnected each reconnect */ + gw->timer->hbeat_acknowledged = true; + /* client connection status */ gw->session = calloc(1, sizeof *gw->session); gw->session->retry.enable = true; diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index b8df9e29..0bddeb45 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -264,6 +264,16 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) jsonb_object_pop(&b, buf, sizeof(buf)); } + if (!gw->timer->hbeat_acknowledged) { + logconf_warn(&gw->conf, "Heartbeat ACK not received, marked as zombie"); + + gw->timer->hbeat_acknowledged = true; + + discord_gateway_reconnect(gw, false); + + return; + } + if (ws_send_text(gw->ws, &info, buf, b.pos)) { io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); logconf_info( @@ -273,6 +283,8 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) ANSI_FG_BRIGHT_GREEN) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", b.pos, info.loginfo.counter + 1); + gw->timer->hbeat_acknowledged = false; + /* update heartbeat timestamp */ gw->timer->hbeat_last = gw->timer->now; if (!gw->timer->hbeat_timer) From 558f1560dfd04c932528119699068e5582060ffa Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Mon, 6 May 2024 20:04:41 -0300 Subject: [PATCH 3/8] fix(concord-once): building on Cygwin This fix is totally based off Anotra's PR commits #172, adapted to work on dev before the PR is merged. --- src/concord-once.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/concord-once.c b/src/concord-once.c index f9621c11..a74ea9e7 100644 --- a/src/concord-once.c +++ b/src/concord-once.c @@ -72,12 +72,16 @@ ccord_global_init() } for (int i = 0; i < 2; i++) { const int on = 1; - if (0 != ioctl(shutdown_fds[i], FIOCLEX, NULL)) { - fputs("Failed to make shutdown pipe close on execute\n", - stderr); - goto fail_pipe_init; - } - if (0 != ioctl(shutdown_fds[i], FIONBIO, &on)) { + + #ifdef FIOCLEX + if (0 != ioctl(shutdown_fds[i], FIOCLEX, NULL)) { + fputs("Failed to make shutdown pipe close on execute\n", + stderr); + goto fail_pipe_init; + } + #endif + + if (0 != ioctl(shutdown_fds[i], (int)FIONBIO, &on)) { fputs("Failed to make shutdown pipe nonblocking\n", stderr); goto fail_pipe_init; } @@ -125,9 +129,17 @@ discord_dup_shutdown_fd(void) if (-1 == shutdown_fds[0]) return -1; if (-1 != (fd = dup(shutdown_fds[0]))) { const int on = 1; - if (0 != ioctl(fd, FIOCLEX, NULL) && 0 != ioctl(fd, FIONBIO, &on)) { + + #ifdef FIOCLEX + if (0 != ioctl(fd, FIOCLEX, NULL)) { + close(fd); + return -1; + } + #endif + + if (0 != ioctl(fd, (int)FIONBIO, &on)) { close(fd); - fd = -1; + return -1; } } return fd; From 5908b4923e13914c898b6dc187ce387b9218bccc Mon Sep 17 00:00:00 2001 From: Landon Date: Thu, 12 Sep 2024 21:56:43 -0400 Subject: [PATCH 4/8] add a dev branch notice --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e289ca75..31ad9e15 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ [ ![discord-shield][] ][discord-invite] [ ![migrating-shield][] ][migrating-link] +## 🚨 Concord is not dead! 🚨 +Development has been happening in the dev branch. We are working on new features and improvements. To access the latest version of the library, please check out the dev branch. + ## About Concord is an asynchronous C99 Discord API library with minimal external dependencies, and a low-level translation of the Discord official documentation to C code. From 7424147b72566758f9121ab062ff2203c3fff832 Mon Sep 17 00:00:00 2001 From: Kana <109441902+ai-kana@users.noreply.github.com> Date: Fri, 13 Sep 2024 19:20:05 -0700 Subject: [PATCH 5/8] Update message_components.PRE.h Fix required value in sending modal --- gencodecs/api/message_components.PRE.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gencodecs/api/message_components.PRE.h b/gencodecs/api/message_components.PRE.h index c11c2763..7d33964c 100644 --- a/gencodecs/api/message_components.PRE.h +++ b/gencodecs/api/message_components.PRE.h @@ -85,7 +85,9 @@ PUB_STRUCT(discord_component) /** whether this componentis required to be filled */ FIELD(required, bool, false) /** a pre-filled value for this component */ + COND_WRITE(self->value != NULL) FIELD_PTR(value, char, *) + COND_END STRUCT_END #endif From f8fe973926e164872d684e6d646f738c72962c7e Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Sun, 10 Sep 2023 14:59:05 -0300 Subject: [PATCH 6/8] chore(embeds): remove dynamic embeds Removes dynamic embeds/embed builders as it has no (good) benefits over static embeds. --- examples/embed.c | 46 +---------- include/channel.h | 151 ----------------------------------- src/discord-misc.c | 195 --------------------------------------------- 3 files changed, 1 insertion(+), 391 deletions(-) diff --git a/examples/embed.c b/examples/embed.c index e953c5dd..ea135c6f 100644 --- a/examples/embed.c +++ b/examples/embed.c @@ -10,15 +10,13 @@ void print_usage(void) { printf("\n\nThis bot demonstrates how to embeds" - " with three different methods.\n" + " with two different methods.\n" "1 - Dynamic-approach (type !dynamic): Load the embed from " "a JSON string.\n" "2 - Static-approach (type !static): A clean initialization " "approach " "using the combination of designated initialization and compound " "literals.\n" - "3 - Builder-approach (type !builder): A dynamic and flexible " - "approach that relies on embed builder functions.\n" "\nTYPE ANY KEY TO START BOT\n"); } @@ -148,47 +146,6 @@ on_static(struct discord *client, const struct discord_message *event) discord_create_message(client, event->channel_id, ¶ms, NULL); } -void -on_builder(struct discord *client, const struct discord_message *event) -{ - if (event->author->bot) return; - - struct discord_embed embed = { - .color = 0x3498DB, - .timestamp = discord_timestamp(client), - }; - - discord_embed_set_title(&embed, "Concord"); - discord_embed_set_description(&embed, "Discord API library"); - discord_embed_set_url(&embed, "https://github.com/Cogmasters/concord"); - - discord_embed_set_footer(&embed, "github.com/Cogmasters/concord", ICON_URL, - NULL); - discord_embed_set_image(&embed, IMAGE_URL, NULL, 0, 0); - discord_embed_set_author(&embed, "Cogmasters", - "https://github.com/Cogmasters", NULL, NULL); - discord_embed_add_field( - &embed, "Want to learn more?", - "Read our " - "[documentation](https://cogmasters.github.io/concord/)!", - false); - discord_embed_add_field( - &embed, "Looking for support?", - "Join our server [here](https://discord.gg/x4hhGQYu)!", false); - - struct discord_create_message params = { - .embeds = - &(struct discord_embeds){ - .size = 1, - .array = &embed, - }, - }; - discord_create_message(client, event->channel_id, ¶ms, NULL); - - /* must cleanup 'embed' afterwards */ - discord_embed_cleanup(&embed); -} - int main(int argc, char *argv[]) { @@ -207,7 +164,6 @@ main(int argc, char *argv[]) discord_set_prefix(client, "!"); discord_set_on_command(client, "dynamic", &on_dynamic); discord_set_on_command(client, "static", &on_static); - discord_set_on_command(client, "builder", &on_builder); print_usage(); fgetc(stdin); // wait for input diff --git a/include/channel.h b/include/channel.h index cd057c5b..da0c8460 100644 --- a/include/channel.h +++ b/include/channel.h @@ -630,157 +630,6 @@ CCORDcode discord_list_joined_private_archived_threads( int limit, struct discord_ret_thread_response_body *ret); -/** @defgroup DiscordAPIChannelEmbed Embed builder - * @brief Dynamic embed builder functions - * @{ */ - -/** - * @brief Add title to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param format printf-like formatting string - * @param ... variadic arguments to be matched to format - */ -void discord_embed_set_title(struct discord_embed *embed, char format[], ...); - -/** - * @brief Add description to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param format printf-like formatting string - * @param ... variadic arguments to be matched to format - */ -void discord_embed_set_description(struct discord_embed *embed, - char format[], - ...); - -/** - * @brief Add URL to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param format printf-like formatting string - * @param ... variadic arguments to be matched to format - */ -void discord_embed_set_url(struct discord_embed *embed, char format[], ...); - -/** - * @brief Add thumbnail to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param url source url of thumbnail - * @param proxy_url a proxied url of the thumbnail - * @param height height of thumbnail - * @param width width of thumbnail - */ -void discord_embed_set_thumbnail(struct discord_embed *embed, - char url[], - char proxy_url[], - int height, - int width); -/** - * @brief Add image to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param url source url of image - * @param proxy_url a proxied url of the image - * @param height height of image - * @param width width of image - */ -void discord_embed_set_image(struct discord_embed *embed, - char url[], - char proxy_url[], - int height, - int width); - -/** - * @brief Add video to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param url source url of video - * @param proxy_url a proxied url of the video - * @param height height of video - * @param width width of video - */ -void discord_embed_set_video(struct discord_embed *embed, - char url[], - char proxy_url[], - int height, - int width); - -/** - * @brief Add footer to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param text footer text - * @param icon_url url of footer icon - * @param proxy_icon_url a proxied url of footer icon - */ -void discord_embed_set_footer(struct discord_embed *embed, - char text[], - char icon_url[], - char proxy_icon_url[]); - -/** - * @brief Add provider to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param name name of provider - * @param url url of provider - */ -void discord_embed_set_provider(struct discord_embed *embed, - char name[], - char url[]); - -/** - * @brief Add author to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param name name of author - * @param url url of author - * @param icon_url url of author icon - * @param proxy_icon_url a proxied url of author icon - */ -void discord_embed_set_author(struct discord_embed *embed, - char name[], - char url[], - char icon_url[], - char proxy_icon_url[]); - -/** - * @brief Add field to embed - * @note the embed must be freed with `discord_embed_cleanup()` after its no - * longer being used - * - * @param embed the embed being modified - * @param name name of the field - * @param value value of the field - * @param Inline whether or not this field should display inline - */ -void discord_embed_add_field(struct discord_embed *embed, - char name[], - char value[], - bool Inline); - -/** @} DiscordAPIChannelEmbed */ - /** @defgroup DiscordAPIChannelHelper Helper functions * @brief Custom helper functions * @{ */ diff --git a/src/discord-misc.c b/src/discord-misc.c index 99266f11..80ecf2ea 100644 --- a/src/discord-misc.c +++ b/src/discord-misc.c @@ -7,201 +7,6 @@ #include "cog-utils.h" #include "carray.h" -void -discord_embed_set_footer(struct discord_embed *embed, - char text[], - char icon_url[], - char proxy_icon_url[]) -{ - if (!text || !*text) { - log_error("Missing 'text'"); - return; - } - - if (embed->footer) - discord_embed_footer_cleanup(embed->footer); - else - embed->footer = malloc(sizeof *embed->footer); - discord_embed_footer_init(embed->footer); - - if (text) cog_strndup(text, strlen(text), &embed->footer->text); - if (icon_url) - cog_strndup(icon_url, strlen(icon_url), &embed->footer->icon_url); - if (proxy_icon_url) - cog_strndup(proxy_icon_url, strlen(proxy_icon_url), - &embed->footer->proxy_icon_url); -} - -void -discord_embed_set_title(struct discord_embed *embed, char format[], ...) -{ - char buf[DISCORD_EMBED_TITLE_LEN]; - va_list args; - int len; - - va_start(args, format); - - len = vsnprintf(buf, sizeof(buf), format, args); - ASSERT_NOT_OOB(len, sizeof(buf)); - - if (embed->title) free(embed->title); - cog_strndup(buf, (size_t)len, &embed->title); - - va_end(args); -} - -void -discord_embed_set_description(struct discord_embed *embed, char format[], ...) -{ - char buf[DISCORD_EMBED_DESCRIPTION_LEN]; - va_list args; - int len; - - va_start(args, format); - - len = vsnprintf(buf, sizeof(buf), format, args); - ASSERT_NOT_OOB(len, sizeof(buf)); - - if (embed->description) free(embed->description); - cog_strndup(buf, (size_t)len, &embed->description); - - va_end(args); -} - -void -discord_embed_set_url(struct discord_embed *embed, char format[], ...) -{ - char buf[2048]; - va_list args; - int len; - - va_start(args, format); - - len = vsnprintf(buf, sizeof(buf), format, args); - ASSERT_NOT_OOB(len, sizeof(buf)); - - if (embed->url) free(embed->url); - cog_strndup(buf, (size_t)len, &embed->url); - - va_end(args); -} - -void -discord_embed_set_thumbnail(struct discord_embed *embed, - char url[], - char proxy_url[], - int height, - int width) -{ - if (embed->thumbnail) - discord_embed_thumbnail_cleanup(embed->thumbnail); - else - embed->thumbnail = malloc(sizeof *embed->thumbnail); - discord_embed_thumbnail_init(embed->thumbnail); - - if (url) cog_strndup(url, strlen(url), &embed->thumbnail->url); - if (proxy_url) - cog_strndup(proxy_url, strlen(proxy_url), - &embed->thumbnail->proxy_url); - if (height) embed->thumbnail->height = height; - if (width) embed->thumbnail->width = width; -} - -void -discord_embed_set_image(struct discord_embed *embed, - char url[], - char proxy_url[], - int height, - int width) -{ - if (embed->image) - discord_embed_image_cleanup(embed->image); - else - embed->image = malloc(sizeof *embed->image); - discord_embed_image_init(embed->image); - - if (url) cog_strndup(url, strlen(url), &embed->image->url); - if (proxy_url) - cog_strndup(proxy_url, strlen(proxy_url), &embed->image->proxy_url); - if (height) embed->image->height = height; - if (width) embed->image->width = width; -} - -void -discord_embed_set_video(struct discord_embed *embed, - char url[], - char proxy_url[], - int height, - int width) -{ - if (embed->video) - discord_embed_video_cleanup(embed->video); - else - embed->video = malloc(sizeof *embed->video); - discord_embed_video_init(embed->video); - - if (url) cog_strndup(url, strlen(url), &embed->video->url); - if (proxy_url) - cog_strndup(proxy_url, strlen(proxy_url), &embed->video->proxy_url); - if (height) embed->video->height = height; - if (width) embed->video->width = width; -} - -void -discord_embed_set_provider(struct discord_embed *embed, - char name[], - char url[]) -{ - if (embed->provider) - discord_embed_provider_cleanup(embed->provider); - else - embed->provider = malloc(sizeof *embed->provider); - discord_embed_provider_init(embed->provider); - - if (name) cog_strndup(name, strlen(name), &embed->provider->name); - if (url) cog_strndup(url, strlen(url), &embed->provider->url); -} - -void -discord_embed_set_author(struct discord_embed *embed, - char name[], - char url[], - char icon_url[], - char proxy_icon_url[]) -{ - if (embed->author) - discord_embed_author_cleanup(embed->author); - else - embed->author = malloc(sizeof *embed->author); - discord_embed_author_init(embed->author); - - if (name) cog_strndup(name, strlen(name), &embed->author->name); - if (url) cog_strndup(url, strlen(url), &embed->author->url); - if (icon_url) - cog_strndup(icon_url, strlen(icon_url), &embed->author->icon_url); - if (proxy_icon_url) - cog_strndup(proxy_icon_url, strlen(proxy_icon_url), - &embed->author->proxy_icon_url); -} - -void -discord_embed_add_field(struct discord_embed *embed, - char name[], - char value[], - bool Inline) -{ - struct discord_embed_field field = { 0 }; - - field.Inline = Inline; - - if (name) cog_strndup(name, strlen(name), &field.name); - if (value) cog_strndup(value, strlen(value), &field.value); - - if (!embed->fields) - embed->fields = calloc(1, sizeof *embed->fields); - carray_append(embed->fields, field); -} - void discord_overwrite_append(struct discord_overwrites *permission_overwrites, u64snowflake id, From fba3b10678be8703a26c6944b6139b82d0f90a58 Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Fri, 28 Oct 2022 18:20:35 -0300 Subject: [PATCH 7/8] docs(guide): add slash commands guide Adding slash commands guide, that will teach how to create (global and guild specific) slash commands, respond to them, and how delete them. References #109 --- docs/guides/slash-commands.md | 153 ++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 docs/guides/slash-commands.md diff --git a/docs/guides/slash-commands.md b/docs/guides/slash-commands.md new file mode 100644 index 00000000..75df5692 --- /dev/null +++ b/docs/guides/slash-commands.md @@ -0,0 +1,153 @@ +# Slash commands + +Slash commands are just a type of application command. You set the `name`, the `description`, and the [`options`](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-interaction-data-option-structure); the `name` and `description` allow users to rapidly identificate your bot's slash command among many others (since it's expected to a lot of other bots have the same slash command names as yours), and the [`options`](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-interaction-data-option-structure) field let the user fill information about the requested command params to send to your bot. + +## Creating a slash command (Globally) + +There are currently two ways of creating/updating a slash command, globally and guild specific (per guild), globals are for all the guilds that your bot is in, and guild specific already explains it by the name. In this topic, we will be creating global slash commands, see the next topic for guild specific slash commands. + +To create the slash commands we will be using the [`discord_create_global_application_command()`](https://cogmasters.github.io/concord/group__DiscordAPIInteractionsApplicationCommand.html#ga348051bd3bd310b9abb34526ae346f8b) function, which takes only `4` parameters, see the example below of creating a slash command called `concordy` with a description of `Concord is really amazing.`: + +*Re-creating a global slash command will make its information change to the new one.* + +```c +struct discord_create_global_application_command params = { + .name = "concordy", + .description = "Concord is really amazing." +}; + +discord_create_global_application_command(client, event->application->id, ¶ms, NULL); +``` + +Done! Sadly, global application commands may take hours to set up, so you may need to wait, till it appears for you to use. + +*That is a resumed guide, you may want to see more fields of the `discord_create_global_application_command` struct, for that, you can click [here](https://cogmasters.github.io/concord/structdiscord__create__guild__application__command.html)* + +## Creating a slash command (Guild specific) + +Guild specific slash commands are slash commands where that will only appear in the guild it wants to appear. It's good for testing since it takes short time for it to appear and change its information, and you can rapidly test things out with it before making global slash commands. + +Making a guild specific slash command, it's very simple and similar to creating a global one, see the example below: + +```c +#define GUILD_ID 1234567898765431 + +struct discord_create_guild_application_command params = { + .name = "concordy", + .description = "Concord is really amazing." +}; + +discord_create_guild_application_command(client, event->application->id, GUILD_ID, ¶ms, NULL); +``` + +To see more fields on the `discord_create_guild_application_command` struct, you can click [here](https://cogmasters.github.io/concord/structdiscord__create__guild__application__command.html). + +## Responding to slash commands + +After we created a slash command, we will need to now respond to those. Concord provides functions that allow you to rapidly respond to them. + +*It doesn't matter if you created a slash command using the global or guild specific topic, you will end up with the same functions to respond to them.* + +```c +void on_interaction(struct discord *client, const struct discord_interaction *interaction) { + if (interaction->type != DISCORD_INTERACTION_APPLICATION_COMMAND) return; + + if (strcmp(interaction->data->name, "concordy") == 0) { + struct discord_interaction_response params = { + .type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE, + .data = &(struct discord_interaction_callback_data) { + .content = "Concord is the best!" // Here, is the content of the message + .flags = DISCORD_MESSAGE_EPHEMERAL // This makes the message only be visible to who executed the slash command + } + }; + discord_create_interaction_response(client, interaction->id, interaction->token, ¶ms, NULL); + } +} +``` + +Yes, that's it, it will respond to the slash command `concordy` with the message `Concord is the best`, being only visible to who executed the slash command. + +## Deleting a slash command + +If you regretted creating a slash command, this topic is for you. In case you want to delete either a global or guild specific (the functions for each one are different) slash command, you can use the function `discord_delete_global_application_command`/`discord_delete_guild_application_command` respectively, see the example below: + + +### Global + +```c +discord_delete_global_application_command(client, interaction->id, bot->id, NULL); +``` + +### Guild specific + +```c +#define GUILD_ID 1234567898765431 + +discord_delete_guild_application_command(client, interaction->id, GUILD_ID, bot->id, NULL); +``` + +If it's global, it may take a while to disappear, but if it's guild specific, it will almost disappear instantly. + +## Code example + +Here is the mix of all the topics above into a single code example. + +You will have `.createslash` that will create a guild specific slash command. The slash command will respond to the slash command called `concordy`. + +```c +#include +#include + +#include +#include + +void on_ready(struct discord *client, const struct discord_ready *bot) { + (void) client; + log_info("Logged in as %s!", bot->user->username); +} + +void on_createslash(struct discord *client, const struct discord_message *msg) { + const struct discord_user *bot = discord_get_self(client); + + struct discord_create_guild_application_command params = { + .name = "concordy", + .description = "Concord is really amazing." + }; + + discord_create_guild_application_command(client, bot->id, msg->guild_id, ¶ms, NULL); + + struct discord_create_message paramsMessage = { + .content = "Done, slash command created." + }; + + discord_create_message(client, msg->channel_id, ¶msMessage, NULL); +} + +void on_interaction(struct discord *client, const struct discord_interaction *interaction) { + if (interaction->type != DISCORD_INTERACTION_APPLICATION_COMMAND) return; + + if (strcmp(interaction->data->name, "concordy") == 0) { + struct discord_interaction_response params = { + .type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE, + .data = &(struct discord_interaction_callback_data) { + .content = "Concord is the best!", + .flags = DISCORD_MESSAGE_EPHEMERAL + } + }; + discord_create_interaction_response(client, interaction->id, interaction->token, ¶ms, NULL); + } +} + +int main(void) { + struct discord *client = discord_config_init("config.json"); + + discord_add_intents(client, DISCORD_GATEWAY_MESSAGE_CONTENT); + + discord_set_on_ready(client, &on_ready); + discord_set_on_interaction_create(client, &on_interaction); + + discord_set_on_command(client, ".createslash", &on_createslash); + + discord_run(client); +} +``` From f434694617bdbff6ed7543a1ceb6b64cacd78a67 Mon Sep 17 00:00:00 2001 From: Landon Date: Fri, 13 Sep 2024 20:16:18 -0400 Subject: [PATCH 8/8] create pkg-config and modify makefile --- .gitignore | 1 + Makefile | 4 ++++ concord.pc | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 concord.pc diff --git a/.gitignore b/.gitignore index d9eabc70..527a8653 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ !Makefile !config.json !.github/** +!concord.pc diff --git a/Makefile b/Makefile index 50c0f1c2..d4db1fa4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ PREFIX ?= /usr/local +SHAREDIR ?= /usr/share DESTINCLUDE_DIR = $(PREFIX)/include/concord DESTLIBDIR = $(PREFIX)/lib +PKGCONFIGDIR = $(SHAREDIR)/pkgconfig SRC_DIR = src INCLUDE_DIR = include @@ -43,12 +45,14 @@ install: install -d $(DESTINCLUDE_DIR) install -m 644 $(INCLUDE_DIR)/*.h $(CORE_DIR)/*.h $(GENCODECS_DIR)/*.h \ $(DESTINCLUDE_DIR) + install -D concord.pc $(PKGCONFIGDIR)/concord.pc uninstall: rm -rf $(PREFIX)/include/concord rm -rf $(PREFIX)/lib/libdiscord.a rm -rf $(PREFIX)/lib/libdiscord.so rm -rf $(PREFIX)/lib/libdiscord.dylib + rm -f $(PKGCONFIGDIR)/concord.pc docs: @ $(MAKE) -C $(GENCODECS_DIR) headers diff --git a/concord.pc b/concord.pc new file mode 100644 index 00000000..c93e5bf1 --- /dev/null +++ b/concord.pc @@ -0,0 +1,11 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${exec_prefix}/include + +Name: concord +URL: https://github.com/Cogmasters/concord +Version: 2.3.0 +Description: A Discord API wrapper library made in C +Libs: -L${libdir} -ldiscord +Cflags: -I${includedir}/concord