From 618b5fbed3e5f0a5a78996b468b10198e8a6629b Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 2 May 2022 23:22:27 -0300 Subject: [PATCH 001/118] feat(gencodecs/api/): generate structure and deserializer methods for events --- gencodecs/api/channel.pre.h | 2 + gencodecs/api/gateway.pre.h | 571 ++++++++++++++++++++++++++++++------ gencodecs/api/guild.pre.h | 3 + 3 files changed, 484 insertions(+), 92 deletions(-) diff --git a/gencodecs/api/channel.pre.h b/gencodecs/api/channel.pre.h index 49ecdc835..b041dabfc 100644 --- a/gencodecs/api/channel.pre.h +++ b/gencodecs/api/channel.pre.h @@ -335,6 +335,8 @@ STRUCT(discord_thread_member) FIELD_TIMESTAMP(join_timestamp) /** any user-thread settings, currently only used for notifications */ FIELD_BITMASK(flags) + /** the id of the guild @note used at `Thread Member Update` */ + FIELD_SNOWFLAKE(guild_id) STRUCT_END /** @CCORD_pub_list{discord_thread_members} */ diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index a7044c5e9..f8513117f 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -142,129 +142,39 @@ ENUM(discord_activity_types) ENUMERATOR_LAST(DISCORD_ACTIVITY_COMPETING, = 5) ENUM_END -/** @CCORD_pub_struct{discord_identify} */ -PUB_STRUCT(discord_identify) - /** authentication token */ - FIELD_PTR(token, char, *) - /** connection properties */ - FIELD_STRUCT_PTR(properties, discord_identify_connection, *) - /** whether this connection supports compression packets */ - FIELD(compress, bool, false) - /** value between 50 and 250, total number of members where the gateway - will stop sending offline members in the guild member list */ - FIELD(large_threshold, int, 50) -#if 0 - /** array of two integers (shard_id, num_shards) */ - FIELD_STRUCT_PTR(shard, integers, *) -#endif - /** presence structure for initial presence information */ - FIELD_STRUCT_PTR(presence, discord_presence_update, *) - /** the gateway intents you wish to receive - @see @ref DiscordInternalGatewayIntents */ - FIELD_BITMASK(intents) -STRUCT_END - -STRUCT(discord_identify_connection) - /** your operating system */ - FIELD_CUSTOM(os, "$os", char, *, INIT_BLANK, CLEANUP_PTR, - GENCODECS_JSON_ENCODER_PTR_char, - GENCODECS_JSON_DECODER_PTR_char, NULL) - /** your library name */ - FIELD_CUSTOM(browser, "$browser", char, *, INIT_BLANK, CLEANUP_PTR, - GENCODECS_JSON_ENCODER_PTR_char, - GENCODECS_JSON_DECODER_PTR_char, NULL) - /** your library name */ - FIELD_CUSTOM(device, "$device", char, *, INIT_BLANK, CLEANUP_PTR, - GENCODECS_JSON_ENCODER_PTR_char, - GENCODECS_JSON_DECODER_PTR_char, NULL) -STRUCT_END - -/** @CCORD_pub_struct{discord_voice_state_status} */ -PUB_STRUCT(discord_voice_state_status) - /** ID of the guild */ - FIELD_SNOWFLAKE(guild_id) - /** ID of the voice channel client wants to join (null if disconnecting) */ - FIELD_SNOWFLAKE(channel_id) - /** is the client muted */ - FIELD(self_mute, bool, false) - /** is the client deafened */ - FIELD(self_deaf, bool, false) -STRUCT_END - -/** @CCORD_pub_struct{discord_presence_update} */ -PUB_STRUCT(discord_presence_update) - /** unix time (in milliseconds) of when the client went idle, or null if - the client is not idle */ - FIELD_TIMESTAMP(since) - /** the user's activities */ - FIELD_STRUCT_PTR(activities, discord_activities, *) - /** the user's new status */ - FIELD_PTR(status, char, *) - /** whether or not the client is afk */ - FIELD(afk, bool, false) -STRUCT_END - -LIST(discord_presence_updates) - LISTTYPE_STRUCT(discord_presence_update) -LIST_END - STRUCT(discord_activity) /** the activity's name */ - COND_WRITE(this->name != NULL) FIELD_PTR(name, char, *) - COND_END /** activity type */ FIELD_ENUM(type, discord_activity_types) /** stream url, is validated when type is 1 */ - COND_WRITE(this->url != NULL) FIELD_PTR(url, char, *) - COND_END /** unix timestamp (in milliseconds)of when the activity was added to the user's session */ - COND_WRITE(this->created_at != 0) FIELD_TIMESTAMP(created_at) - COND_END /** unix timestamps for start and/or end of the game */ - COND_WRITE(this->timestamps != NULL) FIELD_STRUCT_PTR(timestamps, discord_activity_timestamps, *) - COND_END /** application ID for the game */ - COND_WRITE(this->application_id != 0) FIELD_SNOWFLAKE(application_id) - COND_END /** what the player is currently doing */ - COND_WRITE(this->details != NULL) FIELD_PTR(details, char, *) - COND_END /** the user's current party status */ - COND_WRITE(this->state != NULL) FIELD_PTR(state, char, *) - COND_END /** the emoji used for a custom status */ - COND_WRITE(this->emoji != NULL) FIELD_STRUCT_PTR(emoji, discord_activity_emoji, *) - COND_END /** information for the current party of the player */ - COND_WRITE(this->party != NULL) FIELD_STRUCT_PTR(party, discord_activity_party, *) - COND_END /** images for the presence and their hover texts */ - COND_WRITE(this->assets != NULL) FIELD_STRUCT_PTR(assets, discord_activity_assets, *) - COND_END /** secrets for Rich Presence joining and spectating */ - COND_WRITE(this->secrets != NULL) FIELD_STRUCT_PTR(secrets, discord_activity_secrets, *) - COND_END /** whether or not the activity is an instanced game session */ FIELD(instance, bool, false) /** activity flags bitwise mask, describes what they payload includes @see @ref DiscordActivityFlags */ FIELD_BITMASK(flags) /** the custom buttons shown in the Rich Presence (max 2) */ - COND_WRITE(this->buttons != NULL) FIELD_STRUCT_PTR(buttons, discord_activity_buttons, *) - COND_END STRUCT_END LIST(discord_activities) @@ -282,9 +192,7 @@ STRUCT(discord_activity_emoji) /** the name of the emoji */ FIELD_PTR(name, char, *) /** the ID of the emoji */ - COND_WRITE(this->id != 0) FIELD_SNOWFLAKE(id) - COND_END /** whether this emoji is animated */ FIELD(animated, bool, false) STRUCT_END @@ -328,6 +236,483 @@ LIST(discord_activity_buttons) LISTTYPE_STRUCT(discord_activity_button) LIST_END +/** @CCORD_pub_struct{discord_presence_update} */ +PUB_STRUCT(discord_presence_update) + /** the user presence is being updated for */ + FIELD_STRUCT_PTR(user, discord_user, *) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** either "idle", "dnd", "online", or "offline" */ + FIELD_PTR(status, char, *) + /** user's platform-dependent status */ + FIELD_STRUCT_PTR(client_status, discord_client_status, *) + /** user's current activities */ + FIELD_STRUCT_PTR(activities, discord_activities, *) + /** unix time (in milliseconds) of when the client went idle, or null if + the client is not idle */ + FIELD_TIMESTAMP(since) + /** whether or not the client is afk */ + FIELD(afk, bool, false) +STRUCT_END + +STRUCT(discord_client_status) + /** the user's status set for an active desktop (Windows, Linux, Mac) + * application session */ + FIELD_PTR(desktop, char, *) + /** the user's status set for an active mobile (iOS, Android) application + * session */ + FIELD_PTR(mobile, char, *) + /** the user's status set for an active web (browser, bot account) + * application session */ + FIELD_PTR(web, char, *) +STRUCT_END + +LIST(discord_presence_updates) + LISTTYPE_STRUCT(discord_presence_update) +LIST_END + +/* gateway command payloads only need to be encoded into JSON */ +#if !defined(GENCODECS_ON_JSON_DECODER) + +/** @CCORD_pub_struct{discord_identify} */ +PUB_STRUCT(discord_identify) + /** authentication token */ + FIELD_PTR(token, char, *) + /** connection properties */ + FIELD_STRUCT_PTR(properties, discord_identify_connection, *) + /** whether this connection supports compression packets */ + FIELD(compress, bool, false) + /** value between 50 and 250, total number of members where the gateway + will stop sending offline members in the guild member list */ + FIELD(large_threshold, int, 50) + /** array of two integers (shard_id, num_shards) */ + FIELD_STRUCT_PTR(shard, integers, *) + /** presence structure for initial presence information */ + FIELD_STRUCT_PTR(presence, discord_presence_update, *) + /** the gateway intents you wish to receive + @see @ref DiscordInternalGatewayIntents */ + FIELD_BITMASK(intents) +STRUCT_END + +STRUCT(discord_identify_connection) + /** your operating system */ + FIELD_CUSTOM(os, "$os", char, *, INIT_BLANK, CLEANUP_PTR, + GENCODECS_JSON_ENCODER_PTR_char, + GENCODECS_JSON_DECODER_PTR_char, NULL) + /** your library name */ + FIELD_CUSTOM(browser, "$browser", char, *, INIT_BLANK, CLEANUP_PTR, + GENCODECS_JSON_ENCODER_PTR_char, + GENCODECS_JSON_DECODER_PTR_char, NULL) + /** your library name */ + FIELD_CUSTOM(device, "$device", char, *, INIT_BLANK, CLEANUP_PTR, + GENCODECS_JSON_ENCODER_PTR_char, + GENCODECS_JSON_DECODER_PTR_char, NULL) +STRUCT_END + +/** @CCORD_pub_struct{discord_resume} */ +PUB_STRUCT(discord_resume) + /** session token */ + FIELD_PTR(token, char, *) + /** session id */ + FIELD_PTR(session_id, char, *) + /** last sequence number received */ + FIELD(seq, int, 0) +STRUCT_END + +/** @CCORD_pub_struct{discord_request_guild_members} */ +PUB_STRUCT(discord_request_guild_members) + /** id of the guild to get members for */ + FIELD_SNOWFLAKE(guild_id) + /** string that username starts with, or an empty string to return all + * members */ + FIELD_PTR(query, char, *) + /** maximum numberof members to send matching the `query`; a limit of `0` + * can be used with an empty string `query` to return all members */ + COND_WRITE(this->query != NULL) + FIELD(limit, int, 0) + COND_END + /** used to specify if we want the presences of the matched members */ + FIELD(presences, bool, false) + /** used to specify which users you wish to fetch */ + FIELD_STRUCT_PTR(user_ids, snowflakes, *) + /** nonce to identify the `Guild Members Chunk` response */ + COND_WRITE(this->nonce != NULL) + FIELD_PTR(nonce, char, *) + COND_END +STRUCT_END + +/** @CCORD_pub_struct{discord_voice_state_status} */ +PUB_STRUCT(discord_voice_state_status) + /** ID of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** ID of the voice channel client wants to join (null if disconnecting) */ + FIELD_SNOWFLAKE(channel_id) + /** is the client muted */ + FIELD(self_mute, bool, false) + /** is the client deafened */ + FIELD(self_deaf, bool, false) +STRUCT_END + +#endif /* GENCODECS_ON_JSON_DECODER */ + +/* event payloads only need to be decoded into structs */ +#if !defined(GENCODECS_ON_JSON_ENCODER) + +/** @CCORD_pub_struct{discord_ready} */ +PUB_STRUCT(discord_ready) + /** gateway version */ + FIELD(v, int, 0) + /** information about the user including email */ + FIELD_STRUCT_PTR(user, discord_user, *) + /** the guilds the user is in */ + FIELD_STRUCT_PTR(guilds, discord_guilds, *) + /** used for resuming connections */ + FIELD_PTR(session_id, char, *) + /** the shard information associated with this session, if sent when + * identifying*/ + FIELD_STRUCT_PTR(shard, integers, *) + /** contains `id` and `flags` */ + FIELD_STRUCT_PTR(application, discord_application, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_thread_list_sync} */ +PUB_STRUCT(discord_thread_list_sync) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** + * the parent channel ids whose threads are being synced. if omitted, then + * threads were synced for the entire guild. This array may contain + * channel_ids that have no active threads as well, so you know to + * clear data + */ + FIELD_STRUCT_PTR(channel_ids, snowflakes, *) + /** all active threads in the given channels that the current user can access */ + FIELD_STRUCT_PTR(threads, discord_channels, *) + /** all thread member objects from the synced threads for the current user, + * indicating which threads the current user has been added to */ + FIELD_STRUCT_PTR(members, discord_thread_members, *) +STRUCT_END + +/** + * @CCORD_pub_struct{discord_thread_members_update} + * @todo `added_members` may include guild_members and presence objects + */ +PUB_STRUCT(discord_thread_members_update) + /** the id of the thread */ + FIELD_SNOWFLAKE(id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the approximate number of members in the thread, capped at 50 */ + FIELD(member_count, int, 0) + /** the users who were added to the thread */ + FIELD_STRUCT_PTR(added_members, discord_thread_members, *) + /** the id of the users who were removed from the thread */ + FIELD_STRUCT_PTR(removed_member_ids, snowflakes, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_channel_pins_update} */ +PUB_STRUCT(discord_channel_pins_update) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the time at which the most recent pinned message was pinned */ + FIELD_TIMESTAMP(last_pin_timestamp) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_ban_add} */ +PUB_STRUCT(discord_guild_ban_add) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the banned user */ + FIELD_STRUCT_PTR(user, discord_user, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_ban_remove} */ +PUB_STRUCT(discord_guild_ban_remove) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the unbanned user */ + FIELD_STRUCT_PTR(user, discord_user, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_emojis_update} */ +PUB_STRUCT(discord_guild_emojis_update) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** array of emojis */ + FIELD_STRUCT_PTR(emojis, discord_emojis, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_integrations_update} */ +PUB_STRUCT(discord_guild_integrations_update) + /** id of the guild whose integrations were updated */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_member_add} */ +PUB_STRUCT(discord_guild_member_add) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_member_remove} */ +PUB_STRUCT(discord_guild_member_remove) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the user who was removed */ + FIELD_STRUCT_PTR(user, discord_user, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_member_update} */ +PUB_STRUCT(discord_guild_member_update) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** user role ids */ + FIELD_STRUCT_PTR(roles, snowflakes, *) + /** the user */ + FIELD_STRUCT_PTR(user, discord_user, *) + /** nickname of the user in the guild */ + FIELD_PTR(nick, char, *) + /** the member's guild avatar hash */ + FIELD_PTR(avatar, char, *) + /** when the user joined the guild */ + FIELD_TIMESTAMP(joined_at) + /** when the user started boosting the guild */ + FIELD_TIMESTAMP(premium_since) + /** whether the user is deafened in voice channels */ + FIELD(deaf, bool, false) + /** whether the user is muted in voice channels */ + FIELD(mute, bool, false) + /** whether the user has not yet passed the guild's `Membership Screening` + * requirements */ + FIELD(pending, bool, false) + /** + * when the user's timeout will expire and the user will be able to + * communicate in the guild again, `NULL` or a time in the past if the + * user is not timed out */ + FIELD_TIMESTAMP(communication_disabled_until) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_members_chunk} */ +PUB_STRUCT(discord_guild_members_chunk) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** set of guild members */ + FIELD_STRUCT_PTR(members, discord_guild_members, *) + /** the chunk index in the expected chunks for this response + * @note `0 <= chunk_index < chunk_count` */ + FIELD(chunk_index, int, 0) + /** the total number of expected chunks for this response */ + FIELD(chunk_count, int, 0) + /** if passing an invalid id to `REQUEST_GUILD_MEMBERS`, it will be returned + * here */ + FIELD_STRUCT_PTR(not_found, snowflakes, *) + /** if passing true to `REQUEST_GUILD_MEMBERS`, presences of the returned + * members will be here */ + FIELD_STRUCT_PTR(presences, discord_presence_updates, *) + /** the nonce used in the `Guild Members Request` */ + FIELD_PTR(nonce, char, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_role_create} */ +PUB_STRUCT(discord_guild_role_create) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the role created */ + FIELD_STRUCT_PTR(role, discord_role, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_role_update} */ +PUB_STRUCT(discord_guild_role_update) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the role updated */ + FIELD_STRUCT_PTR(role, discord_role, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_guild_role_delete} */ +PUB_STRUCT(discord_guild_role_delete) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the id of the role */ + FIELD_SNOWFLAKE(role_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_scheduled_event_user_add} */ +PUB_STRUCT(discord_guild_scheduled_event_user_add) + /** id of the guild scheduled event */ + FIELD_SNOWFLAKE(guild_scheduled_event_id) + /** id of the user */ + FIELD_SNOWFLAKE(user_id) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_scheduled_event_user_remove} */ +PUB_STRUCT(discord_guild_scheduled_event_user_remove) + /** id of the guild scheduled event */ + FIELD_SNOWFLAKE(guild_scheduled_event_id) + /** id of the user */ + FIELD_SNOWFLAKE(user_id) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_integration_delete} */ +PUB_STRUCT(discord_integration_delete) + /** integration id */ + FIELD_SNOWFLAKE(id) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** id of the bot/OAuth2 application for this Discord integration */ + FIELD_SNOWFLAKE(application_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_invite_create} */ +PUB_STRUCT(discord_invite_create) + /** the channel the invite is for */ + FIELD_SNOWFLAKE(channel_id) + /** the unique invite code */ + FIELD_PTR(code, char, *) + /** the time at which the invite was created */ + FIELD_TIMESTAMP(created_at) + /** the guild of the invite */ + FIELD_SNOWFLAKE(guild_id) + /** the user that created the invite */ + FIELD_STRUCT_PTR(inviter, discord_user, *) + /** how long the inviteis valid for (in seconds) */ + FIELD(max_age, int, 0) + /** the maximum number of times the invite can be used */ + FIELD(max_uses, int, 0) + /** the @ref discord_invite_target_types for this voice channel invite */ + FIELD_ENUM(target_type, discord_invite_target_types) + /** the user whose stream to display for this voice channel stream invite */ + FIELD_STRUCT_PTR(target_user, discord_user, *) + /** the embedded application to open for this voice channel embedded + * application invite*/ + FIELD_STRUCT_PTR(target_application, discord_application, *) + /** whether or not the invite is temporary (invited users will be kicked + * on disconnect unless they're assigned a role) */ + FIELD(temporary, bool, false) + /** how many times the invite has been used (always 0) */ + FIELD(uses, int, 0) +STRUCT_END + +/** @CCORD_pub_struct{discord_invite_delete} */ +PUB_STRUCT(discord_invite_delete) + /** the channel of the invite */ + FIELD_SNOWFLAKE(channel_id) + /** the guild of the invite */ + FIELD_SNOWFLAKE(guild_id) + /** the unique invite code */ + FIELD_PTR(code, char, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_message_delete} */ +PUB_STRUCT(discord_message_delete) + /** the id of the message */ + FIELD_SNOWFLAKE(id) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_message_delete_bulk} */ +PUB_STRUCT(discord_message_delete_bulk) + /** the ids of the messages */ + FIELD_STRUCT_PTR(ids, snowflakes, *) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_message_reaction_add} */ +PUB_STRUCT(discord_message_reaction_add) + /** the id of the user */ + FIELD_SNOWFLAKE(user_id) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the id of the message */ + FIELD_SNOWFLAKE(message_id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the member who reacted if this happened in a guild */ + FIELD_STRUCT_PTR(member, discord_guild_member, *) + /** the emoji used to react */ + FIELD_STRUCT_PTR(emoji, discord_emoji, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_message_reaction_remove} */ +PUB_STRUCT(discord_message_reaction_remove) + /** the id of the user */ + FIELD_SNOWFLAKE(user_id) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the id of the message */ + FIELD_SNOWFLAKE(message_id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the emoji used to react */ + FIELD_STRUCT_PTR(emoji, discord_emoji, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_message_reaction_remove_all} */ +PUB_STRUCT(discord_message_reaction_remove_all) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the id of the message */ + FIELD_SNOWFLAKE(message_id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) +STRUCT_END + +/** @CCORD_pub_struct{discord_message_reaction_remove_emoji} */ +PUB_STRUCT(discord_message_reaction_remove_emoji) + /** the id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** the id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** the id of the message */ + FIELD_SNOWFLAKE(message_id) + /** the emoji that was removed */ + FIELD_STRUCT_PTR(emoji, discord_emoji, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_typing_start} */ +PUB_STRUCT(discord_typing_start) + /** id of the channel */ + FIELD_SNOWFLAKE(channel_id) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** id of the user */ + FIELD_SNOWFLAKE(user_id) + /** unix time (in seconds) of when the user started typing */ + FIELD_TIMESTAMP(timestamp) + /** the member who started typing if this happened in a guild */ + FIELD_STRUCT_PTR(member, discord_guild_member, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_voice_server_update} */ +PUB_STRUCT(discord_voice_server_update) + /** voice connection token */ + FIELD_PTR(token, char, *) + /** the guild this voice server update is for */ + FIELD_SNOWFLAKE(guild_id) + /** the voice server host */ + FIELD_PTR(endpoint, char, *) +STRUCT_END + +/** @CCORD_pub_struct{discord_webhooks_update} */ +PUB_STRUCT(discord_webhooks_update) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** id of the channel */ + FIELD_SNOWFLAKE(channel_id) +STRUCT_END + /** @CCORD_pub_struct{discord_session_start_limit} */ PUB_STRUCT(discord_session_start_limit) /** the total number of session starts the current user is allowed */ @@ -339,3 +724,5 @@ PUB_STRUCT(discord_session_start_limit) /** the number of identify requests allowed per 5 seconds */ FIELD(max_concurrency, int, 0) STRUCT_END + +#endif /* GENCODECS_ON_JSON_ENCODER */ diff --git a/gencodecs/api/guild.pre.h b/gencodecs/api/guild.pre.h index 2f6975739..a88d80d7b 100644 --- a/gencodecs/api/guild.pre.h +++ b/gencodecs/api/guild.pre.h @@ -357,6 +357,9 @@ STRUCT(discord_integration) FIELD(revoked, bool, false) /** the bot/OAuth2 application for discord integrations */ FIELD_STRUCT_PTR(application, discord_integration_application, *) + /** id of the guild @note extra field that may be included at + * `Integration Create` or `Integration Update` */ + FIELD_SNOWFLAKE(guild_id) STRUCT_END LIST(discord_integrations) From 5ce2ad594297f159c0baf29020313ea07842c79f Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 2 May 2022 23:23:17 -0300 Subject: [PATCH 002/118] refactor!: apply 618b5f --- gencodecs/api/gateway.pre.h | 6 - gencodecs/api/guild.pre.h | 2 + include/discord-events.h | 181 +++++++------ include/discord-internal.h | 14 +- src/discord-client.c | 15 +- src/discord-gateway.c | 489 +++++++++++------------------------- 6 files changed, 270 insertions(+), 437 deletions(-) diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index f8513117f..8ea689d8c 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -450,12 +450,6 @@ PUB_STRUCT(discord_guild_integrations_update) FIELD_SNOWFLAKE(guild_id) STRUCT_END -/** @CCORD_pub_struct{discord_guild_member_add} */ -PUB_STRUCT(discord_guild_member_add) - /** id of the guild */ - FIELD_SNOWFLAKE(guild_id) -STRUCT_END - /** @CCORD_pub_struct{discord_guild_member_remove} */ PUB_STRUCT(discord_guild_member_remove) /** id of the guild */ diff --git a/gencodecs/api/guild.pre.h b/gencodecs/api/guild.pre.h index a88d80d7b..7d446e5ef 100644 --- a/gencodecs/api/guild.pre.h +++ b/gencodecs/api/guild.pre.h @@ -318,6 +318,8 @@ PUB_STRUCT(discord_guild_member) communicate in the guild again, null or a time in the past if the user is not timed out */ FIELD_TIMESTAMP(communication_disabled_until) + /** the guild id @note extra field for `Guild Member Add` event */ + FIELD_SNOWFLAKE(guild_id) STRUCT_END /** @CCORD_pub_list{discord_guild_members} */ diff --git a/include/discord-events.h b/include/discord-events.h index 0eaf02942..8f3c2bb31 100644 --- a/include/discord-events.h +++ b/include/discord-events.h @@ -90,98 +90,127 @@ void discord_set_prefix(struct discord *client, char *prefix); /** @brief Idle callback */ typedef void (*discord_ev_idle)(struct discord *client); + +/** @brief Ready callback */ +typedef void (*discord_ev_ready)(struct discord *client, + struct discord_ready *event); + /** @brief Application Command callback */ typedef void (*discord_ev_application_command)( - struct discord *client, const struct discord_application_command *app_cmd); + struct discord *client, struct discord_application_command *event); + /** @brief Channel callback */ typedef void (*discord_ev_channel)(struct discord *client, - const struct discord_channel *channel); + struct discord_channel *event); +/** @brief Thread List Sync callback */ +typedef void (*discord_ev_thread_list_sync)( + struct discord *client, struct discord_thread_list_sync *event); +/** @brief Thread Members Update callback */ +typedef void (*discord_ev_thread_members_update)( + struct discord *client, struct discord_thread_members_update *event); /** @brief Channel Pins Update callback */ -typedef void (*discord_ev_channel_pins_update)(struct discord *client, - u64snowflake guild_id, - u64snowflake channel_id, - u64unix_ms last_pin_timestamp); +typedef void (*discord_ev_channel_pins_update)( + struct discord *client, struct discord_channel_pins_update *event); + +/** @brief Guild Ban Add callback */ +typedef void (*discord_ev_guild_ban_add)(struct discord *client, + struct discord_guild_ban_add *event); +/** @brief Guild Ban Remove callback */ +typedef void (*discord_ev_guild_ban_remove)( + struct discord *client, struct discord_guild_ban_remove *event); + /** @brief Guild callback */ typedef void (*discord_ev_guild)(struct discord *client, - const struct discord_guild *guild); -/** @brief Guild Delete callback */ -typedef void (*discord_ev_guild_delete)(struct discord *client, - u64snowflake guild_id); -/** @brief Guild Role callback */ -typedef void (*discord_ev_guild_role)(struct discord *client, - u64snowflake guild_id, - const struct discord_role *role); -/** @brief Guild Role Delete callback */ -typedef void (*discord_ev_guild_role_delete)(struct discord *client, - u64snowflake guild_id, - u64snowflake role_id); -/** @brief Guild Member callback */ -typedef void (*discord_ev_guild_member)( - struct discord *client, - u64snowflake guild_id, - const struct discord_guild_member *member); + struct discord_guild *event); +/** @brief Guild Emojis Update callback */ +typedef void (*discord_ev_guild_emojis_update)( + struct discord *client, struct discord_guild_emojis_update *event); +/** @brief Guild Integrations Update callback */ +typedef void (*discord_ev_guild_integrations_update)( + struct discord *client, struct discord_guild_integrations_update *event); +/** @brief Guild Member Add callback */ +typedef void (*discord_ev_guild_member)(struct discord *client, + struct discord_guild_member *event); /** @brief Guild Member Remove callback */ typedef void (*discord_ev_guild_member_remove)( + struct discord *client, struct discord_guild_member_remove *event); +/** @brief Guild Member Update callback */ +typedef void (*discord_ev_guild_member_update)( + struct discord *client, struct discord_guild_member_update *event); +/** @brief Guild Members Chunk callback */ +typedef void (*discord_ev_guild_members_chunk)( + struct discord *client, struct discord_guild_members_chunk *event); +/** @brief Guild Role Create callback */ +typedef void (*discord_ev_guild_role_create)( + struct discord *client, struct discord_guild_role_create *event); +/** @brief Guild Role Update callback */ +typedef void (*discord_ev_guild_role_update)( + struct discord *client, struct discord_guild_role_update *event); +/** @brief Guild Role Delete callback */ +typedef void (*discord_ev_guild_role_delete)( + struct discord *client, struct discord_guild_role_delete *event); + +/** @brief Guild Scheduled Event User Add callback */ +typedef void (*discord_ev_guild_scheduled_event_user_add)( struct discord *client, - u64snowflake guild_id, - const struct discord_user *user); -/** @brief Guild Ban callback */ -typedef void (*discord_ev_guild_ban)(struct discord *client, - u64snowflake guild_id, - const struct discord_user *user); -/** @brief Interaction callback */ -typedef void (*discord_ev_interaction)( - struct discord *client, const struct discord_interaction *interaction); + struct discord_guild_scheduled_event_user_add *event); +/** @brief Guild Scheduled Event User Remove callback */ +typedef void (*discord_ev_guild_scheduled_event_user_remove)( + struct discord *client, + struct discord_guild_scheduled_event_user_remove *event); + +/** @brief Integration Delete callback */ +typedef void (*discord_ev_integration_delete)( + struct discord *client, struct discord_integration_delete *event); + +/** @brief Invite Create Event callback */ +typedef void (*discord_ev_invite_create)(struct discord *client, + struct discord_invite_create *event); +/** @brief Invite Delete Event callback */ +typedef void (*discord_ev_invite_delete)(struct discord *client, + struct discord_invite_delete *event); + /** @brief Message callback */ typedef void (*discord_ev_message)(struct discord *client, - const struct discord_message *message); + struct discord_message *event); /** @brief Message Delete callback */ -typedef void (*discord_ev_message_delete)(struct discord *client, - u64snowflake id, - u64snowflake channel_id, - u64snowflake guild_id); +typedef void (*discord_ev_message_delete)( + struct discord *client, struct discord_message_delete *event); /** @brief Message Delete Bulk callback */ -typedef void (*discord_ev_message_delete_bulk)(struct discord *client, - const struct snowflakes *ids, - u64snowflake channel_id, - u64snowflake guild_id); -/** @brief Message Reaction callback */ +typedef void (*discord_ev_message_delete_bulk)( + struct discord *client, struct discord_message_delete_bulk *event); +/** @brief Message Reaction Add callback */ typedef void (*discord_ev_message_reaction_add)( - struct discord *client, - u64snowflake user_id, - u64snowflake channel_id, - u64snowflake message_id, - u64snowflake guild_id, - const struct discord_guild_member *member, - const struct discord_emoji *emoji); + struct discord *client, struct discord_message_reaction_add *member); /** @brief Message Reaction Remove callback */ typedef void (*discord_ev_message_reaction_remove)( - struct discord *client, - u64snowflake user_id, - u64snowflake channel_id, - u64snowflake message_id, - u64snowflake guild_id, - const struct discord_emoji *emoji); + struct discord *client, struct discord_message_reaction_remove *member); /** @brief Message Reaction Remove All callback */ -typedef void (*discord_ev_message_reaction_remove_all)(struct discord *client, - u64snowflake channel_id, - u64snowflake message_id, - u64snowflake guild_id); +typedef void (*discord_ev_message_reaction_remove_all)( + struct discord *client, struct discord_message_reaction_remove_all *event); /** @brief Message Reaction Remove callback */ typedef void (*discord_ev_message_reaction_remove_emoji)( struct discord *client, - u64snowflake channel_id, - u64snowflake message_id, - u64snowflake guild_id, - const struct discord_emoji *emoji); + struct discord_message_reaction_remove_emoji *event); + +/** @brief Typing Start Remove callback */ +typedef void (*discord_ev_typing_start)(struct discord *client, + struct discord_typing_start *event); + /** @brief Voice State Update callback */ typedef void (*discord_ev_voice_state_update)( - struct discord *client, const struct discord_voice_state *voice_state); + struct discord *client, struct discord_voice_state *voice_state); /** @brief Voice Server Update callback */ -typedef void (*discord_ev_voice_server_update)(struct discord *client, - const char *token, - u64snowflake guild_id, - const char *endpoint); +typedef void (*discord_ev_voice_server_update)( + struct discord *client, struct discord_voice_server_update *event); + +/** @brief Webhooks Update callback */ +typedef void (*discord_ev_webhooks_update)( + struct discord *client, struct discord_webhooks_update *event); + +/** @brief Interaction callback */ +typedef void (*discord_ev_interaction)( + struct discord *client, struct discord_interaction *interaction); /** @} DiscordEventCallbackTypes */ @@ -254,7 +283,7 @@ void discord_set_on_cycle(struct discord *client, discord_ev_idle callback); * @param client the client created with discord_init() * @param callback the callback to be triggered on event */ -void discord_set_on_ready(struct discord *client, discord_ev_idle callback); +void discord_set_on_ready(struct discord *client, discord_ev_ready callback); /** * @brief Triggers when a application command is created @@ -371,7 +400,7 @@ void discord_set_on_guild_update(struct discord *client, * @param callback the callback to be triggered on event */ void discord_set_on_guild_delete(struct discord *client, - discord_ev_guild_delete callback); + discord_ev_guild callback); /** * @brief Triggers when a guild role is created @@ -380,7 +409,7 @@ void discord_set_on_guild_delete(struct discord *client, * @param callback the callback to be triggered on event */ void discord_set_on_guild_role_create(struct discord *client, - discord_ev_guild_role callback); + discord_ev_guild_role_create callback); /** * @brief Triggers when a guild role is updated @@ -389,7 +418,7 @@ void discord_set_on_guild_role_create(struct discord *client, * @param callback the callback to be triggered on event */ void discord_set_on_guild_role_update(struct discord *client, - discord_ev_guild_role callback); + discord_ev_guild_role_update callback); /** * @brief Triggers when a guild role is deleted @@ -415,8 +444,8 @@ void discord_set_on_guild_member_add(struct discord *client, * @param client the client created with discord_init() * @param callback the callback to be triggered on event */ -void discord_set_on_guild_member_update(struct discord *client, - discord_ev_guild_member callback); +void discord_set_on_guild_member_update( + struct discord *client, discord_ev_guild_member_update callback); /** * @brief Triggers when a guild member is removed @@ -434,7 +463,7 @@ void discord_set_on_guild_member_remove( * @param callback the callback to be triggered on event */ void discord_set_on_guild_ban_add(struct discord *client, - discord_ev_guild_ban callback); + discord_ev_guild_ban_add callback); /** * @brief Triggers when a guild ban is removed @@ -443,7 +472,7 @@ void discord_set_on_guild_ban_add(struct discord *client, * @param callback the callback to be triggered on event */ void discord_set_on_guild_ban_remove(struct discord *client, - discord_ev_guild_ban callback); + discord_ev_guild_ban_remove callback); /** * @brief Triggers when a interaction is created diff --git a/include/discord-internal.h b/include/discord-internal.h index 2c9a81d73..e5a8706d3 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -457,7 +457,7 @@ void discord_ratelimiter_build(struct discord_ratelimiter *rl, struct discord_gateway_cbs { /** triggers when connection first establishes */ - discord_ev_idle on_ready; + discord_ev_ready on_ready; /** triggers when a command is created */ discord_ev_application_command on_application_command_create; @@ -486,24 +486,24 @@ struct discord_gateway_cbs { /** triggers when a guild's information is updated */ discord_ev_guild on_guild_update; /** triggers when removed from guild */ - discord_ev_guild_delete on_guild_delete; + discord_ev_guild on_guild_delete; /** triggers when a ban occurs */ - discord_ev_guild_ban on_guild_ban_add; + discord_ev_guild_ban_add on_guild_ban_add; /** triggers when a ban is removed */ - discord_ev_guild_ban on_guild_ban_remove; + discord_ev_guild_ban_remove on_guild_ban_remove; /** triggers when a guild member joins a guild */ discord_ev_guild_member on_guild_member_add; /** triggers when a guild member is removed from a guild */ discord_ev_guild_member_remove on_guild_member_remove; /** triggers when a guild member status is updated (ex: receive role) */ - discord_ev_guild_member on_guild_member_update; + discord_ev_guild_member_update on_guild_member_update; /** triggers when a guild role is created */ - discord_ev_guild_role on_guild_role_create; + discord_ev_guild_role_create on_guild_role_create; /** triggers when a guild role is updated */ - discord_ev_guild_role on_guild_role_update; + discord_ev_guild_role_update on_guild_role_update; /** triggers when a guild role is deleted */ discord_ev_guild_role_delete on_guild_role_delete; diff --git a/src/discord-client.c b/src/discord-client.c index a3c27781d..faf974fc3 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -334,14 +334,14 @@ discord_reconnect(struct discord *client, bool resume) } void -discord_set_on_ready(struct discord *client, discord_ev_idle callback) +discord_set_on_ready(struct discord *client, discord_ev_ready callback) { client->gw.cmds.cbs.on_ready = callback; } void discord_set_on_guild_role_create(struct discord *client, - discord_ev_guild_role callback) + discord_ev_guild_role_create callback) { client->gw.cmds.cbs.on_guild_role_create = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); @@ -349,7 +349,7 @@ discord_set_on_guild_role_create(struct discord *client, void discord_set_on_guild_role_update(struct discord *client, - discord_ev_guild_role callback) + discord_ev_guild_role_update callback) { client->gw.cmds.cbs.on_guild_role_update = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); @@ -373,7 +373,7 @@ discord_set_on_guild_member_add(struct discord *client, void discord_set_on_guild_member_update(struct discord *client, - discord_ev_guild_member callback) + discord_ev_guild_member_update callback) { client->gw.cmds.cbs.on_guild_member_update = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MEMBERS); @@ -389,7 +389,7 @@ discord_set_on_guild_member_remove(struct discord *client, void discord_set_on_guild_ban_add(struct discord *client, - discord_ev_guild_ban callback) + discord_ev_guild_ban_add callback) { client->gw.cmds.cbs.on_guild_ban_add = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_BANS); @@ -397,7 +397,7 @@ discord_set_on_guild_ban_add(struct discord *client, void discord_set_on_guild_ban_remove(struct discord *client, - discord_ev_guild_ban callback) + discord_ev_guild_ban_remove callback) { client->gw.cmds.cbs.on_guild_ban_remove = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_BANS); @@ -495,8 +495,7 @@ discord_set_on_guild_update(struct discord *client, discord_ev_guild callback) } void -discord_set_on_guild_delete(struct discord *client, - discord_ev_guild_delete callback) +discord_set_on_guild_delete(struct discord *client, discord_ev_guild callback) { client->gw.cmds.cbs.on_guild_delete = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); diff --git a/src/discord-gateway.c b/src/discord-gateway.c index a722325f6..49fe7fac5 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -358,323 +358,216 @@ get_dispatch_event(char name[]) static void on_guild_create(struct discord_gateway *gw) { - struct discord_guild guild = { 0 }; - - discord_guild_from_jsmnf(gw->payload.data, gw->json, &guild); - - ON(guild_create, &guild); - - discord_guild_cleanup(&guild); + struct discord_guild event = { 0 }; + discord_guild_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_create, &event); + discord_guild_cleanup(&event); } static void on_guild_update(struct discord_gateway *gw) { - struct discord_guild guild = { 0 }; - - discord_guild_from_jsmnf(gw->payload.data, gw->json, &guild); - - ON(guild_update, &guild); - - discord_guild_cleanup(&guild); + struct discord_guild event = { 0 }; + discord_guild_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_update, &event); + discord_guild_cleanup(&event); } static void on_guild_delete(struct discord_gateway *gw) { - u64snowflake guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "id", 2))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - - ON(guild_delete, guild_id); + struct discord_guild event = { 0 }; + discord_guild_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_delete, &event); + discord_guild_cleanup(&event); } static void on_guild_role_create(struct discord_gateway *gw) { - struct discord_role role = { 0 }; - u64snowflake guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "role", 4))) - discord_role_from_jsmnf(f, gw->json, &role); - - ON(guild_role_create, guild_id, &role); - - discord_role_cleanup(&role); + struct discord_guild_role_create event = { 0 }; + discord_guild_role_create_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_role_create, &event); + discord_guild_role_create_cleanup(&event); } static void on_guild_role_update(struct discord_gateway *gw) { - struct discord_role role = { 0 }; - u64snowflake guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "role", 4))) - discord_role_from_jsmnf(f, gw->json, &role); - - ON(guild_role_update, guild_id, &role); - - discord_role_cleanup(&role); + struct discord_guild_role_update event = { 0 }; + discord_guild_role_update_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_role_update, &event); + discord_guild_role_update_cleanup(&event); } static void on_guild_role_delete(struct discord_gateway *gw) { - u64snowflake guild_id = 0, role_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "role_id", 7))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &role_id); - - ON(guild_role_delete, guild_id, role_id); + struct discord_guild_role_delete event = { 0 }; + discord_guild_role_delete_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_role_delete, &event); + discord_guild_role_delete_cleanup(&event); } static void on_guild_member_add(struct discord_gateway *gw) { - struct discord_guild_member member = { 0 }; - u64snowflake guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - discord_guild_member_from_jsmnf(gw->payload.data, gw->json, &member); - - ON(guild_member_add, guild_id, &member); - - discord_guild_member_cleanup(&member); + struct discord_guild_member event = { 0 }; + discord_guild_member_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_member_add, &event); + discord_guild_member_cleanup(&event); } static void on_guild_member_update(struct discord_gateway *gw) { - struct discord_guild_member member = { 0 }; - u64snowflake guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - discord_guild_member_from_jsmnf(gw->payload.data, gw->json, &member); - - ON(guild_member_update, guild_id, &member); - - discord_guild_member_cleanup(&member); + struct discord_guild_member_update event = { 0 }; + discord_guild_member_update_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_member_update, &event); + discord_guild_member_update_cleanup(&event); } static void on_guild_member_remove(struct discord_gateway *gw) { - u64snowflake guild_id = 0; - struct discord_user user = { 0 }; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "user", 4))) - discord_user_from_jsmnf(f, gw->json, &user); - - ON(guild_member_remove, guild_id, &user); - - discord_user_cleanup(&user); + struct discord_guild_member_remove event = { 0 }; + discord_guild_member_remove_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_member_remove, &event); + discord_guild_member_remove_cleanup(&event); } static void on_guild_ban_add(struct discord_gateway *gw) { - u64snowflake guild_id = 0; - struct discord_user user = { 0 }; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "user", 4))) - discord_user_from_jsmnf(f, gw->json, &user); - - ON(guild_ban_add, guild_id, &user); - - discord_user_cleanup(&user); + struct discord_guild_ban_add event = { 0 }; + discord_guild_ban_add_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_ban_add, &event); + discord_guild_ban_add_cleanup(&event); } static void on_guild_ban_remove(struct discord_gateway *gw) { - u64snowflake guild_id = 0; - struct discord_user user = { 0 }; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "user", 4))) - discord_user_from_jsmnf(f, gw->json, &user); - - ON(guild_ban_remove, guild_id, &user); - - discord_user_cleanup(&user); + struct discord_guild_ban_remove event = { 0 }; + discord_guild_ban_remove_from_jsmnf(gw->payload.data, gw->json, &event); + ON(guild_ban_remove, &event); + discord_guild_ban_remove_cleanup(&event); } static void on_application_command_create(struct discord_gateway *gw) { - struct discord_application_command cmd = { 0 }; - - discord_application_command_from_jsmnf(gw->payload.data, gw->json, &cmd); - - ON(application_command_create, &cmd); - - discord_application_command_cleanup(&cmd); + struct discord_application_command event = { 0 }; + discord_application_command_from_jsmnf(gw->payload.data, gw->json, &event); + ON(application_command_create, &event); + discord_application_command_cleanup(&event); } static void on_application_command_update(struct discord_gateway *gw) { - struct discord_application_command cmd = { 0 }; - - discord_application_command_from_jsmnf(gw->payload.data, gw->json, &cmd); - - ON(application_command_update, &cmd); - - discord_application_command_cleanup(&cmd); + struct discord_application_command event = { 0 }; + discord_application_command_from_jsmnf(gw->payload.data, gw->json, &event); + ON(application_command_update, &event); + discord_application_command_cleanup(&event); } static void on_application_command_delete(struct discord_gateway *gw) { - struct discord_application_command cmd = { 0 }; - - discord_application_command_from_jsmnf(gw->payload.data, gw->json, &cmd); - - ON(application_command_delete, &cmd); - - discord_application_command_cleanup(&cmd); + struct discord_application_command event = { 0 }; + discord_application_command_from_jsmnf(gw->payload.data, gw->json, &event); + ON(application_command_delete, &event); + discord_application_command_cleanup(&event); } static void on_channel_create(struct discord_gateway *gw) { - struct discord_channel channel = { 0 }; - - discord_channel_from_jsmnf(gw->payload.data, gw->json, &channel); - - ON(channel_create, &channel); - - discord_channel_cleanup(&channel); + struct discord_channel event = { 0 }; + discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); + ON(channel_create, &event); + discord_channel_cleanup(&event); } static void on_channel_update(struct discord_gateway *gw) { - struct discord_channel channel = { 0 }; - - discord_channel_from_jsmnf(gw->payload.data, gw->json, &channel); - - ON(channel_update, &channel); - - discord_channel_cleanup(&channel); + struct discord_channel event = { 0 }; + discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); + ON(channel_update, &event); + discord_channel_cleanup(&event); } static void on_channel_delete(struct discord_gateway *gw) { - struct discord_channel channel = { 0 }; - - discord_channel_from_jsmnf(gw->payload.data, gw->json, &channel); - - ON(channel_delete, &channel); - - discord_channel_cleanup(&channel); + struct discord_channel event = { 0 }; + discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); + ON(channel_delete, &event); + discord_channel_cleanup(&event); } static void on_channel_pins_update(struct discord_gateway *gw) { - u64snowflake guild_id = 0, channel_id = 0; - u64unix_ms last_pin_timestamp = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "last_pin_timestamp", 18))) - cog_iso8601_to_unix_ms(gw->json + f->v.pos, (size_t)(f->v.len), - &last_pin_timestamp); - - ON(channel_pins_update, guild_id, channel_id, last_pin_timestamp); + struct discord_channel_pins_update event = { 0 }; + discord_channel_pins_update_from_jsmnf(gw->payload.data, gw->json, &event); + ON(channel_pins_update, &event); + discord_channel_pins_update_cleanup(&event); } static void on_thread_create(struct discord_gateway *gw) { - struct discord_channel thread = { 0 }; - - discord_channel_from_jsmnf(gw->payload.data, gw->json, &thread); - - ON(thread_create, &thread); - - discord_channel_cleanup(&thread); + struct discord_channel event = { 0 }; + discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); + ON(thread_create, &event); + discord_channel_cleanup(&event); } static void on_thread_update(struct discord_gateway *gw) { - struct discord_channel thread = { 0 }; - - discord_channel_from_jsmnf(gw->payload.data, gw->json, &thread); - - ON(thread_update, &thread); - - discord_channel_cleanup(&thread); + struct discord_channel event = { 0 }; + discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); + ON(thread_update, &event); + discord_channel_cleanup(&event); } static void on_thread_delete(struct discord_gateway *gw) { - struct discord_channel thread = { 0 }; - - discord_channel_from_jsmnf(gw->payload.data, gw->json, &thread); - - ON(thread_delete, &thread); - - discord_channel_cleanup(&thread); + struct discord_channel event = { 0 }; + discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); + ON(thread_delete, &event); + discord_channel_cleanup(&event); } static void on_interaction_create(struct discord_gateway *gw) { - struct discord_interaction interaction = { 0 }; - - discord_interaction_from_jsmnf(gw->payload.data, gw->json, &interaction); - - ON(interaction_create, &interaction); - - discord_interaction_cleanup(&interaction); + struct discord_interaction event = { 0 }; + discord_interaction_from_jsmnf(gw->payload.data, gw->json, &event); + ON(interaction_create, &event); + discord_interaction_cleanup(&event); } static void on_message_create(struct discord_gateway *gw) { - struct discord_message msg = { 0 }; - - discord_message_from_jsmnf(gw->payload.data, gw->json, &msg); + struct discord_message event = { 0 }; + discord_message_from_jsmnf(gw->payload.data, gw->json, &event); if (gw->cmds.pool - && !strncmp(gw->cmds.prefix.start, msg.content, gw->cmds.prefix.size)) + && !strncmp(gw->cmds.prefix.start, event.content, + gw->cmds.prefix.size)) { - char *cmd_start = msg.content + gw->cmds.prefix.size; + char *cmd_start = event.content + gw->cmds.prefix.size; size_t cmd_len = strcspn(cmd_start, " \n\t\r"); discord_ev_message cmd_cb = NULL; - char *tmp = msg.content; /* hold original ptr */ + char *tmp = event.content; /* hold original ptr */ size_t i; /* match command to its callback */ @@ -697,209 +590,125 @@ on_message_create(struct discord_gateway *gw) if (cmd_cb) { /* skip blank characters after command */ - if (msg.content) { - msg.content = cmd_start + cmd_len; - while (*msg.content && isspace((int)msg.content[0])) - ++msg.content; + if (event.content) { + event.content = cmd_start + cmd_len; + while (*event.content && isspace((int)event.content[0])) + ++event.content; } - cmd_cb(CLIENT(gw, gw), &msg); + cmd_cb(CLIENT(gw, gw), &event); } - msg.content = tmp; /* retrieve original ptr */ + event.content = tmp; /* retrieve original ptr */ } else if (gw->cmds.cbs.on_message_create) { - ON(message_create, &msg); + ON(message_create, &event); } - - discord_message_cleanup(&msg); + discord_message_cleanup(&event); } static void on_message_update(struct discord_gateway *gw) { - struct discord_message msg = { 0 }; - - discord_message_from_jsmnf(gw->payload.data, gw->json, &msg); - - ON(message_update, &msg); - - discord_message_cleanup(&msg); + struct discord_message event = { 0 }; + discord_message_from_jsmnf(gw->payload.data, gw->json, &event); + ON(message_update, &event); + discord_message_cleanup(&event); } static void on_message_delete(struct discord_gateway *gw) { - u64snowflake message_id = 0, channel_id = 0, guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "id", 2))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &message_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - - ON(message_delete, message_id, channel_id, guild_id); + struct discord_message_delete event = { 0 }; + discord_message_delete_from_jsmnf(gw->payload.data, gw->json, &event); + ON(message_delete, &event); + discord_message_delete_cleanup(&event); } static void on_message_delete_bulk(struct discord_gateway *gw) { - struct snowflakes ids = { 0 }; - u64snowflake channel_id = 0, guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "ids", 3))) - snowflakes_from_jsmnf(f, gw->json, &ids); - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - - ON(message_delete_bulk, &ids, channel_id, guild_id); - - snowflakes_cleanup(&ids); + struct discord_message_delete_bulk event = { 0 }; + discord_message_delete_bulk_from_jsmnf(gw->payload.data, gw->json, &event); + ON(message_delete_bulk, &event); + discord_message_delete_bulk_cleanup(&event); } static void on_message_reaction_add(struct discord_gateway *gw) { - u64snowflake user_id = 0, message_id = 0, channel_id = 0, guild_id = 0; - struct discord_guild_member member = { 0 }; - struct discord_emoji emoji = { 0 }; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "user_id", 7))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &user_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "message_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &message_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "member", 6))) - discord_guild_member_from_jsmnf(f, gw->json, &member); - if ((f = jsmnf_find(gw->payload.data, gw->json, "emoji", 5))) - discord_emoji_from_jsmnf(f, gw->json, &emoji); - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - - ON(message_reaction_add, user_id, channel_id, message_id, guild_id, - &member, &emoji); - - discord_guild_member_cleanup(&member); - discord_emoji_cleanup(&emoji); + struct discord_message_reaction_add event = { 0 }; + discord_message_reaction_add_from_jsmnf(gw->payload.data, gw->json, + &event); + ON(message_reaction_add, &event); + discord_message_reaction_add_cleanup(&event); } static void on_message_reaction_remove(struct discord_gateway *gw) { - u64snowflake user_id = 0, message_id = 0, channel_id = 0, guild_id = 0; - struct discord_emoji emoji = { 0 }; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "user_id", 7))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &user_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "message_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &message_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "emoji", 5))) - discord_emoji_from_jsmnf(f, gw->json, &emoji); - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - - ON(message_reaction_remove, user_id, channel_id, message_id, guild_id, - &emoji); - - discord_emoji_cleanup(&emoji); + struct discord_message_reaction_remove event = { 0 }; + discord_message_reaction_remove_from_jsmnf(gw->payload.data, gw->json, + &event); + ON(message_reaction_remove, &event); + discord_message_reaction_remove_cleanup(&event); } static void on_message_reaction_remove_all(struct discord_gateway *gw) { - u64snowflake channel_id = 0, message_id = 0, guild_id = 0; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "message_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &message_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - - ON(message_reaction_remove_all, channel_id, message_id, guild_id); + struct discord_message_reaction_remove_all event = { 0 }; + discord_message_reaction_remove_all_from_jsmnf(gw->payload.data, gw->json, + &event); + ON(message_reaction_remove_all, &event); + discord_message_reaction_remove_all_cleanup(&event); } static void on_message_reaction_remove_emoji(struct discord_gateway *gw) { - u64snowflake channel_id = 0, guild_id = 0, message_id = 0; - struct discord_emoji emoji = { 0 }; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "channel_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &channel_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "message_id", 10))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &message_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "emoji", 5))) - discord_emoji_from_jsmnf(f, gw->json, &emoji); - - ON(message_reaction_remove_emoji, channel_id, guild_id, message_id, - &emoji); - - discord_emoji_cleanup(&emoji); + struct discord_message_reaction_remove_emoji event = { 0 }; + discord_message_reaction_remove_emoji_from_jsmnf(gw->payload.data, + gw->json, &event); + ON(message_reaction_remove_emoji, &event); + discord_message_reaction_remove_emoji_cleanup(&event); } static void on_voice_state_update(struct discord_gateway *gw) { - struct discord_voice_state vs = { 0 }; - - discord_voice_state_from_jsmnf(gw->payload.data, gw->json, &vs); - + struct discord_voice_state event = { 0 }; + discord_voice_state_from_jsmnf(gw->payload.data, gw->json, &event); #ifdef HAS_DISCORD_VOICE - if (vs.user_id == CLIENT(gw, gw)->self.id) { + if (event.user_id == CLIENT(gw, gw)->self.id) { /* we only care about the voice_state_update of bot */ - _discord_on_voice_state_update(CLIENT(gw, gw), &vs); + _discord_on_voice_state_update(CLIENT(gw, gw), &event); } #endif /* HAS_DISCORD_VOICE */ - - if (gw->cmds.cbs.on_voice_state_update) ON(voice_state_update, &vs); - - discord_voice_state_cleanup(&vs); + if (gw->cmds.cbs.on_voice_state_update) ON(voice_state_update, &event); + discord_voice_state_cleanup(&event); } static void on_voice_server_update(struct discord_gateway *gw) { - u64snowflake guild_id = 0; - char token[512], endpoint[1024]; - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->payload.data, gw->json, "token", 5))) - snprintf(token, sizeof(token), "%.*s", (int)f->v.len, - gw->json + f->v.pos); - if ((f = jsmnf_find(gw->payload.data, gw->json, "guild_id", 8))) - sscanf(gw->json + f->v.pos, "%" SCNu64, &guild_id); - if ((f = jsmnf_find(gw->payload.data, gw->json, "endpoint", 8))) - snprintf(endpoint, sizeof(endpoint), "%.*s", (int)f->v.len, - gw->json + f->v.pos); - + struct discord_voice_server_update event = { 0 }; + discord_voice_server_update_from_jsmnf(gw->payload.data, gw->json, &event); #ifdef HAS_DISCORD_VOICE /* this happens for everyone */ - _discord_on_voice_server_update(CLIENT(gw, gw), guild_id, token, endpoint); + _discord_on_voice_server_update(CLIENT(gw, gw), &event); #endif /* HAS_DISCORD_VOICE */ - - if (gw->cmds.cbs.on_voice_server_update) - ON(voice_server_update, token, guild_id, endpoint); + if (gw->cmds.cbs.on_voice_server_update) ON(voice_server_update, &event); + discord_voice_server_update_cleanup(&event); } static void on_ready(struct discord_gateway *gw) { - gw->cmds.cbs.on_ready(CLIENT(gw, gw)); + struct discord_ready event = { 0 }; + discord_ready_from_jsmnf(gw->payload.data, gw->json, &event); + ON(ready, &event); + discord_ready_cleanup(&event); } static void From 936a4828c116fe060aca6d0f0655370677e7782b Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 00:14:33 -0300 Subject: [PATCH 003/118] refactor!(examples): match to 5ce2ad --- examples/8ball.c | 145 +++++++++++++++++++------------------ examples/Makefile | 3 +- examples/audit-log.c | 45 +++++------- examples/ban.c | 33 ++++----- examples/channel.c | 95 +++++++++++------------- examples/components.c | 30 ++++---- examples/copycat.c | 56 ++++++-------- examples/embed.c | 24 +++--- examples/emoji.c | 24 +++--- examples/guild-template.c | 27 +++---- examples/guild.c | 78 +++++++++----------- examples/invite.c | 22 +++--- examples/manual-dm.c | 12 ++- examples/pin.c | 41 +++++------ examples/ping-pong.c | 18 ++--- examples/presence.c | 6 +- examples/reaction.c | 61 ++++++++-------- examples/shell.c | 41 +++++------ examples/slash-commands.c | 36 ++++----- examples/slash-commands2.c | 26 +++---- examples/spam.c | 12 +-- examples/voice.c | 50 ++++++------- examples/webhook.c | 6 +- 23 files changed, 414 insertions(+), 477 deletions(-) diff --git a/examples/8ball.c b/examples/8ball.c index 92ec1f39d..2f5d57365 100644 --- a/examples/8ball.c +++ b/examples/8ball.c @@ -1,89 +1,96 @@ #include #include +#include #include -#include -void on_ready(struct discord *client) -{ - const struct discord_user *bot = discord_get_self(client); +#include "discord.h" - log_info("8ball-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); +void +print_usage(void) +{ + printf("\n\nThis is a bot to demonstrate an easy to make 8ball response " + "to a question.\n" + "1. type '8ball (question)' in chat\n" + "\nTYPE ANY KEY TO START BOT\n"); } -void eight_ball(struct discord *client, const struct discord_message *msg) +void +on_ready(struct discord *client, struct discord_ready *event) { - if(msg->author->bot) return; - - srand(time(0));//generate seed for randomizer - - char *phrases[20] = { //List of 8ball phrases/responses - ":green_circle: It is certain.", - ":green_circle: It is decidedly so.", - ":green_circle: Without a doubt.", - ":green_circle: Yes definitely.", - ":green_circle: You may rely on it.", - ":green_circle: As I see it, yes.", - ":green_circle: Most likely.", - ":green_circle: Outlook good.", - ":green_circle: Yes.", - ":green_circle: Signs Point to Yes.", - ":yellow_circle: Reply hazy, try again.", - ":yellow_circle: Ask again later.", - ":yellow_circle: Better not tell you now.", - ":yellow_circle: Cannot predict now.", - ":yellow_circle: Concentrate and ask again.", - ":red_circle: Don't count on it.", - ":red_circle: My reply is no.", - ":red_circle: My sources say no.", - ":red_circle: Outlook not so good.", - ":red_circle: Very doubtful." - }; - - int answer = rand() % (sizeof(phrases) / sizeof(*phrases)); // random index to phrases array - - struct discord_embed embeds[] = { // simple embed message - { - .title = ":8ball: 8-Ball", - .description = phrases[answer] - } - }; - - struct discord_create_message params = { - .embeds = &(struct discord_embeds) { - .size = sizeof(embeds) / sizeof *embeds, - .array = embeds, - } - }; - - discord_create_message(client, msg->channel_id, ¶ms, NULL); - + log_info("8ball-Bot succesfully connected to Discord as %s#%s!", + event->user->username, event->user->discriminator); } -int main(int argc, char *argv[]) +void +eight_ball(struct discord *client, struct discord_message *event) { - const char *config_file; - if (argc > 1) - config_file = argv[1]; - else - config_file = "../config.json"; + if (event->author->bot) return; + + /* List of 8ball phrases/responses */ + char *phrases[] = { + ":green_circle: It is certain.", + ":green_circle: It is decidedly so.", + ":green_circle: Without a doubt.", + ":green_circle: Yes definitely.", + ":green_circle: You may rely on it.", + ":green_circle: As I see it, yes.", + ":green_circle: Most likely.", + ":green_circle: Outlook good.", + ":green_circle: Yes.", + ":green_circle: Signs Point to Yes.", + ":yellow_circle: Reply hazy, try again.", + ":yellow_circle: Ask again later.", + ":yellow_circle: Better not tell you now.", + ":yellow_circle: Cannot predict now.", + ":yellow_circle: Concentrate and ask again.", + ":red_circle: Don't count on it.", + ":red_circle: My reply is no.", + ":red_circle: My sources say no.", + ":red_circle: Outlook not so good.", + ":red_circle: Very doubtful.", + }; + /* random index to phrases array */ + int answer = rand() % (sizeof(phrases) / sizeof(*phrases)); + + struct discord_embed embeds[] = { { + .title = ":8ball: 8-Ball", + .description = phrases[answer], + } }; + + struct discord_create_message params = { + .embeds = + &(struct discord_embeds){ + .size = sizeof(embeds) / sizeof *embeds, + .array = embeds, + }, + }; + discord_create_message(client, event->channel_id, ¶ms, NULL); +} - ccord_global_init(); - struct discord *client = discord_config_init(config_file); +int +main(int argc, char *argv[]) +{ + const char *config_file; + if (argc > 1) + config_file = argv[1]; + else + config_file = "../config.json"; - discord_set_on_ready(client, &on_ready); + srand(time(0)); - discord_set_on_command(client, "8ball", &eight_ball); + ccord_global_init(); + struct discord *client = discord_config_init(config_file); + assert(NULL != client && "Couldn't initialize client"); - printf("\n\nThis is a bot to demonstrate an easy to make 8ball response to a question.\n" - "1. type '8ball (question)' in chat\n" - "\nTYPE ANY KEY TO START BOT\n"); + discord_set_on_ready(client, &on_ready); - fgetc(stdin); // wait for input + discord_set_on_command(client, "8ball", &eight_ball); - discord_run(client); + print_usage(); + fgetc(stdin); // wait for input - discord_cleanup(client); - ccord_global_cleanup(); + discord_run(client); + discord_cleanup(client); + ccord_global_cleanup(); } diff --git a/examples/Makefile b/examples/Makefile index bf094328f..42dc0249e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -7,7 +7,8 @@ CORE_DIR := $(TOP)/core INCLUDE_DIR := $(TOP)/include GENCODECS_DIR := $(TOP)/gencodecs -BOTS := audit-log \ +BOTS := 8ball \ + audit-log \ ban \ channel \ components \ diff --git a/examples/audit-log.c b/examples/audit-log.c index 05ebeaf2d..189c44dda 100644 --- a/examples/audit-log.c +++ b/examples/audit-log.c @@ -21,44 +21,39 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Audit-Log-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void log_on_guild_member_add(struct discord *client, - u64snowflake guild_id, - const struct discord_guild_member *member) + struct discord_guild_member *event) { - log_info("%s#%s joined guild %" PRIu64, member->user->username, - member->user->discriminator, guild_id); + log_info("%s#%s joined guild %" PRIu64, event->user->username, + event->user->discriminator, event->guild_id); } void log_on_guild_member_update(struct discord *client, - u64snowflake guild_id, - const struct discord_guild_member *member) + struct discord_guild_member_update *event) { char nick[128] = ""; - if (member->nick && *member->nick) - snprintf(nick, sizeof(nick), " (%s)", member->nick); + if (event->nick && *event->nick) + snprintf(nick, sizeof(nick), " (%s)", event->nick); - log_info("%s#%s%s updated (guild %" PRIu64 ")", member->user->username, - member->user->discriminator, nick, guild_id); + log_info("%s#%s%s updated (guild %" PRIu64 ")", event->user->username, + event->user->discriminator, nick, event->guild_id); } void log_on_guild_member_remove(struct discord *client, - u64snowflake guild_id, - const struct discord_user *user) + struct discord_guild_member_remove *event) { - log_info("%s#%s left guild %" PRIu64, user->username, user->discriminator, - guild_id); + log_info("%s#%s left guild %" PRIu64, event->user->username, + event->user->discriminator, event->guild_id); } void @@ -73,7 +68,8 @@ done(struct discord *client, return; } - struct discord_audit_log_entry *entry = &audit_log->audit_log_entries->array[0]; + struct discord_audit_log_entry *entry = + &audit_log->audit_log_entries->array[0]; char text[1028]; snprintf(text, sizeof(text), "<@!%" PRIu64 "> has created <#%" PRIu64 ">!", @@ -93,13 +89,12 @@ fail(struct discord *client, CCORDcode code, void *data) } void -on_audit_channel_create(struct discord *client, - const struct discord_message *msg) +on_audit_channel_create(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_audit_log ret = { .done = &done, @@ -108,10 +103,10 @@ on_audit_channel_create(struct discord *client, .cleanup = &free, }; struct discord_get_guild_audit_log params = { - .user_id = msg->author->id, + .user_id = event->author->id, .action_type = DISCORD_AUDIT_LOG_CHANNEL_CREATE, }; - discord_get_guild_audit_log(client, msg->guild_id, ¶ms, &ret); + discord_get_guild_audit_log(client, event->guild_id, ¶ms, &ret); } int diff --git a/examples/ban.c b/examples/ban.c index ab4df40e3..d23873dc7 100644 --- a/examples/ban.c +++ b/examples/ban.c @@ -16,52 +16,49 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Ban-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void log_on_guild_ban_add(struct discord *client, - u64snowflake guild_id, - const struct discord_user *user) + struct discord_guild_ban_add *event) { - log_info("User `%s#%s` has been banned.", user->username, - user->discriminator); + log_info("User `%s#%s` has been banned.", event->user->username, + event->user->discriminator); } void log_on_guild_ban_remove(struct discord *client, - u64snowflake guild_id, - const struct discord_user *user) + struct discord_guild_ban_remove *event) { - log_info("User `%s#%s` has been unbanned.", user->username, - user->discriminator); + log_info("User `%s#%s` has been unbanned.", event->user->username, + event->user->discriminator); } void -on_ban(struct discord *client, const struct discord_message *msg) +on_ban(struct discord *client, struct discord_message *event) { u64snowflake target_id = 0ULL; - sscanf(msg->content, "%" SCNu64, &target_id); + sscanf(event->content, "%" SCNu64, &target_id); struct discord_create_guild_ban params = { .delete_message_days = 1, .reason = "Someone really dislikes you!", }; - discord_create_guild_ban(client, msg->guild_id, target_id, ¶ms, NULL); + discord_create_guild_ban(client, event->guild_id, target_id, ¶ms, + NULL); } void -on_unban(struct discord *client, const struct discord_message *msg) +on_unban(struct discord *client, struct discord_message *event) { u64snowflake target_id = 0ULL; - sscanf(msg->content, "%" SCNu64, &target_id); + sscanf(event->content, "%" SCNu64, &target_id); - discord_remove_guild_ban(client, msg->guild_id, target_id, NULL); + discord_remove_guild_ban(client, event->guild_id, target_id, NULL); } int diff --git a/examples/channel.c b/examples/channel.c index 614910f73..c89899cfe 100644 --- a/examples/channel.c +++ b/examples/channel.c @@ -24,82 +24,72 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Channel-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -log_on_channel_create(struct discord *client, - const struct discord_channel *channel) +log_on_channel_create(struct discord *client, struct discord_channel *event) { - log_info("Channel %s (%" PRIu64 ") created", channel->name, channel->id); + log_info("Channel %s (%" PRIu64 ") created", event->name, event->id); } void -log_on_channel_update(struct discord *client, - const struct discord_channel *channel) +log_on_channel_update(struct discord *client, struct discord_channel *event) { - log_info("Channel %s (%" PRIu64 ") updated", channel->name, channel->id); + log_info("Channel %s (%" PRIu64 ") updated", event->name, event->id); } void -log_on_channel_delete(struct discord *client, - const struct discord_channel *channel) +log_on_channel_delete(struct discord *client, struct discord_channel *event) { - log_info("Channel %s (%" PRIu64 ") deleted", channel->name, channel->id); + log_info("Channel %s (%" PRIu64 ") deleted", event->name, event->id); } void -log_on_thread_create(struct discord *client, - const struct discord_channel *thread) +log_on_thread_create(struct discord *client, struct discord_channel *event) { - log_info("Thread %s (%" PRIu64 ") created", thread->name, thread->id); + log_info("Thread %s (%" PRIu64 ") created", event->name, event->id); } void -log_on_thread_update(struct discord *client, - const struct discord_channel *thread) +log_on_thread_update(struct discord *client, struct discord_channel *event) { - log_info("Thread %s (%" PRIu64 ") updated", thread->name, thread->id); + log_info("Thread %s (%" PRIu64 ") updated", event->name, event->id); } void -log_on_thread_delete(struct discord *client, - const struct discord_channel *thread) +log_on_thread_delete(struct discord *client, struct discord_channel *event) { - log_info("Thread %s (%" PRIu64 ") deleted", thread->name, thread->id); + log_info("Thread %s (%" PRIu64 ") deleted", event->name, event->id); } void -on_channel_create(struct discord *client, const struct discord_message *msg) +on_channel_create(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - struct discord_create_guild_channel params = { .name = msg->content }; - discord_create_guild_channel(client, msg->guild_id, ¶ms, NULL); + struct discord_create_guild_channel params = { .name = event->content }; + discord_create_guild_channel(client, event->guild_id, ¶ms, NULL); } void -on_channel_rename_this(struct discord *client, - const struct discord_message *msg) +on_channel_rename_this(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - struct discord_modify_channel params = { .name = msg->content }; - discord_modify_channel(client, msg->channel_id, ¶ms, NULL); + struct discord_modify_channel params = { .name = event->content }; + discord_modify_channel(client, event->channel_id, ¶ms, NULL); } void -on_channel_delete_this(struct discord *client, - const struct discord_message *msg) +on_channel_delete_this(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_delete_channel(client, msg->channel_id, NULL); + discord_delete_channel(client, event->channel_id, NULL); } void @@ -130,13 +120,12 @@ fail_get_channel_invites(struct discord *client, CCORDcode code, void *data) } void -on_channel_get_invites(struct discord *client, - const struct discord_message *msg) +on_channel_get_invites(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_invites ret = { .done = &done_get_channel_invites, @@ -144,7 +133,7 @@ on_channel_get_invites(struct discord *client, .data = channel_id, .cleanup = &free, }; - discord_get_channel_invites(client, msg->channel_id, &ret); + discord_get_channel_invites(client, event->channel_id, &ret); } void @@ -173,13 +162,12 @@ fail_create_channel_invite(struct discord *client, CCORDcode code, void *data) } void -on_channel_create_invite(struct discord *client, - const struct discord_message *msg) +on_channel_create_invite(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_invite ret = { .done = &done_create_channel_invite, @@ -187,7 +175,7 @@ on_channel_create_invite(struct discord *client, .data = channel_id, .cleanup = &free, }; - discord_create_channel_invite(client, msg->channel_id, NULL, &ret); + discord_create_channel_invite(client, event->channel_id, NULL, &ret); } void @@ -218,13 +206,12 @@ fail_start_thread(struct discord *client, CCORDcode code, void *data) } void -on_channel_start_thread(struct discord *client, - const struct discord_message *msg) +on_channel_start_thread(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_channel ret = { .done = &done_start_thread, @@ -233,12 +220,12 @@ on_channel_start_thread(struct discord *client, .cleanup = &free, }; - if (msg->message_reference) { + if (event->message_reference) { struct discord_start_thread_with_message params = { .name = "new_thread", }; - discord_start_thread_with_message(client, msg->channel_id, - msg->message_reference->message_id, + discord_start_thread_with_message(client, event->channel_id, + event->message_reference->message_id, ¶ms, &ret); } else { @@ -246,8 +233,8 @@ on_channel_start_thread(struct discord *client, .name = "new_thread", .type = DISCORD_CHANNEL_GUILD_PUBLIC_THREAD, }; - discord_start_thread_without_message(client, msg->channel_id, ¶ms, - &ret); + discord_start_thread_without_message(client, event->channel_id, + ¶ms, &ret); } } diff --git a/examples/components.c b/examples/components.c index 5d094ddae..80fe1eb50 100644 --- a/examples/components.c +++ b/examples/components.c @@ -67,18 +67,16 @@ char JSON[] = "]\n"; void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Components-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -on_dynamic(struct discord *client, const struct discord_message *msg) +on_dynamic(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_components components = { 0 }; discord_components_from_json(JSON, sizeof(JSON), &components); @@ -88,16 +86,16 @@ on_dynamic(struct discord *client, const struct discord_message *msg) "you play?", .components = &components }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); /* must cleanup 'components' afterwards */ discord_components_cleanup(&components); } void -on_static(struct discord *client, const struct discord_message *msg) +on_static(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_select_option select_options[] = { { @@ -166,19 +164,19 @@ on_static(struct discord *client, const struct discord_message *msg) }, }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void on_interaction_create(struct discord *client, - const struct discord_interaction *interaction) + struct discord_interaction *event) { - log_info("Interaction %" PRIu64 " received", interaction->id); + log_info("Interaction %" PRIu64 " received", event->id); - if (!interaction->data || !interaction->data->values) return; + if (!event->data || !event->data->values) return; char values[1024]; - strings_to_json(values, sizeof(values), interaction->data->values); + strings_to_json(values, sizeof(values), event->data->values); char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), @@ -196,8 +194,8 @@ on_interaction_create(struct discord *client, .flags = DISCORD_MESSAGE_EPHEMERAL // 1 << 6 } }; - discord_create_interaction_response(client, interaction->id, - interaction->token, ¶ms, NULL); + discord_create_interaction_response(client, event->id, event->token, + ¶ms, NULL); } int diff --git a/examples/copycat.c b/examples/copycat.c index 1f9bfc3ed..4c2123904 100644 --- a/examples/copycat.c +++ b/examples/copycat.c @@ -19,81 +19,69 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Copycat-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void on_reaction_add(struct discord *client, - u64snowflake user_id, - u64snowflake channel_id, - u64snowflake message_id, - u64snowflake guild_id, - const struct discord_guild_member *member, - const struct discord_emoji *emoji) + struct discord_message_reaction_add *event) { - if (member->user->bot) return; + if (event->member->user->bot) return; - discord_create_reaction(client, channel_id, message_id, emoji->id, - emoji->name, NULL); + discord_create_reaction(client, event->channel_id, event->message_id, + event->emoji->id, event->emoji->name, NULL); } void -on_message_create(struct discord *client, const struct discord_message *msg) +on_message_create(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_create_message params = { - .content = msg->content, - .message_reference = !msg->referenced_message + .content = event->content, + .message_reference = !event->referenced_message ? NULL : &(struct discord_message_reference){ - .message_id = msg->referenced_message->id, - .channel_id = msg->channel_id, - .guild_id = msg->guild_id, + .message_id = event->referenced_message->id, + .channel_id = event->channel_id, + .guild_id = event->guild_id, }, }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void -on_message_update(struct discord *client, const struct discord_message *msg) +on_message_update(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_create_message params = { .content = "I see what you did there." }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void -on_message_delete(struct discord *client, - u64snowflake id, - u64snowflake channel_id, - u64snowflake guild_id) +on_message_delete(struct discord *client, struct discord_message_delete *event) { struct discord_create_message params = { .content = "Did that message just disappear?" }; - discord_create_message(client, channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void on_message_delete_bulk(struct discord *client, - const struct snowflakes *ids, - u64snowflake channel_id, - u64snowflake guild_id) + struct discord_message_delete_bulk *event) { char text[128]; - sprintf(text, "Where did those %d messages go?", ids->size); + sprintf(text, "Where did those %d messages go?", event->ids->size); struct discord_create_message params = { .content = text }; - discord_create_message(client, channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } int diff --git a/examples/embed.c b/examples/embed.c index 0997f9d15..b3f2d5a6d 100644 --- a/examples/embed.c +++ b/examples/embed.c @@ -59,18 +59,16 @@ char JSON[] = "{\n" "}"; void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Embed-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -on_dynamic(struct discord *client, const struct discord_message *msg) +on_dynamic(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; /* load a embed from the json string */ struct discord_embed embed = { 0 }; @@ -85,16 +83,16 @@ on_dynamic(struct discord *client, const struct discord_message *msg) .array = &embed, }, }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); /* must cleanup 'embed' afterwards */ discord_embed_cleanup(&embed); } void -on_static(struct discord *client, const struct discord_message *msg) +on_static(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_embed_field fields[] = { { @@ -146,13 +144,13 @@ on_static(struct discord *client, const struct discord_message *msg) .array = embeds, }, }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void -on_builder(struct discord *client, const struct discord_message *msg) +on_builder(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_embed embed = { .color = 0x3498DB, @@ -184,7 +182,7 @@ on_builder(struct discord *client, const struct discord_message *msg) .array = &embed, }, }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); /* must cleanup 'embed' afterwards */ discord_embed_cleanup(&embed); diff --git a/examples/emoji.c b/examples/emoji.c index afbd3f8bc..2f73006c1 100644 --- a/examples/emoji.c +++ b/examples/emoji.c @@ -17,12 +17,10 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Emoji-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void @@ -82,12 +80,12 @@ fail_list_guild_emojis(struct discord *client, CCORDcode code, void *data) } void -on_list_guild_emojis(struct discord *client, const struct discord_message *msg) +on_list_guild_emojis(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_emojis ret = { .done = &done_list_guild_emojis, @@ -95,7 +93,7 @@ on_list_guild_emojis(struct discord *client, const struct discord_message *msg) .data = channel_id, .cleanup = &free, }; - discord_list_guild_emojis(client, msg->guild_id, &ret); + discord_list_guild_emojis(client, event->guild_id, &ret); } void @@ -127,17 +125,17 @@ fail_get_guild_emoji(struct discord *client, CCORDcode code, void *data) } void -on_get_guild_emoji(struct discord *client, const struct discord_message *msg) +on_get_guild_emoji(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake emoji_id = 0ULL; - sscanf(msg->content, "%" SCNu64, &emoji_id); + sscanf(event->content, "%" SCNu64, &emoji_id); if (!emoji_id) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_emoji ret = { .done = &done_get_guild_emoji, @@ -145,7 +143,7 @@ on_get_guild_emoji(struct discord *client, const struct discord_message *msg) .data = channel_id, .cleanup = &free, }; - discord_get_guild_emoji(client, msg->guild_id, emoji_id, &ret); + discord_get_guild_emoji(client, event->guild_id, emoji_id, &ret); } int diff --git a/examples/guild-template.c b/examples/guild-template.c index c84debb9d..fce3e74ac 100644 --- a/examples/guild-template.c +++ b/examples/guild-template.c @@ -19,12 +19,10 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Guild-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void @@ -58,11 +56,10 @@ fail(struct discord *client, CCORDcode code, void *data) } void -on_get_guild_template(struct discord *client, - const struct discord_message *msg) +on_get_guild_template(struct discord *client, struct discord_message *event) { u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_guild_template ret = { .done = &done, @@ -70,15 +67,14 @@ on_get_guild_template(struct discord *client, .data = channel_id, .cleanup = &free, }; - discord_get_guild_template(client, msg->content, &ret); + discord_get_guild_template(client, event->content, &ret); } void -on_create_guild_template(struct discord *client, - const struct discord_message *msg) +on_create_guild_template(struct discord *client, struct discord_message *event) { u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_guild_template ret = { .done = &done, @@ -92,15 +88,14 @@ on_create_guild_template(struct discord *client, .description = "This is a new server template created with Concord!" }; - discord_create_guild_template(client, msg->guild_id, ¶ms, &ret); + discord_create_guild_template(client, event->guild_id, ¶ms, &ret); } void -on_sync_guild_template(struct discord *client, - const struct discord_message *msg) +on_sync_guild_template(struct discord *client, struct discord_message *event) { u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_guild_template ret = { .done = &done, @@ -109,7 +104,7 @@ on_sync_guild_template(struct discord *client, .cleanup = &free, }; - discord_sync_guild_template(client, msg->guild_id, msg->content, &ret); + discord_sync_guild_template(client, event->guild_id, event->content, &ret); } int diff --git a/examples/guild.c b/examples/guild.c index afa08c9c6..a24681e3d 100644 --- a/examples/guild.c +++ b/examples/guild.c @@ -25,105 +25,99 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Guild-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void log_on_role_create(struct discord *client, - u64snowflake guild_id, - const struct discord_role *role) + struct discord_guild_role_create *event) { - log_warn("Role (%" PRIu64 ") created", role->id); + log_warn("Role (%" PRIu64 ") created", event->role->id); } void log_on_role_update(struct discord *client, - u64snowflake guild_id, - const struct discord_role *role) + struct discord_guild_role_update *event) { - log_warn("Role (%" PRIu64 ") updated", role->id); + log_warn("Role (%" PRIu64 ") updated", event->role->id); } void log_on_role_delete(struct discord *client, - u64snowflake guild_id, - u64snowflake role_id) + struct discord_guild_role_delete *event) { - log_warn("Role (%" PRIu64 ") deleted", role_id); + log_warn("Role (%" PRIu64 ") deleted", event->role_id); } void -on_role_create(struct discord *client, const struct discord_message *msg) +on_role_create(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; char name[128] = ""; - sscanf(msg->content, "%s", name); + sscanf(event->content, "%s", name); if (!*name) { log_error("Couldn't create role `%s`", name); return; } struct discord_create_guild_role params = { .name = name }; - discord_create_guild_role(client, msg->guild_id, ¶ms, NULL); + discord_create_guild_role(client, event->guild_id, ¶ms, NULL); } void -on_role_delete(struct discord *client, const struct discord_message *msg) +on_role_delete(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake role_id = 0; - sscanf(msg->content, "%" SCNu64, &role_id); + sscanf(event->content, "%" SCNu64, &role_id); if (!role_id) { log_error("Invalid format for `guild.role_delete `"); return; } - discord_delete_guild_role(client, msg->guild_id, role_id, NULL); + discord_delete_guild_role(client, event->guild_id, role_id, NULL); } void -on_role_member_add(struct discord *client, const struct discord_message *msg) +on_role_member_add(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake user_id = 0, role_id = 0; - sscanf(msg->content, "%" SCNu64 " %" SCNu64, &user_id, &role_id); + sscanf(event->content, "%" SCNu64 " %" SCNu64, &user_id, &role_id); if (!user_id || !role_id) { log_error( "Invalid format for `guild.role_member_add `"); return; } - discord_add_guild_member_role(client, msg->guild_id, user_id, role_id, + discord_add_guild_member_role(client, event->guild_id, user_id, role_id, NULL); } void -on_role_member_remove(struct discord *client, - const struct discord_message *msg) +on_role_member_remove(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake user_id = 0, role_id = 0; - sscanf(msg->content, "%" SCNu64 " %" SCNu64, &user_id, &role_id); + sscanf(event->content, "%" SCNu64 " %" SCNu64, &user_id, &role_id); if (!user_id || !role_id) { log_error("Invalid format for `guild.role_member_remove " "`"); return; } - discord_remove_guild_member_role(client, msg->guild_id, user_id, role_id, + discord_remove_guild_member_role(client, event->guild_id, user_id, role_id, NULL); } @@ -164,15 +158,15 @@ fail_get_guild_roles(struct discord *client, CCORDcode code, void *data) } void -on_role_list(struct discord *client, const struct discord_message *msg) +on_role_list(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_ret_roles ret = { .done = &done_get_guild_roles, .fail = &fail_get_guild_roles, }; - discord_get_guild_roles(client, msg->guild_id, &ret); + discord_get_guild_roles(client, event->guild_id, &ret); } void @@ -192,13 +186,13 @@ fail_get_guild_member(struct discord *client, CCORDcode code, void *data) } void -on_member_get(struct discord *client, const struct discord_message *msg) +on_member_get(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake user_id = 0; - sscanf(msg->content, "%" SCNu64, &user_id); + sscanf(event->content, "%" SCNu64, &user_id); if (!user_id) { log_error("Invalid format for `guild.member_get `"); return; @@ -208,7 +202,7 @@ on_member_get(struct discord *client, const struct discord_message *msg) .done = &done_get_guild_member, .fail = &fail_get_guild_member, }; - discord_get_guild_member(client, msg->guild_id, user_id, &ret); + discord_get_guild_member(client, event->guild_id, user_id, &ret); } void @@ -249,19 +243,19 @@ fail_get_guild_channels(struct discord *client, CCORDcode code, void *data) char text[256]; snprintf(text, sizeof(text), "Couldn't fetch guild channels: %s", - discord_strerror(code, client)); + discord_strerror(code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, *channel_id, ¶ms, NULL); } void -on_channels_get(struct discord *client, const struct discord_message *msg) +on_channels_get(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_channels ret = { .done = &done_get_guild_channels, @@ -269,7 +263,7 @@ on_channels_get(struct discord *client, const struct discord_message *msg) .data = channel_id, .cleanup = &free, }; - discord_get_guild_channels(client, msg->guild_id, &ret); + discord_get_guild_channels(client, event->guild_id, &ret); } int diff --git a/examples/invite.c b/examples/invite.c index ee83010e6..42254258a 100644 --- a/examples/invite.c +++ b/examples/invite.c @@ -18,12 +18,10 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Invite-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void @@ -51,12 +49,12 @@ fail(struct discord *client, CCORDcode code, void *data) } void -on_invite_get(struct discord *client, const struct discord_message *msg) +on_invite_get(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_invite ret = { .done = &done, @@ -69,16 +67,16 @@ on_invite_get(struct discord *client, const struct discord_message *msg) .with_counts = true, .with_expiration = true, }; - discord_get_invite(client, msg->content, ¶ms, &ret); + discord_get_invite(client, event->content, ¶ms, &ret); } void -on_invite_delete(struct discord *client, const struct discord_message *msg) +on_invite_delete(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_invite ret = { .done = &done, @@ -86,7 +84,7 @@ on_invite_delete(struct discord *client, const struct discord_message *msg) .data = channel_id, .cleanup = &free, }; - discord_delete_invite(client, msg->content, &ret); + discord_delete_invite(client, event->content, &ret); } int diff --git a/examples/manual-dm.c b/examples/manual-dm.c index d94a71071..188f6c5bf 100644 --- a/examples/manual-dm.c +++ b/examples/manual-dm.c @@ -23,20 +23,18 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("ManualDM-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -on_dm_receive(struct discord *client, const struct discord_message *msg) +on_dm_receive(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - printf("%s:%s\n", msg->author->username, msg->content); + printf("%s:%s\n", event->author->username, event->content); } void * diff --git a/examples/pin.c b/examples/pin.c index d770d368d..d36226ca4 100644 --- a/examples/pin.c +++ b/examples/pin.c @@ -20,48 +20,46 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Pin-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -on_pin(struct discord *client, const struct discord_message *msg) +on_pin(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake msg_id = 0; - sscanf(msg->content, "%" SCNu64, &msg_id); + sscanf(event->content, "%" SCNu64, &msg_id); if (!msg_id) { - if (!msg->referenced_message) return; + if (!event->referenced_message) return; - msg_id = msg->referenced_message->id; + msg_id = event->referenced_message->id; } - discord_pin_message(client, msg->channel_id, msg_id, NULL); + discord_pin_message(client, event->channel_id, msg_id, NULL); } void -on_unpin(struct discord *client, const struct discord_message *msg) +on_unpin(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake msg_id = 0; - sscanf(msg->content, "%" SCNu64, &msg_id); + sscanf(event->content, "%" SCNu64, &msg_id); if (!msg_id) { - if (!msg->referenced_message) return; + if (!event->referenced_message) return; - msg_id = msg->referenced_message->id; + msg_id = event->referenced_message->id; } - discord_unpin_message(client, msg->channel_id, msg_id, NULL); + discord_unpin_message(client, event->channel_id, msg_id, NULL); } struct context { @@ -108,13 +106,13 @@ fail_get_pins(struct discord *client, CCORDcode code, void *data) } void -on_get_pins(struct discord *client, const struct discord_message *msg) +on_get_pins(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct context *cxt = malloc(sizeof(struct context)); - cxt->channel_id = msg->channel_id; - cxt->guild_id = msg->guild_id; + cxt->channel_id = event->channel_id; + cxt->guild_id = event->guild_id; struct discord_ret_messages ret = { .done = &done_get_pins, @@ -122,8 +120,7 @@ on_get_pins(struct discord *client, const struct discord_message *msg) .data = cxt, .cleanup = &free, }; - - discord_get_pinned_messages(client, msg->channel_id, &ret); + discord_get_pinned_messages(client, event->channel_id, &ret); } int diff --git a/examples/ping-pong.c b/examples/ping-pong.c index 2d4a80ca9..f3fb3534d 100644 --- a/examples/ping-pong.c +++ b/examples/ping-pong.c @@ -13,30 +13,28 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("PingPong-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -on_ping(struct discord *client, const struct discord_message *msg) +on_ping(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_create_message params = { .content = "pong" }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void -on_pong(struct discord *client, const struct discord_message *msg) +on_pong(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_create_message params = { .content = "ping" }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } int diff --git a/examples/presence.c b/examples/presence.c index a79e64165..f573c1429 100644 --- a/examples/presence.c +++ b/examples/presence.c @@ -16,12 +16,10 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Presence-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); struct discord_activity activities[] = { { diff --git a/examples/reaction.c b/examples/reaction.c index 08f19d93a..b1ff397d6 100644 --- a/examples/reaction.c +++ b/examples/reaction.c @@ -28,12 +28,10 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Reaction-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void @@ -77,12 +75,12 @@ fail_get_users(struct discord *client, CCORDcode code, void *data) } void -on_get_users(struct discord *client, const struct discord_message *msg) +on_get_users(struct discord *client, struct discord_message *event) { - if (msg->author->bot || !msg->referenced_message) return; + if (event->author->bot || !event->referenced_message) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_users ret = { .done = &done_get_users, @@ -92,61 +90,62 @@ on_get_users(struct discord *client, const struct discord_message *msg) }; struct discord_get_reactions params = { .limit = 25 }; - discord_get_reactions(client, msg->channel_id, msg->referenced_message->id, - 0, msg->content, ¶ms, &ret); + discord_get_reactions(client, event->channel_id, + event->referenced_message->id, 0, event->content, + ¶ms, &ret); } void -on_create(struct discord *client, const struct discord_message *msg) +on_create(struct discord *client, struct discord_message *event) { - if (msg->author->bot || !msg->referenced_message) return; + if (event->author->bot || !event->referenced_message) return; - discord_create_reaction(client, msg->referenced_message->channel_id, - msg->referenced_message->id, 0, msg->content, + discord_create_reaction(client, event->referenced_message->channel_id, + event->referenced_message->id, 0, event->content, NULL); } void -on_delete(struct discord *client, const struct discord_message *msg) +on_delete(struct discord *client, struct discord_message *event) { - if (msg->author->bot || !msg->referenced_message) return; + if (event->author->bot || !event->referenced_message) return; discord_delete_all_reactions_for_emoji( - client, msg->referenced_message->channel_id, - msg->referenced_message->id, 0, msg->content, NULL); + client, event->referenced_message->channel_id, + event->referenced_message->id, 0, event->content, NULL); } void -on_delete_all(struct discord *client, const struct discord_message *msg) +on_delete_all(struct discord *client, struct discord_message *event) { - if (msg->author->bot || !msg->referenced_message) return; + if (event->author->bot || !event->referenced_message) return; - discord_delete_all_reactions(client, msg->referenced_message->channel_id, - msg->referenced_message->id, NULL); + discord_delete_all_reactions(client, event->referenced_message->channel_id, + event->referenced_message->id, NULL); } void -on_delete_self(struct discord *client, const struct discord_message *msg) +on_delete_self(struct discord *client, struct discord_message *event) { - if (msg->author->bot || !msg->referenced_message) return; + if (event->author->bot || !event->referenced_message) return; - discord_delete_own_reaction(client, msg->referenced_message->channel_id, - msg->referenced_message->id, 0, msg->content, - NULL); + discord_delete_own_reaction(client, event->referenced_message->channel_id, + event->referenced_message->id, 0, + event->content, NULL); } void -on_delete_user(struct discord *client, const struct discord_message *msg) +on_delete_user(struct discord *client, struct discord_message *event) { - if (msg->author->bot || !msg->referenced_message) return; + if (event->author->bot || !event->referenced_message) return; u64snowflake user_id = 0; char emoji_name[256] = ""; - sscanf(msg->content, "%" SCNu64 " %s", &user_id, emoji_name); + sscanf(event->content, "%" SCNu64 " %s", &user_id, emoji_name); - discord_delete_user_reaction(client, msg->referenced_message->channel_id, - msg->referenced_message->id, user_id, 0, + discord_delete_user_reaction(client, event->referenced_message->channel_id, + event->referenced_message->id, user_id, 0, emoji_name, NULL); } diff --git a/examples/shell.c b/examples/shell.c index 9bddcfed9..6d5bcf54c 100644 --- a/examples/shell.c +++ b/examples/shell.c @@ -22,46 +22,43 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Shell-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void -on_cd(struct discord *client, const struct discord_message *msg) +on_cd(struct discord *client, struct discord_message *event) { - if (msg->author->id != g_sudo_id) return; + if (event->author->id != g_sudo_id) return; char path[PATH_MAX]; - chdir(*msg->content ? msg->content : "."); + chdir(*event->content ? event->content : "."); struct discord_create_message params = { .content = getcwd(path, sizeof(path)), }; - - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void -on_less_like(struct discord *client, const struct discord_message *msg) +on_less_like(struct discord *client, struct discord_message *event) { - if (msg->author->id != g_sudo_id) return; + if (event->author->id != g_sudo_id) return; - if (!msg->content || !*msg->content) { + if (!event->content || !*event->content) { struct discord_create_message params = { .content = "No file specified" }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } else { - struct discord_embed embed = { .title = msg->content }; - struct discord_attachment attachment = { .filename = msg->content }; + struct discord_embed embed = { .title = event->content }; + struct discord_attachment attachment = { .filename = event->content }; char text[512]; - snprintf(text, sizeof(text), "attachment://%s", msg->content); + snprintf(text, sizeof(text), "attachment://%s", event->content); struct discord_create_message params = { .content = text, @@ -77,20 +74,20 @@ on_less_like(struct discord *client, const struct discord_message *msg) }, }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } } void -on_fallback(struct discord *client, const struct discord_message *msg) +on_fallback(struct discord *client, struct discord_message *event) { const size_t MAX_FSIZE = 5e6; // 5 mb const size_t MAX_CHARS = 2000; FILE *fp; - if (msg->author->id != g_sudo_id) return; + if (event->author->id != g_sudo_id) return; - if (NULL == (fp = popen(msg->content, "r"))) { + if (NULL == (fp = popen(event->content, "r"))) { perror("Failed to run command"); return; } @@ -105,7 +102,7 @@ on_fallback(struct discord *client, const struct discord_message *msg) if (fsize <= MAX_CHARS) { struct discord_create_message params = { .content = pathtmp }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } else { struct discord_attachment attachment = { @@ -120,7 +117,7 @@ on_fallback(struct discord *client, const struct discord_message *msg) .array = &attachment, } }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } pclose(fp); diff --git a/examples/slash-commands.c b/examples/slash-commands.c index 923e82f2e..f933e8622 100644 --- a/examples/slash-commands.c +++ b/examples/slash-commands.c @@ -21,26 +21,23 @@ print_usage(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Slash-Commands-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void log_on_app_create(struct discord *client, - const struct discord_application_command *cmd) + struct discord_application_command *event) { - log_info("Application Command %s created", cmd->name); + log_info("Application Command %s created", event->name); } void -on_slash_command_create(struct discord *client, - const struct discord_message *msg) +on_slash_command_create(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; struct discord_application_command_option_choice gender_choices[] = { { @@ -94,7 +91,6 @@ on_slash_command_create(struct discord *client, }; struct discord_create_guild_application_command params = { - .type = DISCORD_APPLICATION_CHAT_INPUT, .name = "fill-form", .description = "A slash command example for form filling", .default_permission = true, @@ -106,27 +102,27 @@ on_slash_command_create(struct discord *client, }; /* Create slash command */ - discord_create_guild_application_command(client, g_app_id, msg->guild_id, + discord_create_guild_application_command(client, g_app_id, event->guild_id, ¶ms, NULL); } void on_interaction_create(struct discord *client, - const struct discord_interaction *interaction) + struct discord_interaction *event) { /* We're only interested on slash commands */ - if (interaction->type != DISCORD_INTERACTION_APPLICATION_COMMAND) return; + if (event->type != DISCORD_INTERACTION_APPLICATION_COMMAND) return; /* Return in case user input is missing for some reason */ - if (!interaction->data || !interaction->data->options) return; + if (!event->data || !event->data->options) return; char *nick = "blank"; int pets = 0; char *gender = "blank"; u64snowflake channel_id = 0; - for (int i = 0; i < interaction->data->options->size; ++i) { - char *name = interaction->data->options->array[i].name; - char *value = interaction->data->options->array[i].value; + for (int i = 0; i < event->data->options->size; ++i) { + char *name = event->data->options->array[i].name; + char *value = event->data->options->array[i].value; if (0 == strcmp(name, "nick")) nick = value; @@ -145,15 +141,15 @@ on_interaction_create(struct discord *client, "Pets: %d\n" "Gender: %s\n" "Favorite channel: <#%" PRIu64 ">\n", - interaction->member->user->id, nick, pets, gender, channel_id); + event->member->user->id, nick, pets, gender, channel_id); struct discord_interaction_response params = { .type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE, .data = &(struct discord_interaction_callback_data){ .content = buf } }; - discord_create_interaction_response(client, interaction->id, - interaction->token, ¶ms, NULL); + discord_create_interaction_response(client, event->id, event->token, + ¶ms, NULL); } int diff --git a/examples/slash-commands2.c b/examples/slash-commands2.c index 3c96c21ae..f0520f1aa 100644 --- a/examples/slash-commands2.c +++ b/examples/slash-commands2.c @@ -35,33 +35,31 @@ print_help(void) } void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - log_info("Slash-Commands-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + event->user->username, event->user->discriminator); } void log_on_app_create(struct discord *client, - const struct discord_application_command *cmd) + struct discord_application_command *event) { - log_info("Application Command %s created", cmd->name); + log_info("Application Command %s created", event->name); } void log_on_app_update(struct discord *client, - const struct discord_application_command *cmd) + struct discord_application_command *event) { - log_info("Application Command %s updated", cmd->name); + log_info("Application Command %s updated", event->name); } void log_on_app_delete(struct discord *client, - const struct discord_application_command *cmd) + struct discord_application_command *event) { - log_info("Application Command %s deleted", cmd->name); + log_info("Application Command %s deleted", event->name); } void @@ -72,9 +70,9 @@ fail_interaction_create(struct discord *client, CCORDcode code, void *data) void on_interaction_create(struct discord *client, - const struct discord_interaction *interaction) + struct discord_interaction *event) { - log_info("Interaction %" PRIu64 " received", interaction->id); + log_info("Interaction %" PRIu64 " received", event->id); struct discord_interaction_callback_data data = { .content = "Hello World!", @@ -88,8 +86,8 @@ on_interaction_create(struct discord *client, .fail = &fail_interaction_create }; - discord_create_interaction_response(client, interaction->id, - interaction->token, ¶ms, &ret); + discord_create_interaction_response(client, event->id, event->token, + ¶ms, &ret); } void * diff --git a/examples/spam.c b/examples/spam.c index aa601361c..30a45a283 100644 --- a/examples/spam.c +++ b/examples/spam.c @@ -31,25 +31,25 @@ char *SPAM[] = { }; void -on_spam_async(struct discord *client, const struct discord_message *msg) +on_spam_async(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; for (size_t i = 0; i < 10; ++i) { struct discord_create_message params = { .content = SPAM[i] }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } } void -on_spam_sync(struct discord *client, const struct discord_message *msg) +on_spam_sync(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; for (size_t i = 0; i < 10; ++i) { struct discord_ret_message ret = { .sync = DISCORD_SYNC_FLAG }; struct discord_create_message params = { .content = SPAM[i] }; - discord_create_message(client, msg->channel_id, ¶ms, &ret); + discord_create_message(client, event->channel_id, ¶ms, &ret); } } diff --git a/examples/voice.c b/examples/voice.c index 573ba64b2..623756179 100644 --- a/examples/voice.c +++ b/examples/voice.c @@ -27,20 +27,18 @@ print_usage(void) } void -log_on_voice_state_update(struct discord *client, - const struct discord_voice_state *vs) +on_ready(struct discord *client, struct discord_ready *event) { - log_info("User <@!%" PRIu64 "> has joined <#%" PRIu64 ">!", vs->user_id, - vs->channel_id); + log_info("Voice-Bot succesfully connected to Discord as %s#%s!", + event->user->username, event->user->discriminator); } void -on_ready(struct discord *client) +log_on_voice_state_update(struct discord *client, + struct discord_voice_state *event) { - const struct discord_user *bot = discord_get_self(client); - - log_info("Voice-Bot succesfully connected to Discord as %s#%s!", - bot->username, bot->discriminator); + log_info("User <@!%" PRIu64 "> has joined <#%" PRIu64 ">!", event->user_id, + event->channel_id); } void @@ -69,13 +67,12 @@ fail_list_voice_regions(struct discord *client, CCORDcode code, void *data) } void -on_list_voice_regions(struct discord *client, - const struct discord_message *msg) +on_list_voice_regions(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = msg->channel_id; + *channel_id = event->channel_id; struct discord_ret_voice_regions ret = { .done = &done_list_voice_regions, @@ -115,17 +112,17 @@ fail_get_vchannel_position(struct discord *client, CCORDcode code, void *data) } void -on_voice_join(struct discord *client, const struct discord_message *msg) +on_voice_join(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; int position = -1; - sscanf(msg->content, "%d", &position); + sscanf(event->content, "%d", &position); struct context *cxt = malloc(sizeof(struct context)); - cxt->channel_id = msg->channel_id; - cxt->guild_id = msg->guild_id; + cxt->channel_id = event->channel_id; + cxt->guild_id = event->guild_id; struct discord_ret_channel ret = { .done = &done_get_vchannel_position, @@ -134,7 +131,7 @@ on_voice_join(struct discord *client, const struct discord_message *msg) .cleanup = &free, }; - discord_get_channel_at_pos(client, msg->guild_id, + discord_get_channel_at_pos(client, event->guild_id, DISCORD_CHANNEL_GUILD_VOICE, position - 1, &ret); } @@ -168,23 +165,23 @@ fail_disconnect_guild_member(struct discord *client, } void -on_voice_kick(struct discord *client, const struct discord_message *msg) +on_voice_kick(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; u64snowflake user_id = 0; - sscanf(msg->content, "%" SCNu64, &user_id); + sscanf(event->content, "%" SCNu64, &user_id); if (!user_id) { struct discord_create_message params = { .content = "Missing user ID" }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } else { struct context *cxt = malloc(sizeof(struct context)); - cxt->channel_id = msg->channel_id; - cxt->guild_id = msg->guild_id; + cxt->channel_id = event->channel_id; + cxt->guild_id = event->guild_id; struct discord_ret_guild_member ret = { .done = &done_disconnect_guild_member, @@ -193,7 +190,8 @@ on_voice_kick(struct discord *client, const struct discord_message *msg) .cleanup = &free, }; - discord_disconnect_guild_member(client, msg->guild_id, user_id, &ret); + discord_disconnect_guild_member(client, event->guild_id, user_id, + &ret); } } diff --git a/examples/webhook.c b/examples/webhook.c index e3bb621c8..3b0b7c807 100644 --- a/examples/webhook.c +++ b/examples/webhook.c @@ -47,14 +47,16 @@ main(int argc, char *argv[]) /* Get Webhook */ { struct discord_ret_webhook ret = { .sync = DISCORD_SYNC_FLAG }; - discord_get_webhook_with_token(client, webhook_id, webhook_token, &ret); + discord_get_webhook_with_token(client, webhook_id, webhook_token, + &ret); } /* Execute Webhook */ { struct discord_ret ret = { .sync = true }; struct discord_execute_webhook params = { .content = "Hello World!" }; - discord_execute_webhook(client, webhook_id, webhook_token, ¶ms, &ret); + discord_execute_webhook(client, webhook_id, webhook_token, ¶ms, + &ret); } free(webhook_token); From d5f936093b05a31839f9a2b14b9c2c18d3ced32d Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 00:44:28 -0300 Subject: [PATCH 004/118] docs(README.md): update minimalistic examples and showcase slash commands --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bdb2be6cb..f037490f7 100644 --- a/README.md +++ b/README.md @@ -15,23 +15,69 @@ Concord is an asynchronous C99 Discord API library. It has minimal external dependencies, and a low-level translation of the Discord official documentation to C code. -### Minimal example +### Examples + +*The following are minimalistic examples, refer to [`examples/`](examples/) for a better overview.* + +#### Slash Commands (new method) + +```c +#include +#include + +void on_ready(struct discord *client, struct discord_ready *event) { + struct discord_create_guild_application_command params = { + .name = "ping", + .description = "Ping command!" + }; + discord_create_guild_application_command(client, event->application->id, + GUILD_ID, ¶ms, NULL); +} + +void on_interaction(struct discord *client, struct discord_interaction *event) { + if (event->type != DISCORD_INTERACTION_APPLICATION_COMMAND) + return; /* return if interaction isn't a slash command */ + + for (int i = 0; i < event->data->options->size; ++i) { + char *command_name = event->data->options->array[i].name; + + if (strcmp(command_name, "ping") == 0) { + struct discord_interaction_response params = { + .type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE, + .data = &(struct discord_interaction_callback_data){ + .content = "pong" + } + } + discord_create_interaction_response(client, event->id, + event->token, ¶ms, NULL); + } + } +} + +int main(void) { + struct discord *client = discord_init(BOT_TOKEN); + discord_set_on_ready(client, &on_ready); + discord_set_on_interaction_create(client, &on_interaction); + discord_run(client); +} +``` + +#### Message Commands (old method) ```c #include #include -void on_ready(struct discord *client) { - const struct discord_user *bot = discord_get_self(client); - log_info("Logged in as %s!", bot->username); +void on_ready(struct discord *client, struct discord_ready *event) { + log_info("Logged in as %s!", event->user->username); } -void on_message(struct discord *client, const struct discord_message *msg) { - if (strcmp(msg->content, "ping") != 0) - return; /* ignore messages that aren't 'ping' */ +void on_message(struct discord *client, struct discord_message *event) { + if (strcmp(event->content, "ping") != 0) + return; /* make sure to respond to "ping" */ struct discord_create_message params = { .content = "pong" }; - discord_create_message(client, msg->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } int main(void) { @@ -41,7 +87,6 @@ int main(void) { discord_run(client); } ``` -*This is a minimalistic example, refer to [`examples/`](examples/) for a better overview.* ## Supported operating systems (minimum requirements) * GNU/Linux 4.x From 1ff790a5c5814e9e69e568d4c5ab6bd5d365afb3 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 00:44:28 -0300 Subject: [PATCH 005/118] docs(README.md): update minimalistic examples and showcase slash commands --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f037490f7..31ef7c93d 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,10 @@ void on_interaction(struct discord *client, struct discord_interaction *event) { } int main(void) { - struct discord *client = discord_init(BOT_TOKEN); - discord_set_on_ready(client, &on_ready); - discord_set_on_interaction_create(client, &on_interaction); - discord_run(client); + struct discord *client = discord_init(BOT_TOKEN); + discord_set_on_ready(client, &on_ready); + discord_set_on_interaction_create(client, &on_interaction); + discord_run(client); } ``` @@ -69,22 +69,22 @@ int main(void) { #include void on_ready(struct discord *client, struct discord_ready *event) { - log_info("Logged in as %s!", event->user->username); + log_info("Logged in as %s!", event->user->username); } void on_message(struct discord *client, struct discord_message *event) { - if (strcmp(event->content, "ping") != 0) - return; /* make sure to respond to "ping" */ + if (strcmp(event->content, "ping") != 0) + return; /* make sure to respond to "ping" */ - struct discord_create_message params = { .content = "pong" }; - discord_create_message(client, event->channel_id, ¶ms, NULL); + struct discord_create_message params = { .content = "pong" }; + discord_create_message(client, event->channel_id, ¶ms, NULL); } int main(void) { - struct discord *client = discord_init(BOT_TOKEN); - discord_set_on_ready(client, &on_ready); - discord_set_on_message_create(client, &on_message); - discord_run(client); + struct discord *client = discord_init(BOT_TOKEN); + discord_set_on_ready(client, &on_ready); + discord_set_on_message_create(client, &on_message); + discord_run(client); } ``` From 4099356bb56aaf24f657da0591081c006c69d505 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 00:44:28 -0300 Subject: [PATCH 006/118] docs(README.md): update minimalistic examples and showcase slash commands --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 31ef7c93d..afe0bdd23 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ void on_interaction(struct discord *client, struct discord_interaction *event) { .data = &(struct discord_interaction_callback_data){ .content = "pong" } - } + }; discord_create_interaction_response(client, event->id, event->token, ¶ms, NULL); } From 6ab2196d5cd2a2bac67181d423d0bf82c92492aa Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 01:15:48 -0300 Subject: [PATCH 007/118] docs(README.md): improve documentation for special compilation flags and targets --- README.md | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index afe0bdd23..bdd03e447 100644 --- a/README.md +++ b/README.md @@ -179,23 +179,6 @@ On Windows with Cygwin, you might need to pass both arguments to use POSIX threa $ CFLAGS="-pthread -lpthread" make ``` -#### Special compilation flags - -The following section outlines flags that can be attached to the Makefile if you wish to override the default compilation behavior with additional functionalities. Example: - -```console -$ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_VOICE" make -``` - -* `-DCCORD_SIGINTCATCH` - * By default Concord will not shutdown gracefully when a SIGINT is received (i.e. Ctrl+c), enable this flag if you wish it to be handled for you. -* `-DCCORD_VOICE` - * Enable experimental Voice Connection handling. -* `-DCCORD_DEBUG_WEBSOCKETS` - * Enable verbose debugging for WebSockets communication. -* `-DCCORD_DEBUG_ADAPTER` - * Enable verbose debugging for REST communication. - ### Configuring Concord The following outlines the default fields of `config.json` @@ -247,6 +230,31 @@ Type a message in any channel the bot is part of and the bot should send an exac With Ctrl+c or with Ctrl+| +### Configure your build + +The following outlines special flags and targets to override the default Makefile build with additional functionalities. + +#### Special compilation flags + +* `-DCCORD_SIGINTCATCH` + * By default Concord will not shutdown gracefully when a SIGINT is received (i.e. Ctrl+c), enable this flag if you wish it to be handled for you. +* `-DCCORD_DEBUG_WEBSOCKETS` + * Enable verbose debugging for WebSockets communication. +* `-DCCORD_DEBUG_ADAPTER` + * Enable verbose debugging for REST communication. + +*Example:* +```console +$ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_DEBUG_ADAPTER" make +``` + +#### Special targets + +* `make voice` + * Enable experimental Voice Connection handling - not production ready. +* `make debug` + * Same as enabling `-DCCORD_DEBUG_WEBSOCKETS` and `-DCCORD_DEBUG_ADAPTER` + ## Installing Concord *(note -- `#` means that you should be running as root)* From fda90d80824b1921ea202881b02de8aa49e4ae53 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 22:10:27 -0300 Subject: [PATCH 008/118] docs(README.md): match old example with new method counterpart --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bdd03e447..befad4ac8 100644 --- a/README.md +++ b/README.md @@ -73,11 +73,10 @@ void on_ready(struct discord *client, struct discord_ready *event) { } void on_message(struct discord *client, struct discord_message *event) { - if (strcmp(event->content, "ping") != 0) - return; /* make sure to respond to "ping" */ - - struct discord_create_message params = { .content = "pong" }; - discord_create_message(client, event->channel_id, ¶ms, NULL); + if (strcmp(event->content, "ping") == 0) { + struct discord_create_message params = { .content = "pong" }; + discord_create_message(client, event->channel_id, ¶ms, NULL); + } } int main(void) { From 8596fe1f2d7ad4b6b853ac5275af4470c73efe18 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 3 May 2022 23:44:14 -0300 Subject: [PATCH 009/118] refactor!(test): match to 5ce2ad --- test/async.c | 38 ++++++++++++++++----------------- test/sync.c | 59 +++++++++++++++++++++++++--------------------------- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/test/async.c b/test/async.c index 4450e98a7..c4f5daadb 100644 --- a/test/async.c +++ b/test/async.c @@ -12,12 +12,10 @@ struct user_cxt { }; void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - - log_info("Succesfully connected to Discord as %s#%s!", bot->username, - bot->discriminator); + log_info("Succesfully connected to Discord as %s#%s!", + event->user->username, event->user->discriminator); } void @@ -41,11 +39,11 @@ reconnect(struct discord *client, } void -on_disconnect(struct discord *client, const struct discord_message *msg) +on_disconnect(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "Disconnecting ...", }, @@ -56,11 +54,11 @@ on_disconnect(struct discord *client, const struct discord_message *msg) } void -on_reconnect(struct discord *client, const struct discord_message *msg) +on_reconnect(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "Reconnecting ...", }, @@ -71,11 +69,11 @@ on_reconnect(struct discord *client, const struct discord_message *msg) } void -on_single(struct discord *client, const struct discord_message *msg) +on_single(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "Hello", }, @@ -108,9 +106,9 @@ send_batch(struct discord *client, } void -on_spam(struct discord *client, const struct discord_message *msg) +on_spam(struct discord *client, struct discord_message *event) { - send_batch(client, NULL, msg); + send_batch(client, NULL, event); } void @@ -133,9 +131,9 @@ send_msg(struct discord *client, void *data, const struct discord_message *msg) } void -on_spam_ordered(struct discord *client, const struct discord_message *msg) +on_spam_ordered(struct discord *client, struct discord_message *event) { - send_msg(client, NULL, msg); + send_msg(client, NULL, event); } void @@ -152,12 +150,12 @@ send_err(struct discord *client, CCORDcode code, void *data) } void -on_force_error(struct discord *client, const struct discord_message *msg) +on_force_error(struct discord *client, struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123; u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - memcpy(channel_id, &msg->channel_id, sizeof(u64snowflake)); + memcpy(channel_id, &event->channel_id, sizeof(u64snowflake)); discord_delete_channel(client, FAUX_CHANNEL_ID, &(struct discord_ret_channel){ diff --git a/test/sync.c b/test/sync.c index 69ec7c193..c1bbccb7d 100644 --- a/test/sync.c +++ b/test/sync.c @@ -18,20 +18,18 @@ bool g_keep_spamming = true; unsigned g_thread_count; void -on_ready(struct discord *client) +on_ready(struct discord *client, struct discord_ready *event) { - const struct discord_user *bot = discord_get_self(client); - - log_info("Succesfully connected to Discord as %s#%s!", bot->username, - bot->discriminator); + log_info("Succesfully connected to Discord as %s#%s!", + event->user->username, event->user->discriminator); } void -on_disconnect(struct discord *client, const struct discord_message *msg) +on_disconnect(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "Disconnecting ...", }, @@ -43,11 +41,11 @@ on_disconnect(struct discord *client, const struct discord_message *msg) } void -on_reconnect(struct discord *client, const struct discord_message *msg) +on_reconnect(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "Reconnecting ...", }, @@ -59,16 +57,16 @@ on_reconnect(struct discord *client, const struct discord_message *msg) } void -on_spam(struct discord *client, const struct discord_message *msg) +on_spam(struct discord *client, struct discord_message *event) { const unsigned threadpool_size = strtol(THREADPOOL_SIZE, NULL, 10); - if (msg->author->bot) return; + if (event->author->bot) return; // prevent blocking all threads pthread_mutex_lock(&g_lock); if (g_thread_count >= threadpool_size - 1) { - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "Too many threads (" THREADPOOL_SIZE @@ -96,7 +94,7 @@ on_spam(struct discord *client, const struct discord_message *msg) if (!keep_alive) break; snprintf(number, sizeof(number), "%d", i); - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = number, }, @@ -107,11 +105,11 @@ on_spam(struct discord *client, const struct discord_message *msg) } void -on_spam_block(struct discord *client, const struct discord_message *msg) +on_spam_block(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = "No 1", }, @@ -121,19 +119,18 @@ on_spam_block(struct discord *client, const struct discord_message *msg) } void -on_spam_block_continue(struct discord *client, - const struct discord_message *msg) +on_spam_block_continue(struct discord *client, struct discord_message *event) { const struct discord_user *bot = discord_get_self(client); char text[32]; int number; - if (msg->author->id != bot->id) return; + if (event->author->id != bot->id) return; - sscanf(msg->content, "No %d", &number); + sscanf(event->content, "No %d", &number); snprintf(text, sizeof(text), "No %d", 1 + number); - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = text, }, @@ -143,9 +140,9 @@ on_spam_block_continue(struct discord *client, } void -on_stop(struct discord *client, const struct discord_message *msg) +on_stop(struct discord *client, struct discord_message *event) { - if (msg->author->bot) return; + if (event->author->bot) return; pthread_mutex_lock(&g_lock); g_keep_spamming = false; @@ -154,12 +151,12 @@ on_stop(struct discord *client, const struct discord_message *msg) } void -on_force_error(struct discord *client, const struct discord_message *msg) +on_force_error(struct discord *client, struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123ULL; CCORDcode code; - if (msg->author->bot) return; + if (event->author->bot) return; code = discord_delete_channel(client, FAUX_CHANNEL_ID, &(struct discord_ret_channel){ @@ -168,7 +165,7 @@ on_force_error(struct discord *client, const struct discord_message *msg) assert(code != CCORD_OK); discord_create_message( - client, msg->channel_id, + client, event->channel_id, &(struct discord_create_message){ .content = (char *)discord_strerror(code, client), }, @@ -178,15 +175,15 @@ on_force_error(struct discord *client, const struct discord_message *msg) } void -on_ping(struct discord *client, const struct discord_message *msg) +on_ping(struct discord *client, struct discord_message *event) { char text[256]; - if (msg->author->bot) return; + if (event->author->bot) return; sprintf(text, "Ping: %d", discord_get_ping(client)); - discord_create_message(client, msg->channel_id, + discord_create_message(client, event->channel_id, &(struct discord_create_message){ .content = text, }, From 5225e660101a706947a2f48c27845950b189b285 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 4 May 2022 00:30:52 -0300 Subject: [PATCH 010/118] fix(gencodecs/api/gateway.pre.h): wrap fields that shouldn't be sent if empty --- gencodecs/api/gateway.pre.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index 8ea689d8c..7bc512278 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -239,18 +239,30 @@ LIST_END /** @CCORD_pub_struct{discord_presence_update} */ PUB_STRUCT(discord_presence_update) /** the user presence is being updated for */ + COND_WRITE(this->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) + COND_END /** id of the guild */ + COND_WRITE(this->guild_id != 0) FIELD_SNOWFLAKE(guild_id) + COND_END /** either "idle", "dnd", "online", or "offline" */ + COND_WRITE(this->status != NULL) FIELD_PTR(status, char, *) + COND_END /** user's platform-dependent status */ + COND_WRITE(this->client_status != NULL) FIELD_STRUCT_PTR(client_status, discord_client_status, *) + COND_END /** user's current activities */ + COND_WRITE(this->activities != NULL) FIELD_STRUCT_PTR(activities, discord_activities, *) + COND_END /** unix time (in milliseconds) of when the client went idle, or null if the client is not idle */ + COND_WRITE(this->since != 0) FIELD_TIMESTAMP(since) + COND_END /** whether or not the client is afk */ FIELD(afk, bool, false) STRUCT_END @@ -286,9 +298,13 @@ PUB_STRUCT(discord_identify) will stop sending offline members in the guild member list */ FIELD(large_threshold, int, 50) /** array of two integers (shard_id, num_shards) */ + COND_WRITE(this->shard != NULL) FIELD_STRUCT_PTR(shard, integers, *) + COND_END /** presence structure for initial presence information */ + COND_WRITE(this->presence != NULL) FIELD_STRUCT_PTR(presence, discord_presence_update, *) + COND_END /** the gateway intents you wish to receive @see @ref DiscordInternalGatewayIntents */ FIELD_BITMASK(intents) From 046af7dc86f8dd09a206e786a81c109c581f0736 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 4 May 2022 00:31:49 -0300 Subject: [PATCH 011/118] wip(discord-adapter): requests are sent in order of arrival, need to fix ratelimiting --- include/discord-internal.h | 4 +-- src/discord-adapter.c | 52 ++++++++------------------------- src/discord-adapter_ratelimit.c | 19 ++++-------- 3 files changed, 20 insertions(+), 55 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index e5a8706d3..2ff656d1a 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -317,8 +317,8 @@ struct discord_bucket { pthread_mutex_t lock; /** pending requests */ QUEUE(struct discord_context) waitq; - /** busy requests */ - QUEUE(struct discord_context) busyq; + /** busy performing request (`NULL` if none) */ + struct discord_context *busy; }; /** diff --git a/src/discord-adapter.c b/src/discord-adapter.c index acff787e9..cbd2c3b46 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -565,45 +565,21 @@ _discord_adapter_send(struct discord_adapter *adapter, io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, adapter->mhandle); - QUEUE_INSERT_TAIL(&cxt->b->busyq, &cxt->entry); + cxt->b->busy = cxt; return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; } -/* send a batch of requests */ -static CCORDcode -_discord_adapter_send_batch(struct discord_adapter *adapter, - struct discord_bucket *b) -{ - CCORDcode code = CCORD_OK; - long i; - - for (i = b->remaining; i > 0; --i) { - if (QUEUE_EMPTY(&b->waitq)) break; - - code = _discord_adapter_send(adapter, b); - if (code != CCORD_OK) break; - } - - return code; -} - static void _discord_adapter_try_send(struct discord_adapter *adapter, struct discord_bucket *b) { + /* TODO: enqueue timer */ + if (!b->remaining); + /* skip busy and non-pending buckets */ - if (!QUEUE_EMPTY(&b->busyq) || QUEUE_EMPTY(&b->waitq)) { - return; - } - /* if bucket is outdated then its necessary to send a single - * request to fetch updated values */ - if (b->reset_tstamp < NOW(adapter)) { + if (!b->busy && !QUEUE_EMPTY(&b->waitq)) _discord_adapter_send(adapter, b); - return; - } - /* send remainder or trigger timeout */ - _discord_adapter_send_batch(adapter, b); } /* TODO: redundant constant return value */ @@ -689,7 +665,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, } /* enqueue request for retry or recycle */ - QUEUE_REMOVE(&cxt->entry); + cxt->b->busy = NULL; if (retry && cxt->retry_attempt++ < adapter->retry_limit) { ua_conn_reset(cxt->conn); @@ -740,22 +716,18 @@ static void _discord_adapter_stop_bucket(struct discord_adapter *adapter, struct discord_bucket *b) { - QUEUE(struct discord_context) * qelem; - struct discord_context *cxt; - CURL *ehandle; + /* cancel busy transfer */ + if (b->busy) { + struct discord_context *cxt = b->busy; + CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); - while (!QUEUE_EMPTY(&b->busyq)) { - qelem = QUEUE_HEAD(&b->busyq); - QUEUE_REMOVE(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - ehandle = ua_conn_get_easy_handle(cxt->conn); + b->busy = NULL; curl_multi_remove_handle(adapter->mhandle, ehandle); /* set for recycling */ ua_conn_stop(cxt->conn); - QUEUE_INSERT_TAIL(adapter->idleq, qelem); + QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); } /* cancel pending tranfers */ diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index 8eb6bbfd1..cf920d421 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -134,7 +134,6 @@ _discord_bucket_init(struct discord_ratelimiter *rl, ERR("Couldn't initialize pthread mutex"); QUEUE_INIT(&b->waitq); - QUEUE_INIT(&b->busyq); pthread_mutex_lock(&rl->global.lock); chash_assign(rl, key, b, RATELIMITER_TABLE); @@ -302,20 +301,14 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, struct discord_bucket *b, struct ua_info *info) { - struct sized_buffer remaining, reset, reset_after; + struct sized_buffer remaining = + ua_info_get_header(info, "x-ratelimit-remaining"), + reset = ua_info_get_header(info, "x-ratelimit-reset"), + reset_after = ua_info_get_header( + info, "x-ratelimit-reset-after"); u64unix_ms now = cog_timestamp_ms(); - long _remaining; - remaining = ua_info_get_header(info, "x-ratelimit-remaining"); - _remaining = remaining.size ? strtol(remaining.start, NULL, 10) : 1L; - - /* skip out of order responses */ - if (_remaining > b->remaining && now < b->reset_tstamp) return; - - b->remaining = _remaining; - - reset = ua_info_get_header(info, "x-ratelimit-reset"); - reset_after = ua_info_get_header(info, "x-ratelimit-reset-after"); + b->remaining = remaining.size ? strtol(remaining.start, NULL, 10) : 1L; /* use X-Ratelimit-Reset-After if available, X-Ratelimit-Reset otherwise */ if (reset_after.size) { From da91be1b6dd4e85b985d3b7abd6d92b7f3cbb50c Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 6 May 2022 00:04:18 -0300 Subject: [PATCH 012/118] wip: enforce ratelimiting using the Timer API --- include/discord-internal.h | 32 +++++++++++++++++++++----------- src/discord-adapter.c | 18 +++++++----------- src/discord-adapter_ratelimit.c | 24 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 2ff656d1a..c6ad7eec4 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -332,8 +332,9 @@ u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, struct discord_bucket *bucket); /** - * @brief Sleep for bucket's cooldown time - * @note this **WILL** block the bucket's execution thread + * @brief Try to sleep bucket for pending cooldown time + * @note this is used for `sync` mode and **WILL** block the bucket's + * execution thread * * @param rl the handle initialized with discord_ratelimiter_init() * @param bucket the bucket to wait on cooldown @@ -341,6 +342,15 @@ u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, void discord_bucket_try_sleep(struct discord_ratelimiter *rl, struct discord_bucket *bucket); +/** + * @brief Try to timeout bucket for pending cooldown time + * + * @param client the client initialized with discord_init() + * @param bucket the bucket to wait on cooldown + */ +void discord_bucket_try_timeout(struct discord *client, + struct discord_bucket *b); + /** * @brief Get a `struct discord_bucket` assigned to `key` * @@ -739,21 +749,21 @@ struct discord_timers { }; /** - * @brief prepare timers for usage + * @brief Prepare timers for usage * * @param client the client created with discord_init() */ void discord_timers_init(struct discord *client); /** - * @brief cleanup timers and call cancel any running ones + * @brief Cleanup timers and call cancel any running ones * * @param client the client created with discord_init() */ void discord_timers_cleanup(struct discord *client); /** - * @brief run all timers that are due + * @brief Run all timers that are due * * @param client the client created with discord_init() * @param timers the timers to run @@ -761,7 +771,7 @@ void discord_timers_cleanup(struct discord *client); void discord_timers_run(struct discord *client, struct discord_timers *timers); /** - * @brief modifies or creates a timer + * @brief Modifies or creates a timer * * @param client the client created with discord_init() * @param timers the timer group to perform this operation on @@ -773,24 +783,24 @@ unsigned _discord_timer_ctl(struct discord *client, struct discord_timer *timer); /** - * @brief modifies or creates a timer + * @brief Modifies or creates a timer * * @param client the client created with discord_init() * @param timer the timer that should be modified - * @return unsigned the id of the timer + * @return the id of the timer */ unsigned discord_internal_timer_ctl(struct discord *client, struct discord_timer *timer); /** - * @brief creates a one shot timer that automatically - * deletes itself upon completion + * @brief Creates a one shot timer that automatically deletes itself upon + * completion * * @param client the client created with discord_init() * @param cb the callback that should be called when timer triggers * @param data user data * @param delay delay before timer should start in milliseconds - * @return unsigned + * @return the id of the timer */ unsigned discord_internal_timer(struct discord *client, discord_ev_timer cb, diff --git a/src/discord-adapter.c b/src/discord-adapter.c index cbd2c3b46..93b2a6253 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -8,9 +8,6 @@ #include "discord.h" #include "discord-internal.h" -/* No-lock alternative to discord_timestamp() */ -#define NOW(p_adapter) (CLIENT(p_adapter, adapter)->gw.timer->now) - static void setopt_cb(struct ua_conn *conn, void *p_token) { @@ -402,10 +399,8 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, /* ASYNCHRONOUS REQUEST LOGIC */ -/* TODO: make this kind of function gencodecs generated (optional) - * - * Only the fields that are required at _discord_context_to_mime() - * are duplicated*/ +/* Only the fields that are required at _discord_context_to_mime() + * are duplicated */ static void _discord_attachments_dup(struct discord_attachments *dest, struct discord_attachments *src) @@ -574,11 +569,12 @@ static void _discord_adapter_try_send(struct discord_adapter *adapter, struct discord_bucket *b) { - /* TODO: enqueue timer */ - if (!b->remaining); + /* skip if bucket is busy performing */ + if (b->busy) return; - /* skip busy and non-pending buckets */ - if (!b->busy && !QUEUE_EMPTY(&b->waitq)) + if (!b->remaining) + discord_bucket_try_timeout(CLIENT(adapter, adapter), b); + else if (!QUEUE_EMPTY(&b->waitq)) _discord_adapter_send(adapter, b); } diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index cf920d421..ecebc63d5 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -245,6 +245,30 @@ discord_bucket_try_sleep(struct discord_ratelimiter *rl, } } +static void +_discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) +{ + (void)client; + struct discord_bucket *b = timer->data; + + b->busy = NULL; /* bucket is no longer busy */ + b->remaining = 1; +} + +void +discord_bucket_try_timeout(struct discord *client, struct discord_bucket *b) +{ + const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); + + b->busy = (void *)0xf; /* bogus value to mark as busy */ + + discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); + + logconf_info(&client->adapter.ratelimiter->conf, + "[%.4s] RATELIMITING (wait %" PRId64 " ms)", b->hash, + delay_ms); +} + /* attempt to find a bucket associated key */ struct discord_bucket * discord_bucket_get(struct discord_ratelimiter *rl, From bd0635891a42d7324728f2a8c056f7c68948058f Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 6 May 2022 00:54:53 -0300 Subject: [PATCH 013/118] fix: segfault from freeing bogus pointer --- include/discord-internal.h | 7 ++++++- src/discord-adapter.c | 2 +- src/discord-adapter_ratelimit.c | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index c6ad7eec4..b1a5b1838 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -303,6 +303,8 @@ void discord_refcounter_decr(struct discord_refcounter *rc, void *data); * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ +#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) + /** @brief The Discord bucket for handling per-group ratelimits */ struct discord_bucket { /** the hash associated with the bucket's ratelimiting group */ @@ -317,7 +319,10 @@ struct discord_bucket { pthread_mutex_t lock; /** pending requests */ QUEUE(struct discord_context) waitq; - /** busy performing request (`NULL` if none) */ + /** + * pointer to currently performing busy request (if any) + * @note `NULL` if free or @ref DISCORD_BUCKET_TIMEOUT if being ratelimited + */ struct discord_context *busy; }; diff --git a/src/discord-adapter.c b/src/discord-adapter.c index 93b2a6253..41e9ecac7 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -713,7 +713,7 @@ _discord_adapter_stop_bucket(struct discord_adapter *adapter, struct discord_bucket *b) { /* cancel busy transfer */ - if (b->busy) { + if (b->busy && b->busy != DISCORD_BUCKET_TIMEOUT) { struct discord_context *cxt = b->busy; CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index ecebc63d5..234193fa2 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -260,7 +260,7 @@ discord_bucket_try_timeout(struct discord *client, struct discord_bucket *b) { const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); - b->busy = (void *)0xf; /* bogus value to mark as busy */ + b->busy = DISCORD_BUCKET_TIMEOUT; discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); From 53ed6338072f95ddcf32a8e9f8eb28b1a3a11e95 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 10:20:26 -0300 Subject: [PATCH 014/118] fix: -Wstringop-overread --- include/discord-internal.h | 8 ++++---- src/discord-adapter_ratelimit.c | 14 ++++++-------- src/discord-gateway.c | 1 - 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index b1a5b1838..342a650fc 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -360,11 +360,11 @@ void discord_bucket_try_timeout(struct discord *client, * @brief Get a `struct discord_bucket` assigned to `key` * * @param rl the handle initialized with discord_ratelimiter_init() - * @param key obtained from discord_ratelimiter_get_key() + * @param key obtained from discord_ratelimiter_build_key() * @return bucket matched to `key` */ struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, - const char key[DISCORD_ROUTE_LEN]); + const char key[]); /** @brief The ratelimiter struct for handling ratelimiting */ struct discord_ratelimiter { @@ -453,13 +453,13 @@ u64unix_ms discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl); * * @param rl the handle initialized with discord_ratelimiter_init() * @param bucket NULL when bucket is first discovered - * @param key obtained from discord_ratelimiter_get_key() + * @param key obtained from discord_ratelimiter_build_key() * @param info informational struct containing details on the current transfer * @note If the bucket was just discovered it will be created here. */ void discord_ratelimiter_build(struct discord_ratelimiter *rl, struct discord_bucket *bucket, - const char key[DISCORD_ROUTE_LEN], + const char key[], struct ua_info *info); /** @} DiscordInternalAdapterRatelimit */ diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index 234193fa2..a203c1906 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -117,7 +117,7 @@ discord_ratelimiter_build_key(enum http_method method, /* initialize bucket and assign it to ratelimiter hashtable */ static struct discord_bucket * _discord_bucket_init(struct discord_ratelimiter *rl, - const char key[DISCORD_ROUTE_LEN], + const char key[], const struct sized_buffer *hash, const long limit) { @@ -190,8 +190,7 @@ discord_ratelimiter_foreach(struct discord_ratelimiter *rl, } static struct discord_bucket * -_discord_bucket_find(struct discord_ratelimiter *rl, - const char key[DISCORD_ROUTE_LEN]) +_discord_bucket_find(struct discord_ratelimiter *rl, const char key[]) { struct discord_bucket *b = NULL; int ret; @@ -271,8 +270,7 @@ discord_bucket_try_timeout(struct discord *client, struct discord_bucket *b) /* attempt to find a bucket associated key */ struct discord_bucket * -discord_bucket_get(struct discord_ratelimiter *rl, - const char key[DISCORD_ROUTE_LEN]) +discord_bucket_get(struct discord_ratelimiter *rl, const char key[]) { struct discord_bucket *b; @@ -291,7 +289,7 @@ discord_bucket_get(struct discord_ratelimiter *rl, static struct discord_bucket * _discord_ratelimiter_get_match(struct discord_ratelimiter *rl, - const char key[DISCORD_ROUTE_LEN], + const char key[], struct ua_info *info) { struct discord_bucket *b; @@ -382,7 +380,7 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, static void _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, struct discord_bucket *b, - const char key[DISCORD_ROUTE_LEN]) + const char key[]) { QUEUE(struct discord_context) queue, *qelem; struct discord_context *cxt; @@ -409,7 +407,7 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, void discord_ratelimiter_build(struct discord_ratelimiter *rl, struct discord_bucket *b, - const char key[DISCORD_ROUTE_LEN], + const char key[], struct ua_info *info) { /* try to match to existing, or create new bucket */ diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 49fe7fac5..17399da40 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -1,7 +1,6 @@ #include #include #include -#include /* offsetof() */ #include /* isspace() */ #include "discord.h" From e8c097669b624260e16e2dda8c8af67143a1a498 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 10:23:22 -0300 Subject: [PATCH 015/118] fix: -Wsign-conversion --- src/discord-gateway.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 17399da40..3b9cc19e5 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -1144,8 +1144,8 @@ on_text_cb(void *p_gw, if (seq) gw->payload.seq = seq; } if ((f = jsmnf_find(gw->parse.pairs, text, "op", 2))) - gw->payload.opcode = - (int)strtol(gw->json + f->v.pos, NULL, 10); + gw->payload.opcode = (enum discord_gateway_opcodes)strtol( + gw->json + f->v.pos, NULL, 10); gw->payload.data = jsmnf_find(gw->parse.pairs, text, "d", 1); } } From ce9be4358c826a00884e63f5a7a0dae0977dfbab Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 10:42:04 -0300 Subject: [PATCH 016/118] feat: move 'struct discord_refcount' from 'struct discord_adapter' to 'struct discord' * fix: replace outdated references to HAS_DISCORD_VOICE with CCORD_VOICE --- Makefile | 4 +- include/discord-internal.h | 139 +++++++++--------- src/channel.c | 5 +- src/discord-adapter.c | 9 +- src/discord-client.c | 10 +- src/discord-gateway.c | 8 +- ...-adapter_refcount.c => discord-refcount.c} | 0 7 files changed, 88 insertions(+), 87 deletions(-) rename src/{discord-adapter_refcount.c => discord-refcount.c} (100%) diff --git a/Makefile b/Makefile index 6f63e645d..7aa0e3677 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,9 @@ THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-adapter.o \ $(SRC_DIR)/discord-adapter_ratelimit.o \ - $(SRC_DIR)/discord-adapter_refcount.o \ + $(SRC_DIR)/discord-refcount.o \ $(SRC_DIR)/discord-client.o \ - $(SRC_DIR)/discord-loop.o \ + $(SRC_DIR)/discord-loop.o \ $(SRC_DIR)/discord-gateway.o \ $(SRC_DIR)/discord-timer.o \ $(SRC_DIR)/discord-misc.o \ diff --git a/include/discord-internal.h b/include/discord-internal.h index 342a650fc..a09c1de3c 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -166,8 +166,6 @@ struct discord_adapter { struct user_agent *ua; /** curl_multi handle for performing non-blocking requests */ CURLM *mhandle; - /** user's data reference counter for automatic cleanup */ - struct discord_refcounter *refcounter; /** buckets discovered (declared at discord-adapter_ratelimit.c) */ struct discord_ratelimiter *ratelimiter; @@ -235,70 +233,6 @@ CCORDcode discord_adapter_perform(struct discord_adapter *adapter); */ void discord_adapter_stop_buckets(struct discord_adapter *adapter); -/** @defgroup DiscordInternalAdapterRefcount Reference counter - * @brief Handle automatic cleanup of user's data - * @{ */ - -/** @brief Automatically cleanup user data - * - * Automatically cleanup user data that is passed around Discord event's - * callbacks once its reference counter reaches 0, meaning there are no - * more callbacks expecting the data */ -struct discord_refcounter { - /** DISCORD_REFCOUNT logging module */ - struct logconf conf; - /** amount of individual user's data held for automatic cleanup */ - int length; - /** cap before increase */ - int capacity; - /** - * individual user's data held for automatic cleanup - * @note datatype declared at discord-adapter_refcount.c - */ - struct _discord_ref *refs; -}; - -/** - * @brief Initialize reference counter handle - * - * A hashtable shall be used for storage and retrieval of user data - * @param conf optional pointer to a parent logconf - * @return the reference counter handle - */ -struct discord_refcounter *discord_refcounter_init(struct logconf *conf); - -/** - * @brief Cleanup refcounter and all user data currently held - * - * @param rc the handle initialized with discord_refcounter_init() - */ -void discord_refcounter_cleanup(struct discord_refcounter *rc); - -/** - * @brief Increment the reference counter for `ret->data` - * - * @param rc the handle initialized with discord_refcounter_init() - * @param data the user arbitrary data to have its reference counter - * @param cleanup user-defined function for cleaning `data` resources once its - * no longer referenced - */ -void discord_refcounter_incr(struct discord_refcounter *rc, - void *data, - void (*cleanup)(void *data)); - -/** - * @brief Decrement the reference counter for `data` - * - * If the count reaches zero then `data` shall be cleanup up with its - * user-defined cleanup function - * @param rc the handle initialized with discord_refcounter_init() - * @param data the user arbitrary data to have its reference counter - * decremented - */ -void discord_refcounter_decr(struct discord_refcounter *rc, void *data); - -/** @} DiscordInternalAdapterRefcount */ - /** @defgroup DiscordInternalAdapterRatelimit Ratelimiting * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ @@ -813,6 +747,73 @@ unsigned discord_internal_timer(struct discord *client, int64_t delay); /** @} DiscordInternalTimer */ + +/** @defgroup DiscordInternalRefcount Reference counter + * @brief Handle automatic cleanup of user's data + * @{ */ + +/** + * @brief Automatically cleanup user data + * + * Automatically cleanup user data that is passed around Discord event's + * callbacks once its reference counter reaches 0, meaning there are no + * more callbacks expecting the data + */ +struct discord_refcounter { + /** DISCORD_REFCOUNT logging module */ + struct logconf conf; + /** amount of individual user's data held for automatic cleanup */ + int length; + /** cap before increase */ + int capacity; + /** + * individual user's data held for automatic cleanup + * @note datatype declared at discord-adapter_refcount.c + */ + struct _discord_ref *refs; +}; + +/** + * @brief Initialize reference counter handle + * + * A hashtable shall be used for storage and retrieval of user data + * @param conf optional pointer to a parent logconf + * @return the reference counter handle + */ +struct discord_refcounter *discord_refcounter_init(struct logconf *conf); + +/** + * @brief Cleanup refcounter and all user data currently held + * + * @param rc the handle initialized with discord_refcounter_init() + */ +void discord_refcounter_cleanup(struct discord_refcounter *rc); + +/** + * @brief Increment the reference counter for `ret->data` + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the user arbitrary data to have its reference counter + * @param cleanup user-defined function for cleaning `data` resources once its + * no longer referenced + */ +void discord_refcounter_incr(struct discord_refcounter *rc, + void *data, + void (*cleanup)(void *data)); + +/** + * @brief Decrement the reference counter for `data` + * + * If the count reaches zero then `data` shall be cleanup up with its + * user-defined cleanup function + * @param rc the handle initialized with discord_refcounter_init() + * @param data the user arbitrary data to have its reference counter + * decremented + */ +void discord_refcounter_decr(struct discord_refcounter *rc, void *data); + +/** @} DiscordInternalRefcount */ + /** * @brief The Discord client handler * @@ -828,10 +829,12 @@ struct discord { struct sized_buffer token; /** the io poller for listening to file descriptors */ struct io_poller *io_poller; - /** the HTTP adapter for performing requests */ + /** the handle for interfacing with Discord's REST API */ struct discord_adapter adapter; - /** the WebSockets handle for establishing a connection to Discord */ + /** the handle for interfacing with Discord's Gateway API */ struct discord_gateway gw; + /** user's data reference counter for automatic cleanup */ + struct discord_refcounter *refcounter; /** the client's user structure */ struct discord_user self; diff --git a/src/channel.c b/src/channel.c index 026b64700..9f4bdaa0a 100644 --- a/src/channel.c +++ b/src/channel.c @@ -45,7 +45,7 @@ _done_get_channels(struct discord *client, cxt->ret.fail(client, CCORD_BAD_PARAMETER, cxt->ret.data); } - discord_refcounter_decr(client->adapter.refcounter, cxt->ret.data); + discord_refcounter_decr(client->refcounter, cxt->ret.data); } CCORDcode @@ -75,8 +75,7 @@ discord_get_channel_at_pos(struct discord *client, /* TODO: the following should be replaced by @ref DiscordInternalTimer * implementation */ if (ret->data) { - discord_refcounter_incr(client->adapter.refcounter, ret->data, - ret->cleanup); + discord_refcounter_incr(client->refcounter, ret->data, ret->cleanup); } /* TODO: fetch channel via caching, and return if results are non-existent diff --git a/src/discord-adapter.c b/src/discord-adapter.c index 41e9ecac7..5bcb3c3df 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -60,7 +60,6 @@ discord_adapter_init(struct discord_adapter *adapter, on_io_poller_curl, adapter); adapter->ratelimiter = discord_ratelimiter_init(&adapter->conf); - adapter->refcounter = discord_refcounter_init(&adapter->conf); /* idleq is malloc'd to guarantee a client cloned by discord_clone() will * share the same queue with the original */ @@ -94,8 +93,6 @@ discord_adapter_cleanup(struct discord_adapter *adapter) discord_adapter_stop_buckets(adapter); /* cleanup discovered buckets */ discord_ratelimiter_cleanup(adapter->ratelimiter); - /* cleanup stored user data */ - discord_refcounter_cleanup(adapter->refcounter); /* cleanup idle requests queue */ QUEUE_MOVE(adapter->idleq, &queue); @@ -510,8 +507,8 @@ _discord_adapter_run_async(struct discord_adapter *adapter, QUEUE_INSERT_TAIL(&cxt->b->waitq, &cxt->entry); if (req->ret.data) - discord_refcounter_incr(adapter->refcounter, req->ret.data, - req->ret.cleanup); + discord_refcounter_incr(CLIENT(adapter, adapter)->refcounter, + req->ret.data, req->ret.cleanup); io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, adapter->mhandle); @@ -670,7 +667,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, } } else { - discord_refcounter_decr(adapter->refcounter, cxt->req.ret.data); + discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, cxt->req.ret.data); _discord_context_reset(cxt); QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); } diff --git a/src/discord-client.c b/src/discord-client.c index faf974fc3..7e294d7e7 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -14,13 +14,14 @@ _discord_init(struct discord *new_client) ccord_global_init(); discord_timers_init(new_client); new_client->io_poller = io_poller_create(); + new_client->refcounter = discord_refcounter_init(&new_client->conf); discord_adapter_init(&new_client->adapter, &new_client->conf, &new_client->token); discord_gateway_init(&new_client->gw, &new_client->conf, &new_client->token); -#ifdef HAS_DISCORD_VOICE +#ifdef CCORD_VOICE discord_voice_connections_init(new_client); -#endif /* HAS_DISCORD_VOICE */ +#endif /* fetch the client user structure */ if (new_client->token.size) { @@ -132,9 +133,10 @@ discord_cleanup(struct discord *client) discord_gateway_cleanup(&client->gw); discord_user_cleanup(&client->self); io_poller_destroy(client->io_poller); -#ifdef HAS_DISCORD_VOICE + discord_refcounter_cleanup(client->refcounter); +#ifdef CCORD_VOICE discord_voice_connections_cleanup(client); -#endif /* HAS_DISCORD_VOICE */ +#endif } else { _discord_clone_cleanup(client); diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 3b9cc19e5..dda5c734b 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -678,12 +678,12 @@ on_voice_state_update(struct discord_gateway *gw) { struct discord_voice_state event = { 0 }; discord_voice_state_from_jsmnf(gw->payload.data, gw->json, &event); -#ifdef HAS_DISCORD_VOICE +#ifdef CCORD_VOICE if (event.user_id == CLIENT(gw, gw)->self.id) { /* we only care about the voice_state_update of bot */ _discord_on_voice_state_update(CLIENT(gw, gw), &event); } -#endif /* HAS_DISCORD_VOICE */ +#endif if (gw->cmds.cbs.on_voice_state_update) ON(voice_state_update, &event); discord_voice_state_cleanup(&event); } @@ -693,10 +693,10 @@ on_voice_server_update(struct discord_gateway *gw) { struct discord_voice_server_update event = { 0 }; discord_voice_server_update_from_jsmnf(gw->payload.data, gw->json, &event); -#ifdef HAS_DISCORD_VOICE +#ifdef CCORD_VOICE /* this happens for everyone */ _discord_on_voice_server_update(CLIENT(gw, gw), &event); -#endif /* HAS_DISCORD_VOICE */ +#endif if (gw->cmds.cbs.on_voice_server_update) ON(voice_server_update, &event); discord_voice_server_update_cleanup(&event); } diff --git a/src/discord-adapter_refcount.c b/src/discord-refcount.c similarity index 100% rename from src/discord-adapter_refcount.c rename to src/discord-refcount.c From 7c13970adaa7afe9b054c0fe46cc6820af880b06 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 11:06:48 -0300 Subject: [PATCH 017/118] fix(discord-voice): outdated jsmn-find syntax --- include/discord-voice.h | 22 ++++++------- src/discord-gateway.c | 5 +-- src/discord-voice.c | 68 +++++++++++++++++++++-------------------- 3 files changed, 47 insertions(+), 48 deletions(-) diff --git a/include/discord-voice.h b/include/discord-voice.h index 0e8b23b0f..2a9a664c2 100644 --- a/include/discord-voice.h +++ b/include/discord-voice.h @@ -109,7 +109,7 @@ struct discord_voice { struct websockets *ws; /** @brief handle reconnect logic */ - /* RECONNECT STRUCTURE */ + /* reconnect structure */ struct { /** will attempt reconnecting if true */ bool enable; @@ -126,6 +126,11 @@ struct discord_voice { /** can start sending/receiving additional events to discord */ bool is_ready; + /** current iteration JSON string data */ + char *json; + /** current iteration JSON string data length */ + size_t length; + /** parse JSON tokens into a `jsmnf_pairs` key/value pairs hashtable */ struct { /** current iteration JSON key/value pairs */ @@ -237,26 +242,21 @@ void discord_send_speaking(struct discord_voice *vc, * @brief Update the voice session with a new session_id * * @param client the client created with discord_init() - * @param vs the voice state that has been updated + * @param event the voice state that has been updated * @todo move to discord-internal.h */ void _discord_on_voice_state_update(struct discord *client, - struct discord_voice_state *vs); + struct discord_voice_state *event); /** * @brief Update the voice session with a new token and url * * @param client the client created with discord_init() - * @param guild_id the guild that houses the voice channel - * @param token the unique token identifier - * @param endpoint unique wss url received - * @todo move to discord-internal.h + * @param event the event contents for server update * @note will prepend with "wss://" and append with "?v=4" */ -void _discord_on_voice_server_update(struct discord *client, - u64snowflake guild_id, - char token[], - char endpoint[]); +void _discord_on_voice_server_update( + struct discord *client, struct discord_voice_server_update *event); /** * @brief Gracefully exits a ongoing Discord Voice connection diff --git a/src/discord-gateway.c b/src/discord-gateway.c index dda5c734b..3995fbec5 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -679,10 +679,8 @@ on_voice_state_update(struct discord_gateway *gw) struct discord_voice_state event = { 0 }; discord_voice_state_from_jsmnf(gw->payload.data, gw->json, &event); #ifdef CCORD_VOICE - if (event.user_id == CLIENT(gw, gw)->self.id) { - /* we only care about the voice_state_update of bot */ + if (event.user_id == CLIENT(gw, gw)->self.id) _discord_on_voice_state_update(CLIENT(gw, gw), &event); - } #endif if (gw->cmds.cbs.on_voice_state_update) ON(voice_state_update, &event); discord_voice_state_cleanup(&event); @@ -694,7 +692,6 @@ on_voice_server_update(struct discord_gateway *gw) struct discord_voice_server_update event = { 0 }; discord_voice_server_update_from_jsmnf(gw->payload.data, gw->json, &event); #ifdef CCORD_VOICE - /* this happens for everyone */ _discord_on_voice_server_update(CLIENT(gw, gw), &event); #endif if (gw->cmds.cbs.on_voice_server_update) ON(voice_server_update, &event); diff --git a/src/discord-voice.c b/src/discord-voice.c index df14be13e..a839a2fc3 100644 --- a/src/discord-voice.c +++ b/src/discord-voice.c @@ -155,8 +155,8 @@ on_hello(struct discord_voice *vc) jsmnf_pair *f; vc->hbeat.tstamp = cog_timestamp_ms(); - if ((f = jsmnf_find(vc->payload.data, "heartbeat_interval", 18))) - hbeat_interval = strtof(f->value.contents, NULL); + if ((f = jsmnf_find(vc->payload.data, vc->json, "heartbeat_interval", 18))) + hbeat_interval = strtof(vc->json + f->v.pos, NULL); vc->hbeat.interval_ms = (hbeat_interval < 5000.0f) ? (u64unix_ms)hbeat_interval : 5000; @@ -199,14 +199,14 @@ on_speaking(struct discord_voice *vc) if (!client->voice_cbs.on_speaking) return; - if ((f = jsmnf_find(vc->payload.data, "user_id", 7))) - sscanf(f->value.contents, "%" SCNu64, &user_id); - if ((f = jsmnf_find(vc->payload.data, "speaking", 8))) - speaking = (int)strtol(f->value.contents, NULL, 10); - if ((f = jsmnf_find(vc->payload.data, "delay", 5))) - delay = (int)strtol(f->value.contents, NULL, 10); - if ((f = jsmnf_find(vc->payload.data, "ssrc", 4))) - ssrc = (int)strtol(f->value.contents, NULL, 10); + if ((f = jsmnf_find(vc->payload.data, vc->json, "user_id", 7))) + sscanf(vc->json + f->v.pos, "%" SCNu64, &user_id); + if ((f = jsmnf_find(vc->payload.data, vc->json, "speaking", 8))) + speaking = (int)strtol(vc->json + f->v.pos, NULL, 10); + if ((f = jsmnf_find(vc->payload.data, vc->json, "delay", 5))) + delay = (int)strtol(vc->json + f->v.pos, NULL, 10); + if ((f = jsmnf_find(vc->payload.data, vc->json, "ssrc", 4))) + ssrc = (int)strtol(vc->json + f->v.pos, NULL, 10); client->voice_cbs.on_speaking(client, vc, user_id, speaking, delay, ssrc); } @@ -228,8 +228,8 @@ on_client_disconnect(struct discord_voice *vc) if (!client->voice_cbs.on_client_disconnect) return; - if ((f = jsmnf_find(vc->payload.data, "user_id", 7))) - sscanf(f->value.contents, "%" SCNu64, &user_id); + if ((f = jsmnf_find(vc->payload.data, vc->json, "user_id", 7))) + sscanf(vc->json + f->v.pos, "%" SCNu64, &user_id); client->voice_cbs.on_client_disconnect(client, vc, user_id); } @@ -243,12 +243,12 @@ on_codec(struct discord_voice *vc) if (!client->voice_cbs.on_codec) return; - if ((f = jsmnf_find(vc->payload.data, "audio_codec", 11))) - snprintf(audio_codec, sizeof(audio_codec), "%.*s", f->value.length, - f->value.contents); - if ((f = jsmnf_find(vc->payload.data, "video_codec", 11))) - snprintf(video_codec, sizeof(video_codec), "%.*s", f->value.length, - f->value.contents); + if ((f = jsmnf_find(vc->payload.data, vc->json, "audio_codec", 11))) + snprintf(audio_codec, sizeof(audio_codec), "%.*s", (int)f->v.len, + vc->json + f->v.pos); + if ((f = jsmnf_find(vc->payload.data, vc->json, "video_codec", 11))) + snprintf(video_codec, sizeof(video_codec), "%.*s", (int)f->v.len, + vc->json + f->v.pos); client->voice_cbs.on_codec(client, vc, audio_codec, video_codec); } @@ -346,6 +346,9 @@ on_text_cb(void *p_vc, struct discord_voice *vc = p_vc; jsmn_parser parser; + vc->json = (char *)text; + vc->length = len; + jsmn_init(&parser); if (0 < jsmn_parse_auto(&parser, text, len, &vc->parse.tokens, &vc->parse.ntokens)) @@ -359,9 +362,10 @@ on_text_cb(void *p_vc, { jsmnf_pair *f; - if ((f = jsmnf_find(vc->parse.pairs, "op", 2))) - vc->payload.opcode = (int)strtol(f->value.contents, NULL, 10); - vc->payload.data = jsmnf_find(vc->parse.pairs, "d", 1); + if ((f = jsmnf_find(vc->parse.pairs, vc->json, "op", 2))) + vc->payload.opcode = + (int)strtol(vc->json + f->v.pos, NULL, 10); + vc->payload.data = jsmnf_find(vc->parse.pairs, vc->json, "d", 1); } } @@ -621,18 +625,18 @@ discord_voice_join(struct discord *client, */ void _discord_on_voice_state_update(struct discord *client, - struct discord_voice_state *vs) + struct discord_voice_state *event) { struct discord_voice *vc = NULL; int i; pthread_mutex_lock(&client_lock); for (i = 0; i < DISCORD_MAX_VCS; ++i) { - if (vs->guild_id == client->vcs[i].guild_id) { + if (event->guild_id == client->vcs[i].guild_id) { vc = client->vcs + i; - if (vs->channel_id) { + if (event->channel_id) { int len = snprintf(vc->session_id, sizeof(vc->session_id), - "%s", vs->session_id); + "%s", event->session_id); ASSERT_NOT_OOB(len, sizeof(vc->session_id)); logconf_info(&vc->conf, @@ -646,7 +650,7 @@ _discord_on_voice_state_update(struct discord *client, pthread_mutex_unlock(&client_lock); if (!vc) { - if (vs->channel_id) { + if (event->channel_id) { logconf_fatal( &client->conf, "This should not happen, cannot find a discord_voice object"); @@ -655,7 +659,7 @@ _discord_on_voice_state_update(struct discord *client, return; } - if (vs->channel_id == 0) { + if (event->channel_id == 0) { logconf_info(&vc->conf, ANSICOLOR("Bot is leaving the current vc", ANSI_BG_BRIGHT_BLUE)); if (vc->ws && ws_is_alive(vc->ws)) @@ -745,9 +749,7 @@ start_voice_ws_thread(void *p_vc) */ void _discord_on_voice_server_update(struct discord *client, - u64snowflake guild_id, - char *token, - char *endpoint) + struct discord_voice_server_update *event) { struct discord_voice *vc = NULL; int len; @@ -755,7 +757,7 @@ _discord_on_voice_server_update(struct discord *client, pthread_mutex_lock(&client_lock); for (i = 0; i < DISCORD_MAX_VCS; ++i) { - if (guild_id == client->vcs[i].guild_id) { + if (event->guild_id == client->vcs[i].guild_id) { vc = client->vcs + i; break; } @@ -767,11 +769,11 @@ _discord_on_voice_server_update(struct discord *client, return; } - len = snprintf(vc->new_token, sizeof(vc->new_token), "%s", token); + len = snprintf(vc->new_token, sizeof(vc->new_token), "%s", event->token); ASSERT_NOT_OOB(len, sizeof(vc->new_token)); len = snprintf(vc->new_url, sizeof(vc->new_url), - "wss://%s" DISCORD_VCS_URL_SUFFIX, endpoint); + "wss://%s" DISCORD_VCS_URL_SUFFIX, event->endpoint); ASSERT_NOT_OOB(len, sizeof(vc->new_url)); /* TODO: replace with the more reliable thread alive check */ From 2435f2e923265ab1e03ace7b702a8e4d854ed15b Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 11:07:37 -0300 Subject: [PATCH 018/118] fix(examples): rename voice.c -> voice-join.c to not conflict with the make's voice target --- examples/Makefile | 64 +++++++++++++++--------------- examples/{voice.c => voice-join.c} | 6 +-- 2 files changed, 36 insertions(+), 34 deletions(-) rename examples/{voice.c => voice-join.c} (97%) diff --git a/examples/Makefile b/examples/Makefile index 42dc0249e..1a4135a62 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,35 +2,37 @@ TOP = .. CC ?= gcc -COGUTILS_DIR := $(TOP)/cog-utils -CORE_DIR := $(TOP)/core -INCLUDE_DIR := $(TOP)/include -GENCODECS_DIR := $(TOP)/gencodecs - -BOTS := 8ball \ - audit-log \ - ban \ - channel \ - components \ - copycat \ - embed \ - emoji \ - fetch-messages \ - guild-template \ - guild \ - invite \ - manual-dm \ - pin \ - ping-pong \ - presence \ - reaction \ - shell \ - slash-commands \ - slash-commands2 \ - spam \ - webhook \ - timers \ - $(XSRC) +COGUTILS_DIR = $(TOP)/cog-utils +CORE_DIR = $(TOP)/core +INCLUDE_DIR = $(TOP)/include +GENCODECS_DIR = $(TOP)/gencodecs + +BOTS = 8ball \ + audit-log \ + ban \ + channel \ + components \ + copycat \ + embed \ + emoji \ + fetch-messages \ + guild-template \ + guild \ + invite \ + manual-dm \ + pin \ + ping-pong \ + presence \ + reaction \ + shell \ + slash-commands \ + slash-commands2 \ + spam \ + webhook \ + timers \ + $(XSRC) + +VOICE_BOT = voice-join CFLAGS = -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) \ -I$(CORE_DIR)/third-party -I$(GENCODECS_DIR) \ @@ -40,7 +42,7 @@ LDFLAGS = -ldiscord -L$(TOP)/lib -lcurl all: $(BOTS) voice: - $(MAKE) XFLAGS=-DCCORD_VOICE XSRC=voice all + $(MAKE) XFLAGS=-DCCORD_VOICE XSRC=$(VOICE_BOT) all .SUFFIXES: .DEFAULT: @@ -51,6 +53,6 @@ echo: @ echo -e 'BOTS: $(BOTS)\n' clean: - @ $(RM) $(BOTS) voice + @ $(RM) $(BOTS) $(VOICE_BOT) .PHONY: all echo clean diff --git a/examples/voice.c b/examples/voice-join.c similarity index 97% rename from examples/voice.c rename to examples/voice-join.c index 623756179..11eff1ac9 100644 --- a/examples/voice.c +++ b/examples/voice-join.c @@ -15,14 +15,14 @@ void print_usage(void) { printf( - "\n\nThis bot is a work in progress, it should demonstrate some " - "Voice related utilities\n" + "\n\nThis bot demonstrates some of the Discord Voice Connections " + "interface\n" "1. Type 'voice.list_regions' to list regions that can be used when " "creating servers\n" "2. Type 'voice.join ' to join a particular voice " "channel by its position\n" "3. Type 'voice.kick ' to kick a particular user from the " - "voice channel he's at\n" + "voice channel they are at\n" "\nTYPE ANY KEY TO START BOT\n"); } From a9e966cca6f2f4e4e49935703a9ede3aa0ec5f9f Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 20:41:33 -0300 Subject: [PATCH 019/118] refactor(gencodecs): make a couple methods public --- gencodecs/api/channel.pre.h | 3 ++- gencodecs/api/gateway.pre.h | 11 ++++++++++- gencodecs/api/guild.pre.h | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/gencodecs/api/channel.pre.h b/gencodecs/api/channel.pre.h index b041dabfc..8fe53be99 100644 --- a/gencodecs/api/channel.pre.h +++ b/gencodecs/api/channel.pre.h @@ -326,7 +326,8 @@ STRUCT(discord_thread_metadata) FIELD_TIMESTAMP(create_timestamp) STRUCT_END -STRUCT(discord_thread_member) +/** @CCORD_pub_struct{discord_thread_member} */ +PUB_STRUCT(discord_thread_member) /** the id of the thread */ FIELD_SNOWFLAKE(id) /** the id of the user */ diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index 7bc512278..1c57bbdc6 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -124,7 +124,8 @@ ENUM(discord_gateway_events) ENUMERATOR(DISCORD_GATEWAY_EVENTS_USER_UPDATE, = 50) ENUMERATOR(DISCORD_GATEWAY_EVENTS_VOICE_STATE_UPDATE, = 51) ENUMERATOR(DISCORD_GATEWAY_EVENTS_VOICE_SERVER_UPDATE, = 52) - ENUMERATOR_LAST(DISCORD_GATEWAY_EVENTS_WEBHOOKS_UPDATE, = 53) + ENUMERATOR(DISCORD_GATEWAY_EVENTS_WEBHOOKS_UPDATE, = 53) + ENUMERATOR_LAST(DISCORD_GATEWAY_EVENTS_MAX, ) ENUM_END ENUM(discord_activity_types) @@ -460,6 +461,14 @@ PUB_STRUCT(discord_guild_emojis_update) FIELD_STRUCT_PTR(emojis, discord_emojis, *) STRUCT_END +/** @CCORD_pub_struct{discord_stickers_update} */ +PUB_STRUCT(discord_guild_stickers_update) + /** id of the guild */ + FIELD_SNOWFLAKE(guild_id) + /** array of stickers */ + FIELD_STRUCT_PTR(stickers, discord_stickers, *) +STRUCT_END + /** @CCORD_pub_struct{discord_guild_integrations_update} */ PUB_STRUCT(discord_guild_integrations_update) /** id of the guild whose integrations were updated */ diff --git a/gencodecs/api/guild.pre.h b/gencodecs/api/guild.pre.h index 7d446e5ef..382eb0ced 100644 --- a/gencodecs/api/guild.pre.h +++ b/gencodecs/api/guild.pre.h @@ -327,7 +327,8 @@ PUB_LIST(discord_guild_members) LISTTYPE_STRUCT(discord_guild_member) LIST_END -STRUCT(discord_integration) +/** @CCORD_pub_struct{discord_integration} */ +PUB_STRUCT(discord_integration) /** integration id */ FIELD_SNOWFLAKE(id) /** integration name */ From 1963048080b6d1915a69d0ed66501ce3523b3910 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 22:34:45 -0300 Subject: [PATCH 020/118] refactor!(discord-gateway): reduce code duplication, allow passing event data around callbacks --- Makefile | 1 + gencodecs/api/gateway.pre.h | 111 +++--- include/discord-events.h | 24 +- include/discord-internal.h | 135 ++----- include/discord-voice.h | 6 +- src/channel.c | 3 +- src/discord-adapter.c | 2 +- src/discord-client.c | 106 +++--- src/discord-gateway.c | 658 +++------------------------------ src/discord-gateway_dispatch.c | 260 +++++++++++++ src/discord-refcount.c | 7 +- 11 files changed, 485 insertions(+), 828 deletions(-) create mode 100644 src/discord-gateway_dispatch.c diff --git a/Makefile b/Makefile index 7aa0e3677..4876c8548 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-client.o \ $(SRC_DIR)/discord-loop.o \ $(SRC_DIR)/discord-gateway.o \ + $(SRC_DIR)/discord-gateway_dispatch.o \ $(SRC_DIR)/discord-timer.o \ $(SRC_DIR)/discord-misc.o \ $(SRC_DIR)/application_command.o \ diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index 1c57bbdc6..ad702b6db 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -71,61 +71,62 @@ ENUM(discord_gateway_opcodes) ENUM_END ENUM(discord_gateway_events) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_NONE, = 0) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_READY, = 1) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_RESUMED, = 2) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_APPLICATION_COMMAND_CREATE, = 3) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_APPLICATION_COMMAND_UPDATE, = 4) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_APPLICATION_COMMAND_DELETE, = 5) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_CHANNEL_CREATE, = 6) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_CHANNEL_UPDATE, = 7) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_CHANNEL_DELETE, = 8) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_CHANNEL_PINS_UPDATE, = 9) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_THREAD_CREATE, = 10) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_THREAD_UPDATE, = 11) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_THREAD_DELETE, = 12) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_THREAD_LIST_SYNC, = 13) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_THREAD_MEMBER_UPDATE, = 14) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_THREAD_MEMBERS_UPDATE, = 15) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_CREATE, = 16) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_UPDATE, = 17) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_DELETE, = 18) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_BAN_ADD, = 19) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_BAN_REMOVE, = 20) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_EMOJIS_UPDATE, = 21) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_STICKERS_UPDATE, = 22) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_INTEGRATIONS_UPDATE, = 23) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_MEMBER_ADD, = 24) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_MEMBER_REMOVE, = 25) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_MEMBER_UPDATE, = 26) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_MEMBERS_CHUNK, = 27) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_ROLE_CREATE, = 28) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_ROLE_UPDATE, = 29) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_GUILD_ROLE_DELETE, = 30) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_INTEGRATION_CREATE, = 31) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_INTEGRATION_UPDATE, = 32) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_INTEGRATION_DELETE, = 33) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_INTERACTION_CREATE, = 34) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_INVITE_CREATE, = 35) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_INVITE_DELETE, = 36) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_CREATE, = 37) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_UPDATE, = 38) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_DELETE, = 39) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_DELETE_BULK, = 40) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_ADD, = 41) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_REMOVE, = 42) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_REMOVE_ALL, = 43) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_REMOVE_EMOJI, = 44) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_PRESENCE_UPDATE, = 45) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_STAGE_INSTANCE_CREATE, = 46) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_STAGE_INSTANCE_DELETE, = 47) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_STAGE_INSTANCE_UPDATE, = 48) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_TYPING_START, = 49) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_USER_UPDATE, = 50) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_VOICE_STATE_UPDATE, = 51) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_VOICE_SERVER_UPDATE, = 52) - ENUMERATOR(DISCORD_GATEWAY_EVENTS_WEBHOOKS_UPDATE, = 53) - ENUMERATOR_LAST(DISCORD_GATEWAY_EVENTS_MAX, ) + ENUMERATOR(DISCORD_EV_NONE, = 0) + ENUMERATOR(DISCORD_EV_READY, = 1) + ENUMERATOR(DISCORD_EV_RESUMED, = 2) + ENUMERATOR(DISCORD_EV_APPLICATION_COMMAND_CREATE, = 3) + ENUMERATOR(DISCORD_EV_APPLICATION_COMMAND_UPDATE, = 4) + ENUMERATOR(DISCORD_EV_APPLICATION_COMMAND_DELETE, = 5) + ENUMERATOR(DISCORD_EV_CHANNEL_CREATE, = 6) + ENUMERATOR(DISCORD_EV_CHANNEL_UPDATE, = 7) + ENUMERATOR(DISCORD_EV_CHANNEL_DELETE, = 8) + ENUMERATOR(DISCORD_EV_CHANNEL_PINS_UPDATE, = 9) + ENUMERATOR(DISCORD_EV_THREAD_CREATE, = 10) + ENUMERATOR(DISCORD_EV_THREAD_UPDATE, = 11) + ENUMERATOR(DISCORD_EV_THREAD_DELETE, = 12) + ENUMERATOR(DISCORD_EV_THREAD_LIST_SYNC, = 13) + ENUMERATOR(DISCORD_EV_THREAD_MEMBER_UPDATE, = 14) + ENUMERATOR(DISCORD_EV_THREAD_MEMBERS_UPDATE, = 15) + ENUMERATOR(DISCORD_EV_GUILD_CREATE, = 16) + ENUMERATOR(DISCORD_EV_GUILD_UPDATE, = 17) + ENUMERATOR(DISCORD_EV_GUILD_DELETE, = 18) + ENUMERATOR(DISCORD_EV_GUILD_BAN_ADD, = 19) + ENUMERATOR(DISCORD_EV_GUILD_BAN_REMOVE, = 20) + ENUMERATOR(DISCORD_EV_GUILD_EMOJIS_UPDATE, = 21) + ENUMERATOR(DISCORD_EV_GUILD_STICKERS_UPDATE, = 22) + ENUMERATOR(DISCORD_EV_GUILD_INTEGRATIONS_UPDATE, = 23) + ENUMERATOR(DISCORD_EV_GUILD_MEMBER_ADD, = 24) + ENUMERATOR(DISCORD_EV_GUILD_MEMBER_REMOVE, = 25) + ENUMERATOR(DISCORD_EV_GUILD_MEMBER_UPDATE, = 26) + ENUMERATOR(DISCORD_EV_GUILD_MEMBERS_CHUNK, = 27) + ENUMERATOR(DISCORD_EV_GUILD_ROLE_CREATE, = 28) + ENUMERATOR(DISCORD_EV_GUILD_ROLE_UPDATE, = 29) + ENUMERATOR(DISCORD_EV_GUILD_ROLE_DELETE, = 30) + ENUMERATOR(DISCORD_EV_INTEGRATION_CREATE, = 31) + ENUMERATOR(DISCORD_EV_INTEGRATION_UPDATE, = 32) + ENUMERATOR(DISCORD_EV_INTEGRATION_DELETE, = 33) + ENUMERATOR(DISCORD_EV_INTERACTION_CREATE, = 34) + ENUMERATOR(DISCORD_EV_INVITE_CREATE, = 35) + ENUMERATOR(DISCORD_EV_INVITE_DELETE, = 36) + ENUMERATOR(DISCORD_EV_MESSAGE_CREATE, = 37) + ENUMERATOR(DISCORD_EV_MESSAGE_UPDATE, = 38) + ENUMERATOR(DISCORD_EV_MESSAGE_DELETE, = 39) + ENUMERATOR(DISCORD_EV_MESSAGE_DELETE_BULK, = 40) + ENUMERATOR(DISCORD_EV_MESSAGE_REACTION_ADD, = 41) + ENUMERATOR(DISCORD_EV_MESSAGE_REACTION_REMOVE, = 42) + ENUMERATOR(DISCORD_EV_MESSAGE_REACTION_REMOVE_ALL, = 43) + ENUMERATOR(DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI, = 44) + ENUMERATOR(DISCORD_EV_PRESENCE_UPDATE, = 45) + ENUMERATOR(DISCORD_EV_STAGE_INSTANCE_CREATE, = 46) + ENUMERATOR(DISCORD_EV_STAGE_INSTANCE_DELETE, = 47) + ENUMERATOR(DISCORD_EV_STAGE_INSTANCE_UPDATE, = 48) + ENUMERATOR(DISCORD_EV_TYPING_START, = 49) + ENUMERATOR(DISCORD_EV_USER_UPDATE, = 50) + ENUMERATOR(DISCORD_EV_VOICE_STATE_UPDATE, = 51) + ENUMERATOR(DISCORD_EV_VOICE_SERVER_UPDATE, = 52) + ENUMERATOR(DISCORD_EV_WEBHOOKS_UPDATE, = 53) + /** amount of enumerators */ + ENUMERATOR_LAST(DISCORD_EV_MAX, ) ENUM_END ENUM(discord_activity_types) diff --git a/include/discord-events.h b/include/discord-events.h index 8f3c2bb31..cdf48f79a 100644 --- a/include/discord-events.h +++ b/include/discord-events.h @@ -105,6 +105,9 @@ typedef void (*discord_ev_channel)(struct discord *client, /** @brief Thread List Sync callback */ typedef void (*discord_ev_thread_list_sync)( struct discord *client, struct discord_thread_list_sync *event); +/** @brief Thread Member Update callback */ +typedef void (*discord_ev_thread_member)(struct discord *client, + struct discord_thread_member *event); /** @brief Thread Members Update callback */ typedef void (*discord_ev_thread_members_update)( struct discord *client, struct discord_thread_members_update *event); @@ -125,6 +128,9 @@ typedef void (*discord_ev_guild)(struct discord *client, /** @brief Guild Emojis Update callback */ typedef void (*discord_ev_guild_emojis_update)( struct discord *client, struct discord_guild_emojis_update *event); +/** @brief Guild Stickers Update callback */ +typedef void (*discord_ev_guild_stickers_update)( + struct discord *client, struct discord_guild_stickers_update *event); /** @brief Guild Integrations Update callback */ typedef void (*discord_ev_guild_integrations_update)( struct discord *client, struct discord_guild_integrations_update *event); @@ -159,6 +165,10 @@ typedef void (*discord_ev_guild_scheduled_event_user_remove)( struct discord *client, struct discord_guild_scheduled_event_user_remove *event); +/** @brief Integration Create callback */ +typedef void (*discord_ev_integration)(struct discord *client, + struct discord_integration *event); + /** @brief Integration Delete callback */ typedef void (*discord_ev_integration_delete)( struct discord *client, struct discord_integration_delete *event); @@ -193,10 +203,22 @@ typedef void (*discord_ev_message_reaction_remove_emoji)( struct discord *client, struct discord_message_reaction_remove_emoji *event); -/** @brief Typing Start Remove callback */ +/** @brief Presence Update callback */ +typedef void (*discord_ev_presence_update)( + struct discord *client, struct discord_presence_update *event); + +/** @brief Stage Instance callback */ +typedef void (*discord_ev_stage_instance)( + struct discord *client, struct discord_stage_instance *event); + +/** @brief Typing Start callback */ typedef void (*discord_ev_typing_start)(struct discord *client, struct discord_typing_start *event); +/** @brief User callback */ +typedef void (*discord_ev_user)(struct discord *client, + struct discord_user *event); + /** @brief Voice State Update callback */ typedef void (*discord_ev_voice_state_update)( struct discord *client, struct discord_voice_state *voice_state); diff --git a/include/discord-internal.h b/include/discord-internal.h index a09c1de3c..e5ae434a8 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -404,84 +404,8 @@ void discord_ratelimiter_build(struct discord_ratelimiter *rl, * @brief Wrapper to the Discord Gateway API * @{ */ -struct discord_gateway_cbs { - /** triggers when connection first establishes */ - discord_ev_ready on_ready; - - /** triggers when a command is created */ - discord_ev_application_command on_application_command_create; - /** triggers when a command is updated */ - discord_ev_application_command on_application_command_update; - /** triggers when a command is deleted */ - discord_ev_application_command on_application_command_delete; - - /** triggers when a channel is created */ - discord_ev_channel on_channel_create; - /** triggers when a channel is updated */ - discord_ev_channel on_channel_update; - /** triggers when a channel is deleted */ - discord_ev_channel on_channel_delete; - /** triggers when a channel pinned messages updates */ - discord_ev_channel_pins_update on_channel_pins_update; - /** triggers when a thread is created */ - discord_ev_channel on_thread_create; - /** triggers when a thread is updated */ - discord_ev_channel on_thread_update; - /** triggers when a thread is deleted */ - discord_ev_channel on_thread_delete; - - /** triggers when guild info is ready, or a guild has joined */ - discord_ev_guild on_guild_create; - /** triggers when a guild's information is updated */ - discord_ev_guild on_guild_update; - /** triggers when removed from guild */ - discord_ev_guild on_guild_delete; - - /** triggers when a ban occurs */ - discord_ev_guild_ban_add on_guild_ban_add; - /** triggers when a ban is removed */ - discord_ev_guild_ban_remove on_guild_ban_remove; - - /** triggers when a guild member joins a guild */ - discord_ev_guild_member on_guild_member_add; - /** triggers when a guild member is removed from a guild */ - discord_ev_guild_member_remove on_guild_member_remove; - /** triggers when a guild member status is updated (ex: receive role) */ - discord_ev_guild_member_update on_guild_member_update; - - /** triggers when a guild role is created */ - discord_ev_guild_role_create on_guild_role_create; - /** triggers when a guild role is updated */ - discord_ev_guild_role_update on_guild_role_update; - /** triggers when a guild role is deleted */ - discord_ev_guild_role_delete on_guild_role_delete; - - /** triggers when a interaction is created */ - discord_ev_interaction on_interaction_create; - - /** triggers when a message is created */ - discord_ev_message on_message_create; - /** trigger when a message is updated */ - discord_ev_message on_message_update; - /** triggers when a message is deleted */ - discord_ev_message_delete on_message_delete; - /** triggers when a bulk of messages is deleted */ - discord_ev_message_delete_bulk on_message_delete_bulk; - /** triggers when a reaction is added to a message */ - discord_ev_message_reaction_add on_message_reaction_add; - /** triggers when a reaction is removed from a message */ - discord_ev_message_reaction_remove on_message_reaction_remove; - /** triggers when all reactions are removed from a message */ - discord_ev_message_reaction_remove_all on_message_reaction_remove_all; - /** triggers when all occurences of a specific reaction is removed from a - * message */ - discord_ev_message_reaction_remove_emoji on_message_reaction_remove_emoji; - - /** triggers when a voice state is updated */ - discord_ev_voice_state_update on_voice_state_update; - /** triggers when a voice server is updated */ - discord_ev_voice_server_update on_voice_server_update; -}; +/** Generic event callback */ +typedef void (*discord_ev)(struct discord *client, void *event); /** @defgroup DiscordInternalGatewaySessionStatus Client's session status * @brief Client's session status @@ -582,29 +506,25 @@ struct discord_gateway { jsmnf_pair *data; } payload; - /** user-commands structure */ + /** the prefix expected for every command */ + struct sized_buffer prefix; + /** user's command/callback pair @see discord_set_on_command() */ struct { - /** the prefix expected for every command */ - struct sized_buffer prefix; - /** user's command/callback pair @see discord_set_on_command() */ - struct { - /** the command string contents */ - char *start; - /** the command string length */ - size_t size; - /** the assigned callback for the command */ - discord_ev_message cb; - } * pool, fallback; - /** amount of command/callback pairs in pool */ - size_t amt; - /** actual size of command/callback pairs in pool */ - size_t cap; - - /** the user's callbacks for Discord events */ - struct discord_gateway_cbs cbs; - /** the event scheduler callback */ - discord_ev_scheduler scheduler; - } cmds; + /** the command string contents */ + char *start; + /** the command string length */ + size_t size; + /** the assigned callback for the command */ + discord_ev_message cb; + } * pool, fallback; + /** amount of command/callback pairs in pool */ + size_t amt; + /** actual size of command/callback pairs in pool */ + size_t cap; + /** the user's callbacks for Discord events */ + discord_ev cbs[DISCORD_EV_MAX]; + /** the event scheduler callback */ + discord_ev_scheduler scheduler; }; /** @@ -673,6 +593,15 @@ void discord_gateway_reconnect(struct discord_gateway *gw, bool resume); */ void discord_gateway_send_presence_update(struct discord_gateway *gw); +/** + * @brief Dispatch user callback matched to event + * + * @param gw the handle initialized with discord_gateway_init() + * @param event the Discord event to be executed + */ +void discord_gateway_dispatch(struct discord_gateway *gw, + enum discord_gateway_events event); + /** @} DiscordInternalGateway */ /** @defgroup DiscordInternalTimer Timer API @@ -796,10 +725,12 @@ void discord_refcounter_cleanup(struct discord_refcounter *rc); * @param data the user arbitrary data to have its reference counter * @param cleanup user-defined function for cleaning `data` resources once its * no longer referenced + * @param should_free whether `data` cleanup should be followed by a free() */ void discord_refcounter_incr(struct discord_refcounter *rc, void *data, - void (*cleanup)(void *data)); + void (*cleanup)(void *data), + bool should_free); /** * @brief Decrement the reference counter for `data` @@ -861,7 +792,7 @@ struct discord { #ifdef CCORD_VOICE struct discord_voice vcs[DISCORD_MAX_VCS]; - struct discord_voice_cbs voice_cbs; + struct discord_voice_evcallbacks voice_cbs; #endif /* CCORD_VOICE */ }; diff --git a/include/discord-voice.h b/include/discord-voice.h index 2a9a664c2..da22ee7af 100644 --- a/include/discord-voice.h +++ b/include/discord-voice.h @@ -62,7 +62,7 @@ typedef void (*discord_ev_voice_codec)(struct discord *client, const char video_codec[]); /* CALLBACKS STRUCTURE */ -struct discord_voice_cbs { +struct discord_voice_evcallbacks { /** triggers on every event loop iteration */ discord_ev_voice_idle on_idle; /** triggers when a user start speaking */ @@ -176,7 +176,7 @@ struct discord_voice { uintmax_t start_time; } udp_service; - struct discord_voice_cbs *p_voice_cbs; + struct discord_voice_evcallbacks *p_voice_cbs; /** * @brief Interval to divide the received packets @@ -285,7 +285,7 @@ void discord_voice_reconnect(struct discord_voice *vc, bool resume); * @param callbacks the voice callbacks that will be executed */ void discord_set_voice_cbs(struct discord *client, - struct discord_voice_cbs *callbacks); + struct discord_voice_evcallbacks *callbacks); /** * @brief Check if a Discord Voice connection is alive diff --git a/src/channel.c b/src/channel.c index 9f4bdaa0a..bd3eced17 100644 --- a/src/channel.c +++ b/src/channel.c @@ -75,7 +75,8 @@ discord_get_channel_at_pos(struct discord *client, /* TODO: the following should be replaced by @ref DiscordInternalTimer * implementation */ if (ret->data) { - discord_refcounter_incr(client->refcounter, ret->data, ret->cleanup); + discord_refcounter_incr(client->refcounter, ret->data, ret->cleanup, + false); } /* TODO: fetch channel via caching, and return if results are non-existent diff --git a/src/discord-adapter.c b/src/discord-adapter.c index 5bcb3c3df..a85dad801 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -508,7 +508,7 @@ _discord_adapter_run_async(struct discord_adapter *adapter, if (req->ret.data) discord_refcounter_incr(CLIENT(adapter, adapter)->refcounter, - req->ret.data, req->ret.cleanup); + req->ret.data, req->ret.cleanup, false); io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, adapter->mhandle); diff --git a/src/discord-client.c b/src/discord-client.c index 7e294d7e7..c417de6fb 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -235,10 +235,10 @@ discord_set_prefix(struct discord *client, char *prefix) { if (!prefix || !*prefix) return; - if (client->gw.cmds.prefix.start) free(client->gw.cmds.prefix.start); + if (client->gw.prefix.start) free(client->gw.prefix.start); - client->gw.cmds.prefix.size = - cog_strndup(prefix, strlen(prefix), &client->gw.cmds.prefix.start); + client->gw.prefix.size = + cog_strndup(prefix, strlen(prefix), &client->gw.prefix.start); } const struct discord_user * @@ -256,41 +256,40 @@ discord_set_on_command(struct discord *client, size_t i; /* fallback callback if prefix is detected, but command isn't specified */ - if (client->gw.cmds.prefix.size && !cmd_len) { - client->gw.cmds.fallback.cb = callback; + if (client->gw.prefix.size && !cmd_len) { + client->gw.fallback.cb = callback; return; } /* if command is already set then modify it */ - for (i = 0; i < client->gw.cmds.amt; i++) { - if (cmd_len == client->gw.cmds.pool[i].size - && 0 == strcmp(command, client->gw.cmds.pool[i].start)) + for (i = 0; i < client->gw.amt; i++) { + if (cmd_len == client->gw.pool[i].size + && 0 == strcmp(command, client->gw.pool[i].start)) { goto _modify; } } - if (i == client->gw.cmds.cap) { + if (i == client->gw.cap) { size_t cap = 8; void *tmp; while (cap <= i) cap <<= 1; - tmp = - realloc(client->gw.cmds.pool, cap * sizeof(*client->gw.cmds.pool)); + tmp = realloc(client->gw.pool, cap * sizeof(*client->gw.pool)); if (!tmp) return; - client->gw.cmds.pool = tmp; - client->gw.cmds.cap = cap; + client->gw.pool = tmp; + client->gw.cap = cap; } - ++client->gw.cmds.amt; - client->gw.cmds.pool[i].size = - cog_strndup(command, cmd_len, &client->gw.cmds.pool[i].start); + ++client->gw.amt; + client->gw.pool[i].size = + cog_strndup(command, cmd_len, &client->gw.pool[i].start); _modify: - client->gw.cmds.pool[i].cb = callback; + client->gw.pool[i].cb = callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); @@ -319,7 +318,7 @@ void discord_set_event_scheduler(struct discord *client, discord_ev_scheduler callback) { - client->gw.cmds.scheduler = callback; + client->gw.scheduler = callback; } void @@ -338,14 +337,14 @@ discord_reconnect(struct discord *client, bool resume) void discord_set_on_ready(struct discord *client, discord_ev_ready callback) { - client->gw.cmds.cbs.on_ready = callback; + client->gw.cbs[DISCORD_EV_READY] = (discord_ev)callback; } void discord_set_on_guild_role_create(struct discord *client, discord_ev_guild_role_create callback) { - client->gw.cmds.cbs.on_guild_role_create = callback; + client->gw.cbs[DISCORD_EV_GUILD_ROLE_CREATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -353,7 +352,7 @@ void discord_set_on_guild_role_update(struct discord *client, discord_ev_guild_role_update callback) { - client->gw.cmds.cbs.on_guild_role_update = callback; + client->gw.cbs[DISCORD_EV_GUILD_ROLE_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -361,7 +360,7 @@ void discord_set_on_guild_role_delete(struct discord *client, discord_ev_guild_role_delete callback) { - client->gw.cmds.cbs.on_guild_role_delete = callback; + client->gw.cbs[DISCORD_EV_GUILD_ROLE_DELETE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -369,7 +368,7 @@ void discord_set_on_guild_member_add(struct discord *client, discord_ev_guild_member callback) { - client->gw.cmds.cbs.on_guild_member_add = callback; + client->gw.cbs[DISCORD_EV_GUILD_MEMBER_ADD] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MEMBERS); } @@ -377,7 +376,7 @@ void discord_set_on_guild_member_update(struct discord *client, discord_ev_guild_member_update callback) { - client->gw.cmds.cbs.on_guild_member_update = callback; + client->gw.cbs[DISCORD_EV_GUILD_MEMBER_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MEMBERS); } @@ -385,7 +384,7 @@ void discord_set_on_guild_member_remove(struct discord *client, discord_ev_guild_member_remove callback) { - client->gw.cmds.cbs.on_guild_member_remove = callback; + client->gw.cbs[DISCORD_EV_GUILD_MEMBER_REMOVE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MEMBERS); } @@ -393,7 +392,7 @@ void discord_set_on_guild_ban_add(struct discord *client, discord_ev_guild_ban_add callback) { - client->gw.cmds.cbs.on_guild_ban_add = callback; + client->gw.cbs[DISCORD_EV_GUILD_BAN_ADD] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_BANS); } @@ -401,7 +400,7 @@ void discord_set_on_guild_ban_remove(struct discord *client, discord_ev_guild_ban_remove callback) { - client->gw.cmds.cbs.on_guild_ban_remove = callback; + client->gw.cbs[DISCORD_EV_GUILD_BAN_REMOVE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_BANS); } @@ -409,28 +408,31 @@ void discord_set_on_application_command_create( struct discord *client, discord_ev_application_command callback) { - client->gw.cmds.cbs.on_application_command_create = callback; + client->gw.cbs[DISCORD_EV_APPLICATION_COMMAND_CREATE] = + (discord_ev)callback; } void discord_set_on_application_command_update( struct discord *client, discord_ev_application_command callback) { - client->gw.cmds.cbs.on_application_command_update = callback; + client->gw.cbs[DISCORD_EV_APPLICATION_COMMAND_UPDATE] = + (discord_ev)callback; } void discord_set_on_application_command_delete( struct discord *client, discord_ev_application_command callback) { - client->gw.cmds.cbs.on_application_command_delete = callback; + client->gw.cbs[DISCORD_EV_APPLICATION_COMMAND_DELETE] = + (discord_ev)callback; } void discord_set_on_channel_create(struct discord *client, discord_ev_channel callback) { - client->gw.cmds.cbs.on_channel_create = callback; + client->gw.cbs[DISCORD_EV_CHANNEL_CREATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -438,7 +440,7 @@ void discord_set_on_channel_update(struct discord *client, discord_ev_channel callback) { - client->gw.cmds.cbs.on_channel_update = callback; + client->gw.cbs[DISCORD_EV_CHANNEL_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -446,7 +448,7 @@ void discord_set_on_channel_delete(struct discord *client, discord_ev_channel callback) { - client->gw.cmds.cbs.on_channel_delete = callback; + client->gw.cbs[DISCORD_EV_CHANNEL_DELETE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -454,7 +456,7 @@ void discord_set_on_channel_pins_update(struct discord *client, discord_ev_channel_pins_update callback) { - client->gw.cmds.cbs.on_channel_pins_update = callback; + client->gw.cbs[DISCORD_EV_CHANNEL_PINS_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -462,7 +464,7 @@ void discord_set_on_thread_create(struct discord *client, discord_ev_channel callback) { - client->gw.cmds.cbs.on_thread_create = callback; + client->gw.cbs[DISCORD_EV_THREAD_CREATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -470,7 +472,7 @@ void discord_set_on_thread_update(struct discord *client, discord_ev_channel callback) { - client->gw.cmds.cbs.on_thread_update = callback; + client->gw.cbs[DISCORD_EV_THREAD_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -478,28 +480,28 @@ void discord_set_on_thread_delete(struct discord *client, discord_ev_channel callback) { - client->gw.cmds.cbs.on_thread_delete = callback; + client->gw.cbs[DISCORD_EV_THREAD_DELETE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } void discord_set_on_guild_create(struct discord *client, discord_ev_guild callback) { - client->gw.cmds.cbs.on_guild_create = callback; + client->gw.cbs[DISCORD_EV_GUILD_CREATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } void discord_set_on_guild_update(struct discord *client, discord_ev_guild callback) { - client->gw.cmds.cbs.on_guild_update = callback; + client->gw.cbs[DISCORD_EV_GUILD_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } void discord_set_on_guild_delete(struct discord *client, discord_ev_guild callback) { - client->gw.cmds.cbs.on_guild_delete = callback; + client->gw.cbs[DISCORD_EV_GUILD_DELETE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILDS); } @@ -507,7 +509,7 @@ void discord_set_on_message_create(struct discord *client, discord_ev_message callback) { - client->gw.cmds.cbs.on_message_create = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_CREATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); } @@ -516,7 +518,7 @@ void discord_set_on_message_update(struct discord *client, discord_ev_message callback) { - client->gw.cmds.cbs.on_message_update = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); } @@ -525,7 +527,7 @@ void discord_set_on_message_delete(struct discord *client, discord_ev_message_delete callback) { - client->gw.cmds.cbs.on_message_delete = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_DELETE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); } @@ -534,7 +536,7 @@ void discord_set_on_message_delete_bulk(struct discord *client, discord_ev_message_delete_bulk callback) { - client->gw.cmds.cbs.on_message_delete_bulk = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_DELETE_BULK] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); } @@ -543,7 +545,7 @@ void discord_set_on_message_reaction_add(struct discord *client, discord_ev_message_reaction_add callback) { - client->gw.cmds.cbs.on_message_reaction_add = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_REACTION_ADD] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS | DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS); @@ -553,7 +555,7 @@ void discord_set_on_message_reaction_remove( struct discord *client, discord_ev_message_reaction_remove callback) { - client->gw.cmds.cbs.on_message_reaction_remove = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_REACTION_REMOVE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS | DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS); @@ -563,7 +565,8 @@ void discord_set_on_message_reaction_remove_all( struct discord *client, discord_ev_message_reaction_remove_all callback) { - client->gw.cmds.cbs.on_message_reaction_remove_all = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_REACTION_REMOVE_ALL] = + (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS | DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS); @@ -573,7 +576,8 @@ void discord_set_on_message_reaction_remove_emoji( struct discord *client, discord_ev_message_reaction_remove_emoji callback) { - client->gw.cmds.cbs.on_message_reaction_remove_emoji = callback; + client->gw.cbs[DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI] = + (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGE_REACTIONS | DISCORD_GATEWAY_DIRECT_MESSAGE_REACTIONS); @@ -583,14 +587,14 @@ void discord_set_on_interaction_create(struct discord *client, discord_ev_interaction callback) { - client->gw.cmds.cbs.on_interaction_create = callback; + client->gw.cbs[DISCORD_EV_INTERACTION_CREATE] = (discord_ev)callback; } void discord_set_on_voice_state_update(struct discord *client, discord_ev_voice_state_update callback) { - client->gw.cmds.cbs.on_voice_state_update = callback; + client->gw.cbs[DISCORD_EV_VOICE_STATE_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_VOICE_STATES); } @@ -598,7 +602,7 @@ void discord_set_on_voice_server_update(struct discord *client, discord_ev_voice_server_update callback) { - client->gw.cmds.cbs.on_voice_server_update = callback; + client->gw.cbs[DISCORD_EV_VOICE_SERVER_UPDATE] = (discord_ev)callback; discord_add_intents(client, DISCORD_GATEWAY_GUILD_VOICE_STATES); } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 3995fbec5..8fa5fb907 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -1,15 +1,11 @@ #include #include #include -#include /* isspace() */ #include "discord.h" #include "discord-internal.h" #include "osname.h" -/* shorten event callback for maintainability purposes */ -#define ON(event, ...) gw->cmds.cbs.on_##event(CLIENT(gw, gw), __VA_ARGS__) - /* return enumerator as string in case of a match */ #define CASE_RETURN_STR(code) \ case code: \ @@ -19,39 +15,35 @@ * @brief Context in case event is scheduled to be triggered * from Concord's worker threads */ -struct _discord_event_context { +struct _discord_gateway_context { /** the discord gateway client */ struct discord_gateway *gw; /** the event unique id value */ enum discord_gateway_events event; - /** the event callback */ - void (*on_event)(struct discord_gateway *gw); }; -static struct _discord_event_context * -_discord_event_context_init(const struct discord_gateway *gw, - enum discord_gateway_events event, - void (*on_event)(struct discord_gateway *gw)) +static struct _discord_gateway_context * +_discord_gateway_context_init(const struct discord_gateway *gw, + enum discord_gateway_events event) { - struct _discord_event_context *cxt = malloc(sizeof *cxt); + struct _discord_gateway_context *cxt = malloc(sizeof *cxt); struct discord *clone = discord_clone(CLIENT(gw, gw)); cxt->gw = &clone->gw; cxt->event = event; - cxt->on_event = on_event; return cxt; } static void -_discord_event_context_cleanup(struct _discord_event_context *cxt) +_discord_gateway_context_cleanup(struct _discord_gateway_context *cxt) { discord_cleanup(CLIENT(cxt->gw, gw)); free(cxt); } static const char * -opcode_print(enum discord_gateway_opcodes opcode) +_discord_gateway_opcode_print(enum discord_gateway_opcodes opcode) { switch (opcode) { CASE_RETURN_STR(DISCORD_GATEWAY_DISPATCH); @@ -71,7 +63,7 @@ opcode_print(enum discord_gateway_opcodes opcode) } static const char * -close_opcode_print(enum discord_gateway_close_opcodes opcode) +_discord_gateway_close_opcode_print(enum discord_gateway_close_opcodes opcode) { switch (opcode) { CASE_RETURN_STR(DISCORD_GATEWAY_CLOSE_REASON_UNKNOWN_ERROR); @@ -101,45 +93,8 @@ close_opcode_print(enum discord_gateway_close_opcodes opcode) } } -void -discord_gateway_send_presence_update(struct discord_gateway *gw) -{ - struct ws_info info = { 0 }; - char buf[2048]; - jsonb b; - - if (!gw->session->is_ready) return; - - jsonb_init(&b); - jsonb_object(&b, buf, sizeof(buf)); - { - jsonb_key(&b, buf, sizeof(buf), "op", 2); - jsonb_number(&b, buf, sizeof(buf), 3); - jsonb_key(&b, buf, sizeof(buf), "d", 1); - discord_presence_update_to_jsonb(&b, buf, sizeof(buf), - gw->id.presence); - jsonb_object_pop(&b, buf, sizeof(buf)); - } - - if (ws_send_text(gw->ws, &info, buf, b.pos)) { - io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); - logconf_info( - &gw->conf, - ANSICOLOR("SEND", ANSI_FG_BRIGHT_GREEN) " PRESENCE UPDATE (%d " - "bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - } - else { - logconf_error( - &gw->conf, - ANSICOLOR("FAIL SEND", ANSI_FG_RED) " PRESENCE UPDATE (%d " - "bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - } -} - static void -send_resume(struct discord_gateway *gw) +_discord_gateway_send_resume(struct discord_gateway *gw) { struct ws_info info = { 0 }; char buf[1024]; @@ -186,7 +141,7 @@ send_resume(struct discord_gateway *gw) } static void -send_identify(struct discord_gateway *gw) +_discord_gateway_send_identify(struct discord_gateway *gw) { struct ws_info info = { 0 }; char buf[1024]; @@ -237,7 +192,7 @@ send_identify(struct discord_gateway *gw) /* send heartbeat pulse to websockets server in order * to maintain connection alive */ static void -send_heartbeat(struct discord_gateway *gw) +_discord_gateway_send_heartbeat(struct discord_gateway *gw) { struct ws_info info = { 0 }; char buf[64]; @@ -285,16 +240,16 @@ on_hello(struct discord_gateway *gw) gw->timer->interval = strtoull(gw->json + f->v.pos, NULL, 10); if (gw->session->status & DISCORD_SESSION_RESUMABLE) - send_resume(gw); + _discord_gateway_send_resume(gw); else - send_identify(gw); + _discord_gateway_send_identify(gw); } static enum discord_gateway_events -get_dispatch_event(char name[]) +_discord_gateway_event_eval(char name[]) { #define RETURN_IF_MATCH(event, str) \ - if (!strcmp(#event, str)) return DISCORD_GATEWAY_EVENTS_##event + if (!strcmp(#event, str)) return DISCORD_EV_##event RETURN_IF_MATCH(READY, name); RETURN_IF_MATCH(RESUMED, name); @@ -349,387 +304,32 @@ get_dispatch_event(char name[]) RETURN_IF_MATCH(VOICE_STATE_UPDATE, name); RETURN_IF_MATCH(VOICE_SERVER_UPDATE, name); RETURN_IF_MATCH(WEBHOOKS_UPDATE, name); - return DISCORD_GATEWAY_EVENTS_NONE; + return DISCORD_EV_NONE; #undef RETURN_IF_MATCH } static void -on_guild_create(struct discord_gateway *gw) -{ - struct discord_guild event = { 0 }; - discord_guild_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_create, &event); - discord_guild_cleanup(&event); -} - -static void -on_guild_update(struct discord_gateway *gw) -{ - struct discord_guild event = { 0 }; - discord_guild_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_update, &event); - discord_guild_cleanup(&event); -} - -static void -on_guild_delete(struct discord_gateway *gw) -{ - struct discord_guild event = { 0 }; - discord_guild_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_delete, &event); - discord_guild_cleanup(&event); -} - -static void -on_guild_role_create(struct discord_gateway *gw) -{ - struct discord_guild_role_create event = { 0 }; - discord_guild_role_create_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_role_create, &event); - discord_guild_role_create_cleanup(&event); -} - -static void -on_guild_role_update(struct discord_gateway *gw) -{ - struct discord_guild_role_update event = { 0 }; - discord_guild_role_update_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_role_update, &event); - discord_guild_role_update_cleanup(&event); -} - -static void -on_guild_role_delete(struct discord_gateway *gw) -{ - struct discord_guild_role_delete event = { 0 }; - discord_guild_role_delete_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_role_delete, &event); - discord_guild_role_delete_cleanup(&event); -} - -static void -on_guild_member_add(struct discord_gateway *gw) -{ - struct discord_guild_member event = { 0 }; - discord_guild_member_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_member_add, &event); - discord_guild_member_cleanup(&event); -} - -static void -on_guild_member_update(struct discord_gateway *gw) -{ - struct discord_guild_member_update event = { 0 }; - discord_guild_member_update_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_member_update, &event); - discord_guild_member_update_cleanup(&event); -} - -static void -on_guild_member_remove(struct discord_gateway *gw) +_discord_gateway_dispatch_thread(void *p_cxt) { - struct discord_guild_member_remove event = { 0 }; - discord_guild_member_remove_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_member_remove, &event); - discord_guild_member_remove_cleanup(&event); -} - -static void -on_guild_ban_add(struct discord_gateway *gw) -{ - struct discord_guild_ban_add event = { 0 }; - discord_guild_ban_add_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_ban_add, &event); - discord_guild_ban_add_cleanup(&event); -} - -static void -on_guild_ban_remove(struct discord_gateway *gw) -{ - struct discord_guild_ban_remove event = { 0 }; - discord_guild_ban_remove_from_jsmnf(gw->payload.data, gw->json, &event); - ON(guild_ban_remove, &event); - discord_guild_ban_remove_cleanup(&event); -} - -static void -on_application_command_create(struct discord_gateway *gw) -{ - struct discord_application_command event = { 0 }; - discord_application_command_from_jsmnf(gw->payload.data, gw->json, &event); - ON(application_command_create, &event); - discord_application_command_cleanup(&event); -} - -static void -on_application_command_update(struct discord_gateway *gw) -{ - struct discord_application_command event = { 0 }; - discord_application_command_from_jsmnf(gw->payload.data, gw->json, &event); - ON(application_command_update, &event); - discord_application_command_cleanup(&event); -} - -static void -on_application_command_delete(struct discord_gateway *gw) -{ - struct discord_application_command event = { 0 }; - discord_application_command_from_jsmnf(gw->payload.data, gw->json, &event); - ON(application_command_delete, &event); - discord_application_command_cleanup(&event); -} - -static void -on_channel_create(struct discord_gateway *gw) -{ - struct discord_channel event = { 0 }; - discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); - ON(channel_create, &event); - discord_channel_cleanup(&event); -} - -static void -on_channel_update(struct discord_gateway *gw) -{ - struct discord_channel event = { 0 }; - discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); - ON(channel_update, &event); - discord_channel_cleanup(&event); -} - -static void -on_channel_delete(struct discord_gateway *gw) -{ - struct discord_channel event = { 0 }; - discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); - ON(channel_delete, &event); - discord_channel_cleanup(&event); -} - -static void -on_channel_pins_update(struct discord_gateway *gw) -{ - struct discord_channel_pins_update event = { 0 }; - discord_channel_pins_update_from_jsmnf(gw->payload.data, gw->json, &event); - ON(channel_pins_update, &event); - discord_channel_pins_update_cleanup(&event); -} - -static void -on_thread_create(struct discord_gateway *gw) -{ - struct discord_channel event = { 0 }; - discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); - ON(thread_create, &event); - discord_channel_cleanup(&event); -} - -static void -on_thread_update(struct discord_gateway *gw) -{ - struct discord_channel event = { 0 }; - discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); - ON(thread_update, &event); - discord_channel_cleanup(&event); -} - -static void -on_thread_delete(struct discord_gateway *gw) -{ - struct discord_channel event = { 0 }; - discord_channel_from_jsmnf(gw->payload.data, gw->json, &event); - ON(thread_delete, &event); - discord_channel_cleanup(&event); -} - -static void -on_interaction_create(struct discord_gateway *gw) -{ - struct discord_interaction event = { 0 }; - discord_interaction_from_jsmnf(gw->payload.data, gw->json, &event); - ON(interaction_create, &event); - discord_interaction_cleanup(&event); -} - -static void -on_message_create(struct discord_gateway *gw) -{ - struct discord_message event = { 0 }; - discord_message_from_jsmnf(gw->payload.data, gw->json, &event); - - if (gw->cmds.pool - && !strncmp(gw->cmds.prefix.start, event.content, - gw->cmds.prefix.size)) - { - char *cmd_start = event.content + gw->cmds.prefix.size; - size_t cmd_len = strcspn(cmd_start, " \n\t\r"); - discord_ev_message cmd_cb = NULL; - - char *tmp = event.content; /* hold original ptr */ - size_t i; - - /* match command to its callback */ - for (i = 0; i < gw->cmds.amt; ++i) { - if (cmd_len == gw->cmds.pool[i].size - && 0 == strncmp(gw->cmds.pool[i].start, cmd_start, cmd_len)) - { - - cmd_cb = gw->cmds.pool[i].cb; - break; - } - } - - /* couldn't match command to callback, get fallback if available */ - if (!cmd_cb && gw->cmds.prefix.size) { - cmd_len = 0; /* no command specified */ - cmd_cb = gw->cmds.fallback.cb ? gw->cmds.fallback.cb - : gw->cmds.cbs.on_message_create; - } - - if (cmd_cb) { - /* skip blank characters after command */ - if (event.content) { - event.content = cmd_start + cmd_len; - while (*event.content && isspace((int)event.content[0])) - ++event.content; - } - - cmd_cb(CLIENT(gw, gw), &event); - } - - event.content = tmp; /* retrieve original ptr */ - } - else if (gw->cmds.cbs.on_message_create) { - ON(message_create, &event); - } - discord_message_cleanup(&event); -} - -static void -on_message_update(struct discord_gateway *gw) -{ - struct discord_message event = { 0 }; - discord_message_from_jsmnf(gw->payload.data, gw->json, &event); - ON(message_update, &event); - discord_message_cleanup(&event); -} - -static void -on_message_delete(struct discord_gateway *gw) -{ - struct discord_message_delete event = { 0 }; - discord_message_delete_from_jsmnf(gw->payload.data, gw->json, &event); - ON(message_delete, &event); - discord_message_delete_cleanup(&event); -} - -static void -on_message_delete_bulk(struct discord_gateway *gw) -{ - struct discord_message_delete_bulk event = { 0 }; - discord_message_delete_bulk_from_jsmnf(gw->payload.data, gw->json, &event); - ON(message_delete_bulk, &event); - discord_message_delete_bulk_cleanup(&event); -} - -static void -on_message_reaction_add(struct discord_gateway *gw) -{ - struct discord_message_reaction_add event = { 0 }; - discord_message_reaction_add_from_jsmnf(gw->payload.data, gw->json, - &event); - ON(message_reaction_add, &event); - discord_message_reaction_add_cleanup(&event); -} - -static void -on_message_reaction_remove(struct discord_gateway *gw) -{ - struct discord_message_reaction_remove event = { 0 }; - discord_message_reaction_remove_from_jsmnf(gw->payload.data, gw->json, - &event); - ON(message_reaction_remove, &event); - discord_message_reaction_remove_cleanup(&event); -} - -static void -on_message_reaction_remove_all(struct discord_gateway *gw) -{ - struct discord_message_reaction_remove_all event = { 0 }; - discord_message_reaction_remove_all_from_jsmnf(gw->payload.data, gw->json, - &event); - ON(message_reaction_remove_all, &event); - discord_message_reaction_remove_all_cleanup(&event); -} - -static void -on_message_reaction_remove_emoji(struct discord_gateway *gw) -{ - struct discord_message_reaction_remove_emoji event = { 0 }; - discord_message_reaction_remove_emoji_from_jsmnf(gw->payload.data, - gw->json, &event); - ON(message_reaction_remove_emoji, &event); - discord_message_reaction_remove_emoji_cleanup(&event); -} - -static void -on_voice_state_update(struct discord_gateway *gw) -{ - struct discord_voice_state event = { 0 }; - discord_voice_state_from_jsmnf(gw->payload.data, gw->json, &event); -#ifdef CCORD_VOICE - if (event.user_id == CLIENT(gw, gw)->self.id) - _discord_on_voice_state_update(CLIENT(gw, gw), &event); -#endif - if (gw->cmds.cbs.on_voice_state_update) ON(voice_state_update, &event); - discord_voice_state_cleanup(&event); -} - -static void -on_voice_server_update(struct discord_gateway *gw) -{ - struct discord_voice_server_update event = { 0 }; - discord_voice_server_update_from_jsmnf(gw->payload.data, gw->json, &event); -#ifdef CCORD_VOICE - _discord_on_voice_server_update(CLIENT(gw, gw), &event); -#endif - if (gw->cmds.cbs.on_voice_server_update) ON(voice_server_update, &event); - discord_voice_server_update_cleanup(&event); -} - -static void -on_ready(struct discord_gateway *gw) -{ - struct discord_ready event = { 0 }; - discord_ready_from_jsmnf(gw->payload.data, gw->json, &event); - ON(ready, &event); - discord_ready_cleanup(&event); -} - -static void -dispatch_run(void *p_cxt) -{ - struct _discord_event_context *cxt = p_cxt; + struct _discord_gateway_context *cxt = p_cxt; logconf_info(&cxt->gw->conf, "Thread " ANSICOLOR("starts", ANSI_FG_RED) " to serve %s", cxt->gw->payload.name); - cxt->on_event(cxt->gw); + discord_gateway_dispatch(cxt->gw, cxt->event); logconf_info(&cxt->gw->conf, "Thread " ANSICOLOR("exits", ANSI_FG_RED) " from serving %s", cxt->gw->payload.name); - _discord_event_context_cleanup(cxt); + _discord_gateway_context_cleanup(cxt); } static void on_dispatch(struct discord_gateway *gw) { - /* event-callback selector */ - void (*on_event)(struct discord_gateway *) = NULL; /* get dispatch event opcode */ enum discord_gateway_events event; enum discord_event_scheduler mode; @@ -748,8 +348,8 @@ on_dispatch(struct discord_gateway *gw) } #endif - switch (event = get_dispatch_event(gw->payload.name)) { - case DISCORD_GATEWAY_EVENTS_READY: { + switch (event = _discord_gateway_event_eval(gw->payload.name)) { + case DISCORD_EV_READY: { jsmnf_pair *f; logconf_info(&gw->conf, "Succesfully started a Discord session!"); @@ -762,183 +362,15 @@ on_dispatch(struct discord_gateway *gw) gw->session->is_ready = true; gw->session->retry.attempt = 0; - if (gw->cmds.cbs.on_ready) on_event = &on_ready; - - send_heartbeat(gw); + _discord_gateway_send_heartbeat(gw); } break; - case DISCORD_GATEWAY_EVENTS_RESUMED: + case DISCORD_EV_RESUMED: logconf_info(&gw->conf, "Succesfully resumed a Discord session!"); gw->session->is_ready = true; gw->session->retry.attempt = 0; - send_heartbeat(gw); - - break; - case DISCORD_GATEWAY_EVENTS_APPLICATION_COMMAND_CREATE: - if (gw->cmds.cbs.on_application_command_create) - on_event = on_application_command_create; - break; - case DISCORD_GATEWAY_EVENTS_APPLICATION_COMMAND_UPDATE: - if (gw->cmds.cbs.on_application_command_update) - on_event = on_application_command_update; - break; - case DISCORD_GATEWAY_EVENTS_APPLICATION_COMMAND_DELETE: - if (gw->cmds.cbs.on_application_command_delete) - on_event = on_application_command_delete; - break; - case DISCORD_GATEWAY_EVENTS_CHANNEL_CREATE: - if (gw->cmds.cbs.on_channel_create) on_event = on_channel_create; - break; - case DISCORD_GATEWAY_EVENTS_CHANNEL_UPDATE: - if (gw->cmds.cbs.on_channel_update) on_event = on_channel_update; - break; - case DISCORD_GATEWAY_EVENTS_CHANNEL_DELETE: - if (gw->cmds.cbs.on_channel_delete) on_event = on_channel_delete; - break; - case DISCORD_GATEWAY_EVENTS_CHANNEL_PINS_UPDATE: - if (gw->cmds.cbs.on_channel_pins_update) - on_event = on_channel_pins_update; - break; - case DISCORD_GATEWAY_EVENTS_THREAD_CREATE: - if (gw->cmds.cbs.on_thread_create) on_event = on_thread_create; - break; - case DISCORD_GATEWAY_EVENTS_THREAD_UPDATE: - if (gw->cmds.cbs.on_thread_update) on_event = on_thread_update; - break; - case DISCORD_GATEWAY_EVENTS_THREAD_DELETE: - if (gw->cmds.cbs.on_thread_delete) on_event = on_thread_delete; - break; - case DISCORD_GATEWAY_EVENTS_THREAD_LIST_SYNC: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_THREAD_MEMBER_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_THREAD_MEMBERS_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_GUILD_CREATE: - if (gw->cmds.cbs.on_guild_create) on_event = on_guild_create; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_UPDATE: - if (gw->cmds.cbs.on_guild_update) on_event = on_guild_update; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_DELETE: - if (gw->cmds.cbs.on_guild_delete) on_event = on_guild_delete; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_BAN_ADD: - if (gw->cmds.cbs.on_guild_ban_add) on_event = on_guild_ban_add; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_BAN_REMOVE: - if (gw->cmds.cbs.on_guild_ban_remove) on_event = on_guild_ban_remove; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_EMOJIS_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_GUILD_STICKERS_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_GUILD_INTEGRATIONS_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_GUILD_MEMBER_ADD: - if (gw->cmds.cbs.on_guild_member_add) on_event = on_guild_member_add; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_MEMBER_UPDATE: - if (gw->cmds.cbs.on_guild_member_update) - on_event = on_guild_member_update; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_MEMBER_REMOVE: - if (gw->cmds.cbs.on_guild_member_remove) - on_event = on_guild_member_remove; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_ROLE_CREATE: - if (gw->cmds.cbs.on_guild_role_create) on_event = on_guild_role_create; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_ROLE_UPDATE: - if (gw->cmds.cbs.on_guild_role_update) on_event = on_guild_role_update; - break; - case DISCORD_GATEWAY_EVENTS_GUILD_ROLE_DELETE: - if (gw->cmds.cbs.on_guild_role_delete) on_event = on_guild_role_delete; - break; - case DISCORD_GATEWAY_EVENTS_INTEGRATION_CREATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_INTEGRATION_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_INTEGRATION_DELETE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_INTERACTION_CREATE: - if (gw->cmds.cbs.on_interaction_create) - on_event = on_interaction_create; - break; - case DISCORD_GATEWAY_EVENTS_INVITE_CREATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_INVITE_DELETE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_CREATE: - if (gw->cmds.pool || gw->cmds.cbs.on_message_create) - on_event = &on_message_create; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_UPDATE: - if (gw->cmds.cbs.on_message_update) on_event = on_message_update; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_DELETE: - if (gw->cmds.cbs.on_message_delete) on_event = on_message_delete; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_DELETE_BULK: - if (gw->cmds.cbs.on_message_delete_bulk) - on_event = on_message_delete_bulk; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_ADD: - if (gw->cmds.cbs.on_message_reaction_add) - on_event = on_message_reaction_add; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_REMOVE: - if (gw->cmds.cbs.on_message_reaction_remove) - on_event = on_message_reaction_remove; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_REMOVE_ALL: - if (gw->cmds.cbs.on_message_reaction_remove_all) - on_event = on_message_reaction_remove_all; - break; - case DISCORD_GATEWAY_EVENTS_MESSAGE_REACTION_REMOVE_EMOJI: - if (gw->cmds.cbs.on_message_reaction_remove_emoji) - on_event = on_message_reaction_remove_emoji; - break; - case DISCORD_GATEWAY_EVENTS_PRESENCE_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_STAGE_INSTANCE_CREATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_STAGE_INSTANCE_DELETE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_STAGE_INSTANCE_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_TYPING_START: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_USER_UPDATE: - /** @todo implement */ - break; - case DISCORD_GATEWAY_EVENTS_VOICE_STATE_UPDATE: - if (gw->cmds.cbs.on_voice_state_update) - on_event = on_voice_state_update; - break; - case DISCORD_GATEWAY_EVENTS_VOICE_SERVER_UPDATE: - if (gw->cmds.cbs.on_voice_server_update) - on_event = on_voice_server_update; - break; - case DISCORD_GATEWAY_EVENTS_WEBHOOKS_UPDATE: - /** @todo implement */ + _discord_gateway_send_heartbeat(gw); break; default: logconf_warn( @@ -947,27 +379,25 @@ on_dispatch(struct discord_gateway *gw) break; } - mode = - gw->cmds.scheduler(CLIENT(gw, gw), gw->json + gw->payload.data->v.pos, - gw->payload.data->v.len, event); - if (!on_event) return; + mode = gw->scheduler(CLIENT(gw, gw), gw->json + gw->payload.data->v.pos, + gw->payload.data->v.len, event); /* user subscribed to event */ switch (mode) { case DISCORD_EVENT_IGNORE: break; case DISCORD_EVENT_MAIN_THREAD: - on_event(gw); + discord_gateway_dispatch(gw, event); break; case DISCORD_EVENT_WORKER_THREAD: { - struct _discord_event_context *cxt = - _discord_event_context_init(gw, event, on_event); - int ret = work_run(&dispatch_run, cxt); + struct _discord_gateway_context *cxt = + _discord_gateway_context_init(gw, event); + int ret = work_run(&_discord_gateway_dispatch_thread, cxt); + if (ret != 0) { log_error("Couldn't execute worker-thread (code %d)", ret); - _discord_event_context_cleanup(cxt); + _discord_gateway_context_cleanup(cxt); } - break; } break; default: ERR("Unknown event handling mode (code: %d)", mode); @@ -1053,7 +483,8 @@ on_close_cb(void *p_gw, logconf_warn( &gw->conf, ANSICOLOR("CLOSE %s", ANSI_FG_RED) " (code: %4d, %zu bytes): '%.*s'", - close_opcode_print(opcode), opcode, len, (int)len, reason); + _discord_gateway_close_opcode_print(opcode), opcode, len, (int)len, + reason); /* user-triggered shutdown */ if (gw->session->status & DISCORD_SESSION_SHUTDOWN) return; @@ -1151,8 +582,9 @@ on_text_cb(void *p_gw, &gw->conf, ANSICOLOR("RCV", ANSI_FG_BRIGHT_YELLOW) " %s%s%s (%zu bytes) [@@@_%zu_@@@]", - opcode_print(gw->payload.opcode), *gw->payload.name ? " -> " : "", - gw->payload.name, len, info->loginfo.counter); + _discord_gateway_opcode_print(gw->payload.opcode), + *gw->payload.name ? " -> " : "", gw->payload.name, len, + info->loginfo.counter); switch (gw->payload.opcode) { case DISCORD_GATEWAY_DISPATCH: @@ -1253,7 +685,7 @@ discord_gateway_init(struct discord_gateway *gw, discord_set_presence(client, &presence); /* default callbacks */ - gw->cmds.scheduler = default_scheduler_cb; + gw->scheduler = default_scheduler_cb; /* check for default prefix in config file */ buf = logconf_get_field(conf, path, sizeof(path) / sizeof *path); @@ -1308,12 +740,12 @@ discord_gateway_cleanup(struct discord_gateway *gw) /* cleanup client session */ free(gw->session); /* cleanup user commands */ - if (gw->cmds.pool) { - for (size_t i = 0; i < gw->cmds.amt; i++) - free(gw->cmds.pool[i].start); - free(gw->cmds.pool); + if (gw->pool) { + for (size_t i = 0; i < gw->amt; i++) + free(gw->pool[i].start); + free(gw->pool); } - if (gw->cmds.prefix.start) free(gw->cmds.prefix.start); + if (gw->prefix.start) free(gw->prefix.start); if (gw->parse.pairs) free(gw->parse.pairs); if (gw->parse.tokens) free(gw->parse.tokens); } @@ -1534,7 +966,7 @@ discord_gateway_perform(struct discord_gateway *gw) /* check if timespan since first pulse is greater than * minimum heartbeat interval required */ if (gw->timer->interval < gw->timer->now - gw->timer->hbeat) { - send_heartbeat(gw); + _discord_gateway_send_heartbeat(gw); } return CCORD_OK; diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c new file mode 100644 index 000000000..d84070b4d --- /dev/null +++ b/src/discord-gateway_dispatch.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include /* isspace() */ + +#include "discord.h" +#include "discord-internal.h" + +#define INIT(type, event_name) \ + { \ + sizeof(struct type), \ + (size_t(*)(jsmnf_pair *, const char *, void *))type##_from_jsmnf, \ + (void (*)(void *))type##_cleanup \ + } + +/** @brief Information for deserializing a Discord event */ +static const struct { + /** size of event's datatype */ + size_t size; + /** event's payload deserializer */ + size_t (*from_jsmnf)(jsmnf_pair *, const char *, void *); + /** event's cleanup */ + void (*cleanup)(void *); +} dispatch[] = { + [DISCORD_EV_READY] = + INIT(discord_ready, ready), + [DISCORD_EV_APPLICATION_COMMAND_CREATE] = + INIT(discord_application_command, application_command_create), + [DISCORD_EV_APPLICATION_COMMAND_UPDATE] = + INIT(discord_application_command, application_command_update), + [DISCORD_EV_APPLICATION_COMMAND_DELETE] = + INIT(discord_application_command, application_command_delete), + [DISCORD_EV_CHANNEL_CREATE] = + INIT(discord_channel, channel_create), + [DISCORD_EV_CHANNEL_UPDATE] = + INIT(discord_channel, channel_update), + [DISCORD_EV_CHANNEL_DELETE] = + INIT(discord_channel, channel_delete), + [DISCORD_EV_CHANNEL_PINS_UPDATE] = + INIT(discord_channel_pins_update, channel_pins_update), + [DISCORD_EV_THREAD_CREATE] = + INIT(discord_channel, thread_create), + [DISCORD_EV_THREAD_UPDATE] = + INIT(discord_channel, thread_update), + [DISCORD_EV_THREAD_DELETE] = + INIT(discord_channel, thread_delete), + [DISCORD_EV_THREAD_LIST_SYNC] = + INIT(discord_thread_list_sync, thread_list_sync), + [DISCORD_EV_THREAD_MEMBER_UPDATE] = + INIT(discord_thread_member, thread_member_update), + [DISCORD_EV_THREAD_MEMBERS_UPDATE] = + INIT(discord_thread_members_update, thread_members_update), + [DISCORD_EV_GUILD_CREATE] = + INIT(discord_guild, guild_create), + [DISCORD_EV_GUILD_UPDATE] = + INIT(discord_guild, guild_update), + [DISCORD_EV_GUILD_DELETE] = + INIT(discord_guild, guild_delete), + [DISCORD_EV_GUILD_BAN_ADD] = + INIT(discord_guild_ban_add, guild_ban_add), + [DISCORD_EV_GUILD_BAN_REMOVE] = + INIT(discord_guild_ban_remove, guild_ban_remove), + [DISCORD_EV_GUILD_EMOJIS_UPDATE] = + INIT(discord_guild_emojis_update, guild_emojis_update), + [DISCORD_EV_GUILD_STICKERS_UPDATE] = + INIT(discord_guild_stickers_update, guild_stickers_update), + [DISCORD_EV_GUILD_INTEGRATIONS_UPDATE] = + INIT(discord_guild_integrations_update, guild_integrations_update), + [DISCORD_EV_GUILD_MEMBER_ADD] = + INIT(discord_guild_member, guild_member_add), + [DISCORD_EV_GUILD_MEMBER_UPDATE] = + INIT(discord_guild_member_update, guild_member_update), + [DISCORD_EV_GUILD_MEMBER_REMOVE] = + INIT(discord_guild_member_remove, guild_member_remove), + [DISCORD_EV_GUILD_ROLE_CREATE] = + INIT(discord_guild_role_create, guild_role_create), + [DISCORD_EV_GUILD_ROLE_UPDATE] = + INIT(discord_guild_role_update, guild_role_update), + [DISCORD_EV_GUILD_ROLE_DELETE] = + INIT(discord_guild_role_delete, guild_role_delete), + [DISCORD_EV_INTEGRATION_CREATE] = + INIT(discord_integration, integration_create), + [DISCORD_EV_INTEGRATION_UPDATE] = + INIT(discord_integration, integration_update), + [DISCORD_EV_INTEGRATION_DELETE] = + INIT(discord_integration_delete, integration_delete), + [DISCORD_EV_INTERACTION_CREATE] = + INIT(discord_interaction, interaction_create), + [DISCORD_EV_INVITE_CREATE] = + INIT(discord_invite_create, invite_create), + [DISCORD_EV_INVITE_DELETE] = + INIT(discord_invite_delete, invite_delete), + [DISCORD_EV_MESSAGE_CREATE] = + INIT(discord_message, message_create), + [DISCORD_EV_MESSAGE_UPDATE] = + INIT(discord_message, message_update), + [DISCORD_EV_MESSAGE_DELETE] = + INIT(discord_message_delete, message_delete), + [DISCORD_EV_MESSAGE_DELETE_BULK] = + INIT(discord_message_delete_bulk, message_delete_bulk), + [DISCORD_EV_MESSAGE_REACTION_ADD] = + INIT(discord_message_reaction_add, message_reaction_add), + [DISCORD_EV_MESSAGE_REACTION_REMOVE] = + INIT(discord_message_reaction_remove, message_reaction_remove), + [DISCORD_EV_MESSAGE_REACTION_REMOVE_ALL] = + INIT(discord_message_reaction_remove_all, message_reaction_remove_all), + [DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI] = + INIT(discord_message_reaction_remove_emoji, + message_reaction_remove_emoji), + [DISCORD_EV_PRESENCE_UPDATE] = + INIT(discord_presence_update, presence_update), + [DISCORD_EV_STAGE_INSTANCE_CREATE] = + INIT(discord_stage_instance, stage_instance_create), + [DISCORD_EV_STAGE_INSTANCE_UPDATE] = + INIT(discord_stage_instance, stage_instance_update), + [DISCORD_EV_STAGE_INSTANCE_DELETE] = + INIT(discord_stage_instance, stage_instance_delete), + [DISCORD_EV_TYPING_START] = + INIT(discord_typing_start, typing_start), + [DISCORD_EV_USER_UPDATE] = + INIT(discord_user, user_update), + [DISCORD_EV_VOICE_STATE_UPDATE] = + INIT(discord_voice_state, voice_state_update), + [DISCORD_EV_VOICE_SERVER_UPDATE] = + INIT(discord_voice_server_update, voice_server_update), + [DISCORD_EV_WEBHOOKS_UPDATE] = + INIT(discord_webhooks_update, webhooks_update), +}; + +static void +_discord_message_cleanup_v(void *message) +{ + discord_message_cleanup(message); + free(message); +} + +/** return true in case user command has been triggered */ +static bool +_discord_gateway_try_command(struct discord_gateway *gw) +{ + jsmnf_pair *f = jsmnf_find(gw->payload.data, gw->json, "content", 7); + + if (gw->pool + && !strncmp(gw->prefix.start, gw->json + f->v.pos, gw->prefix.size)) + { + struct discord_message *event = calloc(1, sizeof *event); + struct discord *client = CLIENT(gw, gw); + discord_ev_message callback = NULL; + char *cmd_start; + size_t cmd_len; + char *tmp; + + discord_message_from_jsmnf(gw->payload.data, gw->json, event); + + cmd_start = event->content + gw->prefix.size; + cmd_len = strcspn(cmd_start, " \n\t\r"); + + tmp = event->content; + + /* match command to its callback */ + for (size_t i = 0; i < gw->amt; ++i) { + if (cmd_len == gw->pool[i].size + && 0 == strncmp(gw->pool[i].start, cmd_start, cmd_len)) + { + + callback = gw->pool[i].cb; + break; + } + } + + /* couldn't match command to callback, get fallback if available */ + if (!callback) { + if (!gw->prefix.size) { + free(event); + return false; + } + + cmd_len = 0; + callback = gw->fallback.cb; + } + + /* skip blank characters after command */ + if (event->content) { + event->content = cmd_start + cmd_len; + while (*event->content && isspace((int)event->content[0])) + ++event->content; + } + + discord_refcounter_incr(client->refcounter, event, + _discord_message_cleanup_v, false); + callback(client, event); + event->content = tmp; /* retrieve original ptr */ + discord_refcounter_decr(client->refcounter, event); + + return true; + } + + return false; +} + +void +discord_gateway_dispatch(struct discord_gateway *gw, + enum discord_gateway_events event) +{ + switch (event) { + case DISCORD_EV_MESSAGE_CREATE: + if (_discord_gateway_try_command(gw)) return; + /* fall-through */ + default: { + if (gw->cbs[event]) { + struct discord *client = CLIENT(gw, gw); + void *data = calloc(1, dispatch[event].size); + + dispatch[event].from_jsmnf(gw->payload.data, gw->json, data); + + discord_refcounter_incr(client->refcounter, data, + dispatch[event].cleanup, true); + gw->cbs[event](client, data); + discord_refcounter_decr(client->refcounter, data); + } + } break; + } +} + +void +discord_gateway_send_presence_update(struct discord_gateway *gw) +{ + struct ws_info info = { 0 }; + char buf[2048]; + jsonb b; + + if (!gw->session->is_ready) return; + + jsonb_init(&b); + jsonb_object(&b, buf, sizeof(buf)); + { + jsonb_key(&b, buf, sizeof(buf), "op", 2); + jsonb_number(&b, buf, sizeof(buf), 3); + jsonb_key(&b, buf, sizeof(buf), "d", 1); + discord_presence_update_to_jsonb(&b, buf, sizeof(buf), + gw->id.presence); + jsonb_object_pop(&b, buf, sizeof(buf)); + } + + if (ws_send_text(gw->ws, &info, buf, b.pos)) { + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); + logconf_info( + &gw->conf, + ANSICOLOR("SEND", ANSI_FG_BRIGHT_GREEN) " PRESENCE UPDATE (%d " + "bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } + else { + logconf_error( + &gw->conf, + ANSICOLOR("FAIL SEND", ANSI_FG_RED) " PRESENCE UPDATE (%d " + "bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } +} diff --git a/src/discord-refcount.c b/src/discord-refcount.c index 6cf20bced..b43ad2beb 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -29,6 +29,8 @@ struct _discord_refvalue { void (*cleanup)(void *data); /** `data` references count */ int visits; + /** whether `data` cleanup should also be followed by a free() */ + bool should_free; }; struct _discord_ref { @@ -44,6 +46,7 @@ static void _discord_refvalue_cleanup(struct _discord_refvalue *value) { if (value->cleanup) value->cleanup(value->data); + if (value->should_free) free(value->data); } static struct _discord_refvalue * @@ -91,7 +94,8 @@ discord_refcounter_cleanup(struct discord_refcounter *rc) void discord_refcounter_incr(struct discord_refcounter *rc, void *data, - void (*cleanup)(void *data)) + void (*cleanup)(void *data), + bool should_free) { struct _discord_refvalue *value = NULL; intptr_t key = (intptr_t)data; @@ -102,6 +106,7 @@ discord_refcounter_incr(struct discord_refcounter *rc, value = _discord_refvalue_find(rc, key); else value = _discord_refvalue_init(rc, key, data, cleanup); + value->should_free = should_free; ++value->visits; } From 1e8ba9321253b52948da588a15aa26218af86f8e Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 22:35:40 -0300 Subject: [PATCH 021/118] chore(test): match 196304 --- test/async.c | 10 +++------- test/sync.c | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/async.c b/test/async.c index c4f5daadb..177a3d241 100644 --- a/test/async.c +++ b/test/async.c @@ -139,10 +139,10 @@ on_spam_ordered(struct discord *client, struct discord_message *event) void send_err(struct discord *client, CCORDcode code, void *data) { - u64snowflake channel_id = *(u64snowflake *)data; + struct discord_message *event = data; discord_create_message( - client, channel_id, + client, event->channel_id, &(struct discord_create_message){ .content = (char *)discord_strerror(code, client), }, @@ -153,15 +153,11 @@ void on_force_error(struct discord *client, struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - - memcpy(channel_id, &event->channel_id, sizeof(u64snowflake)); discord_delete_channel(client, FAUX_CHANNEL_ID, &(struct discord_ret_channel){ .fail = &send_err, - .data = channel_id, - .cleanup = &free, + .data = event, }); } diff --git a/test/sync.c b/test/sync.c index c1bbccb7d..33c96558d 100644 --- a/test/sync.c +++ b/test/sync.c @@ -198,7 +198,7 @@ scheduler(struct discord *client, size_t size, enum discord_gateway_events event) { - if (event == DISCORD_GATEWAY_EVENTS_MESSAGE_CREATE) { + if (event == DISCORD_EV_MESSAGE_CREATE) { char cmd[1024] = ""; jsmntok_t *tokens = NULL; From a98991228dc82f8e4484789be5d0f858c7afb9a8 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 22:37:27 -0300 Subject: [PATCH 022/118] fix(discord-gateway.c): closes #65 --- src/discord-gateway.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 8fa5fb907..31fe4a642 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -715,7 +715,7 @@ discord_gateway_init(struct discord_gateway *gw, char prefix[64] = ""; snprintf(prefix, sizeof(prefix), "%.*s", (int)f->v.len, - gw->json + f->v.pos); + buf.start + f->v.pos); discord_set_prefix(CLIENT(gw, gw), prefix); } } From 46521ae6bc4e4c2b54816f8a9c1af9cdb468ec94 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 7 May 2022 23:09:38 -0300 Subject: [PATCH 023/118] refactor(examples): event data can be now be freely passed around to discord_ret_xxx types --- examples/audit-log.c | 10 +++----- examples/channel.c | 42 ++++++++++++-------------------- examples/emoji.c | 30 +++++++++-------------- examples/guild-template.c | 26 ++++++-------------- examples/guild.c | 14 ++++------- examples/invite.c | 20 +++++---------- examples/pin.c | 24 ++++++------------ examples/reaction.c | 14 ++++------- examples/voice-join.c | 51 ++++++++++++--------------------------- 9 files changed, 75 insertions(+), 156 deletions(-) diff --git a/examples/audit-log.c b/examples/audit-log.c index 189c44dda..eeb6fbb77 100644 --- a/examples/audit-log.c +++ b/examples/audit-log.c @@ -61,7 +61,7 @@ done(struct discord *client, void *data, const struct discord_audit_log *audit_log) { - u64snowflake *channel_id = data; + struct discord_message *event = data; if (!audit_log->audit_log_entries || !audit_log->audit_log_entries->size) { log_warn("No audit log entries found!"); @@ -76,7 +76,7 @@ done(struct discord *client, entry->user_id, entry->target_id); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -93,14 +93,10 @@ on_audit_channel_create(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_audit_log ret = { .done = &done, .fail = &fail, - .data = channel_id, - .cleanup = &free, + .data = event, }; struct discord_get_guild_audit_log params = { .user_id = event->author->id, diff --git a/examples/channel.c b/examples/channel.c index c89899cfe..47ba041e8 100644 --- a/examples/channel.c +++ b/examples/channel.c @@ -102,13 +102,12 @@ done_get_channel_invites(struct discord *client, return; } - u64snowflake *channel_id = data; - + struct discord_message *event = data; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), "%d invite links created.", invites->size); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -124,14 +123,10 @@ on_channel_get_invites(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_invites ret = { .done = &done_get_channel_invites, .fail = &fail_get_channel_invites, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_get_channel_invites(client, event->channel_id, &ret); } @@ -141,24 +136,24 @@ done_create_channel_invite(struct discord *client, void *data, const struct discord_invite *invite) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "https://discord.gg/%s", invite->code); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_create_channel_invite(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; struct discord_create_message params = { .content = "Couldn't create invite", }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -166,14 +161,10 @@ on_channel_create_invite(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_invite ret = { .done = &done_create_channel_invite, .fail = &fail_create_channel_invite, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_create_channel_invite(client, event->channel_id, NULL, &ret); } @@ -183,26 +174,27 @@ done_start_thread(struct discord *client, void *data, const struct discord_channel *thread) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[1024]; - snprintf(text, sizeof(text), "Created thread <#%" PRIu64 ">", *channel_id); + snprintf(text, sizeof(text), "Created thread <#%" PRIu64 ">", + event->channel_id); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_start_thread(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[1024]; snprintf(text, sizeof(text), "Couldn't create thread: %s", discord_strerror(code, client)); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -210,14 +202,10 @@ on_channel_start_thread(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_channel ret = { .done = &done_start_thread, .fail = &fail_start_thread, - .data = channel_id, - .cleanup = &free, + .data = event, }; if (event->message_reference) { diff --git a/examples/emoji.c b/examples/emoji.c index 2f73006c1..a81104bf3 100644 --- a/examples/emoji.c +++ b/examples/emoji.c @@ -28,7 +28,7 @@ done_list_guild_emojis(struct discord *client, void *data, const struct discord_emojis *emojis) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[2000] = ""; if (!emojis->size) { @@ -56,27 +56,27 @@ done_list_guild_emojis(struct discord *client, --i; struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); continue; } } struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_list_guild_emojis(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "Couldn't fetch guild emojis: %s", discord_strerror(code, client)); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -84,14 +84,10 @@ on_list_guild_emojis(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_emojis ret = { .done = &done_list_guild_emojis, .fail = &fail_list_guild_emojis, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_list_guild_emojis(client, event->guild_id, &ret); } @@ -101,27 +97,27 @@ done_get_guild_emoji(struct discord *client, void *data, const struct discord_emoji *emoji) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), "Here you go: <%s:%s:%" PRIu64 ">", emoji->animated ? "a" : "", emoji->name, emoji->id); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_get_guild_emoji(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "Unknown emoji: %s", discord_strerror(code, client)); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -134,14 +130,10 @@ on_get_guild_emoji(struct discord *client, struct discord_message *event) sscanf(event->content, "%" SCNu64, &emoji_id); if (!emoji_id) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_emoji ret = { .done = &done_get_guild_emoji, .fail = &fail_get_guild_emoji, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_get_guild_emoji(client, event->guild_id, emoji_id, &ret); } diff --git a/examples/guild-template.c b/examples/guild-template.c index fce3e74ac..fbeada940 100644 --- a/examples/guild-template.c +++ b/examples/guild-template.c @@ -30,7 +30,7 @@ done(struct discord *client, void *data, const struct discord_guild_template *template) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), @@ -39,33 +39,29 @@ done(struct discord *client, template->name, template->description, template->creator_id); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), "Couldn't perform operation: %s", discord_strerror(code, client)); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void on_get_guild_template(struct discord *client, struct discord_message *event) { - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_guild_template ret = { .done = &done, .fail = &fail, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_get_guild_template(client, event->content, &ret); } @@ -73,14 +69,10 @@ on_get_guild_template(struct discord *client, struct discord_message *event) void on_create_guild_template(struct discord *client, struct discord_message *event) { - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_guild_template ret = { .done = &done, .fail = &fail, - .data = channel_id, - .cleanup = &free, + .data = event, }; struct discord_create_guild_template params = { @@ -94,14 +86,10 @@ on_create_guild_template(struct discord *client, struct discord_message *event) void on_sync_guild_template(struct discord *client, struct discord_message *event) { - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_guild_template ret = { .done = &done, .fail = &fail, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_sync_guild_template(client, event->guild_id, event->content, &ret); diff --git a/examples/guild.c b/examples/guild.c index a24681e3d..0ceeb1121 100644 --- a/examples/guild.c +++ b/examples/guild.c @@ -210,7 +210,7 @@ done_get_guild_channels(struct discord *client, void *data, const struct discord_channels *channels) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[DISCORD_MAX_MESSAGE_LEN]; char *cur = text; @@ -233,20 +233,20 @@ done_get_guild_channels(struct discord *client, } struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_get_guild_channels(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "Couldn't fetch guild channels: %s", discord_strerror(code, client)); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -254,14 +254,10 @@ on_channels_get(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_channels ret = { .done = &done_get_guild_channels, .fail = &fail_get_guild_channels, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_get_guild_channels(client, event->guild_id, &ret); } diff --git a/examples/invite.c b/examples/invite.c index 42254258a..607f7430e 100644 --- a/examples/invite.c +++ b/examples/invite.c @@ -27,25 +27,25 @@ on_ready(struct discord *client, struct discord_ready *event) void done(struct discord *client, void *data, const struct discord_invite *invite) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "Success: https://discord.gg/%s", invite->code); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; struct discord_create_message params = { .content = "Couldn't perform operation." }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -53,14 +53,10 @@ on_invite_get(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_invite ret = { .done = &done, .fail = &fail, - .data = channel_id, - .cleanup = &free, + .data = event, }; struct discord_get_invite params = { @@ -75,14 +71,10 @@ on_invite_delete(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_invite ret = { .done = &done, .fail = &fail, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_delete_invite(client, event->content, &ret); } diff --git a/examples/pin.c b/examples/pin.c index d36226ca4..c1e4c2515 100644 --- a/examples/pin.c +++ b/examples/pin.c @@ -62,17 +62,12 @@ on_unpin(struct discord *client, struct discord_message *event) discord_unpin_message(client, event->channel_id, msg_id, NULL); } -struct context { - u64snowflake channel_id; - u64snowflake guild_id; -}; - void done_get_pins(struct discord *client, void *data, const struct discord_messages *msgs) { - struct context *cxt = data; + struct discord_message *event = data; char text[2000] = "No pins on channel"; char *cur = text; @@ -82,27 +77,27 @@ done_get_pins(struct discord *client, cur += snprintf(cur, end - cur, "https://discord.com/channels/%" PRIu64 "/%" PRIu64 "/%" PRIu64 "\n", - cxt->guild_id, cxt->channel_id, msgs->array[i].id); + event->guild_id, event->channel_id, msgs->array[i].id); if (cur >= end) break; } struct discord_create_message params = { .content = text }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_get_pins(struct discord *client, CCORDcode code, void *data) { - struct context *cxt = data; + struct discord_message *event = data; char text[2000] = ""; snprintf(text, sizeof(text), "Failed fetching pinned messages at <#%" PRIu64 ">", - cxt->channel_id); + event->channel_id); struct discord_create_message params = { .content = text }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -110,15 +105,10 @@ on_get_pins(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - struct context *cxt = malloc(sizeof(struct context)); - cxt->channel_id = event->channel_id; - cxt->guild_id = event->guild_id; - struct discord_ret_messages ret = { .done = &done_get_pins, .fail = &fail_get_pins, - .data = cxt, - .cleanup = &free, + .data = event, }; discord_get_pinned_messages(client, event->channel_id, &ret); } diff --git a/examples/reaction.c b/examples/reaction.c index b1ff397d6..4c02b129f 100644 --- a/examples/reaction.c +++ b/examples/reaction.c @@ -39,7 +39,7 @@ done_get_users(struct discord *client, void *data, const struct discord_users *users) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[2000]; if (!users->size) { @@ -58,20 +58,20 @@ done_get_users(struct discord *client, } struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_get_users(struct discord *client, CCORDcode code, void *data) { - u64snowflake *channel_id = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "Couldn't fetch reactions: %s", discord_strerror(code, client)); struct discord_create_message params = { .content = text }; - discord_create_message(client, *channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -79,14 +79,10 @@ on_get_users(struct discord *client, struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_users ret = { .done = &done_get_users, .fail = &fail_get_users, - .data = channel_id, - .cleanup = &free, + .data = event, }; struct discord_get_reactions params = { .limit = 25 }; diff --git a/examples/voice-join.c b/examples/voice-join.c index 11eff1ac9..dd52accd8 100644 --- a/examples/voice-join.c +++ b/examples/voice-join.c @@ -6,11 +6,6 @@ #include "discord.h" -struct context { - u64snowflake channel_id; - u64snowflake guild_id; -}; - void print_usage(void) { @@ -46,24 +41,24 @@ done_list_voice_regions(struct discord *client, void *data, const struct discord_voice_regions *regions) { - struct context *cxt = data; + struct discord_message *event = data; for (int i = 0; i < regions->size; ++i) { struct discord_create_message params = { .content = regions->array[i].name }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } } void fail_list_voice_regions(struct discord *client, CCORDcode code, void *data) { - struct context *cxt = data; + struct discord_message *event = data; struct discord_create_message params = { .content = "Could not fetch voice regions" }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -71,14 +66,10 @@ on_list_voice_regions(struct discord *client, struct discord_message *event) { if (event->author->bot) return; - u64snowflake *channel_id = malloc(sizeof(u64snowflake)); - *channel_id = event->channel_id; - struct discord_ret_voice_regions ret = { .done = &done_list_voice_regions, .fail = &fail_list_voice_regions, - .data = channel_id, - .cleanup = &free, + .data = event, }; discord_list_voice_regions(client, &ret); @@ -89,26 +80,26 @@ done_get_vchannel_position(struct discord *client, void *data, const struct discord_channel *vchannel) { - struct context *cxt = data; + struct discord_message *event = data; char text[256]; - discord_voice_join(client, cxt->guild_id, vchannel->id, false, false); + discord_voice_join(client, event->guild_id, vchannel->id, false, false); snprintf(text, sizeof(text), "Joining <@!%" PRIu64 "> to <#%" PRIu64 ">!", discord_get_self(client)->id, vchannel->id); struct discord_create_message params = { .content = text }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void fail_get_vchannel_position(struct discord *client, CCORDcode code, void *data) { - struct context *cxt = data; + struct discord_message *event = data; struct discord_create_message params = { .content = "Invalid channel position" }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -120,15 +111,10 @@ on_voice_join(struct discord *client, struct discord_message *event) sscanf(event->content, "%d", &position); - struct context *cxt = malloc(sizeof(struct context)); - cxt->channel_id = event->channel_id; - cxt->guild_id = event->guild_id; - struct discord_ret_channel ret = { .done = &done_get_vchannel_position, .fail = &fail_get_vchannel_position, - .data = cxt, - .cleanup = &free, + .data = event, }; discord_get_channel_at_pos(client, event->guild_id, @@ -141,14 +127,14 @@ done_disconnect_guild_member(struct discord *client, void *data, const struct discord_guild_member *member) { - struct context *cxt = data; + struct discord_message *event = data; char text[256]; snprintf(text, sizeof(text), "<@!%" PRIu64 "> has been kicked from VC", member->user->id); struct discord_create_message params = { .content = text }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -156,12 +142,12 @@ fail_disconnect_guild_member(struct discord *client, CCORDcode code, void *data) { - struct context *cxt = data; + struct discord_message *event = data; struct discord_create_message params = { .content = "Couldn't disconnect user from voice channel" }; - discord_create_message(client, cxt->channel_id, ¶ms, NULL); + discord_create_message(client, event->channel_id, ¶ms, NULL); } void @@ -179,15 +165,10 @@ on_voice_kick(struct discord *client, struct discord_message *event) discord_create_message(client, event->channel_id, ¶ms, NULL); } else { - struct context *cxt = malloc(sizeof(struct context)); - cxt->channel_id = event->channel_id; - cxt->guild_id = event->guild_id; - struct discord_ret_guild_member ret = { .done = &done_disconnect_guild_member, .fail = &fail_disconnect_guild_member, - .data = cxt, - .cleanup = &free, + .data = event, }; discord_disconnect_guild_member(client, event->guild_id, user_id, From b0aea4278459f757d77212488e05b1620e8ba762 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 8 May 2022 10:37:28 -0300 Subject: [PATCH 024/118] fix(discord-gateway): check for DISCORD_EV_NONE, check for NULL fallback.cb --- src/channel.c | 9 ++------- src/discord-gateway_dispatch.c | 14 +++++++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/channel.c b/src/channel.c index bd3eced17..aafeacc4c 100644 --- a/src/channel.c +++ b/src/channel.c @@ -17,7 +17,7 @@ struct _discord_get_channel_at_pos_cxt { }; /* XXX: placeholder until channel is obtained via cache at - * discord-get_channel_at_pos() */ + * discord_get_channel_at_pos() */ static void _done_get_channels(struct discord *client, void *data, @@ -36,8 +36,6 @@ _done_get_channels(struct discord *client, } } - /* TODO: the following should be replaced by @ref DiscordInternalTimer - * implementation */ if (found_ch) { if (cxt->ret.done) cxt->ret.done(client, cxt->ret.data, found_ch); } @@ -72,12 +70,9 @@ discord_get_channel_at_pos(struct discord *client, _ret.data = cxt; _ret.cleanup = &free; - /* TODO: the following should be replaced by @ref DiscordInternalTimer - * implementation */ - if (ret->data) { + if (ret->data) discord_refcounter_incr(client->refcounter, ret->data, ret->cleanup, false); - } /* TODO: fetch channel via caching, and return if results are non-existent */ diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index d84070b4d..6c98e53fe 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -162,7 +162,6 @@ _discord_gateway_try_command(struct discord_gateway *gw) if (cmd_len == gw->pool[i].size && 0 == strncmp(gw->pool[i].start, cmd_start, cmd_len)) { - callback = gw->pool[i].cb; break; } @@ -170,11 +169,11 @@ _discord_gateway_try_command(struct discord_gateway *gw) /* couldn't match command to callback, get fallback if available */ if (!callback) { - if (!gw->prefix.size) { + if (!gw->prefix.size || !gw->fallback.cb) { + discord_message_cleanup(event); free(event); return false; } - cmd_len = 0; callback = gw->fallback.cb; } @@ -206,7 +205,7 @@ discord_gateway_dispatch(struct discord_gateway *gw, case DISCORD_EV_MESSAGE_CREATE: if (_discord_gateway_try_command(gw)) return; /* fall-through */ - default: { + default: if (gw->cbs[event]) { struct discord *client = CLIENT(gw, gw); void *data = calloc(1, dispatch[event].size); @@ -218,7 +217,12 @@ discord_gateway_dispatch(struct discord_gateway *gw, gw->cbs[event](client, data); discord_refcounter_decr(client->refcounter, data); } - } break; + break; + case DISCORD_EV_NONE: + logconf_warn( + &gw->conf, + "Expected unimplemented GATEWAY_DISPATCH event (code: %d)", event); + break; } } From 8505b1792e148bb1c40b94fdf9d0170e8150c434 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 8 May 2022 18:29:36 -0300 Subject: [PATCH 025/118] fix(jsmn-find.h): update to v1.0.2 (fix key-search bug) --- cog-utils/jsmn-find.h | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/cog-utils/jsmn-find.h b/cog-utils/jsmn-find.h index 2dbff623f..a105e3aed 100644 --- a/cog-utils/jsmn-find.h +++ b/cog-utils/jsmn-find.h @@ -187,7 +187,7 @@ JSMN_API long jsmnf_unescape(char buf[], /* compare jsmnf keys */ #define _jsmnf_key_compare(cmp_a, cmp_b) \ (!strncmp(_JSMNF_STRING_B + (cmp_a).pos, _JSMNF_STRING_A + (cmp_b).pos, \ - (cmp_b).len)) + (cmp_a).len)) #define _JSMNF_TABLE_HEAP 0 #define _JSMNF_TABLE_BUCKET struct jsmnf_pair @@ -224,7 +224,8 @@ _jsmnf_load_pairs(struct jsmnf_loader *loader, case JSMN_STRING: case JSMN_PRIMITIVE: break; - default: { /* should be either JSMN_ARRAY or JSMN_OBJECT */ + case JSMN_OBJECT: + case JSMN_ARRAY: { const unsigned top_idx = loader->pairnext + (1 + tok->size), bottom_idx = loader->pairnext; int ret; @@ -243,7 +244,6 @@ _jsmnf_load_pairs(struct jsmnf_loader *loader, if (JSMN_OBJECT == tok->type) { while (curr->size < tok->size) { const struct jsmntok *_key = tok + 1 + offset; - struct jsmnf_pair *found = NULL; struct jsmnftok key, value = { 0 }; key.pos = _key->start; @@ -252,10 +252,10 @@ _jsmnf_load_pairs(struct jsmnf_loader *loader, /* skip Key token */ offset += 1; - /* key->size > 0 means we're dealing with an Object or Array - */ + /* _key->size > 0 means either an Object or Array */ if (_key->size > 0) { const struct jsmntok *_value = tok + 1 + offset; + struct jsmnf_pair *found = NULL; value.pos = _value->start; value.len = _value->end - _value->start; @@ -272,37 +272,35 @@ _jsmnf_load_pairs(struct jsmnf_loader *loader, } else { chash_assign(curr, key, value, _JSMNF_TABLE); - (void)chash_lookup_bucket(curr, key, found, _JSMNF_TABLE); } } - break; } - if (JSMN_ARRAY == tok->type) { + else if (JSMN_ARRAY == tok->type) { for (; curr->size < tok->size; ++curr->size) { const struct jsmntok *_value = tok + 1 + offset; - struct jsmnf_pair *pair = curr->fields + curr->size; + struct jsmnf_pair *element = curr->fields + curr->size; struct jsmnftok value; value.pos = _value->start; value.len = _value->end - _value->start; - ret = _jsmnf_load_pairs(loader, js, pair, _value, + /* assign array element */ + element->v = value; + element->state = CHASH_FILLED; + /* unused for array elements */ + element->k.pos = 0; + element->k.len = 0; + + ret = _jsmnf_load_pairs(loader, js, element, _value, num_tokens - offset, pairs, num_pairs); if (ret < 0) return ret; offset += ret; - - /* assign array element */ - pair->v = value; - pair->state = CHASH_FILLED; - /* unused for array elements */ - pair->k.pos = 0; - pair->k.len = 0; } } break; } - /* fall-through */ + default: case JSMN_UNDEFINED: fputs("Error: JSMN_UNDEFINED token detected, jsmn_parse() failure\n", stderr); From 5c1689ea630eda5f09a2bd9acf3f5f08ac39a425 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 8 May 2022 19:41:51 -0300 Subject: [PATCH 026/118] fix(jsmn-find.h): initialize allocated memory (update to v1.0.3) --- cog-utils/jsmn-find.h | 53 ++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/cog-utils/jsmn-find.h b/cog-utils/jsmn-find.h index a105e3aed..f64e0b41f 100644 --- a/cog-utils/jsmn-find.h +++ b/cog-utils/jsmn-find.h @@ -186,8 +186,9 @@ JSMN_API long jsmnf_unescape(char buf[], /* compare jsmnf keys */ #define _jsmnf_key_compare(cmp_a, cmp_b) \ - (!strncmp(_JSMNF_STRING_B + (cmp_a).pos, _JSMNF_STRING_A + (cmp_b).pos, \ - (cmp_a).len)) + ((cmp_a).len == (cmp_b).len \ + && !strncmp(_JSMNF_STRING_B + (cmp_a).pos, \ + _JSMNF_STRING_A + (cmp_b).pos, (cmp_a).len)) #define _JSMNF_TABLE_HEAP 0 #define _JSMNF_TABLE_BUCKET struct jsmnf_pair @@ -402,6 +403,18 @@ jsmnf_find_path(const struct jsmnf_pair *head, return found; } +#define RECALLOC_OR_ERROR(ptr, prev_size) \ + do { \ + const unsigned new_size = *prev_size * 2; \ + void *tmp = realloc((ptr), new_size * sizeof *(ptr)); \ + if (!tmp) return JSMN_ERROR_NOMEM; \ + \ + *prev_size = new_size; \ + memset((ptr) + *(prev_size), 0, \ + (new_size - *(prev_size)) * sizeof *(ptr)); \ + (ptr) = tmp; \ + } while (0) + JSMN_API int jsmn_parse_auto(struct jsmn_parser *parser, const char *js, @@ -412,25 +425,16 @@ jsmn_parse_auto(struct jsmn_parser *parser, int ret; if (NULL == *p_tokens || !*num_tokens) { - *p_tokens = malloc(sizeof **p_tokens); + *p_tokens = calloc(1, sizeof **p_tokens); *num_tokens = 1; } while (1) { ret = jsmn_parse(parser, js, length, *p_tokens, *num_tokens); - if (ret != JSMN_ERROR_NOMEM) { + if (ret != JSMN_ERROR_NOMEM) break; - } - else { - const unsigned new_num_tokens = *num_tokens * 2; - void *tmp; - - tmp = realloc(*p_tokens, new_num_tokens * sizeof **p_tokens); - if (!tmp) return JSMN_ERROR_NOMEM; - - *num_tokens = new_num_tokens; - *p_tokens = tmp; - } + else + RECALLOC_OR_ERROR(*p_tokens, num_tokens); } return ret; } @@ -446,29 +450,22 @@ jsmnf_load_auto(struct jsmnf_loader *loader, int ret; if (NULL == *p_pairs || !*num_pairs) { - *p_pairs = malloc(sizeof **p_pairs); + *p_pairs = calloc(1, sizeof **p_pairs); *num_pairs = 1; } while (1) { ret = jsmnf_load(loader, js, tokens, num_tokens, *p_pairs, *num_pairs); - if (ret != JSMN_ERROR_NOMEM) { + if (ret != JSMN_ERROR_NOMEM) break; - } - else { - const unsigned new_num_pairs = *num_pairs * 2; - void *tmp; - - tmp = realloc(*p_pairs, new_num_pairs * sizeof **p_pairs); - if (!tmp) return JSMN_ERROR_NOMEM; - - *num_pairs = new_num_pairs; - *p_pairs = tmp; - } + else + RECALLOC_OR_ERROR(*p_pairs, num_pairs); } return ret; } +#undef RECALLOC_OR_ERROR + static int _jsmnf_read_4_digits(char *s, const char *end, unsigned *p_hex) { From 1498dc3b2a8fc8f8104fd6fe620a5b3710cb432f Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 8 May 2022 21:49:54 -0300 Subject: [PATCH 027/118] feat(discord.h): add discord_request_guild_members(), discord_update_voice_state() and discord_update_presence() * feat(discord-gateway): implement all Gateway Commands and refactor existing ones for consistency --- examples/presence.c | 2 +- gencodecs/api/gateway.pre.h | 4 +- include/discord-internal.h | 49 +++++++- include/discord.h | 34 +++++- src/discord-client.c | 25 +++- src/discord-gateway.c | 163 ++----------------------- src/discord-gateway_dispatch.c | 212 ++++++++++++++++++++++++++++++++- src/discord-voice.c | 59 +-------- 8 files changed, 333 insertions(+), 215 deletions(-) diff --git a/examples/presence.c b/examples/presence.c index f573c1429..d2c30cc59 100644 --- a/examples/presence.c +++ b/examples/presence.c @@ -40,7 +40,7 @@ on_ready(struct discord *client, struct discord_ready *event) .since = discord_timestamp(client), }; - discord_set_presence(client, &status); + discord_update_presence(client, &status); } int diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index ad702b6db..d99a8ee20 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -359,8 +359,8 @@ PUB_STRUCT(discord_request_guild_members) COND_END STRUCT_END -/** @CCORD_pub_struct{discord_voice_state_status} */ -PUB_STRUCT(discord_voice_state_status) +/** @CCORD_pub_struct{discord_update_voice_state} */ +PUB_STRUCT(discord_update_voice_state) /** ID of the guild */ FIELD_SNOWFLAKE(guild_id) /** ID of the voice channel client wants to join (null if disconnecting) */ diff --git a/include/discord-internal.h b/include/discord-internal.h index e5ae434a8..5acfd3119 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -586,12 +586,59 @@ void discord_gateway_shutdown(struct discord_gateway *gw); */ void discord_gateway_reconnect(struct discord_gateway *gw, bool resume); +/** + * @brief Trigger the initial handshake with the gateway + * + * @param gw the handle initialized with discord_gateway_init() + * @param event provide client identification information + */ +void discord_gateway_send_identify(struct discord_gateway *gw, + struct discord_identify *event); + +/** + * @brief Replay missed events when a disconnected client resumes + * + * @param gw the handle initialized with discord_gateway_init() + * @param event session resume information + */ +void discord_gateway_send_resume(struct discord_gateway *gw, + struct discord_resume *event); + +/** + * @brief Maintain an active gateway connection + * + * @param gw the handle initialized with discord_gateway_init() + * @param seq the last session sequence number + */ +void discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq); + +/** + * @brief Request all members for a guild or a list of guilds. + * + * @param gw the handle initialized with discord_gateway_init() + * @param event request guild members information + */ +void discord_gateway_send_request_guild_members( + struct discord_gateway *gw, struct discord_request_guild_members *event); + +/** + * @brief Sent when a client wants to join, move or disconnect from a voice + * channel + * + * @param gw the handle initialized with discord_gateway_init() + * @param event request guild members information + */ +void discord_gateway_send_update_voice_state( + struct discord_gateway *gw, struct discord_update_voice_state *event); + /** * @brief Send client's presence status update payload * * @param gw the handle initialized with discord_gateway_init() + * @param event the presence to be set */ -void discord_gateway_send_presence_update(struct discord_gateway *gw); +void discord_gateway_send_presence_update( + struct discord_gateway *gw, struct discord_presence_update *event); /** * @brief Dispatch user callback matched to event diff --git a/include/discord.h b/include/discord.h index 7fc92d5f7..e99fda0f9 100644 --- a/include/discord.h +++ b/include/discord.h @@ -230,15 +230,45 @@ void *discord_set_data(struct discord *client, void *data); void *discord_get_data(struct discord *client); /** - * @brief Set the Client presence state + * @brief Set the client presence status + * @deprecated since v2.0.0, use discord_update_presence() instead * @see discord_presence_add_activity() * * @param client the client created with discord_init() - * @param presence change the client's status to it + * @param presence status to update the client's to */ void discord_set_presence(struct discord *client, struct discord_presence_update *presence); +/** + * @brief Request all members for a guild or a list of guilds + * + * @param client the client created with discord_init() + * @param request request guild members information + */ +void discord_request_guild_members( + struct discord *client, struct discord_request_guild_members *request); + +/** + * @brief Sent when a client wants to join, move or disconnect from a voice + * channel + * + * @param client the client created with discord_init() + * @param update request guild members information + */ +void discord_update_voice_state(struct discord *client, + struct discord_update_voice_state *update); + +/** + * @brief Update the client presence status + * @see discord_presence_add_activity() + * + * @param client the client created with discord_init() + * @param presence status to update the client's to + */ +void discord_update_presence(struct discord *client, + struct discord_presence_update *presence); + /** * @brief Get the client WebSockets ping * @note Only works after a connection has been established via discord_run() diff --git a/src/discord-client.c b/src/discord-client.c index c417de6fb..1020583ed 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -606,12 +606,33 @@ discord_set_on_voice_server_update(struct discord *client, discord_add_intents(client, DISCORD_GATEWAY_GUILD_VOICE_STATES); } +/* deprecated, use discord_update_presence() instead */ void discord_set_presence(struct discord *client, struct discord_presence_update *presence) { - memcpy(client->gw.id.presence, presence, sizeof *presence); - discord_gateway_send_presence_update(&client->gw); + discord_gateway_send_presence_update(&client->gw, presence); +} + +void +discord_request_guild_members(struct discord *client, + struct discord_request_guild_members *request) +{ + discord_gateway_send_request_guild_members(&client->gw, request); +} + +void +discord_update_voice_state(struct discord *client, + struct discord_update_voice_state *update) +{ + discord_gateway_send_update_voice_state(&client->gw, update); +} + +void +discord_update_presence(struct discord *client, + struct discord_presence_update *presence) +{ + discord_gateway_send_presence_update(&client->gw, presence); } int diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 31fe4a642..81d0d194a 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -93,141 +93,6 @@ _discord_gateway_close_opcode_print(enum discord_gateway_close_opcodes opcode) } } -static void -_discord_gateway_send_resume(struct discord_gateway *gw) -{ - struct ws_info info = { 0 }; - char buf[1024]; - jsonb b; - - /* reset */ - gw->session->status ^= DISCORD_SESSION_RESUMABLE; - - jsonb_init(&b); - jsonb_object(&b, buf, sizeof(buf)); - { - jsonb_key(&b, buf, sizeof(buf), "op", 2); - jsonb_number(&b, buf, sizeof(buf), 6); - jsonb_key(&b, buf, sizeof(buf), "d", 1); - jsonb_object(&b, buf, sizeof(buf)); - { - jsonb_key(&b, buf, sizeof(buf), "token", 5); - jsonb_string(&b, buf, sizeof(buf), gw->id.token, - strlen(gw->id.token)); - jsonb_key(&b, buf, sizeof(buf), "session_id", 10); - jsonb_string(&b, buf, sizeof(buf), gw->session->id, - strlen(gw->session->id)); - jsonb_key(&b, buf, sizeof(buf), "seq", 3); - jsonb_number(&b, buf, sizeof(buf), gw->payload.seq); - jsonb_object_pop(&b, buf, sizeof(buf)); - } - jsonb_object_pop(&b, buf, sizeof(buf)); - } - - if (ws_send_text(gw->ws, &info, buf, b.pos)) { - io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); - logconf_info( - &gw->conf, - ANSICOLOR("SEND", - ANSI_FG_BRIGHT_GREEN) " RESUME (%d bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - } - else { - logconf_info(&gw->conf, - ANSICOLOR("FAIL SEND", - ANSI_FG_RED) " RESUME (%d bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - } -} - -static void -_discord_gateway_send_identify(struct discord_gateway *gw) -{ - struct ws_info info = { 0 }; - char buf[1024]; - jsonb b; - - /* Ratelimit check */ - if (gw->timer->now - gw->timer->identify < 5) { - ++gw->session->concurrent; - VASSERT_S(gw->session->concurrent - < gw->session->start_limit.max_concurrency, - "Reach identify request threshold (%d every 5 seconds)", - gw->session->start_limit.max_concurrency); - } - else { - gw->session->concurrent = 0; - } - - jsonb_init(&b); - jsonb_object(&b, buf, sizeof(buf)); - { - jsonb_key(&b, buf, sizeof(buf), "op", 2); - jsonb_number(&b, buf, sizeof(buf), 2); - jsonb_key(&b, buf, sizeof(buf), "d", 1); - discord_identify_to_jsonb(&b, buf, sizeof(buf), &gw->id); - jsonb_object_pop(&b, buf, sizeof(buf)); - } - - if (ws_send_text(gw->ws, &info, buf, b.pos)) { - io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); - logconf_info( - &gw->conf, - ANSICOLOR( - "SEND", - ANSI_FG_BRIGHT_GREEN) " IDENTIFY (%d bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - /* get timestamp for this identify */ - gw->timer->identify = gw->timer->now; - } - else { - logconf_info( - &gw->conf, - ANSICOLOR("FAIL SEND", - ANSI_FG_RED) " IDENTIFY (%d bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - } -} - -/* send heartbeat pulse to websockets server in order - * to maintain connection alive */ -static void -_discord_gateway_send_heartbeat(struct discord_gateway *gw) -{ - struct ws_info info = { 0 }; - char buf[64]; - jsonb b; - - jsonb_init(&b); - jsonb_object(&b, buf, sizeof(buf)); - { - jsonb_key(&b, buf, sizeof(buf), "op", 2); - jsonb_number(&b, buf, sizeof(buf), 1); - jsonb_key(&b, buf, sizeof(buf), "d", 1); - jsonb_number(&b, buf, sizeof(buf), gw->payload.seq); - jsonb_object_pop(&b, buf, sizeof(buf)); - } - - if (ws_send_text(gw->ws, &info, buf, b.pos)) { - io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); - logconf_info( - &gw->conf, - ANSICOLOR( - "SEND", - ANSI_FG_BRIGHT_GREEN) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - /* update heartbeat timestamp */ - gw->timer->hbeat = gw->timer->now; - } - else { - logconf_info( - &gw->conf, - ANSICOLOR("FAIL SEND", - ANSI_FG_RED) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", - b.pos, info.loginfo.counter + 1); - } -} - static void on_hello(struct discord_gateway *gw) { @@ -240,9 +105,12 @@ on_hello(struct discord_gateway *gw) gw->timer->interval = strtoull(gw->json + f->v.pos, NULL, 10); if (gw->session->status & DISCORD_SESSION_RESUMABLE) - _discord_gateway_send_resume(gw); + discord_gateway_send_resume(gw, &(struct discord_resume){ + .session_id = gw->session->id, + .seq = gw->payload.seq, + }); else - _discord_gateway_send_identify(gw); + discord_gateway_send_identify(gw, &gw->id); } static enum discord_gateway_events @@ -362,7 +230,7 @@ on_dispatch(struct discord_gateway *gw) gw->session->is_ready = true; gw->session->retry.attempt = 0; - _discord_gateway_send_heartbeat(gw); + discord_gateway_send_heartbeat(gw, gw->payload.seq); } break; case DISCORD_EV_RESUMED: logconf_info(&gw->conf, "Succesfully resumed a Discord session!"); @@ -370,12 +238,9 @@ on_dispatch(struct discord_gateway *gw) gw->session->is_ready = true; gw->session->retry.attempt = 0; - _discord_gateway_send_heartbeat(gw); + discord_gateway_send_heartbeat(gw, gw->payload.seq); break; default: - logconf_warn( - &gw->conf, - "Expected unimplemented GATEWAY_DISPATCH event (code: %d)", event); break; } @@ -636,13 +501,10 @@ discord_gateway_init(struct discord_gateway *gw, struct logconf *conf, struct sized_buffer *token) { - struct discord *client = CLIENT(gw, gw); /* Web-Sockets callbacks */ struct ws_callbacks cbs = { 0 }; /* Web-Sockets custom attributes */ struct ws_attr attr = { 0 }; - /* Bot default presence update */ - struct discord_presence_update presence = { 0 }; struct sized_buffer buf; /* prefix directive */ char *path[] = { "discord", "default_prefix" }; @@ -656,7 +518,8 @@ discord_gateway_init(struct discord_gateway *gw, /* Web-Sockets handler */ gw->mhandle = curl_multi_init(); - io_poller_curlm_add(client->io_poller, gw->mhandle, on_io_poller_curl, gw); + io_poller_curlm_add(CLIENT(gw, gw)->io_poller, gw->mhandle, + on_io_poller_curl, gw); gw->ws = ws_init(&cbs, gw->mhandle, &attr); logconf_branch(&gw->conf, conf, "DISCORD_GATEWAY"); @@ -680,9 +543,9 @@ discord_gateway_init(struct discord_gateway *gw, /* the bot initial presence */ gw->id.presence = calloc(1, sizeof *gw->id.presence); - presence.status = "online"; - presence.since = cog_timestamp_ms(); - discord_set_presence(client, &presence); + gw->id.presence->status = "online"; + gw->id.presence->since = cog_timestamp_ms(); + discord_gateway_send_presence_update(gw, gw->id.presence); /* default callbacks */ gw->scheduler = default_scheduler_cb; @@ -966,7 +829,7 @@ discord_gateway_perform(struct discord_gateway *gw) /* check if timespan since first pulse is greater than * minimum heartbeat interval required */ if (gw->timer->interval < gw->timer->now - gw->timer->hbeat) { - _discord_gateway_send_heartbeat(gw); + discord_gateway_send_heartbeat(gw, gw->payload.seq); } return CCORD_OK; diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index 6c98e53fe..688ebbf2c 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -227,7 +227,214 @@ discord_gateway_dispatch(struct discord_gateway *gw, } void -discord_gateway_send_presence_update(struct discord_gateway *gw) +discord_gateway_send_identify(struct discord_gateway *gw, + struct discord_identify *identify) +{ + struct ws_info info = { 0 }; + char buf[1024]; + jsonb b; + + /* Ratelimit check */ + if (gw->timer->now - gw->timer->identify < 5) { + ++gw->session->concurrent; + VASSERT_S(gw->session->concurrent + < gw->session->start_limit.max_concurrency, + "Reach identify request threshold (%d every 5 seconds)", + gw->session->start_limit.max_concurrency); + } + else { + gw->session->concurrent = 0; + } + + jsonb_init(&b); + jsonb_object(&b, buf, sizeof(buf)); + { + jsonb_key(&b, buf, sizeof(buf), "op", 2); + jsonb_number(&b, buf, sizeof(buf), 2); + jsonb_key(&b, buf, sizeof(buf), "d", 1); + discord_identify_to_jsonb(&b, buf, sizeof(buf), identify); + jsonb_object_pop(&b, buf, sizeof(buf)); + } + + if (ws_send_text(gw->ws, &info, buf, b.pos)) { + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); + logconf_info( + &gw->conf, + ANSICOLOR( + "SEND", + ANSI_FG_BRIGHT_GREEN) " IDENTIFY (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + /* get timestamp for this identify */ + gw->timer->identify = gw->timer->now; + } + else { + logconf_info( + &gw->conf, + ANSICOLOR("FAIL SEND", + ANSI_FG_RED) " IDENTIFY (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } +} + +void +discord_gateway_send_resume(struct discord_gateway *gw, + struct discord_resume *event) +{ + struct ws_info info = { 0 }; + char buf[1024]; + jsonb b; + + /* reset */ + gw->session->status ^= DISCORD_SESSION_RESUMABLE; + + jsonb_init(&b); + jsonb_object(&b, buf, sizeof(buf)); + { + jsonb_key(&b, buf, sizeof(buf), "op", 2); + jsonb_number(&b, buf, sizeof(buf), 6); + jsonb_key(&b, buf, sizeof(buf), "d", 1); + discord_resume_to_jsonb(&b, buf, sizeof(buf), event); + jsonb_object_pop(&b, buf, sizeof(buf)); + } + + if (ws_send_text(gw->ws, &info, buf, b.pos)) { + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); + logconf_info( + &gw->conf, + ANSICOLOR("SEND", + ANSI_FG_BRIGHT_GREEN) " RESUME (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } + else { + logconf_info(&gw->conf, + ANSICOLOR("FAIL SEND", + ANSI_FG_RED) " RESUME (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } +} + +/* send heartbeat pulse to websockets server in order + * to maintain connection alive */ +void +discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) +{ + struct ws_info info = { 0 }; + char buf[64]; + jsonb b; + + jsonb_init(&b); + jsonb_object(&b, buf, sizeof(buf)); + { + jsonb_key(&b, buf, sizeof(buf), "op", 2); + jsonb_number(&b, buf, sizeof(buf), 1); + jsonb_key(&b, buf, sizeof(buf), "d", 1); + jsonb_number(&b, buf, sizeof(buf), seq); + jsonb_object_pop(&b, buf, sizeof(buf)); + } + + if (ws_send_text(gw->ws, &info, buf, b.pos)) { + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); + logconf_info( + &gw->conf, + ANSICOLOR( + "SEND", + ANSI_FG_BRIGHT_GREEN) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + /* update heartbeat timestamp */ + gw->timer->hbeat = gw->timer->now; + } + else { + logconf_info( + &gw->conf, + ANSICOLOR("FAIL SEND", + ANSI_FG_RED) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } +} + +void +discord_gateway_send_request_guild_members( + struct discord_gateway *gw, struct discord_request_guild_members *event) +{ + struct ws_info info = { 0 }; + char buf[1024]; + jsonb b; + + jsonb_init(&b); + jsonb_object(&b, buf, sizeof(buf)); + { + jsonb_key(&b, buf, sizeof(buf), "op", 2); + jsonb_number(&b, buf, sizeof(buf), 8); + jsonb_key(&b, buf, sizeof(buf), "d", 1); + discord_request_guild_members_to_jsonb(&b, buf, sizeof(buf), event); + jsonb_object_pop(&b, buf, sizeof(buf)); + } + + if (ws_send_text(gw->ws, &info, buf, b.pos)) { + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); + logconf_info( + &gw->conf, + ANSICOLOR("SEND", ANSI_FG_BRIGHT_GREEN) " REQUEST_GUILD_MEMBERS " + "(%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + /* update heartbeat timestamp */ + gw->timer->hbeat = gw->timer->now; + } + else { + logconf_info( + &gw->conf, + ANSICOLOR( + "FAIL SEND", + ANSI_FG_RED) " REQUEST_GUILD_MEMBERS (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } +} + +void +discord_gateway_send_update_voice_state( + struct discord_gateway *gw, struct discord_update_voice_state *event) +{ + struct ws_info info = { 0 }; + char buf[256]; + jsonb b; + + jsonb_init(&b); + jsonb_object(&b, buf, sizeof(buf)); + { + jsonb_key(&b, buf, sizeof(buf), "op", 2); + jsonb_number(&b, buf, sizeof(buf), 4); + jsonb_key(&b, buf, sizeof(buf), "d", 1); + discord_update_voice_state_to_jsonb(&b, buf, sizeof(buf), event); + jsonb_object_pop(&b, buf, sizeof(buf)); + } + + if (ws_send_text(gw->ws, &info, buf, b.pos)) { + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); + logconf_info( + &gw->conf, + ANSICOLOR( + "SEND", + ANSI_FG_BRIGHT_GREEN) " UPDATE_VOICE_STATE " + "(%d bytes): %s channels [@@@_%zu_@@@]", + b.pos, event->channel_id ? "join" : "leave", + info.loginfo.counter + 1); + + /* update heartbeat timestamp */ + gw->timer->hbeat = gw->timer->now; + } + else { + logconf_info( + &gw->conf, + ANSICOLOR( + "FAIL SEND", + ANSI_FG_RED) " UPDATE_VOICE_STATE (%d bytes) [@@@_%zu_@@@]", + b.pos, info.loginfo.counter + 1); + } +} + +void +discord_gateway_send_presence_update(struct discord_gateway *gw, + struct discord_presence_update *presence) { struct ws_info info = { 0 }; char buf[2048]; @@ -241,8 +448,7 @@ discord_gateway_send_presence_update(struct discord_gateway *gw) jsonb_key(&b, buf, sizeof(buf), "op", 2); jsonb_number(&b, buf, sizeof(buf), 3); jsonb_key(&b, buf, sizeof(buf), "d", 1); - discord_presence_update_to_jsonb(&b, buf, sizeof(buf), - gw->id.presence); + discord_presence_update_to_jsonb(&b, buf, sizeof(buf), presence); jsonb_object_pop(&b, buf, sizeof(buf)); } diff --git a/src/discord-voice.c b/src/discord-voice.c index a839a2fc3..076a6c63a 100644 --- a/src/discord-voice.c +++ b/src/discord-voice.c @@ -522,56 +522,6 @@ recycle_active_vc(struct discord_voice *vc, vc->shutdown = false; } -static void -send_voice_state_update(struct discord_voice *vc, - u64snowflake guild_id, - u64snowflake channel_id, - bool self_mute, - bool self_deaf) -{ - struct discord_gateway *gw = &vc->p_client->gw; - char buf[256]; - jsonb b; - - jsonb_init(&b); - jsonb_object(&b, buf, sizeof(buf)); - { - jsonb_key(&b, buf, sizeof(buf), "op", sizeof("op") - 1); - jsonb_number(&b, buf, sizeof(buf), 4); - jsonb_key(&b, buf, sizeof(buf), "d", sizeof("d") - 1); - jsonb_object(&b, buf, sizeof(buf)); - { - char tok[32]; - int toklen = snprintf(tok, sizeof(tok), "%" PRIu64, guild_id); - - jsonb_key(&b, buf, sizeof(buf), "guild_id", 8); - jsonb_token(&b, buf, sizeof(buf), tok, (size_t)toklen); - jsonb_key(&b, buf, sizeof(buf), "channel_id", 10); - if (channel_id) { - toklen = snprintf(tok, sizeof(tok), "%" PRIu64, channel_id); - jsonb_token(&b, buf, sizeof(buf), tok, (size_t)toklen); - } - else { - jsonb_null(&b, buf, sizeof(buf)); - } - jsonb_key(&b, buf, sizeof(buf), "self_mute", 9); - jsonb_bool(&b, buf, sizeof(buf), self_mute); - jsonb_key(&b, buf, sizeof(buf), "self_deaf", 9); - jsonb_bool(&b, buf, sizeof(buf), self_deaf); - jsonb_object_pop(&b, buf, sizeof(buf)); - } - jsonb_object_pop(&b, buf, sizeof(buf)); - } - - logconf_info( - &vc->conf, - ANSICOLOR("SEND", ANSI_FG_BRIGHT_GREEN) " VOICE_STATE_UPDATE (%d " - "bytes): %s channel", - b.pos, channel_id ? "join" : "leave"); - - ws_send_text(gw->ws, NULL, buf, b.pos); -} - enum discord_voice_status discord_voice_join(struct discord *client, u64snowflake guild_id, @@ -613,7 +563,8 @@ discord_voice_join(struct discord *client, } recycle_active_vc(vc, guild_id, vchannel_id); - send_voice_state_update(vc, guild_id, vchannel_id, self_mute, self_deaf); + discord_send_voice_state_update(vc, guild_id, vchannel_id, self_mute, + self_deaf); return DISCORD_VOICE_JOINED; } @@ -835,9 +786,9 @@ discord_voice_shutdown(struct discord_voice *vc) vc->shutdown = true; vc->is_resumable = false; - /* TODO: check if send_voice_state_update() is not being ignored because of - * ws_close() */ - send_voice_state_update(vc, vc->guild_id, 0, false, false); + /* TODO: check if discord_send_voice_state_update() is not being ignored + * because of ws_close() */ + discord_send_voice_state_update(vc, vc->guild_id, 0, false, false); ws_close(vc->ws, WS_CLOSE_REASON_NORMAL, reason, sizeof(reason)); } From 23f03b7a4db3c2e5e021998edce0ea5b8d9483ca Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 9 May 2022 16:29:37 -0300 Subject: [PATCH 028/118] refactor(discord-gateway): move message commands logic to discord-gateway_command.c, update structures and methods accordingly --- Makefile | 1 + include/discord-events.h | 4 +- include/discord-internal.h | 151 ++++++++++++++++++++++------ src/discord-client.c | 54 ++-------- src/discord-gateway.c | 48 ++++----- src/discord-gateway_command.c | 175 +++++++++++++++++++++++++++++++++ src/discord-gateway_dispatch.c | 83 ++-------------- 7 files changed, 339 insertions(+), 177 deletions(-) create mode 100644 src/discord-gateway_command.c diff --git a/Makefile b/Makefile index 4876c8548..73df4a809 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-loop.o \ $(SRC_DIR)/discord-gateway.o \ $(SRC_DIR)/discord-gateway_dispatch.o \ + $(SRC_DIR)/discord-gateway_command.o \ $(SRC_DIR)/discord-timer.o \ $(SRC_DIR)/discord-misc.o \ $(SRC_DIR)/application_command.o \ diff --git a/include/discord-events.h b/include/discord-events.h index cdf48f79a..5a85a68d8 100644 --- a/include/discord-events.h +++ b/include/discord-events.h @@ -80,9 +80,9 @@ void discord_remove_intents(struct discord *client, uint64_t code); * Example: If @a 'help' is a command and @a '!' prefix is set, the command * will only be validated if @a '!help' is sent * @param client the client created with discord_init() - * @param prefix the prefix that should accompany any command + * @param prefix the mandatory command prefix */ -void discord_set_prefix(struct discord *client, char *prefix); +void discord_set_prefix(struct discord *client, const char prefix[]); /** @defgroup DiscordEventCallbackTypes Callback types * @brief Callback types for Discord events diff --git a/include/discord-internal.h b/include/discord-internal.h index 5acfd3119..20321e5b7 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -418,6 +418,123 @@ typedef void (*discord_ev)(struct discord *client, void *event); #define DISCORD_SESSION_SHUTDOWN 1u << 1 /** @} DiscordInternalGatewaySessionStatus */ +/** @brief The handle for storing the Discord response payload */ +struct discord_gateway_payload { + /** current iteration JSON string data */ + char *json; + /** current iteration JSON string data length */ + size_t length; + /** field 'op' */ + enum discord_gateway_opcodes opcode; + /** field 's' */ + int seq; + /** field 't' */ + char name[32]; + /** field 'd' */ + jsmnf_pair *data; +}; + +/** @defgroup DiscordInternalGatewayMessageCommands Message Commands API + * @brief The Message Commands API for registering and parsing user commands + * @{ */ + +/** + * @brief The handle for storing user's message commands + * @see discord_set_on_command() + */ +struct discord_message_commands { + /** DISCORD_MESSAGE_COMMANDS logging module */ + struct logconf conf; + /** the prefix expected for every command */ + struct sized_buffer prefix; + /** fallback message command @see discord_set_on_command() */ + discord_ev_message fallback; + /** amount of message commands created */ + int length; + /** message commands cap before increase */ + int capacity; + /** + * message command entries + * @note datatype declared at discord-gateway_command.c + */ + struct _discord_message_commands_entry *entries; +}; + +/** + * @brief Initialize the fields of the Message Commands handle + * + * @param conf optional pointer to a parent logconf + * @return the message commands handle + */ +struct discord_message_commands *discord_message_commands_init( + struct logconf *conf); + +/** + * @brief Free Message Commands handle + * + * @param cmds the handle initialized with discord_message_commands_init() + */ +void discord_message_commands_cleanup(struct discord_message_commands *cmds); + +/** + * @brief Search for a callback matching the command + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param command the command to be searched for + * @param length the command length + * @return the callback match, `NULL` in case there wasn't a match + */ +discord_ev_message discord_message_commands_find( + struct discord_message_commands *cmds, + const char command[], + size_t length); + +/** + * @brief Add a new command/callback pair, or update an existing command + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param command the message command to be matched with callback + * @param length the command length + * @param callback the callback to be triggered when the command is sent + */ +void discord_message_commands_append(struct discord_message_commands *cmds, + const char command[], + size_t length, + discord_ev_message callback); + +/** + * @brief Set a mandatory prefix before commands + * @see discord_set_on_command() + * + * Example: If @a 'help' is a command and @a '!' prefix is set, the command + * will only be validated if @a '!help' is sent + * @param cmds the handle initialized with discord_message_commands_init() + * @param prefix the mandatory command prefix + * @param length the prefix length + */ +void discord_message_commands_set_prefix(struct discord_message_commands *cmds, + const char prefix[], + size_t length); + +/** + * @brief Read the current `MESSAGE_CREATE` payload and attempt to perform its + * matching callback + * + * @param gw the handle initialized with discord_gateway_init() + * @param payload the event payload to read from + * (assumes its from `MESSAGE_CREATE`) + * @param client the handle initialized with discord_init() + * @note used for its @ref discord_refcounter and passing as a callback + * parameter + * @return `true` if the callback has been performed + */ +bool discord_message_commands_try_perform( + struct discord_message_commands *cmds, + struct discord_gateway_payload *payload, + struct discord *client); + +/** @} DiscordInternalGatewayMessageCommands */ + /** @brief The handle used for establishing a WebSockets connection */ struct discord_gateway { /** DISCORD_GATEWAY logging module */ @@ -477,11 +594,6 @@ struct discord_gateway { } retry; } * session; - /** current iteration JSON string data */ - char *json; - /** current iteration JSON string data length */ - size_t length; - /** parse JSON tokens into a `jsmnf_pairs` key/value pairs hashtable */ struct { /** current iteration JSON key/value pairs */ @@ -495,32 +607,9 @@ struct discord_gateway { } parse; /** response-payload structure */ - struct { - /** field 'op' */ - enum discord_gateway_opcodes opcode; - /** field 's' */ - int seq; - /** field 't' */ - char name[32]; - /** field 'd' */ - jsmnf_pair *data; - } payload; - - /** the prefix expected for every command */ - struct sized_buffer prefix; - /** user's command/callback pair @see discord_set_on_command() */ - struct { - /** the command string contents */ - char *start; - /** the command string length */ - size_t size; - /** the assigned callback for the command */ - discord_ev_message cb; - } * pool, fallback; - /** amount of command/callback pairs in pool */ - size_t amt; - /** actual size of command/callback pairs in pool */ - size_t cap; + struct discord_gateway_payload payload; + /** the user's message commands @see discord_set_on_command() */ + struct discord_message_commands *commands; /** the user's callbacks for Discord events */ discord_ev cbs[DISCORD_EV_MAX]; /** the event scheduler callback */ diff --git a/src/discord-client.c b/src/discord-client.c index 1020583ed..4effbc9dc 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -94,7 +94,8 @@ _discord_clone_gateway(struct discord_gateway *clone, memcpy(clone->payload.data, orig->payload.data, n * sizeof *orig->parse.pairs); - clone->length = cog_strndup(orig->json, orig->length, &clone->json); + clone->payload.length = cog_strndup( + orig->payload.json, orig->payload.length, &clone->payload.json); } struct discord * @@ -114,7 +115,7 @@ static void _discord_clone_gateway_cleanup(struct discord_gateway *clone) { free(clone->payload.data); - free(clone->json); + free(clone->payload.json); } static void @@ -231,14 +232,12 @@ discord_remove_intents(struct discord *client, uint64_t code) } void -discord_set_prefix(struct discord *client, char *prefix) +discord_set_prefix(struct discord *client, const char prefix[]) { if (!prefix || !*prefix) return; - if (client->gw.prefix.start) free(client->gw.prefix.start); - - client->gw.prefix.size = - cog_strndup(prefix, strlen(prefix), &client->gw.prefix.start); + discord_message_commands_set_prefix(client->gw.commands, prefix, + strlen(prefix)); } const struct discord_user * @@ -252,45 +251,8 @@ discord_set_on_command(struct discord *client, char command[], discord_ev_message callback) { - const size_t cmd_len = command ? strlen(command) : 0; - size_t i; - - /* fallback callback if prefix is detected, but command isn't specified */ - if (client->gw.prefix.size && !cmd_len) { - client->gw.fallback.cb = callback; - return; - } - - /* if command is already set then modify it */ - for (i = 0; i < client->gw.amt; i++) { - if (cmd_len == client->gw.pool[i].size - && 0 == strcmp(command, client->gw.pool[i].start)) - { - goto _modify; - } - } - - if (i == client->gw.cap) { - size_t cap = 8; - void *tmp; - - while (cap <= i) - cap <<= 1; - - tmp = realloc(client->gw.pool, cap * sizeof(*client->gw.pool)); - if (!tmp) return; - - client->gw.pool = tmp; - client->gw.cap = cap; - } - - ++client->gw.amt; - client->gw.pool[i].size = - cog_strndup(command, cmd_len, &client->gw.pool[i].start); - -_modify: - client->gw.pool[i].cb = callback; - + discord_message_commands_append(client->gw.commands, command, + strlen(command), callback); discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 81d0d194a..7d893c138 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -101,8 +101,9 @@ on_hello(struct discord_gateway *gw) gw->timer->interval = 0; gw->timer->hbeat = gw->timer->now; - if ((f = jsmnf_find(gw->payload.data, gw->json, "heartbeat_interval", 18))) - gw->timer->interval = strtoull(gw->json + f->v.pos, NULL, 10); + if ((f = jsmnf_find(gw->payload.data, gw->payload.json, + "heartbeat_interval", 18))) + gw->timer->interval = strtoull(gw->payload.json + f->v.pos, NULL, 10); if (gw->session->status & DISCORD_SESSION_RESUMABLE) discord_gateway_send_resume(gw, &(struct discord_resume){ @@ -222,9 +223,10 @@ on_dispatch(struct discord_gateway *gw) logconf_info(&gw->conf, "Succesfully started a Discord session!"); - if ((f = jsmnf_find(gw->payload.data, gw->json, "session_id", 10))) + if ((f = jsmnf_find(gw->payload.data, gw->payload.json, "session_id", + 10))) snprintf(gw->session->id, sizeof(gw->session->id), "%.*s", - (int)f->v.len, gw->json + f->v.pos); + (int)f->v.len, gw->payload.json + f->v.pos); ASSERT_S(*gw->session->id, "Missing session_id from READY event"); gw->session->is_ready = true; @@ -244,7 +246,8 @@ on_dispatch(struct discord_gateway *gw) break; } - mode = gw->scheduler(CLIENT(gw, gw), gw->json + gw->payload.data->v.pos, + mode = gw->scheduler(CLIENT(gw, gw), + gw->payload.json + gw->payload.data->v.pos, gw->payload.data->v.len, event); /* user subscribed to event */ @@ -279,7 +282,7 @@ on_invalid_session(struct discord_gateway *gw) /* attempt to resume if session isn't invalid */ if (gw->payload.data->v.len != 5 - || strncmp("false", gw->json + gw->payload.data->v.pos, 5)) + || strncmp("false", gw->payload.json + gw->payload.data->v.pos, 5)) { gw->session->status |= DISCORD_SESSION_RESUMABLE; reason = "Invalid session, will attempt to resume"; @@ -409,8 +412,8 @@ on_text_cb(void *p_gw, struct discord_gateway *gw = p_gw; jsmn_parser parser; - gw->json = (char *)text; - gw->length = len; + gw->payload.json = (char *)text; + gw->payload.length = len; jsmn_init(&parser); if (0 < jsmn_parse_auto(&parser, text, len, &gw->parse.tokens, @@ -428,17 +431,18 @@ on_text_cb(void *p_gw, if ((f = jsmnf_find(gw->parse.pairs, text, "t", 1))) { if (JSMN_STRING == f->type) snprintf(gw->payload.name, sizeof(gw->payload.name), - "%.*s", (int)f->v.len, gw->json + f->v.pos); + "%.*s", (int)f->v.len, + gw->payload.json + f->v.pos); else *gw->payload.name = '\0'; } if ((f = jsmnf_find(gw->parse.pairs, text, "s", 1))) { - int seq = (int)strtol(gw->json + f->v.pos, NULL, 10); + int seq = (int)strtol(gw->payload.json + f->v.pos, NULL, 10); if (seq) gw->payload.seq = seq; } if ((f = jsmnf_find(gw->parse.pairs, text, "op", 2))) gw->payload.opcode = (enum discord_gateway_opcodes)strtol( - gw->json + f->v.pos, NULL, 10); + gw->payload.json + f->v.pos, NULL, 10); gw->payload.data = jsmnf_find(gw->parse.pairs, text, "d", 1); } } @@ -501,6 +505,8 @@ discord_gateway_init(struct discord_gateway *gw, struct logconf *conf, struct sized_buffer *token) { + struct discord *client = CLIENT(gw, gw); + /* Web-Sockets callbacks */ struct ws_callbacks cbs = { 0 }; /* Web-Sockets custom attributes */ @@ -518,8 +524,7 @@ discord_gateway_init(struct discord_gateway *gw, /* Web-Sockets handler */ gw->mhandle = curl_multi_init(); - io_poller_curlm_add(CLIENT(gw, gw)->io_poller, gw->mhandle, - on_io_poller_curl, gw); + io_poller_curlm_add(client->io_poller, gw->mhandle, on_io_poller_curl, gw); gw->ws = ws_init(&cbs, gw->mhandle, &attr); logconf_branch(&gw->conf, conf, "DISCORD_GATEWAY"); @@ -550,6 +555,9 @@ discord_gateway_init(struct discord_gateway *gw, /* default callbacks */ gw->scheduler = default_scheduler_cb; + /* user message commands */ + gw->commands = discord_message_commands_init(&gw->conf); + /* check for default prefix in config file */ buf = logconf_get_field(conf, path, sizeof(path) / sizeof *path); if (buf.size) { @@ -575,11 +583,8 @@ discord_gateway_init(struct discord_gateway *gw, if (enable_prefix && (f = jsmnf_find(pairs, buf.start, "prefix", 6))) { - char prefix[64] = ""; - - snprintf(prefix, sizeof(prefix), "%.*s", (int)f->v.len, - buf.start + f->v.pos); - discord_set_prefix(CLIENT(gw, gw), prefix); + discord_message_commands_set_prefix( + gw->commands, buf.start + f->v.pos, f->v.len); } } } @@ -603,12 +608,7 @@ discord_gateway_cleanup(struct discord_gateway *gw) /* cleanup client session */ free(gw->session); /* cleanup user commands */ - if (gw->pool) { - for (size_t i = 0; i < gw->amt; i++) - free(gw->pool[i].start); - free(gw->pool); - } - if (gw->prefix.start) free(gw->prefix.start); + discord_message_commands_cleanup(gw->commands); if (gw->parse.pairs) free(gw->parse.pairs); if (gw->parse.tokens) free(gw->parse.tokens); } diff --git a/src/discord-gateway_command.c b/src/discord-gateway_command.c new file mode 100644 index 000000000..4174a648d --- /dev/null +++ b/src/discord-gateway_command.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include /* isspace() */ + +#include "discord.h" +#include "discord-internal.h" + +#define CHASH_KEY_FIELD command +#define CHASH_VALUE_FIELD callback +#define CHASH_BUCKETS_FIELD entries +#include "chash.h" + +#define _key_hash(key, hash) \ + 5031; \ + do { \ + unsigned __CHASH_HINDEX; \ + for (__CHASH_HINDEX = 0; __CHASH_HINDEX < (key).size; \ + ++__CHASH_HINDEX) { \ + (hash) = (((hash) << 1) + (hash)) + (key).start[__CHASH_HINDEX]; \ + } \ + } while (0) + +/* compare jsmnf keys */ +#define _key_compare(cmp_a, cmp_b) \ + ((cmp_a).size == (cmp_b).size \ + && !strncmp((cmp_a).start, (cmp_b).start, (cmp_a).size)) + +/* chash heap-mode (auto-increase hashtable) */ +#define COMMANDS_TABLE_HEAP 1 +#define COMMANDS_TABLE_BUCKET struct _discord_message_commands_entry +#define COMMANDS_TABLE_FREE_KEY(_key) free((_key).start) +#define COMMANDS_TABLE_HASH(_key, _hash) _key_hash(_key, _hash) +#define COMMANDS_TABLE_FREE_VALUE(_value) +#define COMMANDS_TABLE_COMPARE(_cmp_a, _cmp_b) _key_compare(_cmp_a, _cmp_b) +#define COMMANDS_TABLE_INIT(entry, _key, _value) \ + chash_default_init(entry, _key, _value) + +struct _discord_message_commands_entry { + /** message command */ + struct sized_buffer command; + /** the callback assigned to the command */ + discord_ev_message callback; + /** the route state in the hashtable (see chash.h 'State enums') */ + int state; +}; + +struct discord_message_commands * +discord_message_commands_init(struct logconf *conf) +{ + struct discord_message_commands *cmds = chash_init(cmds, COMMANDS_TABLE); + + logconf_branch(&cmds->conf, conf, "DISCORD_MESSAGE_COMMANDS"); + + cmds->fallback = NULL; + memset(&cmds->prefix, 0, sizeof(cmds->prefix)); + + return cmds; +} + +void +discord_message_commands_cleanup(struct discord_message_commands *cmds) +{ + if (cmds->prefix.start) free(cmds->prefix.start); + chash_free(cmds, COMMANDS_TABLE); +} + +discord_ev_message +discord_message_commands_find(struct discord_message_commands *cmds, + const char command[], + size_t length) +{ + struct sized_buffer key = { (char *)command, length }; + discord_ev_message callback = NULL; + int ret; + + ret = chash_contains(cmds, key, ret, COMMANDS_TABLE); + if (ret) { + callback = chash_lookup(cmds, key, callback, COMMANDS_TABLE); + } + + return callback; +} + +void +discord_message_commands_append(struct discord_message_commands *cmds, + const char command[], + size_t length, + discord_ev_message callback) +{ + struct sized_buffer key; + + key.size = cog_strndup(command, length, &key.start); + + /* fallback callback if prefix is detected, but command isn't specified */ + if (cmds->prefix.size && (!command || !*command)) { + cmds->fallback = callback; + return; + } + chash_assign(cmds, key, callback, COMMANDS_TABLE); +} + +static void +_discord_message_cleanup_v(void *message) +{ + discord_message_cleanup(message); + free(message); +} + +void +discord_message_commands_set_prefix(struct discord_message_commands *cmds, + const char prefix[], + size_t length) +{ + if (cmds->prefix.start) free(cmds->prefix.start); + + cmds->prefix.size = cog_strndup(prefix, length, &cmds->prefix.start); +} + +/** return true in case user command has been triggered */ +bool +discord_message_commands_try_perform(struct discord_message_commands *cmds, + struct discord_gateway_payload *payload, + struct discord *client) +{ + jsmnf_pair *f = jsmnf_find(payload->data, payload->json, "content", 7); + + if (cmds->length + && !strncmp(cmds->prefix.start, payload->json + f->v.pos, + cmds->prefix.size)) + { + struct discord_message *event = calloc(1, sizeof *event); + discord_ev_message callback = NULL; + struct sized_buffer command; + char *tmp; + + discord_message_from_jsmnf(payload->data, payload->json, event); + + command.start = event->content + cmds->prefix.size; + command.size = strcspn(command.start, " \n\t\r"); + + tmp = event->content; + + /* match command to its callback */ + if (!(callback = discord_message_commands_find(cmds, command.start, + command.size))) + { + /* couldn't match command to callback, get fallback if available */ + if (!cmds->prefix.size || !cmds->fallback) { + discord_message_cleanup(event); + free(event); + return false; + } + command.size = 0; + callback = cmds->fallback; + } + + /* skip blank characters after command */ + if (event->content) { + event->content = command.start + command.size; + while (*event->content && isspace((int)event->content[0])) + ++event->content; + } + + discord_refcounter_incr(client->refcounter, event, + _discord_message_cleanup_v, false); + callback(client, event); + event->content = tmp; /* retrieve original ptr */ + discord_refcounter_decr(client->refcounter, event); + + return true; + } + + return false; +} diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index 688ebbf2c..6a3abb769 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -1,7 +1,6 @@ #include #include #include -#include /* isspace() */ #include "discord.h" #include "discord-internal.h" @@ -127,90 +126,26 @@ static const struct { INIT(discord_webhooks_update, webhooks_update), }; -static void -_discord_message_cleanup_v(void *message) -{ - discord_message_cleanup(message); - free(message); -} - -/** return true in case user command has been triggered */ -static bool -_discord_gateway_try_command(struct discord_gateway *gw) -{ - jsmnf_pair *f = jsmnf_find(gw->payload.data, gw->json, "content", 7); - - if (gw->pool - && !strncmp(gw->prefix.start, gw->json + f->v.pos, gw->prefix.size)) - { - struct discord_message *event = calloc(1, sizeof *event); - struct discord *client = CLIENT(gw, gw); - discord_ev_message callback = NULL; - char *cmd_start; - size_t cmd_len; - char *tmp; - - discord_message_from_jsmnf(gw->payload.data, gw->json, event); - - cmd_start = event->content + gw->prefix.size; - cmd_len = strcspn(cmd_start, " \n\t\r"); - - tmp = event->content; - - /* match command to its callback */ - for (size_t i = 0; i < gw->amt; ++i) { - if (cmd_len == gw->pool[i].size - && 0 == strncmp(gw->pool[i].start, cmd_start, cmd_len)) - { - callback = gw->pool[i].cb; - break; - } - } - - /* couldn't match command to callback, get fallback if available */ - if (!callback) { - if (!gw->prefix.size || !gw->fallback.cb) { - discord_message_cleanup(event); - free(event); - return false; - } - cmd_len = 0; - callback = gw->fallback.cb; - } - - /* skip blank characters after command */ - if (event->content) { - event->content = cmd_start + cmd_len; - while (*event->content && isspace((int)event->content[0])) - ++event->content; - } - - discord_refcounter_incr(client->refcounter, event, - _discord_message_cleanup_v, false); - callback(client, event); - event->content = tmp; /* retrieve original ptr */ - discord_refcounter_decr(client->refcounter, event); - - return true; - } - - return false; -} - void discord_gateway_dispatch(struct discord_gateway *gw, enum discord_gateway_events event) { + struct discord *client = CLIENT(gw, gw); + switch (event) { case DISCORD_EV_MESSAGE_CREATE: - if (_discord_gateway_try_command(gw)) return; + if (discord_message_commands_try_perform(gw->commands, &gw->payload, + client)) + { + return; + } /* fall-through */ default: if (gw->cbs[event]) { - struct discord *client = CLIENT(gw, gw); void *data = calloc(1, dispatch[event].size); - dispatch[event].from_jsmnf(gw->payload.data, gw->json, data); + dispatch[event].from_jsmnf(gw->payload.data, gw->payload.json, + data); discord_refcounter_incr(client->refcounter, data, dispatch[event].cleanup, true); From d3c1e30b00968573604fe50ef2a1d02667621d02 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 9 May 2022 17:49:11 -0300 Subject: [PATCH 029/118] chore(test/rest.c): skip test missing channel_id --- test/rest.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/rest.c b/test/rest.c index 7f6a12e08..4a8d6d790 100644 --- a/test/rest.c +++ b/test/rest.c @@ -51,6 +51,8 @@ check_sync_fetch_nothing(void *data) u64snowflake ch_id = *(u64snowflake *)data; struct discord_ret ret = { 0 }; + if (!ch_id) SKIPm("Missing channel_id from config.json"); + ret.sync = true; ASSERT_EQ(CCORD_OK, discord_trigger_typing_indicator(CLIENT, ch_id, &ret)); @@ -146,6 +148,8 @@ check_async_fetch_nothing(void *data) struct discord_ret ret = { 0 }; CCORDcode result = CCORD_OK; + if (!ch_id) SKIPm("Missing channel_id from config.json"); + ret.done = on_done; ret.fail = on_fail; ret.data = &result; From 72a45f7bf7af67737465060171d0728a815a563b Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 10 May 2022 11:26:52 -0300 Subject: [PATCH 030/118] refactor: rename discord-gateway_command.c -> discord-messagecommands.c and move field to 'struct discord' --- Makefile | 2 +- include/discord-events.h | 8 +- include/discord-internal.h | 208 +++++++++--------- src/audit_log.c | 2 - src/discord-client.c | 66 ++++-- src/discord-gateway.c | 40 ---- src/discord-gateway_dispatch.c | 4 +- ...ay_command.c => discord-messagecommands.c} | 7 +- 8 files changed, 164 insertions(+), 173 deletions(-) rename src/{discord-gateway_command.c => discord-messagecommands.c} (96%) diff --git a/Makefile b/Makefile index 73df4a809..c3fa83d52 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-loop.o \ $(SRC_DIR)/discord-gateway.o \ $(SRC_DIR)/discord-gateway_dispatch.o \ - $(SRC_DIR)/discord-gateway_command.o \ + $(SRC_DIR)/discord-messagecommands.o \ $(SRC_DIR)/discord-timer.o \ $(SRC_DIR)/discord-misc.o \ $(SRC_DIR)/application_command.o \ diff --git a/include/discord-events.h b/include/discord-events.h index 5a85a68d8..c3a25a0a9 100644 --- a/include/discord-events.h +++ b/include/discord-events.h @@ -257,14 +257,16 @@ void discord_set_on_command(struct discord *client, * The callback is triggered when a user types one of the assigned commands in * a chat visble to the client * @param client the client created with discord_init() + * @param commands array of commands to trigger the callback + * @param amount amount of commands provided * @param callback the callback to be triggered on event - * @param ... commands and a NULL terminator * @note The command and any subjacent empty space is left out of * the message content */ void discord_set_on_commands(struct discord *client, - discord_ev_message callback, - ...); + char *const commands[], + int amount, + discord_ev_message callback); /** * @brief Set the time for wakeup function to be called diff --git a/include/discord-internal.h b/include/discord-internal.h index 20321e5b7..98287ab17 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -434,107 +434,6 @@ struct discord_gateway_payload { jsmnf_pair *data; }; -/** @defgroup DiscordInternalGatewayMessageCommands Message Commands API - * @brief The Message Commands API for registering and parsing user commands - * @{ */ - -/** - * @brief The handle for storing user's message commands - * @see discord_set_on_command() - */ -struct discord_message_commands { - /** DISCORD_MESSAGE_COMMANDS logging module */ - struct logconf conf; - /** the prefix expected for every command */ - struct sized_buffer prefix; - /** fallback message command @see discord_set_on_command() */ - discord_ev_message fallback; - /** amount of message commands created */ - int length; - /** message commands cap before increase */ - int capacity; - /** - * message command entries - * @note datatype declared at discord-gateway_command.c - */ - struct _discord_message_commands_entry *entries; -}; - -/** - * @brief Initialize the fields of the Message Commands handle - * - * @param conf optional pointer to a parent logconf - * @return the message commands handle - */ -struct discord_message_commands *discord_message_commands_init( - struct logconf *conf); - -/** - * @brief Free Message Commands handle - * - * @param cmds the handle initialized with discord_message_commands_init() - */ -void discord_message_commands_cleanup(struct discord_message_commands *cmds); - -/** - * @brief Search for a callback matching the command - * - * @param cmds the handle initialized with discord_message_commands_init() - * @param command the command to be searched for - * @param length the command length - * @return the callback match, `NULL` in case there wasn't a match - */ -discord_ev_message discord_message_commands_find( - struct discord_message_commands *cmds, - const char command[], - size_t length); - -/** - * @brief Add a new command/callback pair, or update an existing command - * - * @param cmds the handle initialized with discord_message_commands_init() - * @param command the message command to be matched with callback - * @param length the command length - * @param callback the callback to be triggered when the command is sent - */ -void discord_message_commands_append(struct discord_message_commands *cmds, - const char command[], - size_t length, - discord_ev_message callback); - -/** - * @brief Set a mandatory prefix before commands - * @see discord_set_on_command() - * - * Example: If @a 'help' is a command and @a '!' prefix is set, the command - * will only be validated if @a '!help' is sent - * @param cmds the handle initialized with discord_message_commands_init() - * @param prefix the mandatory command prefix - * @param length the prefix length - */ -void discord_message_commands_set_prefix(struct discord_message_commands *cmds, - const char prefix[], - size_t length); - -/** - * @brief Read the current `MESSAGE_CREATE` payload and attempt to perform its - * matching callback - * - * @param gw the handle initialized with discord_gateway_init() - * @param payload the event payload to read from - * (assumes its from `MESSAGE_CREATE`) - * @param client the handle initialized with discord_init() - * @note used for its @ref discord_refcounter and passing as a callback - * parameter - * @return `true` if the callback has been performed - */ -bool discord_message_commands_try_perform( - struct discord_message_commands *cmds, - struct discord_gateway_payload *payload, - struct discord *client); - -/** @} DiscordInternalGatewayMessageCommands */ - /** @brief The handle used for establishing a WebSockets connection */ struct discord_gateway { /** DISCORD_GATEWAY logging module */ @@ -608,8 +507,6 @@ struct discord_gateway { /** response-payload structure */ struct discord_gateway_payload payload; - /** the user's message commands @see discord_set_on_command() */ - struct discord_message_commands *commands; /** the user's callbacks for Discord events */ discord_ev cbs[DISCORD_EV_MAX]; /** the event scheduler callback */ @@ -881,6 +778,107 @@ void discord_refcounter_decr(struct discord_refcounter *rc, void *data); /** @} DiscordInternalRefcount */ +/** @defgroup DiscordInternalMessageCommands Message Commands API + * @brief The Message Commands API for registering and parsing user commands + * @{ */ + +/** + * @brief The handle for storing user's message commands + * @see discord_set_on_command() + */ +struct discord_message_commands { + /** DISCORD_MESSAGE_COMMANDS logging module */ + struct logconf conf; + /** the prefix expected for every command */ + struct sized_buffer prefix; + /** fallback message command @see discord_set_on_command() */ + discord_ev_message fallback; + /** amount of message commands created */ + int length; + /** message commands cap before increase */ + int capacity; + /** + * message command entries + * @note datatype declared at discord-gateway_command.c + */ + struct _discord_message_commands_entry *entries; +}; + +/** + * @brief Initialize the fields of the Message Commands handle + * + * @param conf optional pointer to a parent logconf + * @return the message commands handle + */ +struct discord_message_commands *discord_message_commands_init( + struct logconf *conf); + +/** + * @brief Free Message Commands handle + * + * @param cmds the handle initialized with discord_message_commands_init() + */ +void discord_message_commands_cleanup(struct discord_message_commands *cmds); + +/** + * @brief Search for a callback matching the command + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param command the command to be searched for + * @param length the command length + * @return the callback match, `NULL` in case there wasn't a match + */ +discord_ev_message discord_message_commands_find( + struct discord_message_commands *cmds, + const char command[], + size_t length); + +/** + * @brief Add a new command/callback pair, or update an existing command + * + * @param cmds the handle initialized with discord_message_commands_init() + * @param command the message command to be matched with callback + * @param length the command length + * @param callback the callback to be triggered when the command is sent + */ +void discord_message_commands_append(struct discord_message_commands *cmds, + const char command[], + size_t length, + discord_ev_message callback); + +/** + * @brief Set a mandatory prefix before commands + * @see discord_set_on_command() + * + * Example: If @a 'help' is a command and @a '!' prefix is set, the command + * will only be validated if @a '!help' is sent + * @param cmds the handle initialized with discord_message_commands_init() + * @param prefix the mandatory command prefix + * @param length the prefix length + */ +void discord_message_commands_set_prefix(struct discord_message_commands *cmds, + const char prefix[], + size_t length); + +/** + * @brief Read the current @ref DISCORD_EV_MESSAGE_CREATE payload and attempt + * to perform its matching callback + * + * @param gw the handle initialized with discord_gateway_init() + * @note used for its @ref discord_refcounter and passing as a callback + * parameter + * @param cmds the handle initialized with discord_message_commands_init() + * @param payload the event payload to read from + * (assumes its from `MESSAGE_CREATE`) + * @return `true` if the callback has been performed + */ +bool discord_message_commands_try_perform( + struct discord_gateway *gw, + struct discord_message_commands *cmds, + struct discord_gateway_payload *payload); + +/** @} DiscordInternalMessageCommands */ + /** * @brief The Discord client handler * @@ -918,10 +916,12 @@ struct discord { unsigned id; } wakeup_timer; - /** triggers when idle. */ + /** triggers when idle */ discord_ev_idle on_idle; /** triggers once per loop cycle */ discord_ev_idle on_cycle; + /** the user's message commands @see discord_set_on_command() */ + struct discord_message_commands *commands; /** space for user arbitrary data */ void *data; diff --git a/src/audit_log.c b/src/audit_log.c index 60e3bc9ec..106284638 100644 --- a/src/audit_log.c +++ b/src/audit_log.c @@ -6,8 +6,6 @@ #include "discord-internal.h" #include "discord-request.h" -/* FIXME: when response JSON is too large, jsmn crashes on error, most likely - * json_extract() is handling the tokens incorrectly. */ CCORDcode discord_get_guild_audit_log(struct discord *client, u64snowflake guild_id, diff --git a/src/discord-client.c b/src/discord-client.c index 4effbc9dc..bebc23da6 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -15,6 +15,8 @@ _discord_init(struct discord *new_client) discord_timers_init(new_client); new_client->io_poller = io_poller_create(); new_client->refcounter = discord_refcounter_init(&new_client->conf); + new_client->commands = discord_message_commands_init(&new_client->conf); + discord_adapter_init(&new_client->adapter, &new_client->conf, &new_client->token); discord_gateway_init(&new_client->gw, &new_client->conf, @@ -57,8 +59,9 @@ discord_init(const char token[]) struct discord * discord_config_init(const char config_file[]) { + char *path[2] = { "discord", "" }; struct discord *new_client; - char *path[] = { "discord", "token" }; + struct sized_buffer buf; FILE *fp; fp = fopen(config_file, "rb"); @@ -70,6 +73,7 @@ discord_config_init(const char config_file[]) fclose(fp); + path[1] = "token"; new_client->token = logconf_get_field(&new_client->conf, path, sizeof(path) / sizeof *path); if (!strncmp("YOUR-BOT-TOKEN", new_client->token.start, @@ -80,6 +84,40 @@ discord_config_init(const char config_file[]) _discord_init(new_client); + /* check for default prefix in config file */ + path[1] = "default_prefix"; + buf = logconf_get_field(&new_client->conf, path, + sizeof(path) / sizeof *path); + if (buf.size) { + jsmn_parser parser; + jsmntok_t tokens[16]; + + jsmn_init(&parser); + if (0 < jsmn_parse(&parser, buf.start, buf.size, tokens, + sizeof(tokens) / sizeof *tokens)) + { + jsmnf_loader loader; + jsmnf_pair pairs[16]; + + jsmnf_init(&loader); + if (0 < jsmnf_load(&loader, buf.start, tokens, parser.toknext, + pairs, sizeof(pairs) / sizeof *pairs)) + { + bool enable_prefix = false; + jsmnf_pair *f; + + if ((f = jsmnf_find(pairs, buf.start, "enable", 6))) + enable_prefix = ('t' == buf.start[f->v.pos]); + + if (enable_prefix + && (f = jsmnf_find(pairs, buf.start, "prefix", 6))) { + discord_message_commands_set_prefix( + new_client->commands, buf.start + f->v.pos, f->v.len); + } + } + } + } + return new_client; } @@ -135,6 +173,7 @@ discord_cleanup(struct discord *client) discord_user_cleanup(&client->self); io_poller_destroy(client->io_poller); discord_refcounter_cleanup(client->refcounter); + discord_message_commands_cleanup(client->commands); #ifdef CCORD_VOICE discord_voice_connections_cleanup(client); #endif @@ -236,7 +275,7 @@ discord_set_prefix(struct discord *client, const char prefix[]) { if (!prefix || !*prefix) return; - discord_message_commands_set_prefix(client->gw.commands, prefix, + discord_message_commands_set_prefix(client->commands, prefix, strlen(prefix)); } @@ -251,29 +290,20 @@ discord_set_on_command(struct discord *client, char command[], discord_ev_message callback) { - discord_message_commands_append(client->gw.commands, command, - strlen(command), callback); + discord_message_commands_append(client->commands, command, strlen(command), + callback); discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); } void discord_set_on_commands(struct discord *client, - discord_ev_message callback, - ...) + char *const commands[], + int amount, + discord_ev_message callback) { - char *command = NULL; - va_list commands; - - va_start(commands, callback); - - command = va_arg(commands, char *); - while (command != NULL) { - discord_set_on_command(client, command, callback); - command = va_arg(commands, char *); - } - - va_end(commands); + for (int i = 0; i < amount; ++i) + discord_set_on_command(client, commands[i], callback); } void diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 7d893c138..c53ba0a8d 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -511,9 +511,6 @@ discord_gateway_init(struct discord_gateway *gw, struct ws_callbacks cbs = { 0 }; /* Web-Sockets custom attributes */ struct ws_attr attr = { 0 }; - struct sized_buffer buf; - /* prefix directive */ - char *path[] = { "discord", "default_prefix" }; cbs.data = gw; cbs.on_connect = &on_connect_cb; @@ -554,41 +551,6 @@ discord_gateway_init(struct discord_gateway *gw, /* default callbacks */ gw->scheduler = default_scheduler_cb; - - /* user message commands */ - gw->commands = discord_message_commands_init(&gw->conf); - - /* check for default prefix in config file */ - buf = logconf_get_field(conf, path, sizeof(path) / sizeof *path); - if (buf.size) { - jsmn_parser parser; - jsmntok_t tokens[16]; - - jsmn_init(&parser); - if (0 < jsmn_parse(&parser, buf.start, buf.size, tokens, - sizeof(tokens) / sizeof *tokens)) - { - jsmnf_loader loader; - jsmnf_pair pairs[16]; - - jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, buf.start, tokens, parser.toknext, - pairs, sizeof(pairs) / sizeof *pairs)) - { - bool enable_prefix = false; - jsmnf_pair *f; - - if ((f = jsmnf_find(pairs, buf.start, "enable", 6))) - enable_prefix = ('t' == buf.start[f->v.pos]); - - if (enable_prefix - && (f = jsmnf_find(pairs, buf.start, "prefix", 6))) { - discord_message_commands_set_prefix( - gw->commands, buf.start + f->v.pos, f->v.len); - } - } - } - } } void @@ -607,8 +569,6 @@ discord_gateway_cleanup(struct discord_gateway *gw) free(gw->id.presence); /* cleanup client session */ free(gw->session); - /* cleanup user commands */ - discord_message_commands_cleanup(gw->commands); if (gw->parse.pairs) free(gw->parse.pairs); if (gw->parse.tokens) free(gw->parse.tokens); } diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index 6a3abb769..d16cc3a01 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -134,8 +134,8 @@ discord_gateway_dispatch(struct discord_gateway *gw, switch (event) { case DISCORD_EV_MESSAGE_CREATE: - if (discord_message_commands_try_perform(gw->commands, &gw->payload, - client)) + if (discord_message_commands_try_perform(gw, client->commands, + &gw->payload)) { return; } diff --git a/src/discord-gateway_command.c b/src/discord-messagecommands.c similarity index 96% rename from src/discord-gateway_command.c rename to src/discord-messagecommands.c index 4174a648d..7234aea42 100644 --- a/src/discord-gateway_command.c +++ b/src/discord-messagecommands.c @@ -119,9 +119,9 @@ discord_message_commands_set_prefix(struct discord_message_commands *cmds, /** return true in case user command has been triggered */ bool -discord_message_commands_try_perform(struct discord_message_commands *cmds, - struct discord_gateway_payload *payload, - struct discord *client) +discord_message_commands_try_perform(struct discord_gateway *gw, + struct discord_message_commands *cmds, + struct discord_gateway_payload *payload) { jsmnf_pair *f = jsmnf_find(payload->data, payload->json, "content", 7); @@ -129,6 +129,7 @@ discord_message_commands_try_perform(struct discord_message_commands *cmds, && !strncmp(cmds->prefix.start, payload->json + f->v.pos, cmds->prefix.size)) { + struct discord *client = CLIENT(gw, gw); struct discord_message *event = calloc(1, sizeof *event); discord_ev_message callback = NULL; struct sized_buffer command; From 4979d0f06efb928fd9ae27390115e46d5fcb584a Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 10 May 2022 11:30:32 -0300 Subject: [PATCH 031/118] chore(examples/shell.c): match 72a45f --- examples/shell.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/shell.c b/examples/shell.c index 6d5bcf54c..a8e6f1b6f 100644 --- a/examples/shell.c +++ b/examples/shell.c @@ -141,8 +141,10 @@ main(int argc, char *argv[]) discord_set_prefix(client, "$"); discord_set_on_command(client, NULL, &on_fallback); discord_set_on_command(client, "cd", &on_cd); - discord_set_on_commands(client, &on_less_like, "less", "cat", "hexdump", - NULL); + + char *cmds[] = { "less", "cat", "hexdump" }; + discord_set_on_commands(client, cmds, sizeof(cmds) / sizeof *cmds, + &on_less_like); print_usage(); do { From 7e86432604d771c3ba2e93c7ee62caa27e5b0711 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 10 May 2022 12:00:40 -0300 Subject: [PATCH 032/118] fix: boundaries check --- src/discord-client.c | 3 ++- src/discord-messagecommands.c | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/discord-client.c b/src/discord-client.c index bebc23da6..c66084bed 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -290,7 +290,8 @@ discord_set_on_command(struct discord *client, char command[], discord_ev_message callback) { - discord_message_commands_append(client->commands, command, strlen(command), + size_t length = (!command || !*command) ? 0 : strlen(command); + discord_message_commands_append(client->commands, command, length, callback); discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); diff --git a/src/discord-messagecommands.c b/src/discord-messagecommands.c index 7234aea42..524ee5b71 100644 --- a/src/discord-messagecommands.c +++ b/src/discord-messagecommands.c @@ -88,16 +88,17 @@ discord_message_commands_append(struct discord_message_commands *cmds, size_t length, discord_ev_message callback) { - struct sized_buffer key; - - key.size = cog_strndup(command, length, &key.start); - - /* fallback callback if prefix is detected, but command isn't specified */ - if (cmds->prefix.size && (!command || !*command)) { + /* define callback as a fallback callback if prefix is detected, but + * command isn't specified */ + if (cmds->prefix.size && !length) { cmds->fallback = callback; - return; } - chash_assign(cmds, key, callback, COMMANDS_TABLE); + else { + struct sized_buffer key; + + key.size = cog_strndup(command, length, &key.start); + chash_assign(cmds, key, callback, COMMANDS_TABLE); + } } static void @@ -123,7 +124,10 @@ discord_message_commands_try_perform(struct discord_gateway *gw, struct discord_message_commands *cmds, struct discord_gateway_payload *payload) { - jsmnf_pair *f = jsmnf_find(payload->data, payload->json, "content", 7); + jsmnf_pair *f; + + if (!(f = jsmnf_find(payload->data, payload->json, "content", 7))) + return false; if (cmds->length && !strncmp(cmds->prefix.start, payload->json + f->v.pos, From 2f5e26e53f0d0b4a600ce5fae9eb616a265dcb04 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 11 May 2022 18:58:30 -0300 Subject: [PATCH 033/118] chore(cog-utils): remove cog_sized_buffer_from_json(), struct sized_buffer, and dead function declaration --- cog-utils/cog-utils.c | 6 ------ cog-utils/cog-utils.h | 41 ----------------------------------------- 2 files changed, 47 deletions(-) diff --git a/cog-utils/cog-utils.c b/cog-utils/cog-utils.c index 7c8db3bfe..0d4da144f 100644 --- a/cog-utils/cog-utils.c +++ b/cog-utils/cog-utils.c @@ -45,12 +45,6 @@ cog_load_whole_file(const char filename[], size_t *len) return str; } -size_t -cog_sized_buffer_from_json(const char str[], size_t len, struct sized_buffer *buf) -{ - return buf->size = cog_strndup(str, len, &buf->start); -} - long cog_timezone(void) { diff --git a/cog-utils/cog-utils.h b/cog-utils/cog-utils.h index 5ddc84c0a..7027707a7 100644 --- a/cog-utils/cog-utils.h +++ b/cog-utils/cog-utils.h @@ -8,20 +8,6 @@ extern "C" { #endif /* __cplusplus */ -/** - * @brief Sized buffer - * - * A very important data structure that is used - * pervasively in the conversion between JSON strings and C structs, - * http request/response body - */ -struct sized_buffer { - /** the buffer's start */ - char *start; - /** the buffer's size in bytes */ - size_t size; -}; - /** * @brief Load file contents into a string * @@ -41,33 +27,6 @@ char *cog_load_whole_file_fp(FILE *fp, size_t *len); */ char *cog_load_whole_file(const char filename[], size_t *len); -/** - * @brief Fill a structure from a JSON file - * - * @param filename the name of the JSON file to be read - * @param p_data a pointer to the structure to be filled - * @param from_json_cb the callback that will receive the JSON data - * and then fill the structure - * @return 1 on success, 0 on failure - */ -int cog_dati_from_fjson(char filename[], - void *p_data, - void(from_json_cb)(char *str, - size_t len, - void *p_data)); - -/** - * @brief Create a copy of JSON string to a `struct sized_buffer` - * - * @param str the JSON string - * @param len the JSON string length - * @param buf the sized buffer - * @return amount of bytes written to buf - */ -size_t cog_sized_buffer_from_json(const char str[], - size_t len, - struct sized_buffer *buf); - /** * @brief Get the difference between UTC and the latest local standard time, in * seconds. From aabb26ef3ff986ae9efda03814f4f00a2d649723 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 11 May 2022 19:05:33 -0300 Subject: [PATCH 034/118] feat: add discord_config_get_field() and match codebase to 2f5e26e --- cog-utils/logconf.c | 9 ++--- cog-utils/logconf.h | 34 +++++++++++++----- core/types.h | 16 +++++++++ core/user-agent.c | 59 +++++++++++++++++-------------- core/user-agent.h | 21 +++++++++--- core/websockets.c | 44 ++++++++++++------------ include/discord-internal.h | 12 +++---- include/discord.h | 17 +++++++-- include/gateway.h | 8 ++--- src/application_command.c | 16 ++++----- src/channel.c | 20 +++++------ src/discord-adapter.c | 61 ++++++++++++++++++++------------- src/discord-adapter_ratelimit.c | 23 +++++++------ src/discord-client.c | 45 +++++++++++++++--------- src/discord-gateway.c | 4 +-- src/discord-messagecommands.c | 8 ++--- src/emoji.c | 4 +-- src/gateway.c | 19 ++++++---- src/guild.c | 26 +++++++------- src/guild_template.c | 2 +- src/interaction.c | 8 ++--- src/invite.c | 2 +- src/user.c | 8 ++--- src/webhook.c | 10 +++--- test/rest.c | 14 ++++---- test/user-agent.c | 4 +-- 26 files changed, 298 insertions(+), 196 deletions(-) diff --git a/cog-utils/logconf.c b/cog-utils/logconf.c index ae9d1c36f..9f51999d9 100644 --- a/cog-utils/logconf.c +++ b/cog-utils/logconf.c @@ -7,6 +7,7 @@ #include /* getpid() */ #include "logconf.h" +#include "cog-utils.h" #define JSMN_STRICT #define JSMN_HEADER @@ -83,8 +84,8 @@ void logconf_http(struct logconf *conf, struct loginfo *p_info, char url[], - struct sized_buffer header, - struct sized_buffer body, + struct logconf_szbuf header, + struct logconf_szbuf body, char label_fmt[], ...) { @@ -319,10 +320,10 @@ logconf_cleanup(struct logconf *conf) memset(conf, 0, sizeof *conf); } -struct sized_buffer +struct logconf_field logconf_get_field(struct logconf *conf, char *const path[], unsigned depth) { - struct sized_buffer field = { 0 }; + struct logconf_field field = { 0 }; jsmn_parser parser; jsmntok_t tokens[256]; diff --git a/cog-utils/logconf.h b/cog-utils/logconf.h index 548634c6b..1a1d55143 100644 --- a/cog-utils/logconf.h +++ b/cog-utils/logconf.h @@ -8,7 +8,6 @@ extern "C" { #include /* uint64_t */ #include "log.h" -#include "cog-utils.h" #define __ERR(fmt, ...) log_fatal(fmt "%s", __VA_ARGS__) @@ -187,6 +186,25 @@ extern "C" { /** Maximum length for module id */ #define LOGCONF_ID_LEN 64 + 1 +/** + * @brief The read-only `config.json` field + * @see logconf_get_field() + */ +struct logconf_field { + /** the buffer's start */ + const char *start; + /** the buffer's size in bytes */ + size_t size; +}; + +/** @brief Generic sized-buffer */ +struct logconf_szbuf { + /** the buffer's start */ + char *start; + /** the buffer's size in bytes */ + size_t size; +}; + /** * @brief A stackful and modularized wrapper over the popular 'log.c' * facilities @@ -205,7 +223,7 @@ struct logconf { /** if true then logging will be ignored for this module */ _Bool is_disabled; /** config file contents */ - struct sized_buffer file; + struct logconf_szbuf file; /** http logging counter */ int *counter; @@ -274,12 +292,12 @@ void logconf_cleanup(struct logconf *conf); * @param conf the `struct logconf` module * @param path the JSON key path * @param depth the path depth - * @return a read-only sized buffer containing the field's value + * @return a read-only sized buffer containing the field's contents * @see logconf_setup() for initializing `conf` with a config file */ -struct sized_buffer logconf_get_field(struct logconf *conf, - char *const path[], - unsigned depth); +struct logconf_field logconf_get_field(struct logconf *conf, + char *const path[], + unsigned depth); /** * @brief Log HTTP transfers @@ -297,8 +315,8 @@ struct sized_buffer logconf_get_field(struct logconf *conf, void logconf_http(struct logconf *conf, struct loginfo *info, char url[], - struct sized_buffer header, - struct sized_buffer body, + struct logconf_szbuf header, + struct logconf_szbuf body, char label_fmt[], ...); diff --git a/core/types.h b/core/types.h index 90ff4b9e7..a4099e984 100644 --- a/core/types.h +++ b/core/types.h @@ -40,6 +40,22 @@ typedef uint64_t u64bitmask; */ typedef char json_char; +/** @brief Generic sized buffer */ +struct ccord_szbuf { + /** the buffer's start */ + char *start; + /** the buffer's size in bytes */ + size_t size; +}; + +/** @brief Read-only generic sized buffer */ +struct ccord_szbuf_readonly { + /** the buffer's start */ + const char *start; + /** the buffer's size in bytes */ + size_t size; +}; + /** @} ConcordTypes */ #endif /* CONCORD_TYPES_H */ diff --git a/core/user-agent.c b/core/user-agent.c index 4de2a6f74..6af634793 100644 --- a/core/user-agent.c +++ b/core/user-agent.c @@ -15,6 +15,14 @@ logconf_fatal(&conn->ua->conf, "(CURLE code: %d) %s", ecode, \ !*conn->errbuf ? curl_easy_strerror(ecode) : conn->errbuf) +/** @brief Generic sized buffer */ +struct _ua_szbuf { + /** the buffer's start */ + char *start; + /** the buffer's size in bytes */ + size_t size; +}; + struct user_agent { /** * queue of connection nodes for easy reuse @@ -23,7 +31,7 @@ struct user_agent { */ struct ua_conn_queue *connq; /** the base_url for every conn */ - struct sized_buffer base_url; + struct _ua_szbuf base_url; /** the user agent logging module */ struct logconf conf; @@ -55,7 +63,7 @@ struct ua_conn { struct ua_info info; /** request URL */ - struct sized_buffer url; + struct _ua_szbuf url; /** the conn request header */ struct curl_slist *header; @@ -444,24 +452,25 @@ _ua_info_reset(struct ua_info *info) static void _ua_info_populate(struct ua_info *info, struct ua_conn *conn) { - struct sized_buffer header = { conn->info.header.buf, - conn->info.header.len }; - struct sized_buffer body = { conn->info.body.buf, conn->info.body.len }; + struct logconf_szbuf logheader = { conn->info.header.buf, + conn->info.header.len }; + struct logconf_szbuf logbody = { conn->info.body.buf, + conn->info.body.len }; char *resp_url = NULL; memcpy(info, &conn->info, sizeof(struct ua_info)); - info->body.len = cog_strndup(body.start, body.size, &info->body.buf); + info->body.len = cog_strndup(logbody.start, logbody.size, &info->body.buf); info->header.len = - cog_strndup(header.start, header.size, &info->header.buf); + cog_strndup(logheader.start, logheader.size, &info->header.buf); /* get response's code */ curl_easy_getinfo(conn->ehandle, CURLINFO_RESPONSE_CODE, &info->httpcode); /* get response's url */ curl_easy_getinfo(conn->ehandle, CURLINFO_EFFECTIVE_URL, &resp_url); - logconf_http(&conn->ua->conf, &conn->info.loginfo, resp_url, header, body, - "HTTP_RCV_%s(%d)", http_code_print(info->httpcode), + logconf_http(&conn->ua->conf, &conn->info.loginfo, resp_url, logheader, + logbody, "HTTP_RCV_%s(%d)", http_code_print(info->httpcode), info->httpcode); } @@ -514,7 +523,7 @@ void ua_cleanup(struct user_agent *ua) { QUEUE(struct ua_conn) - * ua_queues[] = { &ua->connq->idle, &ua->connq->busy }; + *ua_queues[] = { &ua->connq->idle, &ua->connq->busy }; size_t i; /* cleanup connection queues */ @@ -562,22 +571,22 @@ ua_set_url(struct user_agent *ua, const char base_url[]) static void _ua_conn_set_method(struct ua_conn *conn, enum http_method method, - struct sized_buffer *body) + char *body, + size_t body_size) { - static struct sized_buffer blank_body = { "", 0 }; - char logbuf[1024] = ""; - struct sized_buffer logheader = { logbuf, sizeof(logbuf) }; + struct logconf_szbuf logheader = { logbuf, sizeof(logbuf) }; + struct logconf_szbuf logbody = { body, body_size }; const char *method_str = http_method_print(method); struct logconf *conf = &conn->ua->conf; ua_conn_print_header(conn, logbuf, sizeof(logbuf)); /* make sure body points to something */ - if (!body) body = &blank_body; + if (!body) body = ""; - logconf_http(conf, &conn->info.loginfo, conn->url.start, logheader, *body, - "HTTP_SEND_%s", method_str); + logconf_http(conf, &conn->info.loginfo, conn->url.start, logheader, + logbody, "HTTP_SEND_%s", method_str); logconf_trace(conf, ANSICOLOR("SEND", ANSI_FG_GREEN) " %s [@@@_%zu_@@@]", method_str, conn->info.loginfo.counter); @@ -619,8 +628,8 @@ _ua_conn_set_method(struct ua_conn *conn, } /* set ptr to payload that will be sent via POST/PUT/PATCH */ - curl_easy_setopt(conn->ehandle, CURLOPT_POSTFIELDSIZE, body->size); - curl_easy_setopt(conn->ehandle, CURLOPT_POSTFIELDS, body->start); + curl_easy_setopt(conn->ehandle, CURLOPT_POSTFIELDSIZE, body_size); + curl_easy_setopt(conn->ehandle, CURLOPT_POSTFIELDS, body); } /* combine base url with endpoint and assign it to 'conn' */ @@ -669,7 +678,7 @@ void ua_conn_setup(struct ua_conn *conn, struct ua_conn_attr *attr) { _ua_conn_set_url(conn, attr->base_url, attr->endpoint); - _ua_conn_set_method(conn, attr->method, attr->body); + _ua_conn_set_method(conn, attr->method, attr->body, attr->body_size); } /* get request results */ @@ -813,15 +822,15 @@ ua_info_cleanup(struct ua_info *info) } /** attempt to get value from matching response header field */ -struct sized_buffer +struct ua_szbuf_readonly ua_info_get_header(struct ua_info *info, char field[]) { size_t len = strlen(field); - struct sized_buffer value; + struct ua_szbuf_readonly value; int i; for (i = 0; i < info->header.n_pairs; ++i) { - struct sized_buffer header = { + struct ua_szbuf_readonly header = { info->header.buf + info->header.pairs[i].field.idx, info->header.pairs[i].field.size, }; @@ -842,10 +851,10 @@ ua_info_get_header(struct ua_info *info, char field[]) return value; } -struct sized_buffer +struct ua_szbuf_readonly ua_info_get_body(struct ua_info *info) { - struct sized_buffer body = { info->body.buf, info->body.len }; + struct ua_szbuf_readonly body = { info->body.buf, info->body.len }; return body; } diff --git a/core/user-agent.h b/core/user-agent.h index ef256a59d..a9d155c77 100644 --- a/core/user-agent.h +++ b/core/user-agent.h @@ -96,12 +96,22 @@ struct ua_attr { struct logconf *conf; }; +/** @brief Read-only generic sized buffer */ +struct ua_szbuf_readonly { + /** the buffer's start */ + const char *start; + /** the buffer's size in bytes */ + size_t size; +}; + /** @brief Connection attributes */ struct ua_conn_attr { /** the HTTP method of this transfer (GET, POST, ...) */ enum http_method method; /** the optional request body, can be NULL */ - struct sized_buffer *body; + char *body; + /** the request body size */ + size_t body_size; /** the endpoint to be appended to the base URL */ char *endpoint; /** optional base_url to override ua_set_url(), can be NULL */ @@ -337,17 +347,18 @@ void ua_info_cleanup(struct ua_info *info); * * @param info handle containing information on previous request * @param field the header field to fetch the value - * @return a sized_buffer containing the field's value + * @return a @ref ua_szbuf_readonly containing the field's value */ -struct sized_buffer ua_info_get_header(struct ua_info *info, char field[]); +struct ua_szbuf_readonly ua_info_get_header(struct ua_info *info, + char field[]); /** * @brief Get the response body * * @param info handle containing information on previous request - * @return a sized_buffer containing the response body + * @return a @ref ua_szbuf_readonly containing the response body */ -struct sized_buffer ua_info_get_body(struct ua_info *info); +struct ua_szbuf_readonly ua_info_get_body(struct ua_info *info); #ifdef __cplusplus } diff --git a/core/websockets.c b/core/websockets.c index a5b7f1785..982400fbd 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -196,8 +196,8 @@ cws_on_connect_cb(void *p_ws, CURL *ehandle, const char *ws_protocols) logconf_http( &ws->conf, &ws->info.loginfo, ws->base_url, - (struct sized_buffer){ "", 0 }, - (struct sized_buffer){ (char *)ws_protocols, strlen(ws_protocols) }, + (struct logconf_szbuf){ "", 0 }, + (struct logconf_szbuf){ (char *)ws_protocols, strlen(ws_protocols) }, "WS_RCV_CONNECT"); logconf_trace( @@ -219,8 +219,8 @@ cws_on_close_cb(void *p_ws, size_t len) { struct websockets *ws = p_ws; - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)reason, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)reason, len }; (void)ehandle; _ws_set_status(ws, WS_DISCONNECTING); @@ -247,8 +247,8 @@ static void cws_on_text_cb(void *p_ws, CURL *ehandle, const char *text, size_t len) { struct websockets *ws = p_ws; - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)text, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)text, len }; (void)ehandle; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, @@ -267,8 +267,8 @@ static void cws_on_binary_cb(void *p_ws, CURL *ehandle, const void *mem, size_t len) { struct websockets *ws = p_ws; - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)mem, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)mem, len }; (void)ehandle; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, @@ -289,8 +289,8 @@ cws_on_ping_cb(void *p_ws, CURL *ehandle, const char *reason, size_t len) struct websockets *ws = p_ws; (void)ehandle; #if 0 - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)reason, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)reason, len }; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, logbody, "WS_RCV_PING"); @@ -311,8 +311,8 @@ cws_on_pong_cb(void *p_ws, CURL *ehandle, const char *reason, size_t len) struct websockets *ws = p_ws; (void)ehandle; #if 0 - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)reason, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)reason, len }; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, logbody, "WS_RCV_PONG"); @@ -405,8 +405,8 @@ _ws_close(struct websockets *ws, enum ws_close_reason code, const char reason[]) { - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)reason, strlen(reason) }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)reason, strlen(reason) }; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, logbody, "WS_SEND_CLOSE(%d)", code); @@ -547,8 +547,8 @@ ws_send_binary(struct websockets *ws, const char msg[], size_t msglen) { - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)msg, msglen }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)msg, msglen }; logconf_http(&ws->conf, NULL, ws->base_url, logheader, logbody, "WS_SEND_BINARY"); @@ -585,8 +585,8 @@ ws_send_text(struct websockets *ws, const char text[], size_t len) { - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)text, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)text, len }; logconf_http(&ws->conf, NULL, ws->base_url, logheader, logbody, "WS_SEND_TEXT"); @@ -628,8 +628,8 @@ ws_ping(struct websockets *ws, { (void)info; #if 0 - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)reason, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)reason, len }; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, logbody, "WS_SEND_PING"); @@ -667,8 +667,8 @@ ws_pong(struct websockets *ws, { (void)info; #if 0 - struct sized_buffer logheader = { "", 0 }; - struct sized_buffer logbody = { (char *)reason, len }; + struct logconf_szbuf logheader = { "", 0 }; + struct logconf_szbuf logbody = { (char *)reason, len }; logconf_http(&ws->conf, &ws->info.loginfo, ws->base_url, logheader, logbody, "WS_SEND_PONG"); diff --git a/include/discord-internal.h b/include/discord-internal.h index 98287ab17..203af3ccb 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -138,7 +138,7 @@ struct discord_context { /** request body handle @note buffer is kept and recycled */ struct { /** the request body contents */ - struct sized_buffer buf; + struct ccord_szbuf buf; /** the real size occupied in memory by `buf.start` */ size_t memsize; } body; @@ -186,7 +186,7 @@ struct discord_adapter { */ void discord_adapter_init(struct discord_adapter *adapter, struct logconf *conf, - struct sized_buffer *token); + struct ccord_szbuf_readonly *token); /** * @brief Free a Discord Adapter handle @@ -212,7 +212,7 @@ void discord_adapter_cleanup(struct discord_adapter *adapter); */ CCORDcode discord_adapter_run(struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint_fmt[], ...); @@ -522,7 +522,7 @@ struct discord_gateway { */ void discord_gateway_init(struct discord_gateway *gw, struct logconf *conf, - struct sized_buffer *token); + struct ccord_szbuf_readonly *token); /** * @brief Free a Discord Gateway handle @@ -790,7 +790,7 @@ struct discord_message_commands { /** DISCORD_MESSAGE_COMMANDS logging module */ struct logconf conf; /** the prefix expected for every command */ - struct sized_buffer prefix; + struct ccord_szbuf prefix; /** fallback message command @see discord_set_on_command() */ discord_ev_message fallback; /** amount of message commands created */ @@ -891,7 +891,7 @@ struct discord { /** whether this is the original client or a clone */ bool is_original; /** the bot token */ - struct sized_buffer token; + struct ccord_szbuf_readonly token; /** the io poller for listening to file descriptors */ struct io_poller *io_poller; /** the handle for interfacing with Discord's REST API */ diff --git a/include/discord.h b/include/discord.h index e99fda0f9..3b2912111 100644 --- a/include/discord.h +++ b/include/discord.h @@ -149,13 +149,26 @@ const char *discord_strerror(CCORDcode code, struct discord *client); struct discord *discord_init(const char token[]); /** - * @brief Create a Discord Client handle by a bot.config file + * @brief Create a Discord Client handle by a `config.json` file * - * @param config_file the bot.config file name + * @param config_file the `config.json` file name * @return the newly created Discord Client handle */ struct discord *discord_config_init(const char config_file[]); +/** + * @brief Get the contents from the config file field + * @note only works if your bot has been initialized with discord_config_init() + * + * @param client the client created with discord_config_init() + * @param path the JSON key path + * @param depth the path depth + * @return a read-only sized buffer containing the field's contents + */ +struct ccord_szbuf_readonly discord_config_get_field(struct discord *client, + char *const path[], + unsigned depth); + /** * @brief Clone a discord client * diff --git a/include/gateway.h b/include/gateway.h index f7f73d60f..05dab2574 100644 --- a/include/gateway.h +++ b/include/gateway.h @@ -19,12 +19,12 @@ * @warning This function blocks the running thread * * @param client the client created with discord_init() - * @param ret if successful, a @ref sized_buffer containing the JSON response + * @param ret if successful, a @ref ccord_szbuf containing the JSON response * @param ret a sized buffer containing the response JSON * @CCORD_return */ CCORDcode discord_get_gateway(struct discord *client, - struct sized_buffer *ret); + struct ccord_szbuf *ret); /** * @brief Get a single valid WSS URL, and additional metadata that can help @@ -35,12 +35,12 @@ CCORDcode discord_get_gateway(struct discord *client, * @warning This function blocks the running thread * * @param client the client created with discord_init() - * @param ret if successful, a @ref sized_buffer containing the JSON response + * @param ret if successful, a @ref ccord_szbuf containing the JSON response * @param ret a sized buffer containing the response JSON * @CCORD_return */ CCORDcode discord_get_gateway_bot(struct discord *client, - struct sized_buffer *ret); + struct ccord_szbuf *ret); /** @defgroup DiscordAPIGatewayHelper Helper functions * @brief Custom helper functions diff --git a/src/application_command.c b/src/application_command.c index d32642897..d7131bffd 100644 --- a/src/application_command.c +++ b/src/application_command.c @@ -31,7 +31,7 @@ discord_create_global_application_command( struct discord_ret_application_command *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -79,7 +79,7 @@ discord_edit_global_application_command( struct discord_ret_application_command *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -122,7 +122,7 @@ discord_bulk_overwrite_global_application_command( struct discord_ret_application_commands *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[8192]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -167,7 +167,7 @@ discord_create_guild_application_command( struct discord_ret_application_command *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -221,7 +221,7 @@ discord_edit_guild_application_command( struct discord_ret_application_command *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -270,7 +270,7 @@ discord_bulk_overwrite_guild_application_command( struct discord_ret_application_commands *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[8192]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -340,7 +340,7 @@ discord_edit_application_command_permissions( struct discord_ret_application_command_permission *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[8192]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); @@ -368,7 +368,7 @@ discord_batch_edit_application_command_permissions( struct discord_ret_guild_application_command_permissions *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[8192]; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); diff --git a/src/channel.c b/src/channel.c index aafeacc4c..267c30dd2 100644 --- a/src/channel.c +++ b/src/channel.c @@ -105,7 +105,7 @@ discord_modify_channel(struct discord *client, struct discord_ret_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -206,7 +206,7 @@ discord_create_message(struct discord *client, struct discord_ret_message *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -486,7 +486,7 @@ discord_edit_message(struct discord *client, struct discord_ret_message *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[16384]; /**< @todo dynamic buffer */ CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -530,7 +530,7 @@ discord_bulk_delete_messages(struct discord *client, { struct discord_request req = { 0 }; u64unix_ms now = discord_timestamp(client); - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096] = ""; int i; @@ -567,7 +567,7 @@ discord_edit_channel_permissions( struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -607,7 +607,7 @@ discord_create_channel_invite(struct discord *client, struct discord_ret_invite *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024] = "{}"; size_t len = 2; @@ -650,7 +650,7 @@ discord_follow_news_channel(struct discord *client, struct discord_ret_followed_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[256]; /* should be more than enough for this */ CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -741,7 +741,7 @@ discord_group_dm_add_recipient(struct discord *client, struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -786,7 +786,7 @@ discord_start_thread_with_message( struct discord_ret_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -813,7 +813,7 @@ discord_start_thread_without_message( struct discord_ret_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); diff --git a/src/discord-adapter.c b/src/discord-adapter.c index a85dad801..64ed3ff4d 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -11,7 +11,7 @@ static void setopt_cb(struct ua_conn *conn, void *p_token) { - struct sized_buffer *token = p_token; + struct ccord_szbuf *token = p_token; char auth[128]; int len; @@ -37,7 +37,7 @@ on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) void discord_adapter_init(struct discord_adapter *adapter, struct logconf *conf, - struct sized_buffer *token) + struct ccord_szbuf_readonly *token) { struct ua_attr attr = { 0 }; @@ -108,14 +108,14 @@ discord_adapter_cleanup(struct discord_adapter *adapter) static CCORDcode _discord_adapter_run_sync(struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]); static CCORDcode _discord_adapter_run_async(struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]); @@ -124,19 +124,25 @@ static CCORDcode _discord_adapter_run_async(struct discord_adapter *adapter, CCORDcode discord_adapter_run(struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint_fmt[], ...) { - static struct discord_request blank_req = { 0 }; char endpoint[DISCORD_ENDPT_LEN]; char key[DISCORD_ROUTE_LEN]; va_list args; int len; /* have it point somewhere */ - if (!req) req = &blank_req; + if (!req) { + static struct discord_request blank = { 0 }; + req = ␣ + } + if (!body) { + static struct ccord_szbuf blank = { 0 }; + body = ␣ + } /* build the endpoint string */ va_start(args, endpoint_fmt); @@ -167,7 +173,7 @@ _discord_context_to_mime(curl_mime *mime, void *p_cxt) { struct discord_context *cxt = p_cxt; struct discord_attachments *atchs = &cxt->req.attachments; - struct sized_buffer *body = &cxt->body.buf; + struct ccord_szbuf *body = &cxt->body.buf; curl_mimepart *part; char name[64]; int i; @@ -247,7 +253,7 @@ _discord_adapter_get_info(struct discord_adapter *adapter, "received HTTP method"); return false; case HTTP_TOO_MANY_REQUESTS: { - struct sized_buffer body = ua_info_get_body(info); + struct ua_szbuf_readonly body = ua_info_get_body(info); struct jsmnftok message = { 0 }; double retry_after = 1.0; bool is_global = false; @@ -300,12 +306,11 @@ _discord_adapter_get_info(struct discord_adapter *adapter, static CCORDcode _discord_adapter_run_sync(struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct ua_conn_attr conn_attr = { method, body, endpoint, NULL }; /* throw-away for ua_conn_set_mime() */ struct discord_context cxt = { 0 }; struct discord_bucket *b; @@ -328,7 +333,13 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, ua_conn_add_header(conn, "Content-Type", "application/json"); } - ua_conn_setup(conn, &conn_attr); + ua_conn_setup(conn, &(struct ua_conn_attr){ + .method = method, + .body = body->start, + .body_size = body->size, + .endpoint = endpoint, + .base_url = NULL, + }); pthread_mutex_lock(&b->lock); do { @@ -338,8 +349,8 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, switch (code = ua_conn_easy_perform(conn)) { case CCORD_OK: { struct discord *client = CLIENT(adapter, adapter); + struct ua_szbuf_readonly resp; struct ua_info info = { 0 }; - struct sized_buffer resp; int64_t wait_ms = 0; ua_info_extract(conn, &info); @@ -446,7 +457,7 @@ static void _discord_context_populate(struct discord_context *cxt, struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) @@ -482,7 +493,7 @@ _discord_context_populate(struct discord_context *cxt, static CCORDcode _discord_adapter_run_async(struct discord_adapter *adapter, struct discord_request *req, - struct sized_buffer *body, + struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) @@ -521,21 +532,18 @@ static CCORDcode _discord_adapter_send(struct discord_adapter *adapter, struct discord_bucket *b) { - struct ua_conn_attr conn_attr = { 0 }; struct discord_context *cxt; CURLMcode mcode; CURL *ehandle; + /** TODO: make this a discord_context_xxx() function */ QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->waitq); QUEUE_REMOVE(qelem); QUEUE_INIT(qelem); cxt = QUEUE_DATA(qelem, struct discord_context, entry); cxt->conn = ua_conn_start(adapter->ua); - - conn_attr.method = cxt->method; - conn_attr.body = &cxt->body.buf; - conn_attr.endpoint = cxt->endpoint; + /**/ if (HTTP_MIMEPOST == cxt->method) { ua_conn_add_header(cxt->conn, "Content-Type", "multipart/form-data"); @@ -544,7 +552,13 @@ _discord_adapter_send(struct discord_adapter *adapter, else { ua_conn_add_header(cxt->conn, "Content-Type", "application/json"); } - ua_conn_setup(cxt->conn, &conn_attr); + + ua_conn_setup(cxt->conn, &(struct ua_conn_attr){ + .method = cxt->method, + .body = cxt->body.buf.start, + .body_size = cxt->body.buf.size, + .endpoint = cxt->endpoint, + }); ehandle = ua_conn_get_easy_handle(cxt->conn); @@ -599,7 +613,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, switch (msg->data.result) { case CURLE_OK: { struct ua_info info = { 0 }; - struct sized_buffer body; + struct ua_szbuf_readonly body; ua_info_extract(cxt->conn, &info); retry = _discord_adapter_get_info(adapter, &info, &wait_ms); @@ -667,7 +681,8 @@ _discord_adapter_check_action(struct discord_adapter *adapter, } } else { - discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, cxt->req.ret.data); + discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, + cxt->req.ret.data); _discord_context_reset(cxt); QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); } diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index a203c1906..71df2968b 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -118,7 +118,7 @@ discord_ratelimiter_build_key(enum http_method method, static struct discord_bucket * _discord_bucket_init(struct discord_ratelimiter *rl, const char key[], - const struct sized_buffer *hash, + const struct ua_szbuf_readonly *hash, const long limit) { struct discord_bucket *b = calloc(1, sizeof *b); @@ -145,7 +145,7 @@ _discord_bucket_init(struct discord_ratelimiter *rl, struct discord_ratelimiter * discord_ratelimiter_init(struct logconf *conf) { - const struct sized_buffer keynull = { "null", 4 }, keymiss = { "miss", 4 }; + struct ua_szbuf_readonly keynull = { "null", 4 }, keymiss = { "miss", 4 }; struct discord_ratelimiter *rl = chash_init(rl, RATELIMITER_TABLE); logconf_branch(&rl->conf, conf, "DISCORD_RATELIMIT"); @@ -296,14 +296,14 @@ _discord_ratelimiter_get_match(struct discord_ratelimiter *rl, /* create bucket if it doesn't exist yet */ if (NULL == (b = _discord_bucket_find(rl, key))) { - struct sized_buffer hash = + struct ua_szbuf_readonly hash = ua_info_get_header(info, "x-ratelimit-bucket"); if (!hash.size) { /* bucket is not part of a ratelimiting group */ b = rl->miss; } else { - struct sized_buffer limit = + struct ua_szbuf_readonly limit = ua_info_get_header(info, "x-ratelimit-limit"); long _limit = limit.size ? strtol(limit.start, NULL, 10) : LONG_MAX; @@ -323,18 +323,19 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, struct discord_bucket *b, struct ua_info *info) { - struct sized_buffer remaining = - ua_info_get_header(info, "x-ratelimit-remaining"), - reset = ua_info_get_header(info, "x-ratelimit-reset"), - reset_after = ua_info_get_header( - info, "x-ratelimit-reset-after"); + struct ua_szbuf_readonly remaining = ua_info_get_header( + info, "x-ratelimit-remaining"), + reset = + ua_info_get_header(info, "x-ratelimit-reset"), + reset_after = ua_info_get_header( + info, "x-ratelimit-reset-after"); u64unix_ms now = cog_timestamp_ms(); b->remaining = remaining.size ? strtol(remaining.start, NULL, 10) : 1L; /* use X-Ratelimit-Reset-After if available, X-Ratelimit-Reset otherwise */ if (reset_after.size) { - struct sized_buffer global = + struct ua_szbuf_readonly global = ua_info_get_header(info, "x-ratelimit-global"); u64unix_ms reset_tstamp = now + (u64unix_ms)(1000 * strtod(reset_after.start, NULL)); @@ -351,7 +352,7 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, } } else if (reset.size) { - struct sized_buffer date = ua_info_get_header(info, "date"); + struct ua_szbuf_readonly date = ua_info_get_header(info, "date"); /* get approximate elapsed time since request */ struct PsnipClockTimespec ts; /* the Discord time in milliseconds */ diff --git a/src/discord-client.c b/src/discord-client.c index c66084bed..b65f1c93c 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -61,7 +61,7 @@ discord_config_init(const char config_file[]) { char *path[2] = { "discord", "" }; struct discord *new_client; - struct sized_buffer buf; + struct logconf_field field; FILE *fp; fp = fopen(config_file, "rb"); @@ -74,45 +74,46 @@ discord_config_init(const char config_file[]) fclose(fp); path[1] = "token"; - new_client->token = logconf_get_field(&new_client->conf, path, - sizeof(path) / sizeof *path); - if (!strncmp("YOUR-BOT-TOKEN", new_client->token.start, - new_client->token.size)) - { + field = logconf_get_field(&new_client->conf, path, + sizeof(path) / sizeof *path); + if (!strncmp("YOUR-BOT-TOKEN", field.start, field.size)) memset(&new_client->token, 0, sizeof(new_client->token)); + else { + new_client->token.start = field.start; + new_client->token.size = field.size; } - _discord_init(new_client); /* check for default prefix in config file */ path[1] = "default_prefix"; - buf = logconf_get_field(&new_client->conf, path, - sizeof(path) / sizeof *path); - if (buf.size) { + field = logconf_get_field(&new_client->conf, path, + sizeof(path) / sizeof *path); + if (field.size) { jsmn_parser parser; jsmntok_t tokens[16]; jsmn_init(&parser); - if (0 < jsmn_parse(&parser, buf.start, buf.size, tokens, + if (0 < jsmn_parse(&parser, field.start, field.size, tokens, sizeof(tokens) / sizeof *tokens)) { jsmnf_loader loader; jsmnf_pair pairs[16]; jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, buf.start, tokens, parser.toknext, + if (0 < jsmnf_load(&loader, field.start, tokens, parser.toknext, pairs, sizeof(pairs) / sizeof *pairs)) { bool enable_prefix = false; jsmnf_pair *f; - if ((f = jsmnf_find(pairs, buf.start, "enable", 6))) - enable_prefix = ('t' == buf.start[f->v.pos]); + if ((f = jsmnf_find(pairs, field.start, "enable", 6))) + enable_prefix = ('t' == field.start[f->v.pos]); if (enable_prefix - && (f = jsmnf_find(pairs, buf.start, "prefix", 6))) { - discord_message_commands_set_prefix( - new_client->commands, buf.start + f->v.pos, f->v.len); + && (f = jsmnf_find(pairs, field.start, "prefix", 6))) { + discord_message_commands_set_prefix(new_client->commands, + field.start + f->v.pos, + f->v.len); } } } @@ -664,3 +665,13 @@ discord_get_io_poller(struct discord *client) { return client->io_poller; } + +struct ccord_szbuf_readonly +discord_config_get_field(struct discord *client, + char *const path[], + unsigned depth) +{ + struct logconf_field field = logconf_get_field(&client->conf, path, depth); + + return (struct ccord_szbuf_readonly){ field.start, field.size }; +} diff --git a/src/discord-gateway.c b/src/discord-gateway.c index c53ba0a8d..04cb1078a 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -503,7 +503,7 @@ on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) void discord_gateway_init(struct discord_gateway *gw, struct logconf *conf, - struct sized_buffer *token) + struct ccord_szbuf_readonly *token) { struct discord *client = CLIENT(gw, gw); @@ -664,7 +664,7 @@ CCORDcode discord_gateway_start(struct discord_gateway *gw) { struct discord *client = CLIENT(gw, gw); - struct sized_buffer json = { 0 }; + struct ccord_szbuf json = { 0 }; char url[1024]; CURL *ehandle; diff --git a/src/discord-messagecommands.c b/src/discord-messagecommands.c index 524ee5b71..d32446477 100644 --- a/src/discord-messagecommands.c +++ b/src/discord-messagecommands.c @@ -38,7 +38,7 @@ struct _discord_message_commands_entry { /** message command */ - struct sized_buffer command; + struct ccord_szbuf command; /** the callback assigned to the command */ discord_ev_message callback; /** the route state in the hashtable (see chash.h 'State enums') */ @@ -70,7 +70,7 @@ discord_message_commands_find(struct discord_message_commands *cmds, const char command[], size_t length) { - struct sized_buffer key = { (char *)command, length }; + struct ccord_szbuf key = { (char *)command, length }; discord_ev_message callback = NULL; int ret; @@ -94,7 +94,7 @@ discord_message_commands_append(struct discord_message_commands *cmds, cmds->fallback = callback; } else { - struct sized_buffer key; + struct ccord_szbuf key; key.size = cog_strndup(command, length, &key.start); chash_assign(cmds, key, callback, COMMANDS_TABLE); @@ -136,7 +136,7 @@ discord_message_commands_try_perform(struct discord_gateway *gw, struct discord *client = CLIENT(gw, gw); struct discord_message *event = calloc(1, sizeof *event); discord_ev_message callback = NULL; - struct sized_buffer command; + struct ccord_szbuf command; char *tmp; discord_message_from_jsmnf(payload->data, payload->json, event); diff --git a/src/emoji.c b/src/emoji.c index 5f1e37e10..83a48f46a 100644 --- a/src/emoji.c +++ b/src/emoji.c @@ -46,7 +46,7 @@ discord_create_guild_emoji(struct discord *client, struct discord_ret_emoji *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[2048]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -69,7 +69,7 @@ discord_modify_guild_emoji(struct discord *client, struct discord_ret_emoji *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[2048]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); diff --git a/src/gateway.c b/src/gateway.c index e9246376f..33853b9d9 100644 --- a/src/gateway.c +++ b/src/gateway.c @@ -17,7 +17,7 @@ discord_disconnect_guild_member(struct discord *client, struct discord_ret_guild_member *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[128]; jsonb b; @@ -47,15 +47,21 @@ discord_disconnect_guild_member(struct discord *client, * REST functions ******************************************************************************/ +static size_t +_ccord_szbuf_from_json(const char str[], size_t len, void *p_buf) +{ + struct ccord_szbuf *buf = p_buf; + return buf->size = cog_strndup(str, len, &buf->start); +} + CCORDcode -discord_get_gateway(struct discord *client, struct sized_buffer *ret) +discord_get_gateway(struct discord *client, struct ccord_szbuf *ret) { struct discord_request req = { 0 }; CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); - req.gnrc.from_json = - (size_t(*)(const char *, size_t, void *))cog_sized_buffer_from_json; + req.gnrc.from_json = &_ccord_szbuf_from_json; req.ret.has_type = true; req.ret.sync = ret; @@ -64,14 +70,13 @@ discord_get_gateway(struct discord *client, struct sized_buffer *ret) } CCORDcode -discord_get_gateway_bot(struct discord *client, struct sized_buffer *ret) +discord_get_gateway_bot(struct discord *client, struct ccord_szbuf *ret) { struct discord_request req = { 0 }; CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); - req.gnrc.from_json = - (size_t(*)(const char *, size_t, void *))cog_sized_buffer_from_json; + req.gnrc.from_json = &_ccord_szbuf_from_json; req.ret.has_type = true; req.ret.sync = ret; diff --git a/src/guild.c b/src/guild.c index 18bf634cb..7f01932c6 100644 --- a/src/guild.c +++ b/src/guild.c @@ -12,7 +12,7 @@ discord_create_guild(struct discord *client, struct discord_ret_guild *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, params != NULL, CCORD_BAD_PARAMETER, ""); @@ -63,7 +63,7 @@ discord_modify_guild(struct discord *client, struct discord_ret_guild *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -115,7 +115,7 @@ discord_create_guild_channel(struct discord *client, struct discord_ret_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[2048]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -138,7 +138,7 @@ discord_modify_guild_channel_positions( struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -251,7 +251,7 @@ discord_add_guild_member(struct discord *client, struct discord_ret_guild_member *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -278,7 +278,7 @@ discord_modify_guild_member(struct discord *client, struct discord_ret_guild_member *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[2048]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -301,7 +301,7 @@ discord_modify_current_member(struct discord *client, struct discord_ret_guild_member *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[512]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -325,7 +325,7 @@ discord_modify_current_user_nick( struct discord_ret_guild_member *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[512]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -448,7 +448,7 @@ discord_create_guild_ban(struct discord *client, struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[256]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -508,7 +508,7 @@ discord_create_guild_role(struct discord *client, struct discord_ret_role *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -530,7 +530,7 @@ discord_modify_guild_role_positions( struct discord_ret_roles *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -554,7 +554,7 @@ discord_modify_guild_role(struct discord *client, struct discord_ret_role *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[2048] = "{}"; size_t len = 2; @@ -599,7 +599,7 @@ discord_begin_guild_prune(struct discord *client, struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[4096] = "{}"; size_t len = 2; diff --git a/src/guild_template.c b/src/guild_template.c index d4611da58..ba716888a 100644 --- a/src/guild_template.c +++ b/src/guild_template.c @@ -28,7 +28,7 @@ discord_create_guild_template(struct discord *client, struct discord_ret_guild_template *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[256]; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); diff --git a/src/interaction.c b/src/interaction.c index 2e09ddddf..7632c2a85 100644 --- a/src/interaction.c +++ b/src/interaction.c @@ -15,7 +15,7 @@ discord_create_interaction_response( struct discord_ret_interaction_response *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[4096]; @@ -71,7 +71,7 @@ discord_edit_original_interaction_response( struct discord_ret_interaction_response *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -126,7 +126,7 @@ discord_create_followup_message(struct discord *client, struct discord_ret_webhook *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ char query[4096] = ""; @@ -191,7 +191,7 @@ discord_edit_followup_message(struct discord *client, struct discord_ret_message *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ diff --git a/src/invite.c b/src/invite.c index e3d96141d..8ac74c573 100644 --- a/src/invite.c +++ b/src/invite.c @@ -13,7 +13,7 @@ discord_get_invite(struct discord *client, struct discord_ret_invite *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, NOT_EMPTY_STR(invite_code), CCORD_BAD_PARAMETER, ""); diff --git a/src/user.c b/src/user.c index 4c06333e7..18cdd41d1 100644 --- a/src/user.c +++ b/src/user.c @@ -38,7 +38,7 @@ discord_modify_current_user(struct discord *client, struct discord_ret_user *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, params != NULL, CCORD_BAD_PARAMETER, ""); @@ -70,7 +70,7 @@ discord_leave_guild(struct discord *client, struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body = { "{}", 2 }; + struct ccord_szbuf body = { "{}", 2 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -86,7 +86,7 @@ discord_create_dm(struct discord *client, struct discord_ret_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[128]; CCORD_EXPECT(client, params != NULL, CCORD_BAD_PARAMETER, ""); @@ -106,7 +106,7 @@ discord_create_group_dm(struct discord *client, struct discord_ret_channel *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, params != NULL, CCORD_BAD_PARAMETER, ""); diff --git a/src/webhook.c b/src/webhook.c index 40a057a1c..351e6cb23 100644 --- a/src/webhook.c +++ b/src/webhook.c @@ -13,7 +13,7 @@ discord_create_webhook(struct discord *client, struct discord_ret_webhook *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -100,7 +100,7 @@ discord_modify_webhook(struct discord *client, struct discord_ret_webhook *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); @@ -123,7 +123,7 @@ discord_modify_webhook_with_token( struct discord_ret_webhook *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; char buf[1024]; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); @@ -183,7 +183,7 @@ discord_execute_webhook(struct discord *client, struct discord_ret *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ char query[4096] = ""; @@ -253,7 +253,7 @@ discord_edit_webhook_message(struct discord *client, struct discord_ret_message *ret) { struct discord_request req = { 0 }; - struct sized_buffer body; + struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ diff --git a/test/rest.c b/test/rest.c index 4a8d6d790..09dbde23f 100644 --- a/test/rest.c +++ b/test/rest.c @@ -73,10 +73,11 @@ check_sync_trigger_error_on_bogus_parameter(void) SUITE(synchronous) { - /* get test-channel id */ - struct logconf *conf = discord_get_logconf(CLIENT); char *path[] = { "test", "channel_id" }; - struct sized_buffer json = logconf_get_field(conf, path, 2); + + /* get test-channel id */ + struct ccord_szbuf_readonly json = + discord_config_get_field(CLIENT, path, 2); u64snowflake channel_id = strtoull(json.start, NULL, 10); RUN_TEST(check_sync_fetch_object); @@ -181,10 +182,11 @@ check_async_trigger_error_on_bogus_parameter(void) SUITE(asynchronous) { - /* get test-channel id */ - struct logconf *conf = discord_get_logconf(CLIENT); char *path[] = { "test", "channel_id" }; - struct sized_buffer json = logconf_get_field(conf, path, 2); + + /* get test-channel id */ + struct ccord_szbuf_readonly json = + discord_config_get_field(CLIENT, path, 2); u64snowflake channel_id = strtoull(json.start, NULL, 10); RUN_TEST(check_async_fetch_object); diff --git a/test/user-agent.c b/test/user-agent.c index 7295e1617..7d953e303 100644 --- a/test/user-agent.c +++ b/test/user-agent.c @@ -16,7 +16,6 @@ commit(char *base_url, struct logconf *conf) struct user_agent *ua; struct ua_resp_handle handle = { .ok_cb = load, .ok_obj = NULL }; - struct sized_buffer body = { .start = "{ }", .size = 3 }; struct ua_conn_attr conn_attr = { 0 }; struct ua_info info = { 0 }; @@ -25,7 +24,8 @@ commit(char *base_url, struct logconf *conf) ua = ua_init(&ua_attr); ua_set_url(ua, base_url); - conn_attr.body = &body; + conn_attr.body = "{ }"; + conn_attr.body_size = 3; conn_attr.method = HTTP_POST; conn_attr.endpoint = "/echo?m=POST"; From 6e3d6248d775d1d7e930d3cd9338aefcfee7f3ce Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 12 May 2022 18:54:36 -0300 Subject: [PATCH 035/118] wip: rollback removal of const keyword for event's parameter --- README.md | 8 +-- examples/8ball.c | 4 +- examples/audit-log.c | 11 +++-- examples/ban.c | 10 ++-- examples/channel.c | 37 +++++++++----- examples/components.c | 8 +-- examples/copycat.c | 13 ++--- examples/embed.c | 8 +-- examples/emoji.c | 7 +-- examples/guild-template.c | 11 +++-- examples/guild.c | 23 ++++----- examples/invite.c | 6 +-- examples/manual-dm.c | 4 +- examples/pin.c | 8 +-- examples/ping-pong.c | 6 +-- examples/presence.c | 2 +- examples/reaction.c | 14 +++--- examples/shell.c | 8 +-- examples/slash-commands.c | 9 ++-- examples/slash-commands2.c | 10 ++-- examples/spam.c | 4 +- examples/voice-join.c | 11 +++-- include/discord-events.h | 99 ++++++++++++++++++++------------------ test/async.c | 14 +++--- test/sync.c | 19 ++++---- 25 files changed, 189 insertions(+), 165 deletions(-) diff --git a/README.md b/README.md index befad4ac8..68ce7ec6a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Concord is an asynchronous C99 Discord API library. It has minimal external depe #include #include -void on_ready(struct discord *client, struct discord_ready *event) { +void on_ready(struct discord *client, const struct discord_ready *event) { struct discord_create_guild_application_command params = { .name = "ping", .description = "Ping command!" @@ -34,7 +34,7 @@ void on_ready(struct discord *client, struct discord_ready *event) { GUILD_ID, ¶ms, NULL); } -void on_interaction(struct discord *client, struct discord_interaction *event) { +void on_interaction(struct discord *client, const struct discord_interaction *event) { if (event->type != DISCORD_INTERACTION_APPLICATION_COMMAND) return; /* return if interaction isn't a slash command */ @@ -68,11 +68,11 @@ int main(void) { #include #include -void on_ready(struct discord *client, struct discord_ready *event) { +void on_ready(struct discord *client, const struct discord_ready *event) { log_info("Logged in as %s!", event->user->username); } -void on_message(struct discord *client, struct discord_message *event) { +void on_message(struct discord *client, const struct discord_message *event) { if (strcmp(event->content, "ping") == 0) { struct discord_create_message params = { .content = "pong" }; discord_create_message(client, event->channel_id, ¶ms, NULL); diff --git a/examples/8ball.c b/examples/8ball.c index 2f5d57365..d231e6779 100644 --- a/examples/8ball.c +++ b/examples/8ball.c @@ -15,14 +15,14 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("8ball-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -eight_ball(struct discord *client, struct discord_message *event) +eight_ball(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/audit-log.c b/examples/audit-log.c index eeb6fbb77..519c0a912 100644 --- a/examples/audit-log.c +++ b/examples/audit-log.c @@ -21,7 +21,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Audit-Log-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -29,7 +29,7 @@ on_ready(struct discord *client, struct discord_ready *event) void log_on_guild_member_add(struct discord *client, - struct discord_guild_member *event) + const struct discord_guild_member *event) { log_info("%s#%s joined guild %" PRIu64, event->user->username, event->user->discriminator, event->guild_id); @@ -37,7 +37,7 @@ log_on_guild_member_add(struct discord *client, void log_on_guild_member_update(struct discord *client, - struct discord_guild_member_update *event) + const struct discord_guild_member_update *event) { char nick[128] = ""; @@ -50,7 +50,7 @@ log_on_guild_member_update(struct discord *client, void log_on_guild_member_remove(struct discord *client, - struct discord_guild_member_remove *event) + const struct discord_guild_member_remove *event) { log_info("%s#%s left guild %" PRIu64, event->user->username, event->user->discriminator, event->guild_id); @@ -89,7 +89,8 @@ fail(struct discord *client, CCORDcode code, void *data) } void -on_audit_channel_create(struct discord *client, struct discord_message *event) +on_audit_channel_create(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/ban.c b/examples/ban.c index d23873dc7..6494c8f7e 100644 --- a/examples/ban.c +++ b/examples/ban.c @@ -16,7 +16,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Ban-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -24,7 +24,7 @@ on_ready(struct discord *client, struct discord_ready *event) void log_on_guild_ban_add(struct discord *client, - struct discord_guild_ban_add *event) + const struct discord_guild_ban_add *event) { log_info("User `%s#%s` has been banned.", event->user->username, event->user->discriminator); @@ -32,14 +32,14 @@ log_on_guild_ban_add(struct discord *client, void log_on_guild_ban_remove(struct discord *client, - struct discord_guild_ban_remove *event) + const struct discord_guild_ban_remove *event) { log_info("User `%s#%s` has been unbanned.", event->user->username, event->user->discriminator); } void -on_ban(struct discord *client, struct discord_message *event) +on_ban(struct discord *client, const struct discord_message *event) { u64snowflake target_id = 0ULL; sscanf(event->content, "%" SCNu64, &target_id); @@ -53,7 +53,7 @@ on_ban(struct discord *client, struct discord_message *event) } void -on_unban(struct discord *client, struct discord_message *event) +on_unban(struct discord *client, const struct discord_message *event) { u64snowflake target_id = 0ULL; sscanf(event->content, "%" SCNu64, &target_id); diff --git a/examples/channel.c b/examples/channel.c index 47ba041e8..a9d58f0ad 100644 --- a/examples/channel.c +++ b/examples/channel.c @@ -24,50 +24,56 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Channel-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -log_on_channel_create(struct discord *client, struct discord_channel *event) +log_on_channel_create(struct discord *client, + const struct discord_channel *event) { log_info("Channel %s (%" PRIu64 ") created", event->name, event->id); } void -log_on_channel_update(struct discord *client, struct discord_channel *event) +log_on_channel_update(struct discord *client, + const struct discord_channel *event) { log_info("Channel %s (%" PRIu64 ") updated", event->name, event->id); } void -log_on_channel_delete(struct discord *client, struct discord_channel *event) +log_on_channel_delete(struct discord *client, + const struct discord_channel *event) { log_info("Channel %s (%" PRIu64 ") deleted", event->name, event->id); } void -log_on_thread_create(struct discord *client, struct discord_channel *event) +log_on_thread_create(struct discord *client, + const struct discord_channel *event) { log_info("Thread %s (%" PRIu64 ") created", event->name, event->id); } void -log_on_thread_update(struct discord *client, struct discord_channel *event) +log_on_thread_update(struct discord *client, + const struct discord_channel *event) { log_info("Thread %s (%" PRIu64 ") updated", event->name, event->id); } void -log_on_thread_delete(struct discord *client, struct discord_channel *event) +log_on_thread_delete(struct discord *client, + const struct discord_channel *event) { log_info("Thread %s (%" PRIu64 ") deleted", event->name, event->id); } void -on_channel_create(struct discord *client, struct discord_message *event) +on_channel_create(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -76,7 +82,8 @@ on_channel_create(struct discord *client, struct discord_message *event) } void -on_channel_rename_this(struct discord *client, struct discord_message *event) +on_channel_rename_this(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -85,7 +92,8 @@ on_channel_rename_this(struct discord *client, struct discord_message *event) } void -on_channel_delete_this(struct discord *client, struct discord_message *event) +on_channel_delete_this(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -119,7 +127,8 @@ fail_get_channel_invites(struct discord *client, CCORDcode code, void *data) } void -on_channel_get_invites(struct discord *client, struct discord_message *event) +on_channel_get_invites(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -157,7 +166,8 @@ fail_create_channel_invite(struct discord *client, CCORDcode code, void *data) } void -on_channel_create_invite(struct discord *client, struct discord_message *event) +on_channel_create_invite(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -198,7 +208,8 @@ fail_start_thread(struct discord *client, CCORDcode code, void *data) } void -on_channel_start_thread(struct discord *client, struct discord_message *event) +on_channel_start_thread(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/components.c b/examples/components.c index 80fe1eb50..86f19e41e 100644 --- a/examples/components.c +++ b/examples/components.c @@ -67,14 +67,14 @@ char JSON[] = "]\n"; void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Components-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_dynamic(struct discord *client, struct discord_message *event) +on_dynamic(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -93,7 +93,7 @@ on_dynamic(struct discord *client, struct discord_message *event) } void -on_static(struct discord *client, struct discord_message *event) +on_static(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -169,7 +169,7 @@ on_static(struct discord *client, struct discord_message *event) void on_interaction_create(struct discord *client, - struct discord_interaction *event) + const struct discord_interaction *event) { log_info("Interaction %" PRIu64 " received", event->id); diff --git a/examples/copycat.c b/examples/copycat.c index 4c2123904..e1910e938 100644 --- a/examples/copycat.c +++ b/examples/copycat.c @@ -19,7 +19,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Copycat-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -27,7 +27,7 @@ on_ready(struct discord *client, struct discord_ready *event) void on_reaction_add(struct discord *client, - struct discord_message_reaction_add *event) + const struct discord_message_reaction_add *event) { if (event->member->user->bot) return; @@ -36,7 +36,7 @@ on_reaction_add(struct discord *client, } void -on_message_create(struct discord *client, struct discord_message *event) +on_message_create(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -55,7 +55,7 @@ on_message_create(struct discord *client, struct discord_message *event) } void -on_message_update(struct discord *client, struct discord_message *event) +on_message_update(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -65,7 +65,8 @@ on_message_update(struct discord *client, struct discord_message *event) } void -on_message_delete(struct discord *client, struct discord_message_delete *event) +on_message_delete(struct discord *client, + const struct discord_message_delete *event) { struct discord_create_message params = { .content = "Did that message just disappear?" @@ -75,7 +76,7 @@ on_message_delete(struct discord *client, struct discord_message_delete *event) void on_message_delete_bulk(struct discord *client, - struct discord_message_delete_bulk *event) + const struct discord_message_delete_bulk *event) { char text[128]; sprintf(text, "Where did those %d messages go?", event->ids->size); diff --git a/examples/embed.c b/examples/embed.c index b3f2d5a6d..fc2d71b04 100644 --- a/examples/embed.c +++ b/examples/embed.c @@ -59,14 +59,14 @@ char JSON[] = "{\n" "}"; void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Embed-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_dynamic(struct discord *client, struct discord_message *event) +on_dynamic(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -90,7 +90,7 @@ on_dynamic(struct discord *client, struct discord_message *event) } void -on_static(struct discord *client, struct discord_message *event) +on_static(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -148,7 +148,7 @@ on_static(struct discord *client, struct discord_message *event) } void -on_builder(struct discord *client, struct discord_message *event) +on_builder(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/emoji.c b/examples/emoji.c index a81104bf3..94015a601 100644 --- a/examples/emoji.c +++ b/examples/emoji.c @@ -17,7 +17,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Emoji-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -80,7 +80,8 @@ fail_list_guild_emojis(struct discord *client, CCORDcode code, void *data) } void -on_list_guild_emojis(struct discord *client, struct discord_message *event) +on_list_guild_emojis(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -121,7 +122,7 @@ fail_get_guild_emoji(struct discord *client, CCORDcode code, void *data) } void -on_get_guild_emoji(struct discord *client, struct discord_message *event) +on_get_guild_emoji(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/guild-template.c b/examples/guild-template.c index fbeada940..2217edb49 100644 --- a/examples/guild-template.c +++ b/examples/guild-template.c @@ -19,7 +19,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Guild-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -56,7 +56,8 @@ fail(struct discord *client, CCORDcode code, void *data) } void -on_get_guild_template(struct discord *client, struct discord_message *event) +on_get_guild_template(struct discord *client, + const struct discord_message *event) { struct discord_ret_guild_template ret = { .done = &done, @@ -67,7 +68,8 @@ on_get_guild_template(struct discord *client, struct discord_message *event) } void -on_create_guild_template(struct discord *client, struct discord_message *event) +on_create_guild_template(struct discord *client, + const struct discord_message *event) { struct discord_ret_guild_template ret = { .done = &done, @@ -84,7 +86,8 @@ on_create_guild_template(struct discord *client, struct discord_message *event) } void -on_sync_guild_template(struct discord *client, struct discord_message *event) +on_sync_guild_template(struct discord *client, + const struct discord_message *event) { struct discord_ret_guild_template ret = { .done = &done, diff --git a/examples/guild.c b/examples/guild.c index 0ceeb1121..64a969a71 100644 --- a/examples/guild.c +++ b/examples/guild.c @@ -25,7 +25,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Guild-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -33,27 +33,27 @@ on_ready(struct discord *client, struct discord_ready *event) void log_on_role_create(struct discord *client, - struct discord_guild_role_create *event) + const struct discord_guild_role_create *event) { log_warn("Role (%" PRIu64 ") created", event->role->id); } void log_on_role_update(struct discord *client, - struct discord_guild_role_update *event) + const struct discord_guild_role_update *event) { log_warn("Role (%" PRIu64 ") updated", event->role->id); } void log_on_role_delete(struct discord *client, - struct discord_guild_role_delete *event) + const struct discord_guild_role_delete *event) { log_warn("Role (%" PRIu64 ") deleted", event->role_id); } void -on_role_create(struct discord *client, struct discord_message *event) +on_role_create(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -70,7 +70,7 @@ on_role_create(struct discord *client, struct discord_message *event) } void -on_role_delete(struct discord *client, struct discord_message *event) +on_role_delete(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -86,7 +86,7 @@ on_role_delete(struct discord *client, struct discord_message *event) } void -on_role_member_add(struct discord *client, struct discord_message *event) +on_role_member_add(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -104,7 +104,8 @@ on_role_member_add(struct discord *client, struct discord_message *event) } void -on_role_member_remove(struct discord *client, struct discord_message *event) +on_role_member_remove(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -158,7 +159,7 @@ fail_get_guild_roles(struct discord *client, CCORDcode code, void *data) } void -on_role_list(struct discord *client, struct discord_message *event) +on_role_list(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -186,7 +187,7 @@ fail_get_guild_member(struct discord *client, CCORDcode code, void *data) } void -on_member_get(struct discord *client, struct discord_message *event) +on_member_get(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -250,7 +251,7 @@ fail_get_guild_channels(struct discord *client, CCORDcode code, void *data) } void -on_channels_get(struct discord *client, struct discord_message *event) +on_channels_get(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/invite.c b/examples/invite.c index 607f7430e..0bcec228d 100644 --- a/examples/invite.c +++ b/examples/invite.c @@ -18,7 +18,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Invite-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -49,7 +49,7 @@ fail(struct discord *client, CCORDcode code, void *data) } void -on_invite_get(struct discord *client, struct discord_message *event) +on_invite_get(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -67,7 +67,7 @@ on_invite_get(struct discord *client, struct discord_message *event) } void -on_invite_delete(struct discord *client, struct discord_message *event) +on_invite_delete(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/manual-dm.c b/examples/manual-dm.c index 188f6c5bf..8414145cb 100644 --- a/examples/manual-dm.c +++ b/examples/manual-dm.c @@ -23,14 +23,14 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("ManualDM-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_dm_receive(struct discord *client, struct discord_message *event) +on_dm_receive(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/pin.c b/examples/pin.c index c1e4c2515..e658aac87 100644 --- a/examples/pin.c +++ b/examples/pin.c @@ -20,14 +20,14 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Pin-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_pin(struct discord *client, struct discord_message *event) +on_pin(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -45,7 +45,7 @@ on_pin(struct discord *client, struct discord_message *event) } void -on_unpin(struct discord *client, struct discord_message *event) +on_unpin(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -101,7 +101,7 @@ fail_get_pins(struct discord *client, CCORDcode code, void *data) } void -on_get_pins(struct discord *client, struct discord_message *event) +on_get_pins(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/ping-pong.c b/examples/ping-pong.c index f3fb3534d..0eb2618ff 100644 --- a/examples/ping-pong.c +++ b/examples/ping-pong.c @@ -13,14 +13,14 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("PingPong-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_ping(struct discord *client, struct discord_message *event) +on_ping(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -29,7 +29,7 @@ on_ping(struct discord *client, struct discord_message *event) } void -on_pong(struct discord *client, struct discord_message *event) +on_pong(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/presence.c b/examples/presence.c index d2c30cc59..bf1d10a63 100644 --- a/examples/presence.c +++ b/examples/presence.c @@ -16,7 +16,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Presence-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); diff --git a/examples/reaction.c b/examples/reaction.c index 4c02b129f..fbab68ab8 100644 --- a/examples/reaction.c +++ b/examples/reaction.c @@ -28,7 +28,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Reaction-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -75,7 +75,7 @@ fail_get_users(struct discord *client, CCORDcode code, void *data) } void -on_get_users(struct discord *client, struct discord_message *event) +on_get_users(struct discord *client, const struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; @@ -92,7 +92,7 @@ on_get_users(struct discord *client, struct discord_message *event) } void -on_create(struct discord *client, struct discord_message *event) +on_create(struct discord *client, const struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; @@ -102,7 +102,7 @@ on_create(struct discord *client, struct discord_message *event) } void -on_delete(struct discord *client, struct discord_message *event) +on_delete(struct discord *client, const struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; @@ -112,7 +112,7 @@ on_delete(struct discord *client, struct discord_message *event) } void -on_delete_all(struct discord *client, struct discord_message *event) +on_delete_all(struct discord *client, const struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; @@ -121,7 +121,7 @@ on_delete_all(struct discord *client, struct discord_message *event) } void -on_delete_self(struct discord *client, struct discord_message *event) +on_delete_self(struct discord *client, const struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; @@ -131,7 +131,7 @@ on_delete_self(struct discord *client, struct discord_message *event) } void -on_delete_user(struct discord *client, struct discord_message *event) +on_delete_user(struct discord *client, const struct discord_message *event) { if (event->author->bot || !event->referenced_message) return; diff --git a/examples/shell.c b/examples/shell.c index a8e6f1b6f..590a536e3 100644 --- a/examples/shell.c +++ b/examples/shell.c @@ -22,14 +22,14 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Shell-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_cd(struct discord *client, struct discord_message *event) +on_cd(struct discord *client, const struct discord_message *event) { if (event->author->id != g_sudo_id) return; @@ -44,7 +44,7 @@ on_cd(struct discord *client, struct discord_message *event) } void -on_less_like(struct discord *client, struct discord_message *event) +on_less_like(struct discord *client, const struct discord_message *event) { if (event->author->id != g_sudo_id) return; @@ -79,7 +79,7 @@ on_less_like(struct discord *client, struct discord_message *event) } void -on_fallback(struct discord *client, struct discord_message *event) +on_fallback(struct discord *client, const struct discord_message *event) { const size_t MAX_FSIZE = 5e6; // 5 mb const size_t MAX_CHARS = 2000; diff --git a/examples/slash-commands.c b/examples/slash-commands.c index f933e8622..be7593dcf 100644 --- a/examples/slash-commands.c +++ b/examples/slash-commands.c @@ -21,7 +21,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Slash-Commands-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -29,13 +29,14 @@ on_ready(struct discord *client, struct discord_ready *event) void log_on_app_create(struct discord *client, - struct discord_application_command *event) + const struct discord_application_command *event) { log_info("Application Command %s created", event->name); } void -on_slash_command_create(struct discord *client, struct discord_message *event) +on_slash_command_create(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -108,7 +109,7 @@ on_slash_command_create(struct discord *client, struct discord_message *event) void on_interaction_create(struct discord *client, - struct discord_interaction *event) + const struct discord_interaction *event) { /* We're only interested on slash commands */ if (event->type != DISCORD_INTERACTION_APPLICATION_COMMAND) return; diff --git a/examples/slash-commands2.c b/examples/slash-commands2.c index f0520f1aa..137018e29 100644 --- a/examples/slash-commands2.c +++ b/examples/slash-commands2.c @@ -35,7 +35,7 @@ print_help(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Slash-Commands-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -43,21 +43,21 @@ on_ready(struct discord *client, struct discord_ready *event) void log_on_app_create(struct discord *client, - struct discord_application_command *event) + const struct discord_application_command *event) { log_info("Application Command %s created", event->name); } void log_on_app_update(struct discord *client, - struct discord_application_command *event) + const struct discord_application_command *event) { log_info("Application Command %s updated", event->name); } void log_on_app_delete(struct discord *client, - struct discord_application_command *event) + const struct discord_application_command *event) { log_info("Application Command %s deleted", event->name); } @@ -70,7 +70,7 @@ fail_interaction_create(struct discord *client, CCORDcode code, void *data) void on_interaction_create(struct discord *client, - struct discord_interaction *event) + const struct discord_interaction *event) { log_info("Interaction %" PRIu64 " received", event->id); diff --git a/examples/spam.c b/examples/spam.c index 30a45a283..b7228e95b 100644 --- a/examples/spam.c +++ b/examples/spam.c @@ -31,7 +31,7 @@ char *SPAM[] = { }; void -on_spam_async(struct discord *client, struct discord_message *event) +on_spam_async(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -42,7 +42,7 @@ on_spam_async(struct discord *client, struct discord_message *event) } void -on_spam_sync(struct discord *client, struct discord_message *event) +on_spam_sync(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/examples/voice-join.c b/examples/voice-join.c index dd52accd8..fdbc09d55 100644 --- a/examples/voice-join.c +++ b/examples/voice-join.c @@ -22,7 +22,7 @@ print_usage(void) } void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Voice-Bot succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -30,7 +30,7 @@ on_ready(struct discord *client, struct discord_ready *event) void log_on_voice_state_update(struct discord *client, - struct discord_voice_state *event) + const struct discord_voice_state *event) { log_info("User <@!%" PRIu64 "> has joined <#%" PRIu64 ">!", event->user_id, event->channel_id); @@ -62,7 +62,8 @@ fail_list_voice_regions(struct discord *client, CCORDcode code, void *data) } void -on_list_voice_regions(struct discord *client, struct discord_message *event) +on_list_voice_regions(struct discord *client, + const struct discord_message *event) { if (event->author->bot) return; @@ -103,7 +104,7 @@ fail_get_vchannel_position(struct discord *client, CCORDcode code, void *data) } void -on_voice_join(struct discord *client, struct discord_message *event) +on_voice_join(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -151,7 +152,7 @@ fail_disconnect_guild_member(struct discord *client, } void -on_voice_kick(struct discord *client, struct discord_message *event) +on_voice_kick(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; diff --git a/include/discord-events.h b/include/discord-events.h index c3a25a0a9..b1e2793f0 100644 --- a/include/discord-events.h +++ b/include/discord-events.h @@ -93,146 +93,149 @@ typedef void (*discord_ev_idle)(struct discord *client); /** @brief Ready callback */ typedef void (*discord_ev_ready)(struct discord *client, - struct discord_ready *event); + const struct discord_ready *event); /** @brief Application Command callback */ typedef void (*discord_ev_application_command)( - struct discord *client, struct discord_application_command *event); + struct discord *client, const struct discord_application_command *event); /** @brief Channel callback */ typedef void (*discord_ev_channel)(struct discord *client, - struct discord_channel *event); + const struct discord_channel *event); /** @brief Thread List Sync callback */ typedef void (*discord_ev_thread_list_sync)( - struct discord *client, struct discord_thread_list_sync *event); + struct discord *client, const struct discord_thread_list_sync *event); /** @brief Thread Member Update callback */ -typedef void (*discord_ev_thread_member)(struct discord *client, - struct discord_thread_member *event); +typedef void (*discord_ev_thread_member)( + struct discord *client, const struct discord_thread_member *event); /** @brief Thread Members Update callback */ typedef void (*discord_ev_thread_members_update)( - struct discord *client, struct discord_thread_members_update *event); + struct discord *client, const struct discord_thread_members_update *event); /** @brief Channel Pins Update callback */ typedef void (*discord_ev_channel_pins_update)( - struct discord *client, struct discord_channel_pins_update *event); + struct discord *client, const struct discord_channel_pins_update *event); /** @brief Guild Ban Add callback */ -typedef void (*discord_ev_guild_ban_add)(struct discord *client, - struct discord_guild_ban_add *event); +typedef void (*discord_ev_guild_ban_add)( + struct discord *client, const struct discord_guild_ban_add *event); /** @brief Guild Ban Remove callback */ typedef void (*discord_ev_guild_ban_remove)( - struct discord *client, struct discord_guild_ban_remove *event); + struct discord *client, const struct discord_guild_ban_remove *event); /** @brief Guild callback */ typedef void (*discord_ev_guild)(struct discord *client, - struct discord_guild *event); + const struct discord_guild *event); /** @brief Guild Emojis Update callback */ typedef void (*discord_ev_guild_emojis_update)( - struct discord *client, struct discord_guild_emojis_update *event); + struct discord *client, const struct discord_guild_emojis_update *event); /** @brief Guild Stickers Update callback */ typedef void (*discord_ev_guild_stickers_update)( - struct discord *client, struct discord_guild_stickers_update *event); + struct discord *client, const struct discord_guild_stickers_update *event); /** @brief Guild Integrations Update callback */ typedef void (*discord_ev_guild_integrations_update)( - struct discord *client, struct discord_guild_integrations_update *event); + struct discord *client, + const struct discord_guild_integrations_update *event); /** @brief Guild Member Add callback */ -typedef void (*discord_ev_guild_member)(struct discord *client, - struct discord_guild_member *event); +typedef void (*discord_ev_guild_member)( + struct discord *client, const struct discord_guild_member *event); /** @brief Guild Member Remove callback */ typedef void (*discord_ev_guild_member_remove)( - struct discord *client, struct discord_guild_member_remove *event); + struct discord *client, const struct discord_guild_member_remove *event); /** @brief Guild Member Update callback */ typedef void (*discord_ev_guild_member_update)( - struct discord *client, struct discord_guild_member_update *event); + struct discord *client, const struct discord_guild_member_update *event); /** @brief Guild Members Chunk callback */ typedef void (*discord_ev_guild_members_chunk)( - struct discord *client, struct discord_guild_members_chunk *event); + struct discord *client, const struct discord_guild_members_chunk *event); /** @brief Guild Role Create callback */ typedef void (*discord_ev_guild_role_create)( - struct discord *client, struct discord_guild_role_create *event); + struct discord *client, const struct discord_guild_role_create *event); /** @brief Guild Role Update callback */ typedef void (*discord_ev_guild_role_update)( - struct discord *client, struct discord_guild_role_update *event); + struct discord *client, const struct discord_guild_role_update *event); /** @brief Guild Role Delete callback */ typedef void (*discord_ev_guild_role_delete)( - struct discord *client, struct discord_guild_role_delete *event); + struct discord *client, const struct discord_guild_role_delete *event); /** @brief Guild Scheduled Event User Add callback */ typedef void (*discord_ev_guild_scheduled_event_user_add)( struct discord *client, - struct discord_guild_scheduled_event_user_add *event); + const struct discord_guild_scheduled_event_user_add *event); /** @brief Guild Scheduled Event User Remove callback */ typedef void (*discord_ev_guild_scheduled_event_user_remove)( struct discord *client, - struct discord_guild_scheduled_event_user_remove *event); + const struct discord_guild_scheduled_event_user_remove *event); /** @brief Integration Create callback */ -typedef void (*discord_ev_integration)(struct discord *client, - struct discord_integration *event); +typedef void (*discord_ev_integration)( + struct discord *client, const struct discord_integration *event); /** @brief Integration Delete callback */ typedef void (*discord_ev_integration_delete)( - struct discord *client, struct discord_integration_delete *event); + struct discord *client, const struct discord_integration_delete *event); /** @brief Invite Create Event callback */ -typedef void (*discord_ev_invite_create)(struct discord *client, - struct discord_invite_create *event); +typedef void (*discord_ev_invite_create)( + struct discord *client, const struct discord_invite_create *event); /** @brief Invite Delete Event callback */ -typedef void (*discord_ev_invite_delete)(struct discord *client, - struct discord_invite_delete *event); +typedef void (*discord_ev_invite_delete)( + struct discord *client, const struct discord_invite_delete *event); /** @brief Message callback */ typedef void (*discord_ev_message)(struct discord *client, - struct discord_message *event); + const struct discord_message *event); /** @brief Message Delete callback */ typedef void (*discord_ev_message_delete)( - struct discord *client, struct discord_message_delete *event); + struct discord *client, const struct discord_message_delete *event); /** @brief Message Delete Bulk callback */ typedef void (*discord_ev_message_delete_bulk)( - struct discord *client, struct discord_message_delete_bulk *event); + struct discord *client, const struct discord_message_delete_bulk *event); /** @brief Message Reaction Add callback */ typedef void (*discord_ev_message_reaction_add)( - struct discord *client, struct discord_message_reaction_add *member); + struct discord *client, const struct discord_message_reaction_add *member); /** @brief Message Reaction Remove callback */ typedef void (*discord_ev_message_reaction_remove)( - struct discord *client, struct discord_message_reaction_remove *member); + struct discord *client, + const struct discord_message_reaction_remove *member); /** @brief Message Reaction Remove All callback */ typedef void (*discord_ev_message_reaction_remove_all)( - struct discord *client, struct discord_message_reaction_remove_all *event); + struct discord *client, + const struct discord_message_reaction_remove_all *event); /** @brief Message Reaction Remove callback */ typedef void (*discord_ev_message_reaction_remove_emoji)( struct discord *client, - struct discord_message_reaction_remove_emoji *event); + const struct discord_message_reaction_remove_emoji *event); /** @brief Presence Update callback */ typedef void (*discord_ev_presence_update)( - struct discord *client, struct discord_presence_update *event); + struct discord *client, const struct discord_presence_update *event); /** @brief Stage Instance callback */ typedef void (*discord_ev_stage_instance)( - struct discord *client, struct discord_stage_instance *event); + struct discord *client, const struct discord_stage_instance *event); /** @brief Typing Start callback */ -typedef void (*discord_ev_typing_start)(struct discord *client, - struct discord_typing_start *event); +typedef void (*discord_ev_typing_start)( + struct discord *client, const struct discord_typing_start *event); /** @brief User callback */ typedef void (*discord_ev_user)(struct discord *client, - struct discord_user *event); + const struct discord_user *event); /** @brief Voice State Update callback */ typedef void (*discord_ev_voice_state_update)( - struct discord *client, struct discord_voice_state *voice_state); + struct discord *client, const struct discord_voice_state *event); /** @brief Voice Server Update callback */ typedef void (*discord_ev_voice_server_update)( - struct discord *client, struct discord_voice_server_update *event); + struct discord *client, const struct discord_voice_server_update *event); /** @brief Webhooks Update callback */ typedef void (*discord_ev_webhooks_update)( - struct discord *client, struct discord_webhooks_update *event); + struct discord *client, const struct discord_webhooks_update *event); /** @brief Interaction callback */ typedef void (*discord_ev_interaction)( - struct discord *client, struct discord_interaction *interaction); + struct discord *client, const struct discord_interaction *event); /** @} DiscordEventCallbackTypes */ diff --git a/test/async.c b/test/async.c index 177a3d241..f6db05162 100644 --- a/test/async.c +++ b/test/async.c @@ -12,7 +12,7 @@ struct user_cxt { }; void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); @@ -39,7 +39,7 @@ reconnect(struct discord *client, } void -on_disconnect(struct discord *client, struct discord_message *event) +on_disconnect(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -54,7 +54,7 @@ on_disconnect(struct discord *client, struct discord_message *event) } void -on_reconnect(struct discord *client, struct discord_message *event) +on_reconnect(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -69,7 +69,7 @@ on_reconnect(struct discord *client, struct discord_message *event) } void -on_single(struct discord *client, struct discord_message *event) +on_single(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -106,7 +106,7 @@ send_batch(struct discord *client, } void -on_spam(struct discord *client, struct discord_message *event) +on_spam(struct discord *client, const struct discord_message *event) { send_batch(client, NULL, event); } @@ -131,7 +131,7 @@ send_msg(struct discord *client, void *data, const struct discord_message *msg) } void -on_spam_ordered(struct discord *client, struct discord_message *event) +on_spam_ordered(struct discord *client, const struct discord_message *event) { send_msg(client, NULL, event); } @@ -150,7 +150,7 @@ send_err(struct discord *client, CCORDcode code, void *data) } void -on_force_error(struct discord *client, struct discord_message *event) +on_force_error(struct discord *client, const struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123; diff --git a/test/sync.c b/test/sync.c index 33c96558d..4007892ba 100644 --- a/test/sync.c +++ b/test/sync.c @@ -18,14 +18,14 @@ bool g_keep_spamming = true; unsigned g_thread_count; void -on_ready(struct discord *client, struct discord_ready *event) +on_ready(struct discord *client, const struct discord_ready *event) { log_info("Succesfully connected to Discord as %s#%s!", event->user->username, event->user->discriminator); } void -on_disconnect(struct discord *client, struct discord_message *event) +on_disconnect(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -41,7 +41,7 @@ on_disconnect(struct discord *client, struct discord_message *event) } void -on_reconnect(struct discord *client, struct discord_message *event) +on_reconnect(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -57,7 +57,7 @@ on_reconnect(struct discord *client, struct discord_message *event) } void -on_spam(struct discord *client, struct discord_message *event) +on_spam(struct discord *client, const struct discord_message *event) { const unsigned threadpool_size = strtol(THREADPOOL_SIZE, NULL, 10); @@ -105,7 +105,7 @@ on_spam(struct discord *client, struct discord_message *event) } void -on_spam_block(struct discord *client, struct discord_message *event) +on_spam_block(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -119,7 +119,8 @@ on_spam_block(struct discord *client, struct discord_message *event) } void -on_spam_block_continue(struct discord *client, struct discord_message *event) +on_spam_block_continue(struct discord *client, + const struct discord_message *event) { const struct discord_user *bot = discord_get_self(client); char text[32]; @@ -140,7 +141,7 @@ on_spam_block_continue(struct discord *client, struct discord_message *event) } void -on_stop(struct discord *client, struct discord_message *event) +on_stop(struct discord *client, const struct discord_message *event) { if (event->author->bot) return; @@ -151,7 +152,7 @@ on_stop(struct discord *client, struct discord_message *event) } void -on_force_error(struct discord *client, struct discord_message *event) +on_force_error(struct discord *client, const struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123ULL; CCORDcode code; @@ -175,7 +176,7 @@ on_force_error(struct discord *client, struct discord_message *event) } void -on_ping(struct discord *client, struct discord_message *event) +on_ping(struct discord *client, const struct discord_message *event) { char text[256]; From 1f0f959d320d2e70209faf536411ac1a82c29fab Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 12 May 2022 23:59:49 -0300 Subject: [PATCH 036/118] wip(discord-adapter): rename symbols and improve descriptions --- include/discord-internal.h | 44 +++++++----- include/discord-request.h | 25 ++++--- src/discord-adapter.c | 117 +++++++++++++++++--------------- src/discord-adapter_ratelimit.c | 6 +- src/gateway.c | 12 ++-- 5 files changed, 111 insertions(+), 93 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 203af3ccb..c2ce320e0 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -76,12 +76,16 @@ * @brief Wrapper to the Discord REST API * @{ */ -/** @brief Request's return context */ -struct discord_ret_generic { - /** `true` if may receive a datatype from response*/ +/** @brief Generic request dispatcher */ +struct discord_ret_dispatch { + /** `true` if may receive a datatype from response */ bool has_type; - /** optional callback to be executed on a successful request */ + /** + * optional callback to be executed on a successful request + * @todo should be cast to the original callback signature before calling, + * otherwise its UB + */ union { void (*typed)(struct discord *client, void *data, const void *ret); void (*typeless)(struct discord *client, void *data); @@ -96,7 +100,7 @@ struct discord_ret_generic { }; /** @brief Attributes of response datatype */ -struct discord_generic { +struct discord_ret_response { /** pointer to the datatype in memory */ void *data; /** size of datatype in bytes */ @@ -109,13 +113,13 @@ struct discord_generic { void (*cleanup)(void *data); }; -/** @brief Behavior of request return struct */ +/** @brief Request to be performed */ struct discord_request { - /** request response's return datatype attributes */ - struct discord_generic gnrc; - /** request attributes set by client */ - struct discord_ret_generic ret; - /** in case of HTTP_MIMEPOST, provide attachments */ + /** attributes set by client for request dispatch behavior */ + struct discord_ret_dispatch dispatch; + /** information for parsing response into a datatype (if possible) */ + struct discord_ret_response response; + /** in case of `HTTP_MIMEPOST` provide attachments for file transfer */ struct discord_attachments attachments; }; @@ -128,7 +132,7 @@ struct discord_request { * @brief Context of individual requests that are scheduled to run * asynchronously */ -struct discord_context { +struct discord_adapter_context { /** request return struct attributes */ struct discord_request req; @@ -171,7 +175,7 @@ struct discord_adapter { struct discord_ratelimiter *ratelimiter; /** idle request handles */ - QUEUE(struct discord_context) * idleq; + QUEUE(struct discord_adapter_context) * idleq; /** max amount of retries before a failed request gives up */ int retry_limit; @@ -237,6 +241,10 @@ void discord_adapter_stop_buckets(struct discord_adapter *adapter); * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ +/** + * @brief Value assigned to @ref discord_bucket `busy` field in case it's + * being timed-out + */ #define DISCORD_BUCKET_TIMEOUT (void *)(0xf) /** @brief The Discord bucket for handling per-group ratelimits */ @@ -252,12 +260,12 @@ struct discord_bucket { /** synchronize ratelimiting between threads */ pthread_mutex_t lock; /** pending requests */ - QUEUE(struct discord_context) waitq; + QUEUE(struct discord_adapter_context) waitq; /** * pointer to currently performing busy request (if any) * @note `NULL` if free or @ref DISCORD_BUCKET_TIMEOUT if being ratelimited */ - struct discord_context *busy; + struct discord_adapter_context *busy; }; /** @@ -507,7 +515,11 @@ struct discord_gateway { /** response-payload structure */ struct discord_gateway_payload payload; - /** the user's callbacks for Discord events */ + /** + * the user's callbacks for Discord events + * @todo should be cast to the original callback signature before calling, + * otherwise its UB + */ discord_ev cbs[DISCORD_EV_MAX]; /** the event scheduler callback */ discord_ev_scheduler scheduler; diff --git a/include/discord-request.h b/include/discord-request.h index ae66d8907..8a277b0bd 100644 --- a/include/discord-request.h +++ b/include/discord-request.h @@ -11,9 +11,8 @@ #define _RET_SAFECOPY_TYPED(dest, src) \ do { \ (dest).has_type = true; \ - (dest).done.typed = (void (*)(struct discord * client, void *data, \ - const void *ret))(src) \ - .done; \ + (dest).done.typed = \ + (void (*)(struct discord *, void *, const void *))(src).done; \ (dest).fail = (src).fail; \ (dest).data = (src).data; \ (dest).cleanup = (src).cleanup; \ @@ -41,12 +40,12 @@ */ #define DISCORD_REQ_INIT(req, type, ret) \ do { \ - (req).gnrc.size = sizeof(struct type); \ - (req).gnrc.init = (void (*)(void *))type##_init; \ - (req).gnrc.from_json = \ + (req).response.size = sizeof(struct type); \ + (req).response.init = (void (*)(void *))type##_init; \ + (req).response.from_json = \ (size_t(*)(const char *, size_t, void *))type##_from_json; \ - (req).gnrc.cleanup = (void (*)(void *))type##_cleanup; \ - if (ret) _RET_SAFECOPY_TYPED(req.ret, *ret); \ + (req).response.cleanup = (void (*)(void *))type##_cleanup; \ + if (ret) _RET_SAFECOPY_TYPED(req.dispatch, *ret); \ } while (0) /** @@ -58,11 +57,11 @@ */ #define DISCORD_REQ_LIST_INIT(req, type, ret) \ do { \ - (req).gnrc.size = sizeof(struct type); \ - (req).gnrc.from_json = \ + (req).response.size = sizeof(struct type); \ + (req).response.from_json = \ (size_t(*)(const char *, size_t, void *))type##_from_json; \ - (req).gnrc.cleanup = (void (*)(void *))type##_cleanup; \ - if (ret) _RET_SAFECOPY_TYPED(req.ret, *ret); \ + (req).response.cleanup = (void (*)(void *))type##_cleanup; \ + if (ret) _RET_SAFECOPY_TYPED(req.dispatch, *ret); \ } while (0) /** @@ -72,6 +71,6 @@ * @param ret request attributes */ #define DISCORD_REQ_BLANK_INIT(req, ret) \ - if (ret) _RET_SAFECOPY_TYPELESS(req.ret, *ret) + if (ret) _RET_SAFECOPY_TYPELESS(req.dispatch, *ret) #endif /* DISCORD_REQUEST_H */ diff --git a/src/discord-adapter.c b/src/discord-adapter.c index 64ed3ff4d..5f0524794 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -70,7 +70,7 @@ discord_adapter_init(struct discord_adapter *adapter, } static void -_discord_context_cleanup(struct discord_context *cxt) +_discord_adapter_context_cleanup(struct discord_adapter_context *cxt) { discord_attachments_cleanup(&cxt->req.attachments); if (cxt->body.buf.start) free(cxt->body.buf.start); @@ -80,8 +80,8 @@ _discord_context_cleanup(struct discord_context *cxt) void discord_adapter_cleanup(struct discord_adapter *adapter) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; + QUEUE(struct discord_adapter_context) queue, *qelem; + struct discord_adapter_context *cxt; /* cleanup User-Agent handle */ ua_cleanup(adapter->ua); @@ -98,9 +98,9 @@ discord_adapter_cleanup(struct discord_adapter *adapter) QUEUE_MOVE(adapter->idleq, &queue); while (!QUEUE_EMPTY(&queue)) { qelem = QUEUE_HEAD(&queue); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); + cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); QUEUE_REMOVE(&cxt->entry); - _discord_context_cleanup(cxt); + _discord_adapter_context_cleanup(cxt); } free(adapter->idleq); @@ -155,9 +155,9 @@ discord_adapter_run(struct discord_adapter *adapter, discord_ratelimiter_build_key(method, key, endpoint_fmt, args); va_end(args); - if (req->ret.sync) { /* perform blocking request */ - if (req->ret.has_type && req->ret.sync != DISCORD_SYNC_FLAG) - req->gnrc.data = req->ret.sync; + if (req->dispatch.sync) { /* perform blocking request */ + if (req->dispatch.has_type && req->dispatch.sync != DISCORD_SYNC_FLAG) + req->response.data = req->dispatch.sync; return _discord_adapter_run_sync(adapter, req, body, method, endpoint, key); @@ -169,9 +169,9 @@ discord_adapter_run(struct discord_adapter *adapter, } static void -_discord_context_to_mime(curl_mime *mime, void *p_cxt) +_discord_adapter_context_to_mime(curl_mime *mime, void *p_cxt) { - struct discord_context *cxt = p_cxt; + struct discord_adapter_context *cxt = p_cxt; struct discord_attachments *atchs = &cxt->req.attachments; struct ccord_szbuf *body = &cxt->body.buf; curl_mimepart *part; @@ -312,7 +312,7 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, char key[DISCORD_ROUTE_LEN]) { /* throw-away for ua_conn_set_mime() */ - struct discord_context cxt = { 0 }; + struct discord_adapter_context cxt = { 0 }; struct discord_bucket *b; struct ua_conn *conn; int retry_attempt = 0; @@ -327,7 +327,7 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, cxt.body.buf = *body; ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, &cxt, &_discord_context_to_mime); + ua_conn_set_mime(conn, &cxt, &_discord_adapter_context_to_mime); } else { ua_conn_add_header(conn, "Content-Type", "application/json"); @@ -361,13 +361,14 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, logconf_error(&client->conf, "%.*s", (int)resp.size, resp.start); } - else if (req->gnrc.data) { + else if (req->response.data) { /* initialize ret */ - if (req->gnrc.init) req->gnrc.init(req->gnrc.data); + if (req->response.init) req->response.init(req->response.data); /* populate ret */ - if (req->gnrc.from_json) - req->gnrc.from_json(resp.start, resp.size, req->gnrc.data); + if (req->response.from_json) + req->response.from_json(resp.start, resp.size, + req->response.data); } code = info.code; @@ -407,7 +408,7 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, /* ASYNCHRONOUS REQUEST LOGIC */ -/* Only the fields that are required at _discord_context_to_mime() +/* Only the fields that are required at _discord_adapter_context_to_mime() * are duplicated */ static void _discord_attachments_dup(struct discord_attachments *dest, @@ -437,7 +438,7 @@ _discord_attachments_dup(struct discord_attachments *dest, } static void -_discord_context_reset(struct discord_context *cxt) +_discord_adapter_context_reset(struct discord_adapter_context *cxt) { ua_conn_stop(cxt->conn); @@ -454,13 +455,13 @@ _discord_context_reset(struct discord_context *cxt) } static void -_discord_context_populate(struct discord_context *cxt, - struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) +_discord_adapter_context_populate(struct discord_adapter_context *cxt, + struct discord_adapter *adapter, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) { cxt->method = method; @@ -498,28 +499,31 @@ _discord_adapter_run_async(struct discord_adapter *adapter, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct discord_context *cxt; + struct discord_adapter_context *cxt; if (QUEUE_EMPTY(adapter->idleq)) { /* create new context struct */ - cxt = calloc(1, sizeof(struct discord_context)); + cxt = calloc(1, sizeof(struct discord_adapter_context)); } else { /* recycle a context struct from idleq */ - QUEUE(struct discord_context) *qelem = QUEUE_HEAD(adapter->idleq); + QUEUE(struct discord_adapter_context) *qelem = + QUEUE_HEAD(adapter->idleq); QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); + cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); } QUEUE_INIT(&cxt->entry); - _discord_context_populate(cxt, adapter, req, body, method, endpoint, key); + _discord_adapter_context_populate(cxt, adapter, req, body, method, + endpoint, key); - if (req->ret.high_p) + if (req->dispatch.high_p) QUEUE_INSERT_HEAD(&cxt->b->waitq, &cxt->entry); else QUEUE_INSERT_TAIL(&cxt->b->waitq, &cxt->entry); - if (req->ret.data) + if (req->dispatch.data) discord_refcounter_incr(CLIENT(adapter, adapter)->refcounter, - req->ret.data, req->ret.cleanup, false); + req->dispatch.data, req->dispatch.cleanup, + false); io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, adapter->mhandle); @@ -532,22 +536,22 @@ static CCORDcode _discord_adapter_send(struct discord_adapter *adapter, struct discord_bucket *b) { - struct discord_context *cxt; + struct discord_adapter_context *cxt; CURLMcode mcode; CURL *ehandle; - /** TODO: make this a discord_context_xxx() function */ - QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->waitq); + /** TODO: make this a discord_adapter_context_xxx() function */ + QUEUE(struct discord_adapter_context) *qelem = QUEUE_HEAD(&b->waitq); QUEUE_REMOVE(qelem); QUEUE_INIT(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); + cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); cxt->conn = ua_conn_start(adapter->ua); /**/ if (HTTP_MIMEPOST == cxt->method) { ua_conn_add_header(cxt->conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(cxt->conn, cxt, &_discord_context_to_mime); + ua_conn_set_mime(cxt->conn, cxt, &_discord_adapter_context_to_mime); } else { ua_conn_add_header(cxt->conn, "Content-Type", "application/json"); @@ -603,7 +607,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, struct CURLMsg *msg) { struct discord *client = CLIENT(adapter, adapter); - struct discord_context *cxt; + struct discord_adapter_context *cxt; int64_t wait_ms = 0LL; CCORDcode code; bool retry; @@ -622,26 +626,29 @@ _discord_adapter_check_action(struct discord_adapter *adapter, if (info.code != CCORD_OK) { logconf_error(&client->conf, "%.*s", (int)body.size, body.start); - if (cxt->req.ret.fail) - cxt->req.ret.fail(client, info.code, cxt->req.ret.data); + if (cxt->req.dispatch.fail) + cxt->req.dispatch.fail(client, info.code, + cxt->req.dispatch.data); } - else if (cxt->req.ret.done.typed) { - void *ret = calloc(1, cxt->req.gnrc.size); + else if (cxt->req.dispatch.done.typed) { + void *ret = calloc(1, cxt->req.response.size); /* initialize ret */ - if (cxt->req.gnrc.init) cxt->req.gnrc.init(ret); + if (cxt->req.response.init) cxt->req.response.init(ret); /* populate ret */ - if (cxt->req.gnrc.from_json) - cxt->req.gnrc.from_json(body.start, body.size, ret); + if (cxt->req.response.from_json) + cxt->req.response.from_json(body.start, body.size, ret); - if (cxt->req.ret.has_type) - cxt->req.ret.done.typed(client, cxt->req.ret.data, ret); + if (cxt->req.dispatch.has_type) + cxt->req.dispatch.done.typed(client, cxt->req.dispatch.data, + ret); else - cxt->req.ret.done.typeless(client, cxt->req.ret.data); + cxt->req.dispatch.done.typeless(client, + cxt->req.dispatch.data); /* cleanup ret */ - if (cxt->req.gnrc.cleanup) cxt->req.gnrc.cleanup(ret); + if (cxt->req.response.cleanup) cxt->req.response.cleanup(ret); free(ret); } @@ -664,8 +671,8 @@ _discord_adapter_check_action(struct discord_adapter *adapter, code = CCORD_CURLE_INTERNAL; - if (cxt->req.ret.fail) { - cxt->req.ret.fail(client, code, cxt->req.ret.data); + if (cxt->req.dispatch.fail) { + cxt->req.dispatch.fail(client, code, cxt->req.dispatch.data); } break; @@ -682,8 +689,8 @@ _discord_adapter_check_action(struct discord_adapter *adapter, } else { discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, - cxt->req.ret.data); - _discord_context_reset(cxt); + cxt->req.dispatch.data); + _discord_adapter_context_reset(cxt); QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); } @@ -726,7 +733,7 @@ _discord_adapter_stop_bucket(struct discord_adapter *adapter, { /* cancel busy transfer */ if (b->busy && b->busy != DISCORD_BUCKET_TIMEOUT) { - struct discord_context *cxt = b->busy; + struct discord_adapter_context *cxt = b->busy; CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); b->busy = NULL; diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index 71df2968b..74cc48512 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -383,8 +383,8 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, struct discord_bucket *b, const char key[]) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; + QUEUE(struct discord_adapter_context) queue, *qelem; + struct discord_adapter_context *cxt; QUEUE_MOVE(&rl->null->waitq, &queue); QUEUE_INIT(&rl->null->waitq); @@ -393,7 +393,7 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, qelem = QUEUE_HEAD(&queue); QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); + cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); if (0 == strcmp(cxt->key, key)) { QUEUE_INSERT_TAIL(&b->waitq, qelem); cxt->b = b; diff --git a/src/gateway.c b/src/gateway.c index 33853b9d9..e97a4c9a8 100644 --- a/src/gateway.c +++ b/src/gateway.c @@ -61,9 +61,9 @@ discord_get_gateway(struct discord *client, struct ccord_szbuf *ret) CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); - req.gnrc.from_json = &_ccord_szbuf_from_json; - req.ret.has_type = true; - req.ret.sync = ret; + req.response.from_json = &_ccord_szbuf_from_json; + req.dispatch.has_type = true; + req.dispatch.sync = ret; return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, "/gateway"); @@ -76,9 +76,9 @@ discord_get_gateway_bot(struct discord *client, struct ccord_szbuf *ret) CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); - req.gnrc.from_json = &_ccord_szbuf_from_json; - req.ret.has_type = true; - req.ret.sync = ret; + req.response.from_json = &_ccord_szbuf_from_json; + req.dispatch.has_type = true; + req.dispatch.sync = ret; return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, "/gateway/bot"); From 877c3e2c93cf2ad3c18c76aad418e524b7f1a489 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 13 May 2022 09:20:18 -0400 Subject: [PATCH 037/118] fix(discord-loop.c): prevent spurious wakeups --- src/discord-loop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index e67a27891..c1a718e62 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -116,7 +116,8 @@ discord_run(struct discord *client) } else { poll_time = discord_timer_get_next_trigger( - timers, sizeof timers / sizeof *timers, now, 1000); + timers, sizeof timers / sizeof *timers, now, + now < next_run ? ((next_run - now)) : 0); if (poll_time > 0 && poll_time < 1000) cog_sleep_us(poll_time); } From 9582453f61607e0e86712d6d586434686f441855 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 13 May 2022 09:23:01 -0400 Subject: [PATCH 038/118] refactor(discord-loop.c): rename poll_time to sleep_time --- src/discord-loop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index c1a718e62..1a26058bc 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -115,11 +115,11 @@ discord_run(struct discord *client) client->on_idle(client); } else { - poll_time = discord_timer_get_next_trigger( + int64_t sleep_time = discord_timer_get_next_trigger( timers, sizeof timers / sizeof *timers, now, now < next_run ? ((next_run - now)) : 0); - if (poll_time > 0 && poll_time < 1000) - cog_sleep_us(poll_time); + if (sleep_time > 0 && sleep_time < 1000) + cog_sleep_us(sleep_time); } } From 941132250f324a64de1418470ad652dcdfee2638 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 13 May 2022 14:29:56 -0300 Subject: [PATCH 039/118] refactor(Makefile): use implicit rules to replace .DEFAULT trick --- examples/Makefile | 7 ++----- test/Makefile | 19 ++++++++----------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 1a4135a62..df5405ab2 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -37,17 +37,14 @@ VOICE_BOT = voice-join CFLAGS = -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) \ -I$(CORE_DIR)/third-party -I$(GENCODECS_DIR) \ -O0 -g -pthread -Wall $(XFLAGS) -LDFLAGS = -ldiscord -L$(TOP)/lib -lcurl +LDFLAGS = -L$(TOP)/lib +LDLIBS = -ldiscord -lcurl all: $(BOTS) voice: $(MAKE) XFLAGS=-DCCORD_VOICE XSRC=$(VOICE_BOT) all -.SUFFIXES: -.DEFAULT: - $(CC) $(CFLAGS) -o $@ $@.c $(LDFLAGS) - echo: @ echo -e 'CC: $(CC)\n' @ echo -e 'BOTS: $(BOTS)\n' diff --git a/test/Makefile b/test/Makefile index c62251b40..dabc3dd1e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -2,27 +2,24 @@ TOP = .. CC ?= gcc -COGUTILS_DIR := $(TOP)/cog-utils -CORE_DIR := $(TOP)/core -INCLUDE_DIR := $(TOP)/include -GENCODECS_DIR := $(TOP)/gencodecs +COGUTILS_DIR = $(TOP)/cog-utils +CORE_DIR = $(TOP)/core +INCLUDE_DIR = $(TOP)/include +GENCODECS_DIR = $(TOP)/gencodecs -TEST_DISCORD := rest sync async timeout -TEST_CORE := user-agent websockets +TEST_DISCORD = rest sync async timeout +TEST_CORE = user-agent websockets EXES := $(TEST_DISCORD) $(TEST_GITHUB) $(TEST_CORE) CFLAGS = -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) \ -I$(CORE_DIR)/third-party -I$(GENCODECS_DIR) \ -O0 -g -pthread -Wall -LDFLAGS = -ldiscord -L$(TOP)/lib -lcurl +LDFLAGS = -L$(TOP)/lib +LDLIBS = -ldiscord -lcurl all: $(EXES) -.SUFFIXES: -.DEFAULT: - $(CC) $(CFLAGS) -o $@ $@.c $(LDFLAGS) - echo: @ echo -e 'CC: $(CC)\n' @ echo -e 'EXES: $(EXES)\n' From bbb5f737fdcba4f3c351c2f517dff6a5e555afb8 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 13 May 2022 15:44:09 -0300 Subject: [PATCH 040/118] wip(discord-adapter_request): move 'struct discord_context' logic --- Makefile | 1 + include/discord-internal.h | 140 +++++++++----- src/discord-adapter.c | 318 +++++--------------------------- src/discord-adapter_ratelimit.c | 6 +- src/discord-adapter_request.c | 271 +++++++++++++++++++++++++++ 5 files changed, 413 insertions(+), 323 deletions(-) create mode 100644 src/discord-adapter_request.c diff --git a/Makefile b/Makefile index c3fa83d52..dcc2f7c4a 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-adapter.o \ $(SRC_DIR)/discord-adapter_ratelimit.o \ + $(SRC_DIR)/discord-adapter_request.o \ $(SRC_DIR)/discord-refcount.o \ $(SRC_DIR)/discord-client.o \ $(SRC_DIR)/discord-loop.o \ diff --git a/include/discord-internal.h b/include/discord-internal.h index c2ce320e0..a2065846e 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -72,6 +72,11 @@ #define ASSERT_NOT_OOB(nbytes, destsz) \ ASSERT_S((size_t)nbytes < (size_t)destsz, "Out of bounds write attempt"); +/** URL endpoint threshold length */ +#define DISCORD_ENDPT_LEN 512 +/** Route's unique key threshold length */ +#define DISCORD_ROUTE_LEN 256 + /** @defgroup DiscordInternalAdapter REST API * @brief Wrapper to the Discord REST API * @{ */ @@ -113,53 +118,21 @@ struct discord_ret_response { void (*cleanup)(void *data); }; -/** @brief Request to be performed */ -struct discord_request { - /** attributes set by client for request dispatch behavior */ - struct discord_ret_dispatch dispatch; - /** information for parsing response into a datatype (if possible) */ - struct discord_ret_response response; - /** in case of `HTTP_MIMEPOST` provide attachments for file transfer */ - struct discord_attachments attachments; -}; - -/** URL endpoint threshold length */ -#define DISCORD_ENDPT_LEN 512 -/** Route's unique key threshold length */ -#define DISCORD_ROUTE_LEN 256 - /** - * @brief Context of individual requests that are scheduled to run - * asynchronously + * @brief Macro containing @ref discord_request fields + * @note for @ref discord_context alignment purposes */ -struct discord_adapter_context { - /** request return struct attributes */ - struct discord_request req; - - /** the request's bucket */ - struct discord_bucket *b; - - /** request body handle @note buffer is kept and recycled */ - struct { - /** the request body contents */ - struct ccord_szbuf buf; - /** the real size occupied in memory by `buf.start` */ - size_t memsize; - } body; +#define DISCORD_REQUEST_FIELDS \ + /** attributes set by client for request dispatch behavior */ \ + struct discord_ret_dispatch dispatch; \ + /** information for parsing response into a datatype (if possible) */ \ + struct discord_ret_response response; \ + /** in case of `HTTP_MIMEPOST` provide attachments for file transfer */ \ + struct discord_attachments attachments - /** the request's http method */ - enum http_method method; - /** the request's endpoint */ - char endpoint[DISCORD_ENDPT_LEN]; - /** the request bucket's key */ - char key[DISCORD_ROUTE_LEN]; - /** the connection handler assigned */ - struct ua_conn *conn; - /** the request bucket's queue entry */ - QUEUE entry; - - /** current retry attempt (stop at adapter->retry_limit) */ - int retry_attempt; +/** @brief Request to be performed */ +struct discord_request { + DISCORD_REQUEST_FIELDS; }; /** @brief The handle used for performing HTTP Requests */ @@ -175,7 +148,7 @@ struct discord_adapter { struct discord_ratelimiter *ratelimiter; /** idle request handles */ - QUEUE(struct discord_adapter_context) * idleq; + QUEUE(struct discord_context) * idleq; /** max amount of retries before a failed request gives up */ int retry_limit; @@ -237,6 +210,79 @@ CCORDcode discord_adapter_perform(struct discord_adapter *adapter); */ void discord_adapter_stop_buckets(struct discord_adapter *adapter); +/** @defgroup DiscordInternalAdapterContext Request's context handling (for async) + * @brief Enqueue request contexts for asynchronous purposes + * @{ */ + +/** + * @brief Context of individual requests that are scheduled to run + * asynchronously + * @note its fields are aligned with @ref discord_request, meaning those can be + * cast back and forth + */ +struct discord_context { + DISCORD_REQUEST_FIELDS; + + /** the request's bucket */ + struct discord_bucket *b; + + /** request body handle @note buffer is kept and recycled */ + struct { + /** the request body contents */ + struct ccord_szbuf buf; + /** the real size occupied in memory by `buf.start` */ + size_t memsize; + } body; + + /** the request's http method */ + enum http_method method; + /** the request's endpoint */ + char endpoint[DISCORD_ENDPT_LEN]; + /** the request bucket's key */ + char key[DISCORD_ROUTE_LEN]; + /** the connection handler assigned */ + struct ua_conn *conn; + /** the request bucket's queue entry */ + QUEUE entry; + + /** current retry attempt (stop at adapter->retry_limit) */ + int retry_attempt; +}; + +QUEUE *discord_context_queue_init(void); + +void discord_context_queue_cleanup(QUEUE *cxt_queue); + +void discord_context_bucket_enqueue(struct discord_bucket *b, + struct discord_context *cxt, + bool high_priority); + +struct discord_context *discord_context_bucket_dequeue( + struct discord_bucket *b); + +CURLMcode discord_context_send(struct discord_adapter *adapter, + struct discord_context *cxt, + struct ua_conn *conn); + +void discord_context_to_curlmime(curl_mime *mime, void *p_cxt); + +bool discord_context_retry_enqueue(struct discord_adapter *adapter, + struct discord_context *cxt, + int64_t wait_ms); + +void discord_context_recycle_enqueue(struct discord_adapter *adapter, + struct discord_context *cxt); + +struct discord_context *discord_context_populate( + struct discord_adapter *adapter, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]); + +/** @} DiscordInternalAdapterContext */ + /** @defgroup DiscordInternalAdapterRatelimit Ratelimiting * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ @@ -260,12 +306,12 @@ struct discord_bucket { /** synchronize ratelimiting between threads */ pthread_mutex_t lock; /** pending requests */ - QUEUE(struct discord_adapter_context) waitq; + QUEUE(struct discord_context) waitq; /** * pointer to currently performing busy request (if any) * @note `NULL` if free or @ref DISCORD_BUCKET_TIMEOUT if being ratelimited */ - struct discord_adapter_context *busy; + struct discord_context *busy; }; /** diff --git a/src/discord-adapter.c b/src/discord-adapter.c index 5f0524794..ded60f9e4 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -23,7 +23,7 @@ setopt_cb(struct ua_conn *conn, void *p_token) #ifdef CCORD_DEBUG_ADAPTER curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); -#endif /* CCORD_DEBUG_ADAPTER */ +#endif } static int @@ -63,47 +63,25 @@ discord_adapter_init(struct discord_adapter *adapter, /* idleq is malloc'd to guarantee a client cloned by discord_clone() will * share the same queue with the original */ - adapter->idleq = malloc(sizeof(QUEUE)); - QUEUE_INIT(adapter->idleq); + adapter->idleq = discord_context_queue_init(); - adapter->retry_limit = 3; /* TODO: shouldn't be a hard limit */ -} - -static void -_discord_adapter_context_cleanup(struct discord_adapter_context *cxt) -{ - discord_attachments_cleanup(&cxt->req.attachments); - if (cxt->body.buf.start) free(cxt->body.buf.start); - free(cxt); + adapter->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ } void discord_adapter_cleanup(struct discord_adapter *adapter) { - QUEUE(struct discord_adapter_context) queue, *qelem; - struct discord_adapter_context *cxt; - /* cleanup User-Agent handle */ ua_cleanup(adapter->ua); - + /* cleanup curl's multi handle */ io_poller_curlm_del(CLIENT(adapter, adapter)->io_poller, adapter->mhandle); curl_multi_cleanup(adapter->mhandle); - /* move pending requests to idleq */ discord_adapter_stop_buckets(adapter); /* cleanup discovered buckets */ discord_ratelimiter_cleanup(adapter->ratelimiter); - /* cleanup idle requests queue */ - QUEUE_MOVE(adapter->idleq, &queue); - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); - QUEUE_REMOVE(&cxt->entry); - _discord_adapter_context_cleanup(cxt); - } - - free(adapter->idleq); + discord_context_queue_cleanup(adapter->idleq); } static CCORDcode _discord_adapter_run_sync(struct discord_adapter *adapter, @@ -168,62 +146,6 @@ discord_adapter_run(struct discord_adapter *adapter, key); } -static void -_discord_adapter_context_to_mime(curl_mime *mime, void *p_cxt) -{ - struct discord_adapter_context *cxt = p_cxt; - struct discord_attachments *atchs = &cxt->req.attachments; - struct ccord_szbuf *body = &cxt->body.buf; - curl_mimepart *part; - char name[64]; - int i; - - /* json part */ - if (body->start && body->size) { - part = curl_mime_addpart(mime); - curl_mime_data(part, body->start, body->size); - curl_mime_type(part, "application/json"); - curl_mime_name(part, "payload_json"); - } - - /* attachment part */ - for (i = 0; i < atchs->size; ++i) { - int len = snprintf(name, sizeof(name), "files[%d]", i); - ASSERT_NOT_OOB(len, sizeof(name)); - - if (atchs->array[i].content) { - part = curl_mime_addpart(mime); - curl_mime_data(part, atchs->array[i].content, - atchs->array[i].size ? atchs->array[i].size - : CURL_ZERO_TERMINATED); - curl_mime_filename(part, !atchs->array[i].filename - ? "a.out" - : atchs->array[i].filename); - curl_mime_type(part, !atchs->array[i].content_type - ? "application/octet-stream" - : atchs->array[i].content_type); - curl_mime_name(part, name); - } - else if (atchs->array[i].filename) { - CURLcode code; - - /* fetch local file by the filename */ - part = curl_mime_addpart(mime); - code = curl_mime_filedata(part, atchs->array[i].filename); - if (code != CURLE_OK) { - char errbuf[256]; - snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", - curl_easy_strerror(code), atchs->array[i].filename); - perror(errbuf); - } - curl_mime_type(part, !atchs->array[i].content_type - ? "application/octet-stream" - : atchs->array[i].content_type); - curl_mime_name(part, name); - } - } -} - /* return true if there should be a retry attempt */ static bool _discord_adapter_get_info(struct discord_adapter *adapter, @@ -231,7 +153,7 @@ _discord_adapter_get_info(struct discord_adapter *adapter, int64_t *wait_ms) { if (info->code != CCORD_HTTP_CODE) { - /** CCORD_OK or internal error */ + /* CCORD_OK or internal error */ return false; } @@ -312,7 +234,7 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, char key[DISCORD_ROUTE_LEN]) { /* throw-away for ua_conn_set_mime() */ - struct discord_adapter_context cxt = { 0 }; + struct discord_context cxt = { 0 }; struct discord_bucket *b; struct ua_conn *conn; int retry_attempt = 0; @@ -323,11 +245,11 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, conn = ua_conn_start(adapter->ua); if (HTTP_MIMEPOST == method) { - cxt.req.attachments = req->attachments; + cxt.attachments = req->attachments; cxt.body.buf = *body; ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, &cxt, &_discord_adapter_context_to_mime); + ua_conn_set_mime(conn, &cxt, &discord_context_to_curlmime); } else { ua_conn_add_header(conn, "Content-Type", "application/json"); @@ -408,88 +330,6 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, /* ASYNCHRONOUS REQUEST LOGIC */ -/* Only the fields that are required at _discord_adapter_context_to_mime() - * are duplicated */ -static void -_discord_attachments_dup(struct discord_attachments *dest, - struct discord_attachments *src) -{ - int i; - - if (!src->size) return; - - __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); - for (i = 0; i < src->size; ++i) { - carray_insert(dest, i, src->array[i]); - if (src->array[i].content) { - dest->array[i].size = src->array[i].size - ? src->array[i].size - : strlen(src->array[i].content) + 1; - - dest->array[i].content = malloc(dest->array[i].size); - memcpy(dest->array[i].content, src->array[i].content, - dest->array[i].size); - } - if (src->array[i].filename) - dest->array[i].filename = strdup(src->array[i].filename); - if (src->array[i].content_type) - dest->array[i].content_type = strdup(src->array[i].content_type); - } -} - -static void -_discord_adapter_context_reset(struct discord_adapter_context *cxt) -{ - ua_conn_stop(cxt->conn); - - cxt->b = NULL; - cxt->body.buf.size = 0; - cxt->method = 0; - *cxt->endpoint = '\0'; - *cxt->key = '\0'; - cxt->conn = NULL; - cxt->retry_attempt = 0; - discord_attachments_cleanup(&cxt->req.attachments); - - memset(&cxt->req, 0, sizeof(struct discord_request)); -} - -static void -_discord_adapter_context_populate(struct discord_adapter_context *cxt, - struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - cxt->method = method; - - memcpy(&cxt->req, req, sizeof(struct discord_request)); - _discord_attachments_dup(&cxt->req.attachments, &req->attachments); - - if (body) { - /* copy request body */ - if (body->size > cxt->body.memsize) { - /* needs to increase buffer size */ - void *tmp = realloc(cxt->body.buf.start, body->size); - ASSERT_S(tmp != NULL, "Out of memory"); - - cxt->body.buf.start = tmp; - cxt->body.memsize = body->size; - } - memcpy(cxt->body.buf.start, body->start, body->size); - cxt->body.buf.size = body->size; - } - - /* copy endpoint over to cxt */ - memcpy(cxt->endpoint, endpoint, sizeof(cxt->endpoint)); - /* copy bucket's key */ - memcpy(cxt->key, key, sizeof(cxt->key)); - /* bucket pertaining to the request */ - cxt->b = discord_bucket_get(adapter->ratelimiter, key); -} - /* enqueue a request to be executed asynchronously */ static CCORDcode _discord_adapter_run_async(struct discord_adapter *adapter, @@ -499,35 +339,12 @@ _discord_adapter_run_async(struct discord_adapter *adapter, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct discord_adapter_context *cxt; - - if (QUEUE_EMPTY(adapter->idleq)) { /* create new context struct */ - cxt = calloc(1, sizeof(struct discord_adapter_context)); - } - else { /* recycle a context struct from idleq */ - QUEUE(struct discord_adapter_context) *qelem = - QUEUE_HEAD(adapter->idleq); - QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); - } - QUEUE_INIT(&cxt->entry); - - _discord_adapter_context_populate(cxt, adapter, req, body, method, - endpoint, key); - - if (req->dispatch.high_p) - QUEUE_INSERT_HEAD(&cxt->b->waitq, &cxt->entry); - else - QUEUE_INSERT_TAIL(&cxt->b->waitq, &cxt->entry); - - if (req->dispatch.data) - discord_refcounter_incr(CLIENT(adapter, adapter)->refcounter, - req->dispatch.data, req->dispatch.cleanup, - false); + struct discord_context *cxt = + discord_context_populate(adapter, req, body, method, endpoint, key); - io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, - adapter->mhandle); + discord_context_bucket_enqueue(cxt->b, cxt, cxt->dispatch.high_p); + /* FIXME: redundant return value (constant) */ return CCORD_OK; } @@ -536,48 +353,26 @@ static CCORDcode _discord_adapter_send(struct discord_adapter *adapter, struct discord_bucket *b) { - struct discord_adapter_context *cxt; - CURLMcode mcode; - CURL *ehandle; - - /** TODO: make this a discord_adapter_context_xxx() function */ - QUEUE(struct discord_adapter_context) *qelem = QUEUE_HEAD(&b->waitq); - QUEUE_REMOVE(qelem); - QUEUE_INIT(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); - cxt->conn = ua_conn_start(adapter->ua); - /**/ + struct discord_context *cxt = discord_context_bucket_dequeue(b); + struct ua_conn *conn = ua_conn_start(adapter->ua); if (HTTP_MIMEPOST == cxt->method) { - ua_conn_add_header(cxt->conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(cxt->conn, cxt, &_discord_adapter_context_to_mime); + ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); + ua_conn_set_mime(conn, cxt, &discord_context_to_curlmime); } else { - ua_conn_add_header(cxt->conn, "Content-Type", "application/json"); + ua_conn_add_header(conn, "Content-Type", "application/json"); } - ua_conn_setup(cxt->conn, &(struct ua_conn_attr){ - .method = cxt->method, - .body = cxt->body.buf.start, - .body_size = cxt->body.buf.size, - .endpoint = cxt->endpoint, - }); - - ehandle = ua_conn_get_easy_handle(cxt->conn); - - /* link 'cxt' to 'ehandle' for easy retrieval */ - curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); - - /* initiate libcurl transfer */ - mcode = curl_multi_add_handle(adapter->mhandle, ehandle); - - io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, - adapter->mhandle); - - cxt->b->busy = cxt; + ua_conn_setup(conn, &(struct ua_conn_attr){ + .method = cxt->method, + .body = cxt->body.buf.start, + .body_size = cxt->body.buf.size, + .endpoint = cxt->endpoint, + }); - return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; + return discord_context_send(adapter, cxt, conn) ? CCORD_CURLM_INTERNAL + : CCORD_OK; } static void @@ -593,12 +388,12 @@ _discord_adapter_try_send(struct discord_adapter *adapter, _discord_adapter_send(adapter, b); } -/* TODO: redundant constant return value */ static CCORDcode _discord_adapter_check_pending(struct discord_adapter *adapter) { discord_ratelimiter_foreach(adapter->ratelimiter, adapter, &_discord_adapter_try_send); + /* FIXME: redundant return value (constant) */ return CCORD_OK; } @@ -607,7 +402,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, struct CURLMsg *msg) { struct discord *client = CLIENT(adapter, adapter); - struct discord_adapter_context *cxt; + struct discord_context *cxt; int64_t wait_ms = 0LL; CCORDcode code; bool retry; @@ -626,29 +421,26 @@ _discord_adapter_check_action(struct discord_adapter *adapter, if (info.code != CCORD_OK) { logconf_error(&client->conf, "%.*s", (int)body.size, body.start); - if (cxt->req.dispatch.fail) - cxt->req.dispatch.fail(client, info.code, - cxt->req.dispatch.data); + if (cxt->dispatch.fail) + cxt->dispatch.fail(client, info.code, cxt->dispatch.data); } - else if (cxt->req.dispatch.done.typed) { - void *ret = calloc(1, cxt->req.response.size); + else if (cxt->dispatch.done.typed) { + void *ret = calloc(1, cxt->response.size); /* initialize ret */ - if (cxt->req.response.init) cxt->req.response.init(ret); + if (cxt->response.init) cxt->response.init(ret); /* populate ret */ - if (cxt->req.response.from_json) - cxt->req.response.from_json(body.start, body.size, ret); + if (cxt->response.from_json) + cxt->response.from_json(body.start, body.size, ret); - if (cxt->req.dispatch.has_type) - cxt->req.dispatch.done.typed(client, cxt->req.dispatch.data, - ret); + if (cxt->dispatch.has_type) + cxt->dispatch.done.typed(client, cxt->dispatch.data, ret); else - cxt->req.dispatch.done.typeless(client, - cxt->req.dispatch.data); + cxt->dispatch.done.typeless(client, cxt->dispatch.data); /* cleanup ret */ - if (cxt->req.response.cleanup) cxt->req.response.cleanup(ret); + if (cxt->response.cleanup) cxt->response.cleanup(ret); free(ret); } @@ -671,28 +463,16 @@ _discord_adapter_check_action(struct discord_adapter *adapter, code = CCORD_CURLE_INTERNAL; - if (cxt->req.dispatch.fail) { - cxt->req.dispatch.fail(client, code, cxt->req.dispatch.data); - } + if (cxt->dispatch.fail) + cxt->dispatch.fail(client, code, cxt->dispatch.data); break; } /* enqueue request for retry or recycle */ cxt->b->busy = NULL; - if (retry && cxt->retry_attempt++ < adapter->retry_limit) { - ua_conn_reset(cxt->conn); - - if (wait_ms <= 0) { - QUEUE_INSERT_HEAD(&cxt->b->waitq, &cxt->entry); - } - } - else { - discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, - cxt->req.dispatch.data); - _discord_adapter_context_reset(cxt); - QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); - } + if (!retry || !discord_context_retry_enqueue(adapter, cxt, wait_ms)) + discord_context_recycle_enqueue(adapter, cxt); return code; } @@ -718,8 +498,6 @@ discord_adapter_perform(struct discord_adapter *adapter) if (!msg) break; if (CURLMSG_DONE != msg->msg) continue; - curl_multi_remove_handle(adapter->mhandle, msg->easy_handle); - /* check for request action */ _discord_adapter_check_action(adapter, msg); } @@ -733,16 +511,10 @@ _discord_adapter_stop_bucket(struct discord_adapter *adapter, { /* cancel busy transfer */ if (b->busy && b->busy != DISCORD_BUCKET_TIMEOUT) { - struct discord_adapter_context *cxt = b->busy; - CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); + struct discord_context *cxt = b->busy; b->busy = NULL; - - curl_multi_remove_handle(adapter->mhandle, ehandle); - - /* set for recycling */ - ua_conn_stop(cxt->conn); - QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); + discord_context_recycle_enqueue(adapter, cxt); } /* cancel pending tranfers */ diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index 74cc48512..71df2968b 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -383,8 +383,8 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, struct discord_bucket *b, const char key[]) { - QUEUE(struct discord_adapter_context) queue, *qelem; - struct discord_adapter_context *cxt; + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; QUEUE_MOVE(&rl->null->waitq, &queue); QUEUE_INIT(&rl->null->waitq); @@ -393,7 +393,7 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, qelem = QUEUE_HEAD(&queue); QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_adapter_context, entry); + cxt = QUEUE_DATA(qelem, struct discord_context, entry); if (0 == strcmp(cxt->key, key)) { QUEUE_INSERT_TAIL(&b->waitq, qelem); cxt->b = b; diff --git a/src/discord-adapter_request.c b/src/discord-adapter_request.c new file mode 100644 index 000000000..b0880bb7e --- /dev/null +++ b/src/discord-adapter_request.c @@ -0,0 +1,271 @@ +#include +#include +#include + +#include "discord.h" +#include "discord-internal.h" + +static void +_discord_context_cleanup(struct discord_context *cxt) +{ + discord_attachments_cleanup(&cxt->attachments); + if (cxt->body.buf.start) free(cxt->body.buf.start); + free(cxt); +} + +static struct discord_context * +_discord_context_init(QUEUE *cxt_idle_queue) +{ + struct discord_context *cxt; + + if (QUEUE_EMPTY(cxt_idle_queue)) { /* create new context struct */ + cxt = calloc(1, sizeof(struct discord_context)); + } + else { /* recycle a context struct from idleq */ + QUEUE(struct discord_context) *qelem = QUEUE_HEAD(cxt_idle_queue); + QUEUE_REMOVE(qelem); + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + } + QUEUE_INIT(&cxt->entry); + + return cxt; +} + +QUEUE * +discord_context_queue_init(void) +{ + QUEUE(struct discord_context) *new_cxt_queue = malloc(sizeof(QUEUE)); + QUEUE_INIT(new_cxt_queue); + return new_cxt_queue; +} + +void +discord_context_queue_cleanup(QUEUE *cxt_queue) +{ + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; + + QUEUE_MOVE(cxt_queue, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + QUEUE_REMOVE(&cxt->entry); + _discord_context_cleanup(cxt); + } + free(cxt_queue); +} + +void +discord_context_bucket_enqueue(struct discord_bucket *b, + struct discord_context *cxt, + bool high_priority) +{ + if (high_priority) + QUEUE_INSERT_HEAD(&b->waitq, &cxt->entry); + else + QUEUE_INSERT_TAIL(&b->waitq, &cxt->entry); +} + +struct discord_context * +discord_context_bucket_dequeue(struct discord_bucket *b) +{ + QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->waitq); + QUEUE_REMOVE(qelem); + QUEUE_INIT(qelem); + + return QUEUE_DATA(qelem, struct discord_context, entry); +} + +CURLMcode +discord_context_send(struct discord_adapter *adapter, + struct discord_context *cxt, + struct ua_conn *conn) +{ + CURL *ehandle = ua_conn_get_easy_handle(conn); + CURLMcode mcode; + + cxt->conn = conn; + cxt->b->busy = cxt; + + /* link 'cxt' to 'ehandle' for easy retrieval */ + curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); + + /* initiate libcurl transfer */ + mcode = curl_multi_add_handle(adapter->mhandle, ehandle); + + io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, + adapter->mhandle); + + return mcode; +} + +void +discord_context_to_curlmime(curl_mime *mime, void *p_cxt) +{ + struct discord_context *cxt = p_cxt; + struct discord_attachments *atchs = &cxt->attachments; + struct ccord_szbuf *body = &cxt->body.buf; + curl_mimepart *part; + char name[64]; + + /* json part */ + if (body->start && body->size) { + part = curl_mime_addpart(mime); + curl_mime_data(part, body->start, body->size); + curl_mime_type(part, "application/json"); + curl_mime_name(part, "payload_json"); + } + + /* attachment part */ + for (int i = 0; i < atchs->size; ++i) { + int len = snprintf(name, sizeof(name), "files[%d]", i); + ASSERT_NOT_OOB(len, sizeof(name)); + + if (atchs->array[i].content) { + part = curl_mime_addpart(mime); + curl_mime_data(part, atchs->array[i].content, + atchs->array[i].size ? atchs->array[i].size + : CURL_ZERO_TERMINATED); + curl_mime_filename(part, !atchs->array[i].filename + ? "a.out" + : atchs->array[i].filename); + curl_mime_type(part, !atchs->array[i].content_type + ? "application/octet-stream" + : atchs->array[i].content_type); + curl_mime_name(part, name); + } + else if (atchs->array[i].filename) { + CURLcode code; + + /* fetch local file by the filename */ + part = curl_mime_addpart(mime); + code = curl_mime_filedata(part, atchs->array[i].filename); + if (code != CURLE_OK) { + char errbuf[256]; + snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", + curl_easy_strerror(code), atchs->array[i].filename); + perror(errbuf); + } + curl_mime_type(part, !atchs->array[i].content_type + ? "application/octet-stream" + : atchs->array[i].content_type); + curl_mime_name(part, name); + } + } +} + +bool +discord_context_retry_enqueue(struct discord_adapter *adapter, + struct discord_context *cxt, + int64_t wait_ms) +{ + if (adapter->retry_limit < cxt->retry_attempt++) return false; + + CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); + + curl_multi_remove_handle(adapter->mhandle, ehandle); + ua_conn_reset(cxt->conn); + if (wait_ms <= 0) discord_context_bucket_enqueue(cxt->b, cxt, true); + + return true; +} + +void +discord_context_recycle_enqueue(struct discord_adapter *adapter, + struct discord_context *cxt) +{ + CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); + + curl_multi_remove_handle(adapter->mhandle, ehandle); + if (cxt->conn) ua_conn_stop(cxt->conn); + + discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, + cxt->dispatch.data); + + cxt->b = NULL; + cxt->body.buf.size = 0; + cxt->method = 0; + *cxt->endpoint = '\0'; + *cxt->key = '\0'; + cxt->conn = NULL; + cxt->retry_attempt = 0; + discord_attachments_cleanup(&cxt->attachments); + memset(cxt, 0, sizeof(struct discord_request)); + + QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); +} + +/* Only the fields that are required at discord_context_to_curlmime() + * are duplicated */ +static void +_discord_attachments_dup(struct discord_attachments *dest, + struct discord_attachments *src) +{ + int i; + + if (!src->size) return; + + __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); + for (i = 0; i < src->size; ++i) { + carray_insert(dest, i, src->array[i]); + if (src->array[i].content) { + dest->array[i].size = src->array[i].size + ? src->array[i].size + : strlen(src->array[i].content) + 1; + + dest->array[i].content = malloc(dest->array[i].size); + memcpy(dest->array[i].content, src->array[i].content, + dest->array[i].size); + } + if (src->array[i].filename) + dest->array[i].filename = strdup(src->array[i].filename); + if (src->array[i].content_type) + dest->array[i].content_type = strdup(src->array[i].content_type); + } +} + +struct discord_context * +discord_context_populate(struct discord_adapter *adapter, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) +{ + struct discord *client = CLIENT(adapter, adapter); + struct discord_context *cxt = _discord_context_init(adapter->idleq); + + cxt->method = method; + + memcpy(cxt, req, sizeof *req); + _discord_attachments_dup(&cxt->attachments, &req->attachments); + + if (body) { + /* copy request body */ + if (body->size > cxt->body.memsize) { + /* needs to increase buffer size */ + void *tmp = realloc(cxt->body.buf.start, body->size); + ASSERT_S(tmp != NULL, "Out of memory"); + + cxt->body.buf.start = tmp; + cxt->body.memsize = body->size; + } + memcpy(cxt->body.buf.start, body->start, body->size); + cxt->body.buf.size = body->size; + } + + /* copy endpoint over to cxt */ + memcpy(cxt->endpoint, endpoint, sizeof(cxt->endpoint)); + /* copy bucket's key */ + memcpy(cxt->key, key, sizeof(cxt->key)); + /* bucket pertaining to the request */ + cxt->b = discord_bucket_get(adapter->ratelimiter, key); + + if (req->dispatch.data) + discord_refcounter_incr(client->refcounter, req->dispatch.data, + req->dispatch.cleanup, false); + + io_poller_curlm_enable_perform(client->io_poller, adapter->mhandle); + + return cxt; +} From 592d916d557c37be3cd30d515f4de5a82f0337ed Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 15 May 2022 00:03:15 -0300 Subject: [PATCH 041/118] refactor(discord-adapter): move asynchronous request handling to discord-async.c and add 'struct discord_async' handle --- Makefile | 2 +- core/types.h | 10 ++ include/discord-internal.h | 273 ++++++++++++++++++++------------ src/discord-adapter.c | 162 ++++++++++++------- src/discord-adapter_async.c | 254 +++++++++++++++++++++++++++++ src/discord-adapter_ratelimit.c | 22 +-- src/discord-adapter_request.c | 271 ------------------------------- src/discord-loop.c | 9 +- 8 files changed, 559 insertions(+), 444 deletions(-) create mode 100644 src/discord-adapter_async.c delete mode 100644 src/discord-adapter_request.c diff --git a/Makefile b/Makefile index dcc2f7c4a..d04b5dba1 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,8 @@ THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ $(THIRDP_DIR)/priority_queue.o DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-adapter.o \ + $(SRC_DIR)/discord-adapter_async.o \ $(SRC_DIR)/discord-adapter_ratelimit.o \ - $(SRC_DIR)/discord-adapter_request.o \ $(SRC_DIR)/discord-refcount.o \ $(SRC_DIR)/discord-client.o \ $(SRC_DIR)/discord-loop.o \ diff --git a/core/types.h b/core/types.h index a4099e984..c8ce14eeb 100644 --- a/core/types.h +++ b/core/types.h @@ -56,6 +56,16 @@ struct ccord_szbuf_readonly { size_t size; }; +/** @brief Reusable generic sized buffer */ +struct ccord_szbuf_reusable { + /** the buffer's start */ + char *start; + /** the buffer's relative size in bytes */ + size_t size; + /** the buffer's real size in bytes */ + size_t realsize; +}; + /** @} ConcordTypes */ #endif /* CONCORD_TYPES_H */ diff --git a/include/discord-internal.h b/include/discord-internal.h index a2065846e..0742b2eaf 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -135,30 +135,171 @@ struct discord_request { DISCORD_REQUEST_FIELDS; }; -/** @brief The handle used for performing HTTP Requests */ +/** @defgroup DiscordInternalAdapterAsync Async + * async) + * @brief Store contexts of individual asynchronous requests + * @{ */ + +/** + * @brief Context of individual requests that are scheduled to run + * asynchronously + * @note its fields are aligned with @ref discord_request + */ +struct discord_context { + DISCORD_REQUEST_FIELDS; + + /** the request's bucket */ + struct discord_bucket *b; + /** request body handle @note buffer is kept and reused */ + + struct ccord_szbuf_reusable body; + /** the request's http method */ + enum http_method method; + /** the request's endpoint */ + char endpoint[DISCORD_ENDPT_LEN]; + /** the request bucket's key */ + char key[DISCORD_ROUTE_LEN]; + /** the connection handler assigned */ + struct ua_conn *conn; + /** the request bucket's queue entry */ + QUEUE entry; + + /** current retry attempt (stop at adapter->retry_limit) */ + int retry_attempt; +}; + +/** @brief The handle used for handling asynchronous requests */ +struct discord_async { + /** DISCORD_ASYNC logging module */ + struct logconf conf; + /** curl_multi handle for performing asynchronous requests */ + CURLM *mhandle; + /** idle request contexts */ + QUEUE(struct discord_context) * idle_contexts; +}; + +/** + * @brief Initialize an Async handle + * + * This shall initialize a `CURLM` multi handle for performing requests + * asynchronously, and a queue for storing individual requests contexts + * @param async the async handle to be initialized + * @param conf pointer to @ref discord_adapter logging module + */ +void discord_async_init(struct discord_async *async, struct logconf *conf); + +/** + * @brief Free an Async handle + * + * @param async the handle initialized with discord_async_init() + */ +void discord_async_cleanup(struct discord_async *async); + +/** + * @brief Insert request's context into bucket's pending queue + * @todo this doesn't have to be done manually, + * discord_async_start_context() should take care of it + * + * @param cxt the request context obtained via discord_async_start_context() + * @param b the bucket to insert the request to + * @param high_priority if high priority then request shall be prioritized over + * already enqueued requests + */ +void discord_context_bucket_insert(struct discord_context *cxt, + struct discord_bucket *b, + bool high_priority); + +/** + * @brief Remove head request's context from bucket's pending queue + * + * @param b the bucket to fetch the request from + * @return the request's context + */ +struct discord_context *discord_context_bucket_remove( + struct discord_bucket *b); + +/** + * @brief Kickstart the request by adding it to libcurl's request multiplexer + * (`CURLM` multi handle) + * + * @param async the async handle initialized with discord_async_init() + * @param cxt the context of the request to be sent over + * @param conn the @ref ua_conn connection handle + * @return CCORDcode for how the request went, @ref CCORD_CURLM_INTERNAL means + * something wrong happened + */ +CCORDcode discord_async_add_request(struct discord_async *async, + struct discord_context *cxt, + struct ua_conn *conn); + +/** + * @brief Request failed, enqueue it back to bucket's first position + * for next attempt + * + * @param async the async handle initialized with discord_async_init() + * @param cxt the failed request's context to be set for retry + * @param wait_ms in case of a @ref HTTP_TOO_MANY_REQUESTS, this is the + * ratelimiting time to wait for + * @return `true` if request can be retried + */ +bool discord_async_retry_context(struct discord_async *async, + struct discord_context *cxt, + int64_t wait_ms); + +/** + * @brief Insert a @ref discord_context structure into `async.idle_contexts` + * queue for recycling + * + * @param async the async handle initialized with discord_async_init() + * @param cxt the request context to be recycled + */ +void discord_async_recycle_context(struct discord_async *async, + struct discord_context *cxt); + +/** + * @brief Start request's context + * + * @param async the async handle initialized with discord_async_init() + * @param req the request containing preliminary information for its dispatch + * and response's parsing + * @param body the request's body + * @param method the request's HTTP method + * @param endpoint the request's endpoint + * @param key the request bucket's group for ratelimiting + * @return the initialized request context + */ +struct discord_context *discord_async_start_context( + struct discord_async *async, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]); + +/** @} DiscordInternalAdapterAsync */ + +/** @brief The handle used for interfacing with Discord's REST API */ struct discord_adapter { /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ struct logconf conf; /** the user agent handle for performing requests */ struct user_agent *ua; - /** curl_multi handle for performing non-blocking requests */ - CURLM *mhandle; + /** store individual contexts from asynchronous requests */ + struct discord_async async; - /** buckets discovered (declared at discord-adapter_ratelimit.c) */ + /** enforce ratelimiting on discovered buckets */ struct discord_ratelimiter *ratelimiter; - /** idle request handles */ - QUEUE(struct discord_context) * idleq; - /** max amount of retries before a failed request gives up */ int retry_limit; }; /** - * @brief Initialize the fields of a Discord Adapter handle + * @brief Initialize an Adapter handle * + * Structure used for interfacing with the Discord's REST API * @param adapter the adapter handle to be initialized - * @param conf optional pointer to a parent logconf + * @param conf pointer to @ref discord logging module * @param token the bot token */ void discord_adapter_init(struct discord_adapter *adapter, @@ -166,7 +307,7 @@ void discord_adapter_init(struct discord_adapter *adapter, struct ccord_szbuf_readonly *token); /** - * @brief Free a Discord Adapter handle + * @brief Free an Adapter handle * * @param adapter the handle initialized with discord_adapter_init() */ @@ -200,89 +341,16 @@ CCORDcode discord_adapter_run(struct discord_adapter *adapter, * @param adapter the handle initialized with discord_adapter_init() * @CCORD_return */ -CCORDcode discord_adapter_perform(struct discord_adapter *adapter); +CCORDcode discord_adapter_async_perform(struct discord_adapter *adapter); /** * @brief Stop all bucket's on-going, pending and timed-out requests * - * The requests will be moved over to client's 'idleq' queue + * The requests will be moved over to client's 'idle_contexts' queue * @param adapter the handle initialized with discord_adapter_init() */ void discord_adapter_stop_buckets(struct discord_adapter *adapter); -/** @defgroup DiscordInternalAdapterContext Request's context handling (for async) - * @brief Enqueue request contexts for asynchronous purposes - * @{ */ - -/** - * @brief Context of individual requests that are scheduled to run - * asynchronously - * @note its fields are aligned with @ref discord_request, meaning those can be - * cast back and forth - */ -struct discord_context { - DISCORD_REQUEST_FIELDS; - - /** the request's bucket */ - struct discord_bucket *b; - - /** request body handle @note buffer is kept and recycled */ - struct { - /** the request body contents */ - struct ccord_szbuf buf; - /** the real size occupied in memory by `buf.start` */ - size_t memsize; - } body; - - /** the request's http method */ - enum http_method method; - /** the request's endpoint */ - char endpoint[DISCORD_ENDPT_LEN]; - /** the request bucket's key */ - char key[DISCORD_ROUTE_LEN]; - /** the connection handler assigned */ - struct ua_conn *conn; - /** the request bucket's queue entry */ - QUEUE entry; - - /** current retry attempt (stop at adapter->retry_limit) */ - int retry_attempt; -}; - -QUEUE *discord_context_queue_init(void); - -void discord_context_queue_cleanup(QUEUE *cxt_queue); - -void discord_context_bucket_enqueue(struct discord_bucket *b, - struct discord_context *cxt, - bool high_priority); - -struct discord_context *discord_context_bucket_dequeue( - struct discord_bucket *b); - -CURLMcode discord_context_send(struct discord_adapter *adapter, - struct discord_context *cxt, - struct ua_conn *conn); - -void discord_context_to_curlmime(curl_mime *mime, void *p_cxt); - -bool discord_context_retry_enqueue(struct discord_adapter *adapter, - struct discord_context *cxt, - int64_t wait_ms); - -void discord_context_recycle_enqueue(struct discord_adapter *adapter, - struct discord_context *cxt); - -struct discord_context *discord_context_populate( - struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); - -/** @} DiscordInternalAdapterContext */ - /** @defgroup DiscordInternalAdapterRatelimit Ratelimiting * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ @@ -306,7 +374,7 @@ struct discord_bucket { /** synchronize ratelimiting between threads */ pthread_mutex_t lock; /** pending requests */ - QUEUE(struct discord_context) waitq; + QUEUE(struct discord_context) pending_queue; /** * pointer to currently performing busy request (if any) * @note `NULL` if free or @ref DISCORD_BUCKET_TIMEOUT if being ratelimited @@ -338,10 +406,10 @@ void discord_bucket_try_sleep(struct discord_ratelimiter *rl, /** * @brief Try to timeout bucket for pending cooldown time * - * @param client the client initialized with discord_init() + * @param adapter the handle initialized with discord_adapter_init() * @param bucket the bucket to wait on cooldown */ -void discord_bucket_try_timeout(struct discord *client, +void discord_bucket_try_timeout(struct discord_adapter *adapter, struct discord_bucket *b); /** @@ -389,7 +457,7 @@ struct discord_ratelimiter { * @brief Initialize ratelimiter handle * * A hashtable shall be used for storage and retrieval of discovered buckets - * @param conf optional pointer to a parent logconf + * @param conf pointer to @ref discord_adapter logging module * @return the ratelimiter handle */ struct discord_ratelimiter *discord_ratelimiter_init(struct logconf *conf); @@ -397,7 +465,7 @@ struct discord_ratelimiter *discord_ratelimiter_init(struct logconf *conf); /** * @brief Cleanup all buckets that have been discovered * - * @note pending requests will be moved to `adapter.idleq` + * @note pending requests will be moved to `adapter.idle_contexts` * @param rl the handle initialized with discord_ratelimiter_init() */ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); @@ -409,10 +477,10 @@ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); * @param adapter the handle initialized with discord_adapter_init() * @param iter the user callback to be called per bucket */ -void discord_ratelimiter_foreach(struct discord_ratelimiter *rl, - struct discord_adapter *adapter, - void (*iter)(struct discord_adapter *adapter, - struct discord_bucket *b)); +void discord_ratelimiter_foreach_bucket( + struct discord_ratelimiter *rl, + struct discord_adapter *adapter, + void (*iter)(struct discord_adapter *adapter, struct discord_bucket *b)); /** * @brief Build unique key formed from the HTTP method and endpoint @@ -488,7 +556,7 @@ struct discord_gateway_payload { jsmnf_pair *data; }; -/** @brief The handle used for establishing a WebSockets connection */ +/** @brief The handle used for interfacing with Discord's Gateway API */ struct discord_gateway { /** DISCORD_GATEWAY logging module */ struct logconf conf; @@ -572,10 +640,11 @@ struct discord_gateway { }; /** - * @brief Initialize the fields of Discord Gateway handle + * @brief Initialize a Gateway handle * + * Structure used for interfacing with the Discord's Gateway API * @param gw the gateway handle to be initialized - * @param conf optional pointer to a parent logconf + * @param conf pointer to @ref discord logging module * @param token the bot token */ void discord_gateway_init(struct discord_gateway *gw, @@ -583,7 +652,7 @@ void discord_gateway_init(struct discord_gateway *gw, struct ccord_szbuf_readonly *token); /** - * @brief Free a Discord Gateway handle + * @brief Free a Gateway handle * * @param gw the handle initialized with discord_gateway_init() */ @@ -797,7 +866,7 @@ struct discord_refcounter { * @brief Initialize reference counter handle * * A hashtable shall be used for storage and retrieval of user data - * @param conf optional pointer to a parent logconf + * @param conf pointer to @ref discord logging module * @return the reference counter handle */ struct discord_refcounter *discord_refcounter_init(struct logconf *conf); @@ -863,16 +932,16 @@ struct discord_message_commands { }; /** - * @brief Initialize the fields of the Message Commands handle + * @brief Initialize a Message Commands handle * - * @param conf optional pointer to a parent logconf + * @param conf pointer to @ref discord logging module * @return the message commands handle */ struct discord_message_commands *discord_message_commands_init( struct logconf *conf); /** - * @brief Free Message Commands handle + * @brief Free a Message Commands handle * * @param cmds the handle initialized with discord_message_commands_init() */ diff --git a/src/discord-adapter.c b/src/discord-adapter.c index ded60f9e4..03b09530f 100644 --- a/src/discord-adapter.c +++ b/src/discord-adapter.c @@ -26,14 +26,6 @@ setopt_cb(struct ua_conn *conn, void *p_token) #endif } -static int -on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) -{ - (void)io; - (void)mhandle; - return discord_adapter_perform(user_data); -} - void discord_adapter_init(struct discord_adapter *adapter, struct logconf *conf, @@ -55,16 +47,9 @@ discord_adapter_init(struct discord_adapter *adapter, ua_set_opt(adapter->ua, token, &setopt_cb); } - adapter->mhandle = curl_multi_init(); - io_poller_curlm_add(CLIENT(adapter, adapter)->io_poller, adapter->mhandle, - on_io_poller_curl, adapter); - + discord_async_init(&adapter->async, &adapter->conf); adapter->ratelimiter = discord_ratelimiter_init(&adapter->conf); - /* idleq is malloc'd to guarantee a client cloned by discord_clone() will - * share the same queue with the original */ - adapter->idleq = discord_context_queue_init(); - adapter->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ } @@ -73,15 +58,12 @@ discord_adapter_cleanup(struct discord_adapter *adapter) { /* cleanup User-Agent handle */ ua_cleanup(adapter->ua); - /* cleanup curl's multi handle */ - io_poller_curlm_del(CLIENT(adapter, adapter)->io_poller, adapter->mhandle); - curl_multi_cleanup(adapter->mhandle); - /* move pending requests to idleq */ + /* move pending requests to idle_contexts */ discord_adapter_stop_buckets(adapter); + /* cleanup idle requests queue */ + discord_async_cleanup(&adapter->async); /* cleanup discovered buckets */ discord_ratelimiter_cleanup(adapter->ratelimiter); - /* cleanup idle requests queue */ - discord_context_queue_cleanup(adapter->idleq); } static CCORDcode _discord_adapter_run_sync(struct discord_adapter *adapter, @@ -222,6 +204,64 @@ _discord_adapter_get_info(struct discord_adapter *adapter, } } +/* + * data is a `void *[2]`, where the first element is a + * `struct discord_attachment` and the second element is a + * `struct ccord_szbuf` containing the request body */ +static void +_discord_adapter_request_to_multipart(curl_mime *mime, void *data) +{ + struct discord_attachments *atchs = ((void **)data)[0]; + struct ccord_szbuf *body = ((void **)data)[1]; + curl_mimepart *part; + char name[64]; + + /* json part */ + if (body->start && body->size) { + part = curl_mime_addpart(mime); + curl_mime_data(part, body->start, body->size); + curl_mime_type(part, "application/json"); + curl_mime_name(part, "payload_json"); + } + + /* attachment part */ + for (int i = 0; i < atchs->size; ++i) { + int len = snprintf(name, sizeof(name), "files[%d]", i); + ASSERT_NOT_OOB(len, sizeof(name)); + + if (atchs->array[i].content) { + part = curl_mime_addpart(mime); + curl_mime_data(part, atchs->array[i].content, + atchs->array[i].size ? atchs->array[i].size + : CURL_ZERO_TERMINATED); + curl_mime_filename(part, !atchs->array[i].filename + ? "a.out" + : atchs->array[i].filename); + curl_mime_type(part, !atchs->array[i].content_type + ? "application/octet-stream" + : atchs->array[i].content_type); + curl_mime_name(part, name); + } + else if (atchs->array[i].filename) { + CURLcode code; + + /* fetch local file by the filename */ + part = curl_mime_addpart(mime); + code = curl_mime_filedata(part, atchs->array[i].filename); + if (code != CURLE_OK) { + char errbuf[256]; + snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", + curl_easy_strerror(code), atchs->array[i].filename); + perror(errbuf); + } + curl_mime_type(part, !atchs->array[i].content_type + ? "application/octet-stream" + : atchs->array[i].content_type); + curl_mime_name(part, name); + } + } +} + /* SYNCHRONOUS REQUEST LOGIC */ /* perform a blocking request */ @@ -233,8 +273,7 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - /* throw-away for ua_conn_set_mime() */ - struct discord_context cxt = { 0 }; + void *data[2] = { &req->attachments, body }; struct discord_bucket *b; struct ua_conn *conn; int retry_attempt = 0; @@ -245,11 +284,8 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, conn = ua_conn_start(adapter->ua); if (HTTP_MIMEPOST == method) { - cxt.attachments = req->attachments; - cxt.body.buf = *body; - ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, &cxt, &discord_context_to_curlmime); + ua_conn_set_mime(conn, data, &_discord_adapter_request_to_multipart); } else { ua_conn_add_header(conn, "Content-Type", "application/json"); @@ -339,26 +375,38 @@ _discord_adapter_run_async(struct discord_adapter *adapter, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct discord_context *cxt = - discord_context_populate(adapter, req, body, method, endpoint, key); + struct discord_context *cxt = discord_async_start_context( + &adapter->async, req, body, method, endpoint, key); - discord_context_bucket_enqueue(cxt->b, cxt, cxt->dispatch.high_p); + discord_context_bucket_insert(cxt, cxt->b, req->dispatch.high_p); /* FIXME: redundant return value (constant) */ return CCORD_OK; } +static void +_discord_context_to_multipart(curl_mime *mime, void *p_cxt) +{ + struct discord_context *cxt = p_cxt; + void *data[2] = { &cxt->attachments, &(struct ccord_szbuf){ + cxt->body.start, + cxt->body.size, + } }; + + _discord_adapter_request_to_multipart(mime, data); +} + /* add a request to libcurl's multi handle */ static CCORDcode -_discord_adapter_send(struct discord_adapter *adapter, - struct discord_bucket *b) +_discord_adapter_add_request(struct discord_adapter *adapter, + struct discord_bucket *b) { - struct discord_context *cxt = discord_context_bucket_dequeue(b); + struct discord_context *cxt = discord_context_bucket_remove(b); struct ua_conn *conn = ua_conn_start(adapter->ua); if (HTTP_MIMEPOST == cxt->method) { ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, cxt, &discord_context_to_curlmime); + ua_conn_set_mime(conn, cxt, &_discord_context_to_multipart); } else { ua_conn_add_header(conn, "Content-Type", "application/json"); @@ -366,33 +414,33 @@ _discord_adapter_send(struct discord_adapter *adapter, ua_conn_setup(conn, &(struct ua_conn_attr){ .method = cxt->method, - .body = cxt->body.buf.start, - .body_size = cxt->body.buf.size, + .body = cxt->body.start, + .body_size = cxt->body.size, .endpoint = cxt->endpoint, + .base_url = NULL, }); - return discord_context_send(adapter, cxt, conn) ? CCORD_CURLM_INTERNAL - : CCORD_OK; + return discord_async_add_request(&adapter->async, cxt, conn); } static void -_discord_adapter_try_send(struct discord_adapter *adapter, - struct discord_bucket *b) +_discord_adapter_try_add_request(struct discord_adapter *adapter, + struct discord_bucket *b) { /* skip if bucket is busy performing */ if (b->busy) return; if (!b->remaining) - discord_bucket_try_timeout(CLIENT(adapter, adapter), b); - else if (!QUEUE_EMPTY(&b->waitq)) - _discord_adapter_send(adapter, b); + discord_bucket_try_timeout(adapter, b); + else if (!QUEUE_EMPTY(&b->pending_queue)) + _discord_adapter_add_request(adapter, b); } static CCORDcode _discord_adapter_check_pending(struct discord_adapter *adapter) { - discord_ratelimiter_foreach(adapter->ratelimiter, adapter, - &_discord_adapter_try_send); + discord_ratelimiter_foreach_bucket(adapter->ratelimiter, adapter, + &_discord_adapter_try_add_request); /* FIXME: redundant return value (constant) */ return CCORD_OK; } @@ -471,14 +519,14 @@ _discord_adapter_check_action(struct discord_adapter *adapter, /* enqueue request for retry or recycle */ cxt->b->busy = NULL; - if (!retry || !discord_context_retry_enqueue(adapter, cxt, wait_ms)) - discord_context_recycle_enqueue(adapter, cxt); + if (!retry || !discord_async_retry_context(&adapter->async, cxt, wait_ms)) + discord_async_recycle_context(&adapter->async, cxt); return code; } CCORDcode -discord_adapter_perform(struct discord_adapter *adapter) +discord_adapter_async_perform(struct discord_adapter *adapter) { CURLMcode mcode; CCORDcode code; @@ -487,13 +535,15 @@ discord_adapter_perform(struct discord_adapter *adapter) if (CCORD_OK != (code = _discord_adapter_check_pending(adapter))) return code; - if (CURLM_OK != (mcode = curl_multi_socket_all(adapter->mhandle, &alive))) + if (CURLM_OK + != (mcode = curl_multi_socket_all(adapter->async.mhandle, &alive))) return CCORD_CURLM_INTERNAL; /* ask for any messages/informationals from the individual transfers */ while (1) { int msgq = 0; - struct CURLMsg *msg = curl_multi_info_read(adapter->mhandle, &msgq); + struct CURLMsg *msg = + curl_multi_info_read(adapter->async.mhandle, &msgq); if (!msg) break; if (CURLMSG_DONE != msg->msg) continue; @@ -514,17 +564,17 @@ _discord_adapter_stop_bucket(struct discord_adapter *adapter, struct discord_context *cxt = b->busy; b->busy = NULL; - discord_context_recycle_enqueue(adapter, cxt); + discord_async_recycle_context(&adapter->async, cxt); } /* cancel pending tranfers */ - QUEUE_ADD(adapter->idleq, &b->waitq); - QUEUE_INIT(&b->waitq); + QUEUE_ADD(adapter->async.idle_contexts, &b->pending_queue); + QUEUE_INIT(&b->pending_queue); } void discord_adapter_stop_buckets(struct discord_adapter *adapter) { - discord_ratelimiter_foreach(adapter->ratelimiter, adapter, - &_discord_adapter_stop_bucket); + discord_ratelimiter_foreach_bucket(adapter->ratelimiter, adapter, + &_discord_adapter_stop_bucket); } diff --git a/src/discord-adapter_async.c b/src/discord-adapter_async.c new file mode 100644 index 000000000..8259811c2 --- /dev/null +++ b/src/discord-adapter_async.c @@ -0,0 +1,254 @@ +#include +#include +#include + +#include "discord.h" +#include "discord-internal.h" + +static struct discord_context * +_discord_context_init(void) +{ + return calloc(1, sizeof(struct discord_context)); +} + +static void +_discord_context_cleanup(struct discord_context *cxt) +{ + discord_attachments_cleanup(&cxt->attachments); + if (cxt->body.start) free(cxt->body.start); + free(cxt); +} + +static struct discord_context * +_discord_context_get(struct discord_async *async) +{ + struct discord_context *cxt; + + if (QUEUE_EMPTY(async->idle_contexts)) { /* create new context struct */ + cxt = _discord_context_init(); + } + else { /* recycle a context struct from idle_contexts */ + QUEUE(struct discord_context) *qelem = + QUEUE_HEAD(async->idle_contexts); + + QUEUE_REMOVE(qelem); + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + } + QUEUE_INIT(&cxt->entry); + + return cxt; +} + +static int +_on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) +{ + (void)io; + (void)mhandle; + return discord_adapter_async_perform(user_data); +} + +void +discord_async_init(struct discord_async *async, struct logconf *conf) +{ + struct discord_adapter *adapter = + CONTAINEROF(async, struct discord_adapter, async); + + logconf_branch(&async->conf, conf, "DISCORD_ASYNC"); + + /* idle_contexts is malloc'd to guarantee a client cloned by + * discord_clone() will share the same queue with the original */ + async->idle_contexts = malloc(sizeof *async->idle_contexts); + QUEUE_INIT(async->idle_contexts); + + async->mhandle = curl_multi_init(); + io_poller_curlm_add(CLIENT(adapter, adapter)->io_poller, async->mhandle, + &_on_io_poller_curl, adapter); +} + +void +discord_async_cleanup(struct discord_async *async) +{ + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; + + QUEUE_MOVE(async->idle_contexts, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + QUEUE_REMOVE(&cxt->entry); + _discord_context_cleanup(cxt); + } + free(async->idle_contexts); + + /* cleanup curl's multi handle */ + io_poller_curlm_del(CLIENT(async, adapter.async)->io_poller, + async->mhandle); + curl_multi_cleanup(async->mhandle); +} + +void +discord_context_bucket_insert(struct discord_context *cxt, + struct discord_bucket *b, + bool high_priority) +{ + if (high_priority) + QUEUE_INSERT_HEAD(&b->pending_queue, &cxt->entry); + else + QUEUE_INSERT_TAIL(&b->pending_queue, &cxt->entry); +} + +struct discord_context * +discord_context_bucket_remove(struct discord_bucket *b) +{ + QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->pending_queue); + QUEUE_REMOVE(qelem); + QUEUE_INIT(qelem); + + return QUEUE_DATA(qelem, struct discord_context, entry); +} + +CCORDcode +discord_async_add_request(struct discord_async *async, + struct discord_context *cxt, + struct ua_conn *conn) +{ + CURL *ehandle = ua_conn_get_easy_handle(conn); + CURLMcode mcode; + + cxt->conn = conn; + cxt->b->busy = cxt; + + /* link 'cxt' to 'ehandle' for easy retrieval */ + curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); + + /* initiate libcurl transfer */ + mcode = curl_multi_add_handle(async->mhandle, ehandle); + + io_poller_curlm_enable_perform(CLIENT(async, adapter.async)->io_poller, + async->mhandle); + + return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; +} + +bool +discord_async_retry_context(struct discord_async *async, + struct discord_context *cxt, + int64_t wait_ms) +{ + struct discord_adapter *adapter = + CONTAINEROF(async, struct discord_adapter, async); + + if (adapter->retry_limit < cxt->retry_attempt++) return false; + + CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); + + curl_multi_remove_handle(async->mhandle, ehandle); + ua_conn_reset(cxt->conn); + + /* FIXME: wait_ms > 0 should be dealt with aswell */ + if (wait_ms <= 0) discord_context_bucket_insert(cxt, cxt->b, true); + + return true; +} + +void +discord_async_recycle_context(struct discord_async *async, + struct discord_context *cxt) +{ + CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); + + curl_multi_remove_handle(async->mhandle, ehandle); + if (cxt->conn) ua_conn_stop(cxt->conn); + + discord_refcounter_decr(CLIENT(async, adapter.async)->refcounter, + cxt->dispatch.data); + + cxt->b = NULL; + cxt->body.size = 0; + cxt->method = 0; + *cxt->endpoint = '\0'; + *cxt->key = '\0'; + cxt->conn = NULL; + cxt->retry_attempt = 0; + discord_attachments_cleanup(&cxt->attachments); + memset(cxt, 0, sizeof(struct discord_request)); + + QUEUE_INSERT_TAIL(async->idle_contexts, &cxt->entry); +} + +/* Only the fields that are required at _discord_adapter_request_to_multipart() + * are duplicated */ +static void +_discord_attachments_dup(struct discord_attachments *dest, + struct discord_attachments *src) +{ + int i; + + if (!src->size) return; + + __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); + for (i = 0; i < src->size; ++i) { + carray_insert(dest, i, src->array[i]); + if (src->array[i].content) { + dest->array[i].size = src->array[i].size + ? src->array[i].size + : strlen(src->array[i].content) + 1; + + dest->array[i].content = malloc(dest->array[i].size); + memcpy(dest->array[i].content, src->array[i].content, + dest->array[i].size); + } + if (src->array[i].filename) + dest->array[i].filename = strdup(src->array[i].filename); + if (src->array[i].content_type) + dest->array[i].content_type = strdup(src->array[i].content_type); + } +} + +struct discord_context * +discord_async_start_context(struct discord_async *async, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) +{ + struct discord_adapter *adapter = + CONTAINEROF(async, struct discord_adapter, async); + struct discord *client = CLIENT(adapter, adapter); + struct discord_context *cxt = _discord_context_get(async); + + cxt->method = method; + + memcpy(cxt, req, sizeof *req); + _discord_attachments_dup(&cxt->attachments, &req->attachments); + + if (body) { + /* copy request body */ + if (body->size > cxt->body.realsize) { + /* needs to increase buffer size */ + void *tmp = realloc(cxt->body.start, body->size); + ASSERT_S(tmp != NULL, "Out of memory"); + + cxt->body.start = tmp; + cxt->body.realsize = body->size; + } + memcpy(cxt->body.start, body->start, body->size); + cxt->body.size = body->size; + } + + /* copy endpoint over to cxt */ + memcpy(cxt->endpoint, endpoint, sizeof(cxt->endpoint)); + /* copy bucket's key */ + memcpy(cxt->key, key, sizeof(cxt->key)); + /* bucket pertaining to the request */ + cxt->b = discord_bucket_get(adapter->ratelimiter, key); + + if (req->dispatch.data) + discord_refcounter_incr(client->refcounter, req->dispatch.data, + req->dispatch.cleanup, false); + + io_poller_curlm_enable_perform(client->io_poller, async->mhandle); + + return cxt; +} diff --git a/src/discord-adapter_ratelimit.c b/src/discord-adapter_ratelimit.c index 71df2968b..450a9bf0e 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-adapter_ratelimit.c @@ -133,7 +133,7 @@ _discord_bucket_init(struct discord_ratelimiter *rl, if (pthread_mutex_init(&b->lock, NULL)) ERR("Couldn't initialize pthread mutex"); - QUEUE_INIT(&b->waitq); + QUEUE_INIT(&b->pending_queue); pthread_mutex_lock(&rl->global.lock); chash_assign(rl, key, b, RATELIMITER_TABLE); @@ -173,10 +173,10 @@ discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) } void -discord_ratelimiter_foreach(struct discord_ratelimiter *rl, - struct discord_adapter *adapter, - void (*iter)(struct discord_adapter *adapter, - struct discord_bucket *b)) +discord_ratelimiter_foreach_bucket( + struct discord_ratelimiter *rl, + struct discord_adapter *adapter, + void (*iter)(struct discord_adapter *adapter, struct discord_bucket *b)) { struct _discord_route *r; int i; @@ -255,8 +255,10 @@ _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) } void -discord_bucket_try_timeout(struct discord *client, struct discord_bucket *b) +discord_bucket_try_timeout(struct discord_adapter *adapter, + struct discord_bucket *b) { + struct discord *client = CLIENT(adapter, adapter); const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); b->busy = DISCORD_BUCKET_TIMEOUT; @@ -386,8 +388,8 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, QUEUE(struct discord_context) queue, *qelem; struct discord_context *cxt; - QUEUE_MOVE(&rl->null->waitq, &queue); - QUEUE_INIT(&rl->null->waitq); + QUEUE_MOVE(&rl->null->pending_queue, &queue); + QUEUE_INIT(&rl->null->pending_queue); while (!QUEUE_EMPTY(&queue)) { qelem = QUEUE_HEAD(&queue); @@ -395,11 +397,11 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, cxt = QUEUE_DATA(qelem, struct discord_context, entry); if (0 == strcmp(cxt->key, key)) { - QUEUE_INSERT_TAIL(&b->waitq, qelem); + QUEUE_INSERT_TAIL(&b->pending_queue, qelem); cxt->b = b; } else { - QUEUE_INSERT_TAIL(&rl->null->waitq, qelem); + QUEUE_INSERT_TAIL(&rl->null->pending_queue, qelem); } } } diff --git a/src/discord-adapter_request.c b/src/discord-adapter_request.c deleted file mode 100644 index b0880bb7e..000000000 --- a/src/discord-adapter_request.c +++ /dev/null @@ -1,271 +0,0 @@ -#include -#include -#include - -#include "discord.h" -#include "discord-internal.h" - -static void -_discord_context_cleanup(struct discord_context *cxt) -{ - discord_attachments_cleanup(&cxt->attachments); - if (cxt->body.buf.start) free(cxt->body.buf.start); - free(cxt); -} - -static struct discord_context * -_discord_context_init(QUEUE *cxt_idle_queue) -{ - struct discord_context *cxt; - - if (QUEUE_EMPTY(cxt_idle_queue)) { /* create new context struct */ - cxt = calloc(1, sizeof(struct discord_context)); - } - else { /* recycle a context struct from idleq */ - QUEUE(struct discord_context) *qelem = QUEUE_HEAD(cxt_idle_queue); - QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - } - QUEUE_INIT(&cxt->entry); - - return cxt; -} - -QUEUE * -discord_context_queue_init(void) -{ - QUEUE(struct discord_context) *new_cxt_queue = malloc(sizeof(QUEUE)); - QUEUE_INIT(new_cxt_queue); - return new_cxt_queue; -} - -void -discord_context_queue_cleanup(QUEUE *cxt_queue) -{ - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; - - QUEUE_MOVE(cxt_queue, &queue); - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - QUEUE_REMOVE(&cxt->entry); - _discord_context_cleanup(cxt); - } - free(cxt_queue); -} - -void -discord_context_bucket_enqueue(struct discord_bucket *b, - struct discord_context *cxt, - bool high_priority) -{ - if (high_priority) - QUEUE_INSERT_HEAD(&b->waitq, &cxt->entry); - else - QUEUE_INSERT_TAIL(&b->waitq, &cxt->entry); -} - -struct discord_context * -discord_context_bucket_dequeue(struct discord_bucket *b) -{ - QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->waitq); - QUEUE_REMOVE(qelem); - QUEUE_INIT(qelem); - - return QUEUE_DATA(qelem, struct discord_context, entry); -} - -CURLMcode -discord_context_send(struct discord_adapter *adapter, - struct discord_context *cxt, - struct ua_conn *conn) -{ - CURL *ehandle = ua_conn_get_easy_handle(conn); - CURLMcode mcode; - - cxt->conn = conn; - cxt->b->busy = cxt; - - /* link 'cxt' to 'ehandle' for easy retrieval */ - curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); - - /* initiate libcurl transfer */ - mcode = curl_multi_add_handle(adapter->mhandle, ehandle); - - io_poller_curlm_enable_perform(CLIENT(adapter, adapter)->io_poller, - adapter->mhandle); - - return mcode; -} - -void -discord_context_to_curlmime(curl_mime *mime, void *p_cxt) -{ - struct discord_context *cxt = p_cxt; - struct discord_attachments *atchs = &cxt->attachments; - struct ccord_szbuf *body = &cxt->body.buf; - curl_mimepart *part; - char name[64]; - - /* json part */ - if (body->start && body->size) { - part = curl_mime_addpart(mime); - curl_mime_data(part, body->start, body->size); - curl_mime_type(part, "application/json"); - curl_mime_name(part, "payload_json"); - } - - /* attachment part */ - for (int i = 0; i < atchs->size; ++i) { - int len = snprintf(name, sizeof(name), "files[%d]", i); - ASSERT_NOT_OOB(len, sizeof(name)); - - if (atchs->array[i].content) { - part = curl_mime_addpart(mime); - curl_mime_data(part, atchs->array[i].content, - atchs->array[i].size ? atchs->array[i].size - : CURL_ZERO_TERMINATED); - curl_mime_filename(part, !atchs->array[i].filename - ? "a.out" - : atchs->array[i].filename); - curl_mime_type(part, !atchs->array[i].content_type - ? "application/octet-stream" - : atchs->array[i].content_type); - curl_mime_name(part, name); - } - else if (atchs->array[i].filename) { - CURLcode code; - - /* fetch local file by the filename */ - part = curl_mime_addpart(mime); - code = curl_mime_filedata(part, atchs->array[i].filename); - if (code != CURLE_OK) { - char errbuf[256]; - snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", - curl_easy_strerror(code), atchs->array[i].filename); - perror(errbuf); - } - curl_mime_type(part, !atchs->array[i].content_type - ? "application/octet-stream" - : atchs->array[i].content_type); - curl_mime_name(part, name); - } - } -} - -bool -discord_context_retry_enqueue(struct discord_adapter *adapter, - struct discord_context *cxt, - int64_t wait_ms) -{ - if (adapter->retry_limit < cxt->retry_attempt++) return false; - - CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); - - curl_multi_remove_handle(adapter->mhandle, ehandle); - ua_conn_reset(cxt->conn); - if (wait_ms <= 0) discord_context_bucket_enqueue(cxt->b, cxt, true); - - return true; -} - -void -discord_context_recycle_enqueue(struct discord_adapter *adapter, - struct discord_context *cxt) -{ - CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); - - curl_multi_remove_handle(adapter->mhandle, ehandle); - if (cxt->conn) ua_conn_stop(cxt->conn); - - discord_refcounter_decr(CLIENT(adapter, adapter)->refcounter, - cxt->dispatch.data); - - cxt->b = NULL; - cxt->body.buf.size = 0; - cxt->method = 0; - *cxt->endpoint = '\0'; - *cxt->key = '\0'; - cxt->conn = NULL; - cxt->retry_attempt = 0; - discord_attachments_cleanup(&cxt->attachments); - memset(cxt, 0, sizeof(struct discord_request)); - - QUEUE_INSERT_TAIL(adapter->idleq, &cxt->entry); -} - -/* Only the fields that are required at discord_context_to_curlmime() - * are duplicated */ -static void -_discord_attachments_dup(struct discord_attachments *dest, - struct discord_attachments *src) -{ - int i; - - if (!src->size) return; - - __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); - for (i = 0; i < src->size; ++i) { - carray_insert(dest, i, src->array[i]); - if (src->array[i].content) { - dest->array[i].size = src->array[i].size - ? src->array[i].size - : strlen(src->array[i].content) + 1; - - dest->array[i].content = malloc(dest->array[i].size); - memcpy(dest->array[i].content, src->array[i].content, - dest->array[i].size); - } - if (src->array[i].filename) - dest->array[i].filename = strdup(src->array[i].filename); - if (src->array[i].content_type) - dest->array[i].content_type = strdup(src->array[i].content_type); - } -} - -struct discord_context * -discord_context_populate(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - struct discord *client = CLIENT(adapter, adapter); - struct discord_context *cxt = _discord_context_init(adapter->idleq); - - cxt->method = method; - - memcpy(cxt, req, sizeof *req); - _discord_attachments_dup(&cxt->attachments, &req->attachments); - - if (body) { - /* copy request body */ - if (body->size > cxt->body.memsize) { - /* needs to increase buffer size */ - void *tmp = realloc(cxt->body.buf.start, body->size); - ASSERT_S(tmp != NULL, "Out of memory"); - - cxt->body.buf.start = tmp; - cxt->body.memsize = body->size; - } - memcpy(cxt->body.buf.start, body->start, body->size); - cxt->body.buf.size = body->size; - } - - /* copy endpoint over to cxt */ - memcpy(cxt->endpoint, endpoint, sizeof(cxt->endpoint)); - /* copy bucket's key */ - memcpy(cxt->key, key, sizeof(cxt->key)); - /* bucket pertaining to the request */ - cxt->b = discord_bucket_get(adapter->ratelimiter, key); - - if (req->dispatch.data) - discord_refcounter_incr(client->refcounter, req->dispatch.data, - req->dispatch.cleanup, false); - - io_poller_curlm_enable_perform(client->io_poller, adapter->mhandle); - - return cxt; -} diff --git a/src/discord-loop.c b/src/discord-loop.c index 1a26058bc..f028de658 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -83,10 +83,10 @@ discord_timer_get_next_trigger(struct discord_timers *const timers[], CCORDcode discord_run(struct discord *client) { - int64_t next_run, now; - CCORDcode code; struct discord_timers *const timers[] = { &client->timers.internal, &client->timers.user }; + int64_t next_run, now; + CCORDcode code; while (1) { BREAK_ON_FAIL(code, discord_gateway_start(&client->gw)); @@ -134,7 +134,7 @@ discord_run(struct discord *client) if (-1 == poll_result) { /* TODO: handle poll error here */ - // use poll_errno instead of errno + /* use poll_errno instead of errno */ (void)poll_errno; } @@ -142,7 +142,8 @@ discord_run(struct discord *client) if (next_run <= now) { BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); - BREAK_ON_FAIL(code, discord_adapter_perform(&client->adapter)); + BREAK_ON_FAIL(code, + discord_adapter_async_perform(&client->adapter)); /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; From f3e9ba5adc0f34c35d4c96dc1bad7b5ed0ac8634 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 11:02:00 -0300 Subject: [PATCH 042/118] refactor: rename Adapter -> REST --- Makefile | 8 +- README.md | 8 +- docs/INTERNALS.md | 173 ------------- include/discord-internal.h | 81 +++--- src/application_command.c | 116 ++++----- src/audit_log.c | 6 +- src/channel.c | 230 +++++++++--------- src/discord-client.c | 6 +- src/discord-loop.c | 5 +- src/{discord-adapter.c => discord-rest.c} | 202 ++++++++------- ...d-adapter_async.c => discord-rest_async.c} | 30 +-- ...r_ratelimit.c => discord-rest_ratelimit.c} | 19 +- src/emoji.c | 26 +- src/gateway.c | 13 +- src/guild.c | 163 ++++++------- src/guild_template.c | 14 +- src/interaction.c | 48 ++-- src/invite.c | 8 +- src/user.c | 31 ++- src/voice.c | 4 +- src/webhook.c | 66 ++--- 21 files changed, 533 insertions(+), 724 deletions(-) rename src/{discord-adapter.c => discord-rest.c} (68%) rename src/{discord-adapter_async.c => discord-rest_async.c} (87%) rename src/{discord-adapter_ratelimit.c => discord-rest_ratelimit.c} (95%) diff --git a/Makefile b/Makefile index d04b5dba1..4df987b40 100644 --- a/Makefile +++ b/Makefile @@ -31,10 +31,10 @@ THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ $(THIRDP_DIR)/threadpool.o \ $(THIRDP_DIR)/priority_queue.o DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ - $(SRC_DIR)/discord-adapter.o \ - $(SRC_DIR)/discord-adapter_async.o \ - $(SRC_DIR)/discord-adapter_ratelimit.o \ $(SRC_DIR)/discord-refcount.o \ + $(SRC_DIR)/discord-rest.o \ + $(SRC_DIR)/discord-rest_async.o \ + $(SRC_DIR)/discord-rest_ratelimit.o \ $(SRC_DIR)/discord-client.o \ $(SRC_DIR)/discord-loop.o \ $(SRC_DIR)/discord-gateway.o \ @@ -77,7 +77,7 @@ voice: @ $(MAKE) XFLAGS=-DCCORD_VOICE XOBJ=$(SRC_DIR)/discord-voice.o all debug: - @ $(MAKE) XFLAGS="-DCCORD_DEBUG_WEBSOCKETS -DCCORD_DEBUG_ADAPTER" all + @ $(MAKE) XFLAGS="-DCCORD_DEBUG_WEBSOCKETS -DCCORD_DEBUG_HTTP" all test: all @ $(MAKE) -C $(TEST_DIR) diff --git a/README.md b/README.md index 68ce7ec6a..ddc48d6af 100644 --- a/README.md +++ b/README.md @@ -239,12 +239,12 @@ The following outlines special flags and targets to override the default Makefil * By default Concord will not shutdown gracefully when a SIGINT is received (i.e. Ctrl+c), enable this flag if you wish it to be handled for you. * `-DCCORD_DEBUG_WEBSOCKETS` * Enable verbose debugging for WebSockets communication. -* `-DCCORD_DEBUG_ADAPTER` - * Enable verbose debugging for REST communication. +* `-DCCORD_DEBUG_HTTP` + * Enable verbose debugging for HTTP communication. *Example:* ```console -$ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_DEBUG_ADAPTER" make +$ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_DEBUG_HTTP" make ``` #### Special targets @@ -252,7 +252,7 @@ $ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_DEBUG_ADAPTER" make * `make voice` * Enable experimental Voice Connection handling - not production ready. * `make debug` - * Same as enabling `-DCCORD_DEBUG_WEBSOCKETS` and `-DCCORD_DEBUG_ADAPTER` + * Same as enabling `-DCCORD_DEBUG_WEBSOCKETS` and `-DCCORD_DEBUG_HTTP` ## Installing Concord diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index c1f514146..4548be6a6 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -49,177 +49,4 @@ The `src/` folder is where we place all of our Discord API wrapping logic. The `core/` folder is where we place all of Concord core's logic, such as handling of the WebSockets and REST protocols, threadpool management, etc. - - If you have any questions, feel free to join our [Discord server](https://discord.gg/Y7Xa6MA82v). diff --git a/include/discord-internal.h b/include/discord-internal.h index 0742b2eaf..4de97dfaa 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -77,7 +77,7 @@ /** Route's unique key threshold length */ #define DISCORD_ROUTE_LEN 256 -/** @defgroup DiscordInternalAdapter REST API +/** @defgroup DiscordInternalREST REST API * @brief Wrapper to the Discord REST API * @{ */ @@ -135,8 +135,7 @@ struct discord_request { DISCORD_REQUEST_FIELDS; }; -/** @defgroup DiscordInternalAdapterAsync Async - * async) +/** @defgroup DiscordInternalRESTAsync Async request's handling * @brief Store contexts of individual asynchronous requests * @{ */ @@ -164,7 +163,7 @@ struct discord_context { /** the request bucket's queue entry */ QUEUE entry; - /** current retry attempt (stop at adapter->retry_limit) */ + /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; }; @@ -184,7 +183,7 @@ struct discord_async { * This shall initialize a `CURLM` multi handle for performing requests * asynchronously, and a queue for storing individual requests contexts * @param async the async handle to be initialized - * @param conf pointer to @ref discord_adapter logging module + * @param conf pointer to @ref discord_rest logging module */ void discord_async_init(struct discord_async *async, struct logconf *conf); @@ -276,10 +275,10 @@ struct discord_context *discord_async_start_context( char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]); -/** @} DiscordInternalAdapterAsync */ +/** @} DiscordInternalRESTAsync */ /** @brief The handle used for interfacing with Discord's REST API */ -struct discord_adapter { +struct discord_rest { /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ struct logconf conf; /** the user agent handle for performing requests */ @@ -295,30 +294,30 @@ struct discord_adapter { }; /** - * @brief Initialize an Adapter handle + * @brief Initialize an REST handle * * Structure used for interfacing with the Discord's REST API - * @param adapter the adapter handle to be initialized + * @param rest the REST handle to be initialized * @param conf pointer to @ref discord logging module * @param token the bot token */ -void discord_adapter_init(struct discord_adapter *adapter, - struct logconf *conf, - struct ccord_szbuf_readonly *token); +void discord_rest_init(struct discord_rest *rest, + struct logconf *conf, + struct ccord_szbuf_readonly *token); /** - * @brief Free an Adapter handle + * @brief Free an REST handle * - * @param adapter the handle initialized with discord_adapter_init() + * @param rest the handle initialized with discord_rest_init() */ -void discord_adapter_cleanup(struct discord_adapter *adapter); +void discord_rest_cleanup(struct discord_rest *rest); /** * @brief Perform a request to Discord * - * This functions is a selector over discord_adapter_run() or - * discord_adapter_run_async() - * @param adapter the handle initialized with discord_adapter_init() + * This functions is a selector over discord_rest_run() or + * discord_rest_run_async() + * @param rest the handle initialized with discord_rest_init() * @param req return object of request * @param body the body sent for methods that require (ex: post), leave as * null if unecessary @@ -328,30 +327,30 @@ void discord_adapter_cleanup(struct discord_adapter *adapter); * @note if sync is set then this function will block the thread and perform it * immediately */ -CCORDcode discord_adapter_run(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint_fmt[], - ...); +CCORDcode discord_rest_run(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint_fmt[], + ...); /** * @brief Check and manage on-going, pending and timed-out requests * - * @param adapter the handle initialized with discord_adapter_init() + * @param rest the handle initialized with discord_rest_init() * @CCORD_return */ -CCORDcode discord_adapter_async_perform(struct discord_adapter *adapter); +CCORDcode discord_rest_async_perform(struct discord_rest *rest); /** * @brief Stop all bucket's on-going, pending and timed-out requests * * The requests will be moved over to client's 'idle_contexts' queue - * @param adapter the handle initialized with discord_adapter_init() + * @param rest the handle initialized with discord_rest_init() */ -void discord_adapter_stop_buckets(struct discord_adapter *adapter); +void discord_rest_stop_buckets(struct discord_rest *rest); -/** @defgroup DiscordInternalAdapterRatelimit Ratelimiting +/** @defgroup DiscordInternalRESTRatelimit Ratelimiting * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ @@ -406,10 +405,10 @@ void discord_bucket_try_sleep(struct discord_ratelimiter *rl, /** * @brief Try to timeout bucket for pending cooldown time * - * @param adapter the handle initialized with discord_adapter_init() + * @param rest the handle initialized with discord_rest_init() * @param bucket the bucket to wait on cooldown */ -void discord_bucket_try_timeout(struct discord_adapter *adapter, +void discord_bucket_try_timeout(struct discord_rest *rest, struct discord_bucket *b); /** @@ -432,7 +431,7 @@ struct discord_ratelimiter { int capacity; /** * routes matched to individual buckets - * @note datatype declared at discord-adapter_ratelimit.c + * @note datatype declared at discord-rest_ratelimit.c */ struct _discord_route *routes; /** singleton bucket for requests that haven't been matched to a @@ -457,7 +456,7 @@ struct discord_ratelimiter { * @brief Initialize ratelimiter handle * * A hashtable shall be used for storage and retrieval of discovered buckets - * @param conf pointer to @ref discord_adapter logging module + * @param conf pointer to @ref discord_rest logging module * @return the ratelimiter handle */ struct discord_ratelimiter *discord_ratelimiter_init(struct logconf *conf); @@ -465,7 +464,7 @@ struct discord_ratelimiter *discord_ratelimiter_init(struct logconf *conf); /** * @brief Cleanup all buckets that have been discovered * - * @note pending requests will be moved to `adapter.idle_contexts` + * @note pending requests will be moved to `rest.idle_contexts` * @param rl the handle initialized with discord_ratelimiter_init() */ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); @@ -474,13 +473,13 @@ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); * @brief Iterate known buckets * * @param rl the handle initialized with discord_ratelimiter_init() - * @param adapter the handle initialized with discord_adapter_init() + * @param rest the handle initialized with discord_rest_init() * @param iter the user callback to be called per bucket */ void discord_ratelimiter_foreach_bucket( struct discord_ratelimiter *rl, - struct discord_adapter *adapter, - void (*iter)(struct discord_adapter *adapter, struct discord_bucket *b)); + struct discord_rest *rest, + void (*iter)(struct discord_rest *rest, struct discord_bucket *b)); /** * @brief Build unique key formed from the HTTP method and endpoint @@ -518,9 +517,9 @@ void discord_ratelimiter_build(struct discord_ratelimiter *rl, const char key[], struct ua_info *info); -/** @} DiscordInternalAdapterRatelimit */ +/** @} DiscordInternalRESTRatelimit */ -/** @} DiscordInternalAdapter */ +/** @} DiscordInternalREST */ /** @defgroup DiscordInternalGateway WebSockets API * @brief Wrapper to the Discord Gateway API @@ -857,7 +856,7 @@ struct discord_refcounter { int capacity; /** * individual user's data held for automatic cleanup - * @note datatype declared at discord-adapter_refcount.c + * @note datatype declared at discord-rest_refcount.c */ struct _discord_ref *refs; }; @@ -1022,7 +1021,7 @@ struct discord { /** the io poller for listening to file descriptors */ struct io_poller *io_poller; /** the handle for interfacing with Discord's REST API */ - struct discord_adapter adapter; + struct discord_rest rest; /** the handle for interfacing with Discord's Gateway API */ struct discord_gateway gw; /** user's data reference counter for automatic cleanup */ diff --git a/src/application_command.c b/src/application_command.c index d7131bffd..88c465e34 100644 --- a/src/application_command.c +++ b/src/application_command.c @@ -18,9 +18,9 @@ discord_get_global_application_commands( DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/applications/%" PRIu64 "/commands", - application_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/applications/%" PRIu64 "/commands", + application_id); } CCORDcode @@ -46,9 +46,9 @@ discord_create_global_application_command( buf, sizeof(buf), params); body.start = buf; - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/applications/%" PRIu64 "/commands", - application_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/applications/%" PRIu64 "/commands", + application_id); } CCORDcode @@ -65,9 +65,9 @@ discord_get_global_application_command( DISCORD_REQ_INIT(req, discord_application_command, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/applications/%" PRIu64 "/commands/%" PRIu64, - application_id, command_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/applications/%" PRIu64 "/commands/%" PRIu64, + application_id, command_id); } CCORDcode @@ -91,9 +91,9 @@ discord_edit_global_application_command( DISCORD_REQ_INIT(req, discord_application_command, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/applications/%" PRIu64 "/commands/%" PRIu64, - application_id, command_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/applications/%" PRIu64 "/commands/%" PRIu64, + application_id, command_id); } CCORDcode @@ -109,9 +109,9 @@ discord_delete_global_application_command(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/applications/%" PRIu64 "/commands/%" PRIu64, - application_id, command_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/applications/%" PRIu64 "/commands/%" PRIu64, + application_id, command_id); } CCORDcode @@ -133,9 +133,9 @@ discord_bulk_overwrite_global_application_command( DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/applications/%" PRIu64 "/commands", - application_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/applications/%" PRIu64 "/commands", + application_id); } CCORDcode @@ -152,10 +152,10 @@ discord_get_guild_application_commands( DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands", - application_id, guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands", + application_id, guild_id); } CCORDcode @@ -183,10 +183,10 @@ discord_create_guild_application_command( DISCORD_REQ_INIT(req, discord_application_command, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands", - application_id, guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands", + application_id, guild_id); } CCORDcode @@ -205,10 +205,10 @@ discord_get_guild_application_command( DISCORD_REQ_INIT(req, discord_application_command, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/%" PRIu64, - application_id, guild_id, command_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/%" PRIu64, + application_id, guild_id, command_id); } CCORDcode @@ -234,10 +234,10 @@ discord_edit_guild_application_command( DISCORD_REQ_INIT(req, discord_application_command, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/%" PRIu64, - application_id, guild_id, command_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/%" PRIu64, + application_id, guild_id, command_id); } CCORDcode @@ -255,10 +255,10 @@ discord_delete_guild_application_command(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/%" PRIu64, - application_id, guild_id, command_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/%" PRIu64, + application_id, guild_id, command_id); } CCORDcode @@ -282,10 +282,10 @@ discord_bulk_overwrite_guild_application_command( DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands", - application_id, guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands", + application_id, guild_id); } CCORDcode @@ -302,10 +302,10 @@ discord_get_guild_application_command_permissions( DISCORD_REQ_LIST_INIT(req, discord_application_command_permissions, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/permissions", - application_id, guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/permissions", + application_id, guild_id); } CCORDcode @@ -324,10 +324,10 @@ discord_get_application_command_permissions( DISCORD_REQ_INIT(req, discord_application_command_permission, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/%" PRIu64 "/permissions", - application_id, guild_id, command_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/%" PRIu64 "/permissions", + application_id, guild_id, command_id); } CCORDcode @@ -353,10 +353,10 @@ discord_edit_application_command_permissions( DISCORD_REQ_INIT(req, discord_application_command_permission, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/%" PRIu64 "/permissions", - application_id, guild_id, command_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/%" PRIu64 "/permissions", + application_id, guild_id, command_id); } CCORDcode @@ -381,8 +381,8 @@ discord_batch_edit_application_command_permissions( DISCORD_REQ_LIST_INIT(req, discord_application_command_permissions, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/applications/%" PRIu64 "/guilds/%" PRIu64 - "/commands/permissions", - application_id, guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/applications/%" PRIu64 "/guilds/%" PRIu64 + "/commands/permissions", + application_id, guild_id); } diff --git a/src/audit_log.c b/src/audit_log.c index 106284638..d46e827ef 100644 --- a/src/audit_log.c +++ b/src/audit_log.c @@ -47,7 +47,7 @@ discord_get_guild_audit_log(struct discord *client, DISCORD_REQ_INIT(req, discord_audit_log, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/audit-logs%s", guild_id, - query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/audit-logs%s", guild_id, + query); } diff --git a/src/channel.c b/src/channel.c index 267c30dd2..fe8444ac8 100644 --- a/src/channel.c +++ b/src/channel.c @@ -94,8 +94,8 @@ discord_get_channel(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64, channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64, channel_id); } CCORDcode @@ -116,8 +116,8 @@ discord_modify_channel(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/channels/%" PRIu64, channel_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/channels/%" PRIu64, channel_id); } CCORDcode @@ -131,8 +131,8 @@ discord_delete_channel(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64, channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64, channel_id); } CCORDcode @@ -176,9 +176,9 @@ discord_get_channel_messages(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_messages, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/messages%s%s", - channel_id, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/messages%s%s", channel_id, + *query ? "?" : "", query); } CCORDcode @@ -194,9 +194,9 @@ discord_get_channel_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/messages/%" PRIu64, - channel_id, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/messages/%" PRIu64, + channel_id, message_id); } CCORDcode @@ -226,8 +226,8 @@ discord_create_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/channels/%" PRIu64 "/messages", channel_id); + return discord_rest_run(&client->rest, &req, &body, method, + "/channels/%" PRIu64 "/messages", channel_id); } CCORDcode @@ -243,10 +243,10 @@ discord_crosspost_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_POST, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/crosspost", - channel_id, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_POST, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/crosspost", + channel_id, message_id); } CCORDcode @@ -276,10 +276,10 @@ discord_create_reaction(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - code = discord_adapter_run(&client->adapter, &req, NULL, HTTP_PUT, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/reactions/%s/@me", - channel_id, message_id, emoji_endpoint); + code = discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/reactions/%s/@me", + channel_id, message_id, emoji_endpoint); curl_free(pct_emoji_name); @@ -313,10 +313,10 @@ discord_delete_own_reaction(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - code = discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/reactions/%s/@me", - channel_id, message_id, emoji_endpoint); + code = discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/reactions/%s/@me", + channel_id, message_id, emoji_endpoint); curl_free(pct_emoji_name); @@ -352,10 +352,10 @@ discord_delete_user_reaction(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - code = discord_adapter_run( - &client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions/%s/%" PRIu64, - channel_id, message_id, emoji_endpoint, user_id); + code = discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/reactions/%s/%" PRIu64, + channel_id, message_id, emoji_endpoint, user_id); curl_free(pct_emoji_name); @@ -412,10 +412,10 @@ discord_get_reactions(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_users, ret); - code = discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/reactions/%s%s", - channel_id, message_id, emoji_endpoint, query); + code = discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/reactions/%s%s", + channel_id, message_id, emoji_endpoint, query); curl_free(pct_emoji_name); @@ -435,10 +435,10 @@ discord_delete_all_reactions(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/reactions", - channel_id, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/reactions", + channel_id, message_id); } CCORDcode @@ -468,10 +468,10 @@ discord_delete_all_reactions_for_emoji(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - code = discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/reactions/%s", - channel_id, message_id, emoji_endpoint); + code = discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/reactions/%s", + channel_id, message_id, emoji_endpoint); curl_free(pct_emoji_name); @@ -498,9 +498,9 @@ discord_edit_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/channels/%" PRIu64 "/messages/%" PRIu64, - channel_id, message_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/channels/%" PRIu64 "/messages/%" PRIu64, + channel_id, message_id); } CCORDcode @@ -516,9 +516,9 @@ discord_delete_message(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/messages/%" PRIu64, - channel_id, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/messages/%" PRIu64, + channel_id, message_id); } /** @todo add duplicated ID verification */ @@ -553,9 +553,9 @@ discord_bulk_delete_messages(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/channels/%" PRIu64 "/messages/bulk-delete", - channel_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/channels/%" PRIu64 "/messages/bulk-delete", + channel_id); } CCORDcode @@ -580,9 +580,9 @@ discord_edit_channel_permissions( DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/channels/%" PRIu64 "/permissions/%" PRIu64, - channel_id, overwrite_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/channels/%" PRIu64 "/permissions/%" PRIu64, + channel_id, overwrite_id); } CCORDcode @@ -596,8 +596,8 @@ discord_get_channel_invites(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_invites, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/invites", channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/invites", channel_id); } CCORDcode @@ -621,8 +621,8 @@ discord_create_channel_invite(struct discord *client, DISCORD_REQ_INIT(req, discord_invite, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/channels/%" PRIu64 "/invites", channel_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/channels/%" PRIu64 "/invites", channel_id); } CCORDcode @@ -638,9 +638,9 @@ discord_delete_channel_permission(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/permissions/%" PRIu64, - channel_id, overwrite_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/permissions/%" PRIu64, + channel_id, overwrite_id); } CCORDcode @@ -663,8 +663,8 @@ discord_follow_news_channel(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/channels/%" PRIu64 "/followers", channel_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/channels/%" PRIu64 "/followers", channel_id); } CCORDcode @@ -678,8 +678,8 @@ discord_trigger_typing_indicator(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_POST, - "/channels/%" PRIu64 "/typing", channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_POST, + "/channels/%" PRIu64 "/typing", channel_id); } CCORDcode @@ -693,8 +693,8 @@ discord_get_pinned_messages(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_messages, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/pins", channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/pins", channel_id); } CCORDcode @@ -710,9 +710,9 @@ discord_pin_message(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_PUT, - "/channels/%" PRIu64 "/pins/%" PRIu64, - channel_id, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + "/channels/%" PRIu64 "/pins/%" PRIu64, channel_id, + message_id); } CCORDcode @@ -728,9 +728,9 @@ discord_unpin_message(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/pins/%" PRIu64, - channel_id, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/pins/%" PRIu64, channel_id, + message_id); } CCORDcode @@ -754,9 +754,9 @@ discord_group_dm_add_recipient(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/channels/%" PRIu64 "/recipients/%" PRIu64, - channel_id, user_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/channels/%" PRIu64 "/recipients/%" PRIu64, + channel_id, user_id); } CCORDcode @@ -772,9 +772,9 @@ discord_group_dm_remove_recipient(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/recipients/%" PRIu64, - channel_id, user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/recipients/%" PRIu64, + channel_id, user_id); } CCORDcode @@ -799,10 +799,10 @@ discord_start_thread_with_message( DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/channels/%" PRIu64 "/messages/%" PRIu64 - "/threads", - channel_id, message_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/channels/%" PRIu64 "/messages/%" PRIu64 + "/threads", + channel_id, message_id); } CCORDcode @@ -825,8 +825,8 @@ discord_start_thread_without_message( DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/channels/%" PRIu64 "/threads", channel_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/channels/%" PRIu64 "/threads", channel_id); } CCORDcode @@ -840,9 +840,9 @@ discord_join_thread(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_PUT, - "/channels/%" PRIu64 "/thread-members/@me", - channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + "/channels/%" PRIu64 "/thread-members/@me", + channel_id); } CCORDcode @@ -858,9 +858,9 @@ discord_add_thread_member(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_PUT, - "/channels/%" PRIu64 "/thread-members/" PRIu64, - channel_id, user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + "/channels/%" PRIu64 "/thread-members/" PRIu64, + channel_id, user_id); } CCORDcode @@ -874,9 +874,9 @@ discord_leave_thread(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/thread-members/@me", - channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/thread-members/@me", + channel_id); } CCORDcode @@ -892,9 +892,9 @@ discord_remove_thread_member(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/channels/%" PRIu64 "/thread-members/" PRIu64, - channel_id, user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/channels/%" PRIu64 "/thread-members/" PRIu64, + channel_id, user_id); } CCORDcode @@ -908,9 +908,9 @@ discord_list_thread_members(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_thread_members, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/thread-members", - channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/thread-members", + channel_id); } CCORDcode @@ -924,9 +924,9 @@ discord_list_active_threads(struct discord *client, DISCORD_REQ_INIT(req, discord_thread_response_body, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/threads/active", - channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/threads/active", + channel_id); } CCORDcode @@ -956,10 +956,10 @@ discord_list_public_archived_threads( DISCORD_REQ_INIT(req, discord_thread_response_body, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 - "/threads/archived/public%s%s", - channel_id, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 + "/threads/archived/public%s%s", + channel_id, *query ? "?" : "", query); } CCORDcode @@ -989,10 +989,10 @@ discord_list_private_archived_threads( DISCORD_REQ_INIT(req, discord_thread_response_body, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 - "/threads/archived/private%s%s", - channel_id, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 + "/threads/archived/private%s%s", + channel_id, *query ? "?" : "", query); } CCORDcode @@ -1022,8 +1022,8 @@ discord_list_joined_private_archived_threads( DISCORD_REQ_INIT(req, discord_thread_response_body, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 - "/users/@me/threads/archived/private%s%s", - channel_id, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 + "/users/@me/threads/archived/private%s%s", + channel_id, *query ? "?" : "", query); } diff --git a/src/discord-client.c b/src/discord-client.c index b65f1c93c..cac045291 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -17,8 +17,8 @@ _discord_init(struct discord *new_client) new_client->refcounter = discord_refcounter_init(&new_client->conf); new_client->commands = discord_message_commands_init(&new_client->conf); - discord_adapter_init(&new_client->adapter, &new_client->conf, - &new_client->token); + discord_rest_init(&new_client->rest, &new_client->conf, + &new_client->token); discord_gateway_init(&new_client->gw, &new_client->conf, &new_client->token); #ifdef CCORD_VOICE @@ -169,7 +169,7 @@ discord_cleanup(struct discord *client) if (client->is_original) { discord_timers_cleanup(client); logconf_cleanup(&client->conf); - discord_adapter_cleanup(&client->adapter); + discord_rest_cleanup(&client->rest); discord_gateway_cleanup(&client->gw); discord_user_cleanup(&client->self); io_poller_destroy(client->io_poller); diff --git a/src/discord-loop.c b/src/discord-loop.c index f028de658..fb8117fc5 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -142,8 +142,7 @@ discord_run(struct discord *client) if (next_run <= now) { BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); - BREAK_ON_FAIL(code, - discord_adapter_async_perform(&client->adapter)); + BREAK_ON_FAIL(code, discord_rest_async_perform(&client->rest)); /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; @@ -152,7 +151,7 @@ discord_run(struct discord *client) /* stop all pending requests in case of connection shutdown */ if (true == discord_gateway_end(&client->gw)) { - discord_adapter_stop_buckets(&client->adapter); + discord_rest_stop_buckets(&client->rest); break; } } diff --git a/src/discord-adapter.c b/src/discord-rest.c similarity index 68% rename from src/discord-adapter.c rename to src/discord-rest.c index 03b09530f..529ce17e7 100644 --- a/src/discord-adapter.c +++ b/src/discord-rest.c @@ -21,73 +21,73 @@ setopt_cb(struct ua_conn *conn, void *p_token) ua_conn_add_header(conn, "Authorization", auth); -#ifdef CCORD_DEBUG_ADAPTER +#ifdef CCORD_DEBUG_HTTP curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); #endif } void -discord_adapter_init(struct discord_adapter *adapter, - struct logconf *conf, - struct ccord_szbuf_readonly *token) +discord_rest_init(struct discord_rest *rest, + struct logconf *conf, + struct ccord_szbuf_readonly *token) { struct ua_attr attr = { 0 }; attr.conf = conf; - adapter->ua = ua_init(&attr); - ua_set_url(adapter->ua, DISCORD_API_BASE_URL); + rest->ua = ua_init(&attr); + ua_set_url(rest->ua, DISCORD_API_BASE_URL); if (!token->size) { /* no token means a webhook-only client */ - logconf_branch(&adapter->conf, conf, "DISCORD_WEBHOOK"); + logconf_branch(&rest->conf, conf, "DISCORD_WEBHOOK"); } else { /* bot client */ - logconf_branch(&adapter->conf, conf, "DISCORD_HTTP"); - ua_set_opt(adapter->ua, token, &setopt_cb); + logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); + ua_set_opt(rest->ua, token, &setopt_cb); } - discord_async_init(&adapter->async, &adapter->conf); - adapter->ratelimiter = discord_ratelimiter_init(&adapter->conf); + discord_async_init(&rest->async, &rest->conf); + rest->ratelimiter = discord_ratelimiter_init(&rest->conf); - adapter->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ + rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ } void -discord_adapter_cleanup(struct discord_adapter *adapter) +discord_rest_cleanup(struct discord_rest *rest) { /* cleanup User-Agent handle */ - ua_cleanup(adapter->ua); + ua_cleanup(rest->ua); /* move pending requests to idle_contexts */ - discord_adapter_stop_buckets(adapter); + discord_rest_stop_buckets(rest); /* cleanup idle requests queue */ - discord_async_cleanup(&adapter->async); + discord_async_cleanup(&rest->async); /* cleanup discovered buckets */ - discord_ratelimiter_cleanup(adapter->ratelimiter); + discord_ratelimiter_cleanup(rest->ratelimiter); } -static CCORDcode _discord_adapter_run_sync(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); +static CCORDcode _discord_rest_run_sync(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]); -static CCORDcode _discord_adapter_run_async(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); +static CCORDcode _discord_rest_run_async(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]); /* template function for performing requests */ CCORDcode -discord_adapter_run(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint_fmt[], - ...) +discord_rest_run(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint_fmt[], + ...) { char endpoint[DISCORD_ENDPT_LEN]; char key[DISCORD_ROUTE_LEN]; @@ -119,20 +119,18 @@ discord_adapter_run(struct discord_adapter *adapter, if (req->dispatch.has_type && req->dispatch.sync != DISCORD_SYNC_FLAG) req->response.data = req->dispatch.sync; - return _discord_adapter_run_sync(adapter, req, body, method, endpoint, - key); + return _discord_rest_run_sync(rest, req, body, method, endpoint, key); } /* enqueue asynchronous request */ - return _discord_adapter_run_async(adapter, req, body, method, endpoint, - key); + return _discord_rest_run_async(rest, req, body, method, endpoint, key); } /* return true if there should be a retry attempt */ static bool -_discord_adapter_get_info(struct discord_adapter *adapter, - struct ua_info *info, - int64_t *wait_ms) +_discord_rest_get_info(struct discord_rest *rest, + struct ua_info *info, + int64_t *wait_ms) { if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ @@ -147,12 +145,12 @@ _discord_adapter_get_info(struct discord_adapter *adapter, return false; case HTTP_UNAUTHORIZED: logconf_fatal( - &adapter->conf, + &rest->conf, "UNAUTHORIZED: Please provide a valid authentication token"); info->code = CCORD_DISCORD_BAD_AUTH; return false; case HTTP_METHOD_NOT_ALLOWED: - logconf_fatal(&adapter->conf, + logconf_fatal(&rest->conf, "METHOD_NOT_ALLOWED: The server couldn't recognize the " "received HTTP method"); return false; @@ -189,7 +187,7 @@ _discord_adapter_get_info(struct discord_adapter *adapter, *wait_ms = (int64_t)(1000 * retry_after); if (*wait_ms < 0) *wait_ms = 0; - logconf_warn(&adapter->conf, + logconf_warn(&rest->conf, "429 %s RATELIMITING (wait: %" PRId64 " ms) : %.*s", is_global ? "GLOBAL" : "", *wait_ms, message.len, body.start + message.pos); @@ -209,7 +207,7 @@ _discord_adapter_get_info(struct discord_adapter *adapter, * `struct discord_attachment` and the second element is a * `struct ccord_szbuf` containing the request body */ static void -_discord_adapter_request_to_multipart(curl_mime *mime, void *data) +_discord_rest_request_to_multipart(curl_mime *mime, void *data) { struct discord_attachments *atchs = ((void **)data)[0]; struct ccord_szbuf *body = ((void **)data)[1]; @@ -266,12 +264,12 @@ _discord_adapter_request_to_multipart(curl_mime *mime, void *data) /* perform a blocking request */ static CCORDcode -_discord_adapter_run_sync(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) +_discord_rest_run_sync(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) { void *data[2] = { &req->attachments, body }; struct discord_bucket *b; @@ -280,12 +278,12 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, bool retry; CCORDcode code; - b = discord_bucket_get(adapter->ratelimiter, key); - conn = ua_conn_start(adapter->ua); + b = discord_bucket_get(rest->ratelimiter, key); + conn = ua_conn_start(rest->ua); if (HTTP_MIMEPOST == method) { ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, data, &_discord_adapter_request_to_multipart); + ua_conn_set_mime(conn, data, &_discord_rest_request_to_multipart); } else { ua_conn_add_header(conn, "Content-Type", "application/json"); @@ -301,18 +299,18 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, pthread_mutex_lock(&b->lock); do { - discord_bucket_try_sleep(adapter->ratelimiter, b); + discord_bucket_try_sleep(rest->ratelimiter, b); /* perform blocking request, and check results */ switch (code = ua_conn_easy_perform(conn)) { case CCORD_OK: { - struct discord *client = CLIENT(adapter, adapter); + struct discord *client = CLIENT(rest, rest); struct ua_szbuf_readonly resp; struct ua_info info = { 0 }; int64_t wait_ms = 0; ua_info_extract(conn, &info); - retry = _discord_adapter_get_info(adapter, &info, &wait_ms); + retry = _discord_rest_get_info(rest, &info, &wait_ms); resp = ua_info_get_body(&info); if (info.code != CCORD_OK) { @@ -337,25 +335,25 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, * TODO: create discord_timestamp_update() */ ws_timestamp_update(client->gw.ws); - discord_ratelimiter_build(adapter->ratelimiter, b, key, &info); + discord_ratelimiter_build(rest->ratelimiter, b, key, &info); cog_sleep_ms(wait_ms); ua_info_cleanup(&info); } break; case CCORD_CURLE_INTERNAL: - logconf_error(&adapter->conf, + logconf_error(&rest->conf, "Curl internal error, will retry again"); retry = true; break; default: - logconf_error(&adapter->conf, "CCORD code: %d", code); + logconf_error(&rest->conf, "CCORD code: %d", code); retry = false; break; } ua_conn_reset(conn); - } while (retry && retry_attempt++ < adapter->retry_limit); + } while (retry && retry_attempt++ < rest->retry_limit); pthread_mutex_unlock(&b->lock); /* reset conn and mark it as free to use */ @@ -368,15 +366,15 @@ _discord_adapter_run_sync(struct discord_adapter *adapter, /* enqueue a request to be executed asynchronously */ static CCORDcode -_discord_adapter_run_async(struct discord_adapter *adapter, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) +_discord_rest_run_async(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) { struct discord_context *cxt = discord_async_start_context( - &adapter->async, req, body, method, endpoint, key); + &rest->async, req, body, method, endpoint, key); discord_context_bucket_insert(cxt, cxt->b, req->dispatch.high_p); @@ -393,16 +391,15 @@ _discord_context_to_multipart(curl_mime *mime, void *p_cxt) cxt->body.size, } }; - _discord_adapter_request_to_multipart(mime, data); + _discord_rest_request_to_multipart(mime, data); } /* add a request to libcurl's multi handle */ static CCORDcode -_discord_adapter_add_request(struct discord_adapter *adapter, - struct discord_bucket *b) +_discord_rest_add_request(struct discord_rest *rest, struct discord_bucket *b) { struct discord_context *cxt = discord_context_bucket_remove(b); - struct ua_conn *conn = ua_conn_start(adapter->ua); + struct ua_conn *conn = ua_conn_start(rest->ua); if (HTTP_MIMEPOST == cxt->method) { ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); @@ -420,36 +417,35 @@ _discord_adapter_add_request(struct discord_adapter *adapter, .base_url = NULL, }); - return discord_async_add_request(&adapter->async, cxt, conn); + return discord_async_add_request(&rest->async, cxt, conn); } static void -_discord_adapter_try_add_request(struct discord_adapter *adapter, - struct discord_bucket *b) +_discord_rest_try_add_request(struct discord_rest *rest, + struct discord_bucket *b) { /* skip if bucket is busy performing */ if (b->busy) return; if (!b->remaining) - discord_bucket_try_timeout(adapter, b); + discord_bucket_try_timeout(rest, b); else if (!QUEUE_EMPTY(&b->pending_queue)) - _discord_adapter_add_request(adapter, b); + _discord_rest_add_request(rest, b); } static CCORDcode -_discord_adapter_check_pending(struct discord_adapter *adapter) +_discord_rest_check_pending(struct discord_rest *rest) { - discord_ratelimiter_foreach_bucket(adapter->ratelimiter, adapter, - &_discord_adapter_try_add_request); + discord_ratelimiter_foreach_bucket(rest->ratelimiter, rest, + &_discord_rest_try_add_request); /* FIXME: redundant return value (constant) */ return CCORD_OK; } static CCORDcode -_discord_adapter_check_action(struct discord_adapter *adapter, - struct CURLMsg *msg) +_discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) { - struct discord *client = CLIENT(adapter, adapter); + struct discord *client = CLIENT(rest, rest); struct discord_context *cxt; int64_t wait_ms = 0LL; CCORDcode code; @@ -463,7 +459,7 @@ _discord_adapter_check_action(struct discord_adapter *adapter, struct ua_szbuf_readonly body; ua_info_extract(cxt->conn, &info); - retry = _discord_adapter_get_info(adapter, &info, &wait_ms); + retry = _discord_rest_get_info(rest, &info, &wait_ms); body = ua_info_get_body(&info); if (info.code != CCORD_OK) { @@ -494,19 +490,18 @@ _discord_adapter_check_action(struct discord_adapter *adapter, code = info.code; - discord_ratelimiter_build(adapter->ratelimiter, cxt->b, cxt->key, - &info); + discord_ratelimiter_build(rest->ratelimiter, cxt->b, cxt->key, &info); ua_info_cleanup(&info); } break; case CURLE_READ_ERROR: - logconf_warn(&adapter->conf, "Read error, will retry again"); + logconf_warn(&rest->conf, "Read error, will retry again"); retry = true; code = CCORD_CURLE_INTERNAL; break; default: - logconf_error(&adapter->conf, "(CURLE code: %d)", msg->data.result); + logconf_error(&rest->conf, "(CURLE code: %d)", msg->data.result); retry = false; code = CCORD_CURLE_INTERNAL; @@ -519,62 +514,59 @@ _discord_adapter_check_action(struct discord_adapter *adapter, /* enqueue request for retry or recycle */ cxt->b->busy = NULL; - if (!retry || !discord_async_retry_context(&adapter->async, cxt, wait_ms)) - discord_async_recycle_context(&adapter->async, cxt); + if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) + discord_async_recycle_context(&rest->async, cxt); return code; } CCORDcode -discord_adapter_async_perform(struct discord_adapter *adapter) +discord_rest_async_perform(struct discord_rest *rest) { CURLMcode mcode; CCORDcode code; int alive = 0; - if (CCORD_OK != (code = _discord_adapter_check_pending(adapter))) - return code; + if (CCORD_OK != (code = _discord_rest_check_pending(rest))) return code; if (CURLM_OK - != (mcode = curl_multi_socket_all(adapter->async.mhandle, &alive))) + != (mcode = curl_multi_socket_all(rest->async.mhandle, &alive))) return CCORD_CURLM_INTERNAL; /* ask for any messages/informationals from the individual transfers */ while (1) { int msgq = 0; - struct CURLMsg *msg = - curl_multi_info_read(adapter->async.mhandle, &msgq); + struct CURLMsg *msg = curl_multi_info_read(rest->async.mhandle, &msgq); if (!msg) break; if (CURLMSG_DONE != msg->msg) continue; /* check for request action */ - _discord_adapter_check_action(adapter, msg); + _discord_rest_check_action(rest, msg); } return CCORD_OK; } static void -_discord_adapter_stop_bucket(struct discord_adapter *adapter, - struct discord_bucket *b) +_discord_rest_stop_bucket(struct discord_rest *rest, struct discord_bucket *b) { /* cancel busy transfer */ if (b->busy && b->busy != DISCORD_BUCKET_TIMEOUT) { struct discord_context *cxt = b->busy; b->busy = NULL; - discord_async_recycle_context(&adapter->async, cxt); + discord_async_recycle_context(&rest->async, cxt); } /* cancel pending tranfers */ - QUEUE_ADD(adapter->async.idle_contexts, &b->pending_queue); + QUEUE_ADD(rest->async.idle_contexts, &b->pending_queue); QUEUE_INIT(&b->pending_queue); } void -discord_adapter_stop_buckets(struct discord_adapter *adapter) +discord_rest_stop_buckets(struct discord_rest *rest) { - discord_ratelimiter_foreach_bucket(adapter->ratelimiter, adapter, - &_discord_adapter_stop_bucket); + discord_ratelimiter_foreach_bucket(rest->ratelimiter, rest, + &_discord_rest_stop_bucket); } diff --git a/src/discord-adapter_async.c b/src/discord-rest_async.c similarity index 87% rename from src/discord-adapter_async.c rename to src/discord-rest_async.c index 8259811c2..e2c411845 100644 --- a/src/discord-adapter_async.c +++ b/src/discord-rest_async.c @@ -44,14 +44,13 @@ _on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) { (void)io; (void)mhandle; - return discord_adapter_async_perform(user_data); + return discord_rest_async_perform(user_data); } void discord_async_init(struct discord_async *async, struct logconf *conf) { - struct discord_adapter *adapter = - CONTAINEROF(async, struct discord_adapter, async); + struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); logconf_branch(&async->conf, conf, "DISCORD_ASYNC"); @@ -61,8 +60,8 @@ discord_async_init(struct discord_async *async, struct logconf *conf) QUEUE_INIT(async->idle_contexts); async->mhandle = curl_multi_init(); - io_poller_curlm_add(CLIENT(adapter, adapter)->io_poller, async->mhandle, - &_on_io_poller_curl, adapter); + io_poller_curlm_add(CLIENT(rest, rest)->io_poller, async->mhandle, + &_on_io_poller_curl, rest); } void @@ -81,8 +80,7 @@ discord_async_cleanup(struct discord_async *async) free(async->idle_contexts); /* cleanup curl's multi handle */ - io_poller_curlm_del(CLIENT(async, adapter.async)->io_poller, - async->mhandle); + io_poller_curlm_del(CLIENT(async, rest.async)->io_poller, async->mhandle); curl_multi_cleanup(async->mhandle); } @@ -124,7 +122,7 @@ discord_async_add_request(struct discord_async *async, /* initiate libcurl transfer */ mcode = curl_multi_add_handle(async->mhandle, ehandle); - io_poller_curlm_enable_perform(CLIENT(async, adapter.async)->io_poller, + io_poller_curlm_enable_perform(CLIENT(async, rest.async)->io_poller, async->mhandle); return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; @@ -135,10 +133,9 @@ discord_async_retry_context(struct discord_async *async, struct discord_context *cxt, int64_t wait_ms) { - struct discord_adapter *adapter = - CONTAINEROF(async, struct discord_adapter, async); + struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); - if (adapter->retry_limit < cxt->retry_attempt++) return false; + if (rest->retry_limit < cxt->retry_attempt++) return false; CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); @@ -160,7 +157,7 @@ discord_async_recycle_context(struct discord_async *async, curl_multi_remove_handle(async->mhandle, ehandle); if (cxt->conn) ua_conn_stop(cxt->conn); - discord_refcounter_decr(CLIENT(async, adapter.async)->refcounter, + discord_refcounter_decr(CLIENT(async, rest.async)->refcounter, cxt->dispatch.data); cxt->b = NULL; @@ -176,7 +173,7 @@ discord_async_recycle_context(struct discord_async *async, QUEUE_INSERT_TAIL(async->idle_contexts, &cxt->entry); } -/* Only the fields that are required at _discord_adapter_request_to_multipart() +/* Only the fields that are required at _discord_rest_request_to_multipart() * are duplicated */ static void _discord_attachments_dup(struct discord_attachments *dest, @@ -213,9 +210,8 @@ discord_async_start_context(struct discord_async *async, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct discord_adapter *adapter = - CONTAINEROF(async, struct discord_adapter, async); - struct discord *client = CLIENT(adapter, adapter); + struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); + struct discord *client = CLIENT(rest, rest); struct discord_context *cxt = _discord_context_get(async); cxt->method = method; @@ -242,7 +238,7 @@ discord_async_start_context(struct discord_async *async, /* copy bucket's key */ memcpy(cxt->key, key, sizeof(cxt->key)); /* bucket pertaining to the request */ - cxt->b = discord_bucket_get(adapter->ratelimiter, key); + cxt->b = discord_bucket_get(rest->ratelimiter, key); if (req->dispatch.data) discord_refcounter_incr(client->refcounter, req->dispatch.data, diff --git a/src/discord-adapter_ratelimit.c b/src/discord-rest_ratelimit.c similarity index 95% rename from src/discord-adapter_ratelimit.c rename to src/discord-rest_ratelimit.c index 450a9bf0e..cc10389b9 100644 --- a/src/discord-adapter_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -173,10 +173,10 @@ discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) } void -discord_ratelimiter_foreach_bucket( - struct discord_ratelimiter *rl, - struct discord_adapter *adapter, - void (*iter)(struct discord_adapter *adapter, struct discord_bucket *b)) +discord_ratelimiter_foreach_bucket(struct discord_ratelimiter *rl, + struct discord_rest *rest, + void (*iter)(struct discord_rest *rest, + struct discord_bucket *b)) { struct _discord_route *r; int i; @@ -184,7 +184,7 @@ discord_ratelimiter_foreach_bucket( pthread_mutex_lock(&rl->global.lock); for (i = 0; i < rl->capacity; ++i) { r = rl->routes + i; - if (CHASH_FILLED == r->state) (*iter)(adapter, r->bucket); + if (CHASH_FILLED == r->state) (*iter)(rest, r->bucket); } pthread_mutex_unlock(&rl->global.lock); } @@ -255,17 +255,16 @@ _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) } void -discord_bucket_try_timeout(struct discord_adapter *adapter, - struct discord_bucket *b) +discord_bucket_try_timeout(struct discord_rest *rest, struct discord_bucket *b) { - struct discord *client = CLIENT(adapter, adapter); + struct discord *client = CLIENT(rest, rest); const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); b->busy = DISCORD_BUCKET_TIMEOUT; discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); - logconf_info(&client->adapter.ratelimiter->conf, + logconf_info(&client->rest.ratelimiter->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", b->hash, delay_ms); } @@ -349,7 +348,7 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, pthread_rwlock_unlock(&rl->global.rwlock); } else { - /* lock single bucket, timeout at discord_adapter_run() */ + /* lock single bucket, timeout at discord_rest_run() */ b->reset_tstamp = reset_tstamp; } } diff --git a/src/emoji.c b/src/emoji.c index 83a48f46a..fafc44bae 100644 --- a/src/emoji.c +++ b/src/emoji.c @@ -17,8 +17,8 @@ discord_list_guild_emojis(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_emojis, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/emojis", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/emojis", guild_id); } CCORDcode @@ -34,9 +34,9 @@ discord_get_guild_emoji(struct discord *client, DISCORD_REQ_INIT(req, discord_emoji, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, - emoji_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, + emoji_id); } CCORDcode @@ -57,8 +57,8 @@ discord_create_guild_emoji(struct discord *client, DISCORD_REQ_INIT(req, discord_emoji, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/guilds/%" PRIu64 "/emojis", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/guilds/%" PRIu64 "/emojis", guild_id); } CCORDcode @@ -81,9 +81,9 @@ discord_modify_guild_emoji(struct discord *client, DISCORD_REQ_INIT(req, discord_emoji, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, - emoji_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, + emoji_id); } CCORDcode @@ -99,7 +99,7 @@ discord_delete_guild_emoji(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, - emoji_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, + emoji_id); } diff --git a/src/gateway.c b/src/gateway.c index e97a4c9a8..901234de8 100644 --- a/src/gateway.c +++ b/src/gateway.c @@ -38,9 +38,9 @@ discord_disconnect_guild_member(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_member, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/members/%" PRIu64, - guild_id, user_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, + user_id); } /****************************************************************************** @@ -65,8 +65,7 @@ discord_get_gateway(struct discord *client, struct ccord_szbuf *ret) req.dispatch.has_type = true; req.dispatch.sync = ret; - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/gateway"); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, "/gateway"); } CCORDcode @@ -80,6 +79,6 @@ discord_get_gateway_bot(struct discord *client, struct ccord_szbuf *ret) req.dispatch.has_type = true; req.dispatch.sync = ret; - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/gateway/bot"); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/gateway/bot"); } diff --git a/src/guild.c b/src/guild.c index 7f01932c6..e52384448 100644 --- a/src/guild.c +++ b/src/guild.c @@ -22,8 +22,7 @@ discord_create_guild(struct discord *client, DISCORD_REQ_INIT(req, discord_guild, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/guilds"); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, "/guilds"); } CCORDcode @@ -37,8 +36,8 @@ discord_get_guild(struct discord *client, DISCORD_REQ_INIT(req, discord_guild, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64, guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64, guild_id); } CCORDcode @@ -52,8 +51,8 @@ discord_get_guild_preview(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_preview, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/preview", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/preview", guild_id); } CCORDcode @@ -74,8 +73,8 @@ discord_modify_guild(struct discord *client, DISCORD_REQ_INIT(req, discord_guild, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64, guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64, guild_id); } CCORDcode @@ -89,8 +88,8 @@ discord_delete_guild(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64, guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64, guild_id); } CCORDcode @@ -104,8 +103,8 @@ discord_get_guild_channels(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_channels, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/channels", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/channels", guild_id); } CCORDcode @@ -126,8 +125,8 @@ discord_create_guild_channel(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/guilds/%" PRIu64 "/channels", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/guilds/%" PRIu64 "/channels", guild_id); } CCORDcode @@ -150,8 +149,8 @@ discord_modify_guild_channel_positions( DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/channels", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/channels", guild_id); } CCORDcode @@ -167,9 +166,9 @@ discord_get_guild_member(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_member, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/members/%" PRIu64, - guild_id, user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, + user_id); } CCORDcode @@ -201,9 +200,9 @@ discord_list_guild_members(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_guild_members, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/members%s%s", guild_id, - *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/members%s%s", guild_id, + *query ? "?" : "", query); } CCORDcode @@ -221,7 +220,8 @@ discord_search_guild_members(struct discord *client, int offset = 0; if (params->query) { - char *pe_query = curl_escape(params->query, (int)strlen(params->query)); + char *pe_query = + curl_escape(params->query, (int)strlen(params->query)); offset += snprintf(query + offset, sizeof(query) - (size_t)offset, "query=%s", pe_query); @@ -238,9 +238,9 @@ discord_search_guild_members(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_guild_members, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/members/search%s%s", - guild_id, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/members/search%s%s", guild_id, + *query ? "?" : "", query); } CCORDcode @@ -265,9 +265,9 @@ discord_add_guild_member(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_member, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/guilds/%" PRIu64 "/members/%" PRIu64, - guild_id, user_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, + user_id); } CCORDcode @@ -290,9 +290,9 @@ discord_modify_guild_member(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_member, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/members/%" PRIu64, - guild_id, user_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, + user_id); } CCORDcode discord_modify_current_member(struct discord *client, @@ -314,8 +314,8 @@ discord_modify_current_member(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_member, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/members/@me", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/members/@me", guild_id); } CCORDcode discord_modify_current_user_nick( @@ -342,9 +342,8 @@ discord_modify_current_user_nick( DISCORD_REQ_INIT(req, discord_guild_member, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/members/@me/nick", - guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/members/@me/nick", guild_id); } CCORDcode @@ -362,10 +361,10 @@ discord_add_guild_member_role(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_PUT, - "/guilds/%" PRIu64 "/members/%" PRIu64 - "/roles/%" PRIu64, - guild_id, user_id, role_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + "/guilds/%" PRIu64 "/members/%" PRIu64 + "/roles/%" PRIu64, + guild_id, user_id, role_id); } CCORDcode @@ -383,10 +382,10 @@ discord_remove_guild_member_role(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64 "/members/%" PRIu64 - "/roles/%" PRIu64, - guild_id, user_id, role_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64 "/members/%" PRIu64 + "/roles/%" PRIu64, + guild_id, user_id, role_id); } CCORDcode @@ -402,9 +401,9 @@ discord_remove_guild_member(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64 "/members/%" PRIu64, - guild_id, user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, + user_id); } CCORDcode @@ -418,8 +417,8 @@ discord_get_guild_bans(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_bans, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/bans", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/bans", guild_id); } CCORDcode @@ -435,9 +434,9 @@ discord_get_guild_ban(struct discord *client, DISCORD_REQ_INIT(req, discord_ban, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, - user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, + user_id); } CCORDcode @@ -464,9 +463,9 @@ discord_create_guild_ban(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PUT, - "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, - user_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, + user_id); } CCORDcode discord_remove_guild_ban(struct discord *client, @@ -481,9 +480,9 @@ discord_remove_guild_ban(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, - user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, + user_id); } CCORDcode @@ -497,8 +496,8 @@ discord_get_guild_roles(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_roles, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/roles", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/roles", guild_id); } CCORDcode @@ -518,8 +517,8 @@ discord_create_guild_role(struct discord *client, DISCORD_REQ_INIT(req, discord_role, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/guilds/%" PRIu64 "/roles", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/guilds/%" PRIu64 "/roles", guild_id); } CCORDcode @@ -542,8 +541,8 @@ discord_modify_guild_role_positions( DISCORD_REQ_LIST_INIT(req, discord_roles, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/roles", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/roles", guild_id); } CCORDcode @@ -569,9 +568,9 @@ discord_modify_guild_role(struct discord *client, DISCORD_REQ_INIT(req, discord_role, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/guilds/%" PRIu64 "/roles/%" PRIu64, guild_id, - role_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/guilds/%" PRIu64 "/roles/%" PRIu64, guild_id, + role_id); } CCORDcode @@ -587,9 +586,9 @@ discord_delete_guild_role(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64 "/roles/%" PRIu64, guild_id, - role_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64 "/roles/%" PRIu64, guild_id, + role_id); } CCORDcode @@ -613,8 +612,8 @@ discord_begin_guild_prune(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/guilds/%" PRIu64 "/prune", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/guilds/%" PRIu64 "/prune", guild_id); } CCORDcode @@ -628,8 +627,8 @@ discord_get_guild_invites(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_invites, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/invites", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/invites", guild_id); } CCORDcode @@ -645,9 +644,9 @@ discord_delete_guild_integrations(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/guilds/%" PRIu64 "/integrations/%" PRIu64, - guild_id, integration_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/guilds/%" PRIu64 "/integrations/%" PRIu64, + guild_id, integration_id); } CCORDcode @@ -661,8 +660,8 @@ discord_get_guild_vanity_url(struct discord *client, DISCORD_REQ_INIT(req, discord_invite, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/vanity-url", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/vanity-url", guild_id); } CCORDcode @@ -676,6 +675,6 @@ discord_get_guild_welcome_screen(struct discord *client, DISCORD_REQ_INIT(req, discord_welcome_screen, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/welcome-screen", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/welcome-screen", guild_id); } diff --git a/src/guild_template.c b/src/guild_template.c index ba716888a..7baa8b700 100644 --- a/src/guild_template.c +++ b/src/guild_template.c @@ -17,8 +17,8 @@ discord_get_guild_template(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_template, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/templates/%s", code); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/templates/%s", code); } CCORDcode @@ -39,8 +39,8 @@ discord_create_guild_template(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_template, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/guilds/%" PRIu64 "/templates", guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/guilds/%" PRIu64 "/templates", guild_id); } CCORDcode @@ -55,7 +55,7 @@ discord_sync_guild_template(struct discord *client, DISCORD_REQ_INIT(req, discord_guild_template, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_PUT, - "/guilds/%" PRIu64 "/templates/%s", guild_id, - code); + return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + "/guilds/%" PRIu64 "/templates/%s", guild_id, + code); } diff --git a/src/interaction.c b/src/interaction.c index 7632c2a85..37ec23736 100644 --- a/src/interaction.c +++ b/src/interaction.c @@ -37,9 +37,9 @@ discord_create_interaction_response( DISCORD_REQ_INIT(req, discord_interaction_response, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/interactions/%" PRIu64 "/%s/callback", - interaction_id, interaction_token); + return discord_rest_run(&client->rest, &req, &body, method, + "/interactions/%" PRIu64 "/%s/callback", + interaction_id, interaction_token); } CCORDcode @@ -57,9 +57,9 @@ discord_get_original_interaction_response( DISCORD_REQ_INIT(req, discord_interaction_response, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/webhooks/%" PRIu64 "/%s/messages/@original", - application_id, interaction_token); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/webhooks/%" PRIu64 "/%s/messages/@original", + application_id, interaction_token); } CCORDcode @@ -94,9 +94,9 @@ discord_edit_original_interaction_response( DISCORD_REQ_INIT(req, discord_interaction_response, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/webhooks/%" PRIu64 "/%s/messages/@original", - application_id, interaction_token); + return discord_rest_run(&client->rest, &req, &body, method, + "/webhooks/%" PRIu64 "/%s/messages/@original", + application_id, interaction_token); } CCORDcode @@ -113,9 +113,9 @@ discord_delete_original_interaction_response(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/webhooks/%" PRIu64 "/%s/messages/@original", - application_id, interaction_token); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/webhooks/%" PRIu64 "/%s/messages/@original", + application_id, interaction_token); } CCORDcode @@ -156,9 +156,9 @@ discord_create_followup_message(struct discord *client, DISCORD_REQ_INIT(req, discord_webhook, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/webhooks/%" PRIu64 "/%s%s%s", application_id, - interaction_token, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, &body, method, + "/webhooks/%" PRIu64 "/%s%s%s", application_id, + interaction_token, *query ? "?" : "", query); } CCORDcode @@ -177,9 +177,9 @@ discord_get_followup_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/webhooks/%" PRIu64 "/%s/%" PRIu64, - application_id, interaction_token, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/webhooks/%" PRIu64 "/%s/%" PRIu64, + application_id, interaction_token, message_id); } CCORDcode @@ -215,9 +215,9 @@ discord_edit_followup_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, - application_id, interaction_token, message_id); + return discord_rest_run(&client->rest, &req, &body, method, + "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, + application_id, interaction_token, message_id); } CCORDcode @@ -236,7 +236,7 @@ discord_delete_followup_message(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, - application_id, interaction_token, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, + application_id, interaction_token, message_id); } diff --git a/src/invite.c b/src/invite.c index 8ac74c573..b246aea45 100644 --- a/src/invite.c +++ b/src/invite.c @@ -24,8 +24,8 @@ discord_get_invite(struct discord *client, DISCORD_REQ_INIT(req, discord_invite, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_GET, - "/invites/%s", invite_code); + return discord_rest_run(&client->rest, &req, &body, HTTP_GET, + "/invites/%s", invite_code); } CCORDcode @@ -39,6 +39,6 @@ discord_delete_invite(struct discord *client, DISCORD_REQ_INIT(req, discord_invite, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/invites/%s", invite_code); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/invites/%s", invite_code); } diff --git a/src/user.c b/src/user.c index 18cdd41d1..1311f37e1 100644 --- a/src/user.c +++ b/src/user.c @@ -13,8 +13,7 @@ discord_get_current_user(struct discord *client, struct discord_ret_user *ret) DISCORD_REQ_INIT(req, discord_user, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/users/@me"); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, "/users/@me"); } CCORDcode @@ -28,8 +27,8 @@ discord_get_user(struct discord *client, DISCORD_REQ_INIT(req, discord_user, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/users/%" PRIu64, user_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/users/%" PRIu64, user_id); } CCORDcode @@ -48,8 +47,8 @@ discord_modify_current_user(struct discord *client, DISCORD_REQ_INIT(req, discord_user, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/users/@me"); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/users/@me"); } CCORDcode @@ -60,8 +59,8 @@ discord_get_current_user_guilds(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_guilds, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/users/@me/guilds"); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/users/@me/guilds"); } CCORDcode @@ -76,8 +75,8 @@ discord_leave_guild(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_DELETE, - "/users/@me/guilds/%" PRIu64, guild_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_DELETE, + "/users/@me/guilds/%" PRIu64, guild_id); } CCORDcode @@ -96,8 +95,8 @@ discord_create_dm(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/users/@me/channels"); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/users/@me/channels"); } CCORDcode @@ -119,8 +118,8 @@ discord_create_group_dm(struct discord *client, DISCORD_REQ_INIT(req, discord_channel, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/users/@me/channels"); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/users/@me/channels"); } CCORDcode @@ -131,6 +130,6 @@ discord_get_user_connections(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_connections, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/users/@me/connections"); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/users/@me/connections"); } diff --git a/src/voice.c b/src/voice.c index e60a20a21..9694e3e3c 100644 --- a/src/voice.c +++ b/src/voice.c @@ -14,6 +14,6 @@ discord_list_voice_regions(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_voice_regions, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/voice/regions"); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/voice/regions"); } diff --git a/src/webhook.c b/src/webhook.c index 351e6cb23..baa812389 100644 --- a/src/webhook.c +++ b/src/webhook.c @@ -25,8 +25,8 @@ discord_create_webhook(struct discord *client, DISCORD_REQ_INIT(req, discord_webhook, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_POST, - "/channels/%" PRIu64 "/webhooks", channel_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + "/channels/%" PRIu64 "/webhooks", channel_id); } CCORDcode @@ -40,8 +40,8 @@ discord_get_channel_webhooks(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_webhooks, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/channels/%" PRIu64 "/webhooks", channel_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/channels/%" PRIu64 "/webhooks", channel_id); } CCORDcode @@ -55,8 +55,8 @@ discord_get_guild_webhooks(struct discord *client, DISCORD_REQ_LIST_INIT(req, discord_webhooks, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/guilds/%" PRIu64 "/webhooks", guild_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/guilds/%" PRIu64 "/webhooks", guild_id); } CCORDcode @@ -70,8 +70,8 @@ discord_get_webhook(struct discord *client, DISCORD_REQ_INIT(req, discord_webhook, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/webhooks/%" PRIu64, webhook_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/webhooks/%" PRIu64, webhook_id); } CCORDcode @@ -88,9 +88,9 @@ discord_get_webhook_with_token(struct discord *client, DISCORD_REQ_INIT(req, discord_webhook, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/webhooks/%" PRIu64 "/%s", webhook_id, - webhook_token); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/webhooks/%" PRIu64 "/%s", webhook_id, + webhook_token); } CCORDcode @@ -110,8 +110,8 @@ discord_modify_webhook(struct discord *client, DISCORD_REQ_INIT(req, discord_webhook, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/webhooks/%" PRIu64, webhook_id); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/webhooks/%" PRIu64, webhook_id); } CCORDcode @@ -136,9 +136,9 @@ discord_modify_webhook_with_token( DISCORD_REQ_INIT(req, discord_webhook, ret); - return discord_adapter_run(&client->adapter, &req, &body, HTTP_PATCH, - "/webhooks/%" PRIu64 "/%s", webhook_id, - webhook_token); + return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + "/webhooks/%" PRIu64 "/%s", webhook_id, + webhook_token); } CCORDcode @@ -152,8 +152,8 @@ discord_delete_webhook(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/webhooks/%" PRIu64, webhook_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/webhooks/%" PRIu64, webhook_id); } CCORDcode @@ -170,9 +170,9 @@ discord_delete_webhook_with_token(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/webhooks/%" PRIu64 "/%s", webhook_id, - webhook_token); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/webhooks/%" PRIu64 "/%s", webhook_id, + webhook_token); } CCORDcode @@ -218,9 +218,9 @@ discord_execute_webhook(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/webhooks/%" PRIu64 "/%s%s%s", webhook_id, - webhook_token, *query ? "?" : "", query); + return discord_rest_run(&client->rest, &req, &body, method, + "/webhooks/%" PRIu64 "/%s%s%s", webhook_id, + webhook_token, *query ? "?" : "", query); } CCORDcode @@ -239,9 +239,9 @@ discord_get_webhook_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_GET, - "/webhooks/%" PRIu64 "/%s/%" PRIu64, webhook_id, - webhook_token, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + "/webhooks/%" PRIu64 "/%s/%" PRIu64, webhook_id, + webhook_token, message_id); } CCORDcode @@ -276,9 +276,9 @@ discord_edit_webhook_message(struct discord *client, DISCORD_REQ_INIT(req, discord_message, ret); - return discord_adapter_run(&client->adapter, &req, &body, method, - "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, - webhook_id, webhook_token, message_id); + return discord_rest_run(&client->rest, &req, &body, method, + "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, + webhook_id, webhook_token, message_id); } CCORDcode @@ -297,7 +297,7 @@ discord_delete_webhook_message(struct discord *client, DISCORD_REQ_BLANK_INIT(req, ret); - return discord_adapter_run(&client->adapter, &req, NULL, HTTP_DELETE, - "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, - webhook_id, webhook_token, message_id); + return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, + webhook_id, webhook_token, message_id); } From ecca9db06caf5d34e5668ea4883696c2d637b0b8 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 13:40:41 -0300 Subject: [PATCH 043/118] refactor(discord-rest): rename and move discord_context_bucket_insert() -> discord_bucket_add_context and discord_context_bucket_remove() -> discord_bucket_remove_context() --- include/discord-internal.h | 67 ++++++++++++++++++------------------ src/discord-rest.c | 4 +-- src/discord-rest_async.c | 23 +------------ src/discord-rest_ratelimit.c | 21 +++++++++++ 4 files changed, 57 insertions(+), 58 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 4de97dfaa..eef21278e 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -32,9 +32,9 @@ /** * @brief Get container `type` from a field `ptr` * - * @param ptr the field contained in `type` - * @param type the container datatype - * @param path the path to the field from the container POV + * @param[in] ptr the field contained in `type` + * @param[in] type the container datatype + * @param[in] path the path to the field from the container POV */ #define CONTAINEROF(ptr, type, path) \ ((type *)((char *)(ptr)-offsetof(type, path))) @@ -49,10 +49,11 @@ /** * @brief log and return `code` if `expect` condition is false * - * @param expect the expected outcome - * @param client the discord client - * @param error return CCORDcode error - * @param reason for return + * @param[in] client the Discord client + * @param[in] expect the expected outcome + * @param[in] code return CCORDcode error code + * @param[in] reason for return + * @return the provided @ref CCORDcode `code` parameter */ #define CCORD_EXPECT(client, expect, code, reason) \ do { \ @@ -66,8 +67,8 @@ * @brief Shortcut for checking OOB-write attempts * @note unsigned values are expected * - * @param nbytes amount of bytes to be written - * @param destsz size of dest in bytes + * @param[in] nbytes amount of bytes to be written + * @param[in] destsz size of dest in bytes */ #define ASSERT_NOT_OOB(nbytes, destsz) \ ASSERT_S((size_t)nbytes < (size_t)destsz, "Out of bounds write attempt"); @@ -194,29 +195,6 @@ void discord_async_init(struct discord_async *async, struct logconf *conf); */ void discord_async_cleanup(struct discord_async *async); -/** - * @brief Insert request's context into bucket's pending queue - * @todo this doesn't have to be done manually, - * discord_async_start_context() should take care of it - * - * @param cxt the request context obtained via discord_async_start_context() - * @param b the bucket to insert the request to - * @param high_priority if high priority then request shall be prioritized over - * already enqueued requests - */ -void discord_context_bucket_insert(struct discord_context *cxt, - struct discord_bucket *b, - bool high_priority); - -/** - * @brief Remove head request's context from bucket's pending queue - * - * @param b the bucket to fetch the request from - * @return the request's context - */ -struct discord_context *discord_context_bucket_remove( - struct discord_bucket *b); - /** * @brief Kickstart the request by adding it to libcurl's request multiplexer * (`CURLM` multi handle) @@ -421,6 +399,27 @@ void discord_bucket_try_timeout(struct discord_rest *rest, struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, const char key[]); +/** + * @brief Insert request's context into bucket's pending queue + * + * @param b the bucket to insert the request to + * @param cxt the request context obtained via discord_async_start_context() + * @param high_priority if high priority then request shall be prioritized over + * already enqueued requests + */ +void discord_bucket_add_context(struct discord_bucket *b, + struct discord_context *cxt, + bool high_priority); + +/** + * @brief Remove head request's context from bucket's pending queue + * + * @param b the bucket to fetch the request from + * @return the request's context + */ +struct discord_context *discord_bucket_remove_context( + struct discord_bucket *b); + /** @brief The ratelimiter struct for handling ratelimiting */ struct discord_ratelimiter { /** DISCORD_RATELIMIT logging module */ @@ -856,7 +855,7 @@ struct discord_refcounter { int capacity; /** * individual user's data held for automatic cleanup - * @note datatype declared at discord-rest_refcount.c + * @note datatype declared at discord-refcount.c */ struct _discord_ref *refs; }; @@ -925,7 +924,7 @@ struct discord_message_commands { int capacity; /** * message command entries - * @note datatype declared at discord-gateway_command.c + * @note datatype declared at discord-messagecommands.c */ struct _discord_message_commands_entry *entries; }; diff --git a/src/discord-rest.c b/src/discord-rest.c index 529ce17e7..a6418b697 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -376,7 +376,7 @@ _discord_rest_run_async(struct discord_rest *rest, struct discord_context *cxt = discord_async_start_context( &rest->async, req, body, method, endpoint, key); - discord_context_bucket_insert(cxt, cxt->b, req->dispatch.high_p); + discord_bucket_add_context(cxt->b, cxt, req->dispatch.high_p); /* FIXME: redundant return value (constant) */ return CCORD_OK; @@ -398,7 +398,7 @@ _discord_context_to_multipart(curl_mime *mime, void *p_cxt) static CCORDcode _discord_rest_add_request(struct discord_rest *rest, struct discord_bucket *b) { - struct discord_context *cxt = discord_context_bucket_remove(b); + struct discord_context *cxt = discord_bucket_remove_context(b); struct ua_conn *conn = ua_conn_start(rest->ua); if (HTTP_MIMEPOST == cxt->method) { diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index e2c411845..ee16b0065 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -84,27 +84,6 @@ discord_async_cleanup(struct discord_async *async) curl_multi_cleanup(async->mhandle); } -void -discord_context_bucket_insert(struct discord_context *cxt, - struct discord_bucket *b, - bool high_priority) -{ - if (high_priority) - QUEUE_INSERT_HEAD(&b->pending_queue, &cxt->entry); - else - QUEUE_INSERT_TAIL(&b->pending_queue, &cxt->entry); -} - -struct discord_context * -discord_context_bucket_remove(struct discord_bucket *b) -{ - QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->pending_queue); - QUEUE_REMOVE(qelem); - QUEUE_INIT(qelem); - - return QUEUE_DATA(qelem, struct discord_context, entry); -} - CCORDcode discord_async_add_request(struct discord_async *async, struct discord_context *cxt, @@ -143,7 +122,7 @@ discord_async_retry_context(struct discord_async *async, ua_conn_reset(cxt->conn); /* FIXME: wait_ms > 0 should be dealt with aswell */ - if (wait_ms <= 0) discord_context_bucket_insert(cxt, cxt->b, true); + if (wait_ms <= 0) discord_bucket_add_context(cxt->b, cxt, true); return true; } diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index cc10389b9..b3a0c82b7 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -420,3 +420,24 @@ discord_ratelimiter_build(struct discord_ratelimiter *rl, /* populate bucket with response header values */ _discord_bucket_populate(rl, b, info); } + +void +discord_bucket_add_context(struct discord_bucket *b, + struct discord_context *cxt, + bool high_priority) +{ + if (high_priority) + QUEUE_INSERT_HEAD(&b->pending_queue, &cxt->entry); + else + QUEUE_INSERT_TAIL(&b->pending_queue, &cxt->entry); +} + +struct discord_context * +discord_bucket_remove_context(struct discord_bucket *b) +{ + QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->pending_queue); + QUEUE_REMOVE(qelem); + QUEUE_INIT(qelem); + + return QUEUE_DATA(qelem, struct discord_context, entry); +} From ef36167847530eca2da733fd595ce55490d9e178 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 17:31:34 -0300 Subject: [PATCH 044/118] feat: add discord_claim() and discord_unclaim(), so that the client may take ownership of a callback parameter provided by Concord --- include/discord-internal.h | 39 +++++++++++--- include/discord-templates.h | 7 ++- include/discord.h | 23 ++++++++ src/discord-client.c | 14 +++++ src/discord-refcount.c | 104 ++++++++++++++++++++++++++---------- 5 files changed, 149 insertions(+), 38 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index eef21278e..33fce7bcc 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -876,30 +876,57 @@ struct discord_refcounter *discord_refcounter_init(struct logconf *conf); */ void discord_refcounter_cleanup(struct discord_refcounter *rc); +/** + * @brief Claim ownership of `data` + * @see discord_refcounter_unclaim() + * + * After ownership is claimed `data` will no longer be cleaned automatically, + * but shall be immediatelly cleaned once discord_refcounter_unclaim() is + * called + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data to have its ownership claimed + * @return `true` if `data` was found and claimed + */ +bool discord_refcounter_claim(struct discord_refcounter *rc, void *data); + +/** + * @brief Unclaim ownership of `data` + * @see discord_refcounter_claim() + * + * This function will have `data` cleanup method will be immediatelly called + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data to have its ownership unclaimed + * @return `true` if `data` was found, unclaimed, and free'd + */ +bool discord_refcounter_unclaim(struct discord_refcounter *rc, void *data); + /** * @brief Increment the reference counter for `ret->data` + * @see discord_refcounter_decr() * * @param rc the handle initialized with discord_refcounter_init() - * @param data the user arbitrary data to have its reference counter - * @param cleanup user-defined function for cleaning `data` resources once its + * @param data the data to have its reference counter incremented + * @param cleanup function for cleaning `data` resources once its * no longer referenced * @param should_free whether `data` cleanup should be followed by a free() + * @return `true` if `data` reference count has been successfully incremented */ -void discord_refcounter_incr(struct discord_refcounter *rc, +bool discord_refcounter_incr(struct discord_refcounter *rc, void *data, void (*cleanup)(void *data), bool should_free); /** * @brief Decrement the reference counter for `data` + * @see discord_refcounter_incr() * * If the count reaches zero then `data` shall be cleanup up with its * user-defined cleanup function * @param rc the handle initialized with discord_refcounter_init() - * @param data the user arbitrary data to have its reference counter - * decremented + * @param data the data to have its reference counter decremented + * @return `true` if `data` reference count has been successfully decremented */ -void discord_refcounter_decr(struct discord_refcounter *rc, void *data); +bool discord_refcounter_decr(struct discord_refcounter *rc, void *data); /** @} DiscordInternalRefcount */ diff --git a/include/discord-templates.h b/include/discord-templates.h index 0b92dd852..d8a59168f 100644 --- a/include/discord-templates.h +++ b/include/discord-templates.h @@ -15,13 +15,13 @@ #define DISCORDT_RET_DEFAULT_FIELDS \ /** optional callback to be executed on a failed request */ \ void (*fail)(struct discord * client, CCORDcode code, void *data); \ - /** user arbitrary data to be retrieved at `done` or `fail` callbacks */ \ + /** user arbitrary data to be passed to `done` or `fail` callbacks */ \ void *data; \ /** cleanup for when `data` is no longer needed \ - @note this only has to be defined once, it shall be called once \ + @note this only has to be defined once, it shall be called when \ `data` is no longer referenced by any callback */ \ void (*cleanup)(void *data); \ - /** if `true` then request will take priority over already enqueued \ + /** if `true` then request will be prioritized over already enqueued \ requests */ \ bool high_p @@ -133,5 +133,4 @@ DISCORDT_RETURN(guild_application_command_permissions); DISCORDT_RETURN(interaction_response); /** @} DiscordAPIInteractionsReact */ - #endif /* DISCORD_TEMPLATES_H */ diff --git a/include/discord.h b/include/discord.h index 3b2912111..898f23b0b 100644 --- a/include/discord.h +++ b/include/discord.h @@ -139,6 +139,29 @@ const char *discord_strerror(CCORDcode code, struct discord *client); #include "discord-events.h" +/** + * @brief Claim ownership of a function parameter provided by Concord + * @see discord_unclaim() + * + * @param client the client initialized with discord_init() + * @param param a function parameter provided by Concord + * @return pointer to `param` (for one-liners) + */ +#define discord_claim(client, param) __discord_claim(client, param), param +void __discord_claim(struct discord *client, const void *data); + +/** + * @brief Unclaim ownership of a function parameter provided by Concord + * @note this will trigger the cleanup method of the parameter, so this should + * only be called when you no longer plan to use it + * @see discord_claim() + * + * @param client the client initialized with discord_init() + * @param param a function parameter provided by Concord, that has been + * previously claimed with discord_claim() + */ +void discord_unclaim(struct discord *client, const void *data); + /** * @brief Create a Discord Client handle by its token * @see discord_get_logconf() to configure logging behavior diff --git a/src/discord-client.c b/src/discord-client.c index cac045291..ef044762d 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -675,3 +675,17 @@ discord_config_get_field(struct discord *client, return (struct ccord_szbuf_readonly){ field.start, field.size }; } + +void +__discord_claim(struct discord *client, const void *param) +{ + ASSERT_S(discord_refcounter_claim(client->refcounter, (void *)param), + "Failed attempt to claim non-Concord function parameter"); +} + +void +discord_unclaim(struct discord *client, const void *param) +{ + ASSERT_S(discord_refcounter_unclaim(client->refcounter, (void *)param), + "Failed attempt to unclaim non-Concord function parameter"); +} diff --git a/src/discord-refcount.c b/src/discord-refcount.c index b43ad2beb..8a654596f 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -27,7 +27,12 @@ struct _discord_refvalue { * @note this only has to be assigned once, it is automatically called once * `data` is no longer referenced by any callback */ void (*cleanup)(void *data); - /** `data` references count */ + /** + * `data` references count + * @note if `-1` then `data` has been claimed with + * discord_refcounter_claim() and will be cleaned up once + * discord_refcount_unclaim() is called + */ int visits; /** whether `data` cleanup should also be followed by a free() */ bool should_free; @@ -50,29 +55,44 @@ _discord_refvalue_cleanup(struct _discord_refvalue *value) } static struct _discord_refvalue * -_discord_refvalue_find(struct discord_refcounter *rc, intptr_t key) +_discord_refvalue_find(struct discord_refcounter *rc, void *data) { struct _discord_ref *ref = NULL; - ref = chash_lookup_bucket(rc, key, ref, REFCOUNTER_TABLE); + ref = chash_lookup_bucket(rc, (intptr_t)data, ref, REFCOUNTER_TABLE); return &ref->value; } static struct _discord_refvalue * _discord_refvalue_init(struct discord_refcounter *rc, - intptr_t key, void *data, - void (*cleanup)(void *data)) + void (*cleanup)(void *data), + bool should_free) { - struct _discord_refvalue value; + struct _discord_refvalue value = { + .data = data, + .cleanup = cleanup, + .visits = 0, + .should_free = should_free, + }; - value.data = data; - value.cleanup = cleanup; - value.visits = 0; - chash_assign(rc, key, value, REFCOUNTER_TABLE); + chash_assign(rc, (intptr_t)data, value, REFCOUNTER_TABLE); - return _discord_refvalue_find(rc, key); + return _discord_refvalue_find(rc, data); +} + +static bool +_discord_refvalue_contains(struct discord_refcounter *rc, void *data) +{ + bool ret = chash_contains(rc, (intptr_t)data, ret, REFCOUNTER_TABLE); + return ret; +} + +static void +_discord_refvalue_delete(struct discord_refcounter *rc, void *data) +{ + chash_delete(rc, (intptr_t)data, REFCOUNTER_TABLE); } struct discord_refcounter * @@ -91,37 +111,65 @@ discord_refcounter_cleanup(struct discord_refcounter *rc) chash_free(rc, REFCOUNTER_TABLE); } -void +bool +discord_refcounter_claim(struct discord_refcounter *rc, void *data) +{ + if (_discord_refvalue_contains(rc, data)) { + struct _discord_refvalue *value = _discord_refvalue_find(rc, data); + + value->visits = -1; + return true; + } + return false; +} + +bool +discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) +{ + if (_discord_refvalue_contains(rc, data)) { + struct _discord_refvalue *value = _discord_refvalue_find(rc, data); + + if (value->visits == -1) { + _discord_refvalue_delete(rc, data); + return true; + } + } + return false; +} + +bool discord_refcounter_incr(struct discord_refcounter *rc, void *data, void (*cleanup)(void *data), bool should_free) { - struct _discord_refvalue *value = NULL; - intptr_t key = (intptr_t)data; - int ret; + struct _discord_refvalue *value; - ret = chash_contains(rc, key, ret, REFCOUNTER_TABLE); - if (ret) - value = _discord_refvalue_find(rc, key); + if (_discord_refvalue_contains(rc, data)) + value = _discord_refvalue_find(rc, data); else - value = _discord_refvalue_init(rc, key, data, cleanup); - value->should_free = should_free; - ++value->visits; + value = _discord_refvalue_init(rc, data, cleanup, should_free); + + if (value->visits != -1) { + ++value->visits; + return true; + } + return false; } -void +bool discord_refcounter_decr(struct discord_refcounter *rc, void *data) { struct _discord_refvalue *value = NULL; - intptr_t key = (intptr_t)data; - int ret; - ret = chash_contains(rc, key, ret, REFCOUNTER_TABLE); - if (ret) { - value = _discord_refvalue_find(rc, key); + if (_discord_refvalue_contains(rc, data)) + value = _discord_refvalue_find(rc, data); + + if (value && value->visits != -1) { if (0 == --value->visits) { - chash_delete(rc, key, REFCOUNTER_TABLE); + _discord_refvalue_delete(rc, data); } + return true; } + return false; } From daadd3f40753960a9ca4ff388d177a7de23cec38 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 19:02:26 -0300 Subject: [PATCH 045/118] chore(chash): add non-malloc alternatives to chash_init() and chash_free() --- cog-utils/chash.h | 48 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/cog-utils/chash.h b/cog-utils/chash.h index d66ec801d..a3fc90f12 100644 --- a/cog-utils/chash.h +++ b/cog-utils/chash.h @@ -1,7 +1,11 @@ +/* Modified by Lucas Müller (muller.lucas@hotmail.com), 16 May 2022 + * - add __chash_init() and __chash_free() as a non-malloc option + */ + #ifndef CWARE_LIBCHASH_H #define CWARE_LIBCHASH_H -#define CWARE_LIBCHASH_VERSION "2.0.0" +#define CWARE_LIBCHASH_VERSION "x.0.0" /* How big heap-allocated hashtables are by default */ #ifndef CHASH_INITIAL_SIZE @@ -222,12 +226,8 @@ do { \ - /* operations */ -#define chash_init(hashtable, namespace) \ - NULL; \ - \ - (hashtable) = malloc(sizeof((*(hashtable)))); \ +#define __chash_init(hashtable, namespace) \ (hashtable)->CHASH_LENGTH_FIELD = 0; \ (hashtable)->CHASH_CAPACITY_FIELD = CHASH_INITIAL_SIZE; \ (hashtable)->CHASH_BUCKETS_FIELD = malloc(CHASH_INITIAL_SIZE \ @@ -235,6 +235,12 @@ do { \ memset((hashtable)->CHASH_BUCKETS_FIELD, 0, \ sizeof(*((hashtable)->CHASH_BUCKETS_FIELD)) * CHASH_INITIAL_SIZE) +#define chash_init(hashtable, namespace) \ + NULL; \ + \ + (hashtable) = malloc(sizeof((*(hashtable)))); \ + __chash_init(hashtable, namespace) + #define chash_init_stack(hashtable, buffer, _length, namespace) \ (*(hashtable)); \ \ @@ -378,6 +384,34 @@ do { \ storage = ((hashtable)->CHASH_BUCKETS_FIELD + __CHASH_HASH); \ } while(0) +#define __chash_free(hashtable, namespace) \ +do { \ + __chash_assert_nonnull(__chash_free, hashtable); \ + __chash_assert_nonnull(__chash_free, (hashtable)->CHASH_BUCKETS_FIELD); \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + \ + while((hashtable)->CHASH_CAPACITY_FIELD != -1) { \ + if((hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_STATE_FIELD != CHASH_FILLED) { \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + continue; \ + } \ + \ + namespace ##_FREE_KEY( \ + (hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_KEY_FIELD); \ + namespace ##_FREE_VALUE( \ + (hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \ + .CHASH_VALUE_FIELD); \ + (hashtable)->CHASH_CAPACITY_FIELD--; \ + (hashtable)->CHASH_LENGTH_FIELD--; \ + } \ + \ + if((namespace ## _HEAP) == 1) { \ + free((hashtable)->CHASH_BUCKETS_FIELD); \ + } \ +} while(0) + #define chash_free(hashtable, namespace) \ do { \ __chash_assert_nonnull(chash_free, hashtable); \ @@ -405,7 +439,7 @@ do { \ free((hashtable)->CHASH_BUCKETS_FIELD); \ free((hashtable)); \ } \ -} while(0); +} while(0) #define chash_is_full(hashtable, namespace) \ (((hashtable)->CHASH_LENGTH_FIELD) == ((hashtable)->CHASH_CAPACITY_FIELD)) From 3bd5a67cc6a50374f49df8a1c78705b0d1344a87 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 19:04:15 -0300 Subject: [PATCH 046/118] wip: match 'struct discord_refcounter' and 'struct discord_message_commands' to daadd3, experiment with cleanup callback that receives a 'struct discord' parameter --- include/discord-internal.h | 30 ++++++++----- include/discord-templates.h | 7 ++- include/discord.h | 2 +- src/channel.c | 17 ++++--- src/discord-client.c | 20 +++++---- src/discord-gateway.c | 82 +++++++++++++--------------------- src/discord-gateway_dispatch.c | 18 +++++--- src/discord-messagecommands.c | 31 ++++++------- src/discord-refcount.c | 29 ++++++------ src/discord-rest_async.c | 4 +- 10 files changed, 119 insertions(+), 121 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 33fce7bcc..cd671a6bd 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -544,12 +544,15 @@ struct discord_gateway_payload { char *json; /** current iteration JSON string data length */ size_t length; + /** field 'op' */ enum discord_gateway_opcodes opcode; /** field 's' */ int seq; /** field 't' */ char name[32]; + /** field 't' enumerator value */ + enum discord_gateway_events event; /** field 'd' */ jsmnf_pair *data; }; @@ -755,10 +758,8 @@ void discord_gateway_send_presence_update( * @brief Dispatch user callback matched to event * * @param gw the handle initialized with discord_gateway_init() - * @param event the Discord event to be executed */ -void discord_gateway_dispatch(struct discord_gateway *gw, - enum discord_gateway_events event); +void discord_gateway_dispatch(struct discord_gateway *gw); /** @} DiscordInternalGateway */ @@ -864,10 +865,11 @@ struct discord_refcounter { * @brief Initialize reference counter handle * * A hashtable shall be used for storage and retrieval of user data + * @param rc the reference counter handle to be initialized * @param conf pointer to @ref discord logging module - * @return the reference counter handle */ -struct discord_refcounter *discord_refcounter_init(struct logconf *conf); +void discord_refcounter_init(struct discord_refcounter *rc, + struct logconf *conf); /** * @brief Cleanup refcounter and all user data currently held @@ -913,7 +915,8 @@ bool discord_refcounter_unclaim(struct discord_refcounter *rc, void *data); */ bool discord_refcounter_incr(struct discord_refcounter *rc, void *data, - void (*cleanup)(void *data), + void (*cleanup)(struct discord *client, + void *data), bool should_free); /** @@ -959,11 +962,12 @@ struct discord_message_commands { /** * @brief Initialize a Message Commands handle * + * @param cmds the message commands handle to be initialized * @param conf pointer to @ref discord logging module * @return the message commands handle */ -struct discord_message_commands *discord_message_commands_init( - struct logconf *conf); +void discord_message_commands_init(struct discord_message_commands *cmds, + struct logconf *conf); /** * @brief Free a Message Commands handle @@ -1046,12 +1050,16 @@ struct discord { struct ccord_szbuf_readonly token; /** the io poller for listening to file descriptors */ struct io_poller *io_poller; + + /** the user's message commands @see discord_set_on_command() */ + struct discord_message_commands commands; + /** user's data reference counter for automatic cleanup */ + struct discord_refcounter refcounter; + /** the handle for interfacing with Discord's REST API */ struct discord_rest rest; /** the handle for interfacing with Discord's Gateway API */ struct discord_gateway gw; - /** user's data reference counter for automatic cleanup */ - struct discord_refcounter *refcounter; /** the client's user structure */ struct discord_user self; @@ -1072,8 +1080,6 @@ struct discord { discord_ev_idle on_idle; /** triggers once per loop cycle */ discord_ev_idle on_cycle; - /** the user's message commands @see discord_set_on_command() */ - struct discord_message_commands *commands; /** space for user arbitrary data */ void *data; diff --git a/include/discord-templates.h b/include/discord-templates.h index d8a59168f..4d2a8f3fc 100644 --- a/include/discord-templates.h +++ b/include/discord-templates.h @@ -17,10 +17,9 @@ void (*fail)(struct discord * client, CCORDcode code, void *data); \ /** user arbitrary data to be passed to `done` or `fail` callbacks */ \ void *data; \ - /** cleanup for when `data` is no longer needed \ - @note this only has to be defined once, it shall be called when \ - `data` is no longer referenced by any callback */ \ - void (*cleanup)(void *data); \ + /** cleanup method to be called for `data`, once its no longer \ + being referenced */ \ + void (*cleanup)(struct discord * client, void *data); \ /** if `true` then request will be prioritized over already enqueued \ requests */ \ bool high_p diff --git a/include/discord.h b/include/discord.h index 898f23b0b..9068321fc 100644 --- a/include/discord.h +++ b/include/discord.h @@ -147,7 +147,7 @@ const char *discord_strerror(CCORDcode code, struct discord *client); * @param param a function parameter provided by Concord * @return pointer to `param` (for one-liners) */ -#define discord_claim(client, param) __discord_claim(client, param), param +#define discord_claim(client, param) (__discord_claim(client, param), param) void __discord_claim(struct discord *client, const void *data); /** diff --git a/src/channel.c b/src/channel.c index fe8444ac8..5ba1d5816 100644 --- a/src/channel.c +++ b/src/channel.c @@ -43,7 +43,7 @@ _done_get_channels(struct discord *client, cxt->ret.fail(client, CCORD_BAD_PARAMETER, cxt->ret.data); } - discord_refcounter_decr(client->refcounter, cxt->ret.data); + discord_refcounter_decr(&client->refcounter, cxt->ret.data); } CCORDcode @@ -54,7 +54,7 @@ discord_get_channel_at_pos(struct discord *client, struct discord_ret_channel *ret) { struct _discord_get_channel_at_pos_cxt *cxt; - struct discord_ret_channels _ret = { 0 }; + struct discord_ret_channels current_ret = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); @@ -65,18 +65,17 @@ discord_get_channel_at_pos(struct discord *client, cxt->position = position; cxt->ret = *ret; - _ret.done = &_done_get_channels; - _ret.fail = ret->fail; - _ret.data = cxt; - _ret.cleanup = &free; + current_ret.done = &_done_get_channels; + current_ret.fail = ret->fail; + current_ret.data = cxt; if (ret->data) - discord_refcounter_incr(client->refcounter, ret->data, ret->cleanup, - false); + discord_refcounter_incr(&client->refcounter, ret->data, ret->cleanup, + true); /* TODO: fetch channel via caching, and return if results are non-existent */ - return discord_get_guild_channels(client, guild_id, &_ret); + return discord_get_guild_channels(client, guild_id, ¤t_ret); } /****************************************************************************** diff --git a/src/discord-client.c b/src/discord-client.c index ef044762d..6b3c42909 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -14,8 +14,9 @@ _discord_init(struct discord *new_client) ccord_global_init(); discord_timers_init(new_client); new_client->io_poller = io_poller_create(); - new_client->refcounter = discord_refcounter_init(&new_client->conf); - new_client->commands = discord_message_commands_init(&new_client->conf); + + discord_refcounter_init(&new_client->refcounter, &new_client->conf); + discord_message_commands_init(&new_client->commands, &new_client->conf); discord_rest_init(&new_client->rest, &new_client->conf, &new_client->token); @@ -111,7 +112,7 @@ discord_config_init(const char config_file[]) if (enable_prefix && (f = jsmnf_find(pairs, field.start, "prefix", 6))) { - discord_message_commands_set_prefix(new_client->commands, + discord_message_commands_set_prefix(&new_client->commands, field.start + f->v.pos, f->v.len); } @@ -173,8 +174,8 @@ discord_cleanup(struct discord *client) discord_gateway_cleanup(&client->gw); discord_user_cleanup(&client->self); io_poller_destroy(client->io_poller); - discord_refcounter_cleanup(client->refcounter); - discord_message_commands_cleanup(client->commands); + discord_refcounter_cleanup(&client->refcounter); + discord_message_commands_cleanup(&client->commands); #ifdef CCORD_VOICE discord_voice_connections_cleanup(client); #endif @@ -276,7 +277,7 @@ discord_set_prefix(struct discord *client, const char prefix[]) { if (!prefix || !*prefix) return; - discord_message_commands_set_prefix(client->commands, prefix, + discord_message_commands_set_prefix(&client->commands, prefix, strlen(prefix)); } @@ -292,7 +293,8 @@ discord_set_on_command(struct discord *client, discord_ev_message callback) { size_t length = (!command || !*command) ? 0 : strlen(command); - discord_message_commands_append(client->commands, command, length, + + discord_message_commands_append(&client->commands, command, length, callback); discord_add_intents(client, DISCORD_GATEWAY_GUILD_MESSAGES | DISCORD_GATEWAY_DIRECT_MESSAGES); @@ -679,13 +681,13 @@ discord_config_get_field(struct discord *client, void __discord_claim(struct discord *client, const void *param) { - ASSERT_S(discord_refcounter_claim(client->refcounter, (void *)param), + ASSERT_S(discord_refcounter_claim(&client->refcounter, (void *)param), "Failed attempt to claim non-Concord function parameter"); } void discord_unclaim(struct discord *client, const void *param) { - ASSERT_S(discord_refcounter_unclaim(client->refcounter, (void *)param), + ASSERT_S(discord_refcounter_unclaim(&client->refcounter, (void *)param), "Failed attempt to unclaim non-Concord function parameter"); } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 04cb1078a..9f54ddcc2 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -11,37 +11,6 @@ case code: \ return #code -/** - * @brief Context in case event is scheduled to be triggered - * from Concord's worker threads - */ -struct _discord_gateway_context { - /** the discord gateway client */ - struct discord_gateway *gw; - /** the event unique id value */ - enum discord_gateway_events event; -}; - -static struct _discord_gateway_context * -_discord_gateway_context_init(const struct discord_gateway *gw, - enum discord_gateway_events event) -{ - struct _discord_gateway_context *cxt = malloc(sizeof *cxt); - struct discord *clone = discord_clone(CLIENT(gw, gw)); - - cxt->gw = &clone->gw; - cxt->event = event; - - return cxt; -} - -static void -_discord_gateway_context_cleanup(struct _discord_gateway_context *cxt) -{ - discord_cleanup(CLIENT(cxt->gw, gw)); - free(cxt); -} - static const char * _discord_gateway_opcode_print(enum discord_gateway_opcodes opcode) { @@ -114,12 +83,12 @@ on_hello(struct discord_gateway *gw) discord_gateway_send_identify(gw, &gw->id); } -static enum discord_gateway_events -_discord_gateway_event_eval(char name[]) -{ #define RETURN_IF_MATCH(event, str) \ if (!strcmp(#event, str)) return DISCORD_EV_##event +static enum discord_gateway_events +_discord_gateway_event_eval(char name[]) +{ RETURN_IF_MATCH(READY, name); RETURN_IF_MATCH(RESUMED, name); RETURN_IF_MATCH(APPLICATION_COMMAND_CREATE, name); @@ -174,33 +143,44 @@ _discord_gateway_event_eval(char name[]) RETURN_IF_MATCH(VOICE_SERVER_UPDATE, name); RETURN_IF_MATCH(WEBHOOKS_UPDATE, name); return DISCORD_EV_NONE; +} #undef RETURN_IF_MATCH + +static struct discord_gateway * +_discord_gateway_clone(const struct discord_gateway *gw) +{ + return &discord_clone(CLIENT(gw, gw))->gw; } static void -_discord_gateway_dispatch_thread(void *p_cxt) +_discord_gateway_clone_cleanup(struct discord_gateway *clone) { - struct _discord_gateway_context *cxt = p_cxt; + discord_cleanup(CLIENT(clone, gw)); +} + +static void +_discord_gateway_dispatch_thread(void *p_gw) +{ + struct discord_gateway *gw = p_gw; - logconf_info(&cxt->gw->conf, + logconf_info(&gw->conf, "Thread " ANSICOLOR("starts", ANSI_FG_RED) " to serve %s", - cxt->gw->payload.name); + gw->payload.name); - discord_gateway_dispatch(cxt->gw, cxt->event); + discord_gateway_dispatch(gw); - logconf_info(&cxt->gw->conf, + logconf_info(&gw->conf, "Thread " ANSICOLOR("exits", ANSI_FG_RED) " from serving %s", - cxt->gw->payload.name); + gw->payload.name); - _discord_gateway_context_cleanup(cxt); + _discord_gateway_clone_cleanup(gw); } static void on_dispatch(struct discord_gateway *gw) { /* get dispatch event opcode */ - enum discord_gateway_events event; enum discord_event_scheduler mode; /* XXX: this should only apply for user dispatched payloads? */ @@ -217,7 +197,7 @@ on_dispatch(struct discord_gateway *gw) } #endif - switch (event = _discord_gateway_event_eval(gw->payload.name)) { + switch (gw->payload.event) { case DISCORD_EV_READY: { jsmnf_pair *f; @@ -248,23 +228,22 @@ on_dispatch(struct discord_gateway *gw) mode = gw->scheduler(CLIENT(gw, gw), gw->payload.json + gw->payload.data->v.pos, - gw->payload.data->v.len, event); + gw->payload.data->v.len, gw->payload.event); /* user subscribed to event */ switch (mode) { case DISCORD_EVENT_IGNORE: break; case DISCORD_EVENT_MAIN_THREAD: - discord_gateway_dispatch(gw, event); + discord_gateway_dispatch(gw); break; case DISCORD_EVENT_WORKER_THREAD: { - struct _discord_gateway_context *cxt = - _discord_gateway_context_init(gw, event); - int ret = work_run(&_discord_gateway_dispatch_thread, cxt); + struct discord_gateway *clone = _discord_gateway_clone(gw); + int ret = work_run(&_discord_gateway_dispatch_thread, clone); if (ret != 0) { log_error("Couldn't execute worker-thread (code %d)", ret); - _discord_gateway_context_cleanup(cxt); + _discord_gateway_clone_cleanup(clone); } } break; default: @@ -435,6 +414,9 @@ on_text_cb(void *p_gw, gw->payload.json + f->v.pos); else *gw->payload.name = '\0'; + + gw->payload.event = + _discord_gateway_event_eval(gw->payload.name); } if ((f = jsmnf_find(gw->parse.pairs, text, "s", 1))) { int seq = (int)strtol(gw->payload.json + f->v.pos, NULL, 10); diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index d16cc3a01..e1b0f2638 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -126,15 +126,21 @@ static const struct { INIT(discord_webhooks_update, webhooks_update), }; +static void +_discord_gateway_dispatch_cleanup(struct discord *client, void *data) +{ + dispatch[client->gw.payload.event].cleanup(data); +} + void -discord_gateway_dispatch(struct discord_gateway *gw, - enum discord_gateway_events event) +discord_gateway_dispatch(struct discord_gateway *gw) { + const enum discord_gateway_events event = gw->payload.event; struct discord *client = CLIENT(gw, gw); switch (event) { case DISCORD_EV_MESSAGE_CREATE: - if (discord_message_commands_try_perform(gw, client->commands, + if (discord_message_commands_try_perform(gw, &client->commands, &gw->payload)) { return; @@ -147,10 +153,10 @@ discord_gateway_dispatch(struct discord_gateway *gw, dispatch[event].from_jsmnf(gw->payload.data, gw->payload.json, data); - discord_refcounter_incr(client->refcounter, data, - dispatch[event].cleanup, true); + discord_refcounter_incr(&client->refcounter, data, + _discord_gateway_dispatch_cleanup, true); gw->cbs[event](client, data); - discord_refcounter_decr(client->refcounter, data); + discord_refcounter_decr(&client->refcounter, data); } break; case DISCORD_EV_NONE: diff --git a/src/discord-messagecommands.c b/src/discord-messagecommands.c index d32446477..1c5dd82b2 100644 --- a/src/discord-messagecommands.c +++ b/src/discord-messagecommands.c @@ -45,24 +45,23 @@ struct _discord_message_commands_entry { int state; }; -struct discord_message_commands * -discord_message_commands_init(struct logconf *conf) +void +discord_message_commands_init(struct discord_message_commands *cmds, + struct logconf *conf) { - struct discord_message_commands *cmds = chash_init(cmds, COMMANDS_TABLE); + __chash_init(cmds, COMMANDS_TABLE); logconf_branch(&cmds->conf, conf, "DISCORD_MESSAGE_COMMANDS"); cmds->fallback = NULL; memset(&cmds->prefix, 0, sizeof(cmds->prefix)); - - return cmds; } void discord_message_commands_cleanup(struct discord_message_commands *cmds) { if (cmds->prefix.start) free(cmds->prefix.start); - chash_free(cmds, COMMANDS_TABLE); + __chash_free(cmds, COMMANDS_TABLE); } discord_ev_message @@ -101,13 +100,6 @@ discord_message_commands_append(struct discord_message_commands *cmds, } } -static void -_discord_message_cleanup_v(void *message) -{ - discord_message_cleanup(message); - free(message); -} - void discord_message_commands_set_prefix(struct discord_message_commands *cmds, const char prefix[], @@ -118,6 +110,15 @@ discord_message_commands_set_prefix(struct discord_message_commands *cmds, cmds->prefix.size = cog_strndup(prefix, length, &cmds->prefix.start); } +static void +_discord_message_cleanup_v(struct discord *client, void *message) +{ + (void)client; + + discord_message_cleanup(message); + free(message); +} + /** return true in case user command has been triggered */ bool discord_message_commands_try_perform(struct discord_gateway *gw, @@ -167,11 +168,11 @@ discord_message_commands_try_perform(struct discord_gateway *gw, ++event->content; } - discord_refcounter_incr(client->refcounter, event, + discord_refcounter_incr(&client->refcounter, event, _discord_message_cleanup_v, false); callback(client, event); event->content = tmp; /* retrieve original ptr */ - discord_refcounter_decr(client->refcounter, event); + discord_refcounter_decr(&client->refcounter, event); return true; } diff --git a/src/discord-refcount.c b/src/discord-refcount.c index 8a654596f..92073037e 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -12,8 +12,9 @@ #define REFCOUNTER_TABLE_HEAP 1 #define REFCOUNTER_TABLE_BUCKET struct _discord_ref #define REFCOUNTER_TABLE_FREE_KEY(_key) -#define REFCOUNTER_TABLE_HASH(_key, _hash) ((intptr_t)(_key)) -#define REFCOUNTER_TABLE_FREE_VALUE(_value) _discord_refvalue_cleanup(&_value) +#define REFCOUNTER_TABLE_HASH(_key, _hash) ((intptr_t)(_key)) +#define REFCOUNTER_TABLE_FREE_VALUE(_value) \ + _discord_refvalue_cleanup(&_value, client) #define REFCOUNTER_TABLE_COMPARE(_cmp_a, _cmp_b) (_cmp_a == _cmp_b) #define REFCOUNTER_TABLE_INIT(ref, _key, _value) \ memset(&ref, 0, sizeof(ref)); \ @@ -26,7 +27,7 @@ struct _discord_refvalue { * cleanup for when `data` is no longer needed * @note this only has to be assigned once, it is automatically called once * `data` is no longer referenced by any callback */ - void (*cleanup)(void *data); + void (*cleanup)(struct discord *client, void *data); /** * `data` references count * @note if `-1` then `data` has been claimed with @@ -48,9 +49,10 @@ struct _discord_ref { }; static void -_discord_refvalue_cleanup(struct _discord_refvalue *value) +_discord_refvalue_cleanup(struct _discord_refvalue *value, + struct discord *client) { - if (value->cleanup) value->cleanup(value->data); + if (value->cleanup) value->cleanup(client, value->data); if (value->should_free) free(value->data); } @@ -67,9 +69,10 @@ _discord_refvalue_find(struct discord_refcounter *rc, void *data) static struct _discord_refvalue * _discord_refvalue_init(struct discord_refcounter *rc, void *data, - void (*cleanup)(void *data), + void (*cleanup)(struct discord *client, void *data), bool should_free) { + struct discord *client = CLIENT(rc, refcounter); struct _discord_refvalue value = { .data = data, .cleanup = cleanup, @@ -92,23 +95,23 @@ _discord_refvalue_contains(struct discord_refcounter *rc, void *data) static void _discord_refvalue_delete(struct discord_refcounter *rc, void *data) { + struct discord *client = CLIENT(rc, refcounter); chash_delete(rc, (intptr_t)data, REFCOUNTER_TABLE); } -struct discord_refcounter * -discord_refcounter_init(struct logconf *conf) +void +discord_refcounter_init(struct discord_refcounter *rc, struct logconf *conf) { - struct discord_refcounter *rc = chash_init(rc, REFCOUNTER_TABLE); + __chash_init(rc, REFCOUNTER_TABLE); logconf_branch(&rc->conf, conf, "DISCORD_REFCOUNT"); - - return rc; } void discord_refcounter_cleanup(struct discord_refcounter *rc) { - chash_free(rc, REFCOUNTER_TABLE); + struct discord *client = CLIENT(rc, refcounter); + __chash_free(rc, REFCOUNTER_TABLE); } bool @@ -140,7 +143,7 @@ discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) bool discord_refcounter_incr(struct discord_refcounter *rc, void *data, - void (*cleanup)(void *data), + void (*cleanup)(struct discord *client, void *data), bool should_free) { struct _discord_refvalue *value; diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index ee16b0065..3ff373719 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -136,7 +136,7 @@ discord_async_recycle_context(struct discord_async *async, curl_multi_remove_handle(async->mhandle, ehandle); if (cxt->conn) ua_conn_stop(cxt->conn); - discord_refcounter_decr(CLIENT(async, rest.async)->refcounter, + discord_refcounter_decr(&CLIENT(async, rest.async)->refcounter, cxt->dispatch.data); cxt->b = NULL; @@ -220,7 +220,7 @@ discord_async_start_context(struct discord_async *async, cxt->b = discord_bucket_get(rest->ratelimiter, key); if (req->dispatch.data) - discord_refcounter_incr(client->refcounter, req->dispatch.data, + discord_refcounter_incr(&client->refcounter, req->dispatch.data, req->dispatch.cleanup, false); io_poller_curlm_enable_perform(client->io_poller, async->mhandle); From a0c0d1e3a8c883cdfbc3a4a562f58378f9885c1f Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 23:15:11 -0300 Subject: [PATCH 047/118] refactor: use CONTAINEROF() and CLIENT() macros where possible --- include/discord-internal.h | 266 ++++++++++++++++----------------- src/discord-gateway_dispatch.c | 2 +- src/discord-messagecommands.c | 5 +- src/discord-rest.c | 39 +++-- src/discord-rest_async.c | 2 +- src/discord-rest_ratelimit.c | 63 ++++---- 6 files changed, 191 insertions(+), 186 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index cd671a6bd..4163f711b 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -255,88 +255,111 @@ struct discord_context *discord_async_start_context( /** @} DiscordInternalRESTAsync */ -/** @brief The handle used for interfacing with Discord's REST API */ -struct discord_rest { - /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ - struct logconf conf; - /** the user agent handle for performing requests */ - struct user_agent *ua; - /** store individual contexts from asynchronous requests */ - struct discord_async async; +/** @defgroup DiscordInternalRESTRatelimit Ratelimiting + * @brief Enforce ratelimiting per the official Discord Documentation + * @{ */ - /** enforce ratelimiting on discovered buckets */ - struct discord_ratelimiter *ratelimiter; +/** + * @brief Value assigned to @ref discord_bucket `busy` field in case it's + * being timed-out + */ +#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) - /** max amount of retries before a failed request gives up */ - int retry_limit; +/** @brief The ratelimiter struct for handling ratelimiting */ +struct discord_ratelimiter { + /** DISCORD_RATELIMIT logging module */ + struct logconf conf; + /** amount of bucket's routes discovered */ + int length; + /** route's cap before increase */ + int capacity; + /** + * routes matched to individual buckets + * @note datatype declared at discord-rest_ratelimit.c + */ + struct _discord_route *routes; + /** singleton bucket for requests that haven't been matched to a + * known or new bucket (i.e first time running the request) */ + struct discord_bucket *null; + /** singleton bucket for requests that are not part of any known + * ratelimiting group */ + struct discord_bucket *miss; + + /* client-wide ratelimiting timeout */ + struct { + /** global ratelimit */ + u64unix_ms wait_ms; + /** global rwlock */ + pthread_rwlock_t rwlock; + /** global lock */ + pthread_mutex_t lock; + } * global; }; /** - * @brief Initialize an REST handle + * @brief Initialize ratelimiter handle * - * Structure used for interfacing with the Discord's REST API - * @param rest the REST handle to be initialized - * @param conf pointer to @ref discord logging module - * @param token the bot token + * A hashtable shall be used for storage and retrieval of discovered buckets + * @param rl the ratelimiter handle to be initialized + * @param conf pointer to @ref discord_rest logging module + * @return the ratelimiter handle */ -void discord_rest_init(struct discord_rest *rest, - struct logconf *conf, - struct ccord_szbuf_readonly *token); +void discord_ratelimiter_init(struct discord_ratelimiter *rl, + struct logconf *conf); /** - * @brief Free an REST handle + * @brief Cleanup all buckets that have been discovered * - * @param rest the handle initialized with discord_rest_init() + * @note pending requests will be moved to `rest.idle_contexts` + * @param rl the handle initialized with discord_ratelimiter_init() */ -void discord_rest_cleanup(struct discord_rest *rest); +void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); /** - * @brief Perform a request to Discord + * @brief Iterate known buckets * - * This functions is a selector over discord_rest_run() or - * discord_rest_run_async() - * @param rest the handle initialized with discord_rest_init() - * @param req return object of request - * @param body the body sent for methods that require (ex: post), leave as - * null if unecessary - * @param method the method in opcode format of the request being sent - * @param endpoint_fmt the printf-like endpoint formatting string - * @CCORD_return - * @note if sync is set then this function will block the thread and perform it - * immediately + * @param rl the handle initialized with discord_ratelimiter_init() + * @param iter the user callback to be called per bucket */ -CCORDcode discord_rest_run(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint_fmt[], - ...); +void discord_ratelimiter_foreach_bucket( + struct discord_ratelimiter *rl, + void (*iter)(struct discord_ratelimiter *rl, struct discord_bucket *b)); /** - * @brief Check and manage on-going, pending and timed-out requests + * @brief Build unique key formed from the HTTP method and endpoint + * @see https://discord.com/developers/docs/topics/rate-limits * - * @param rest the handle initialized with discord_rest_init() - * @CCORD_return + * @param[in] method the request method + * @param[out] key unique key for matching to buckets + * @param[in] endpoint_fmt the printf-like endpoint formatting string + * @param[in] args variadic arguments matched to `endpoint_fmt` */ -CCORDcode discord_rest_async_perform(struct discord_rest *rest); +void discord_ratelimiter_build_key(enum http_method method, + char key[DISCORD_ROUTE_LEN], + const char endpoint_fmt[], + va_list args); /** - * @brief Stop all bucket's on-going, pending and timed-out requests + * @brief Get global timeout timestamp * - * The requests will be moved over to client's 'idle_contexts' queue - * @param rest the handle initialized with discord_rest_init() + * @param rl the handle initialized with discord_ratelimiter_init() + * @return the most recent global timeout timestamp */ -void discord_rest_stop_buckets(struct discord_rest *rest); - -/** @defgroup DiscordInternalRESTRatelimit Ratelimiting - * @brief Enforce ratelimiting per the official Discord Documentation - * @{ */ +u64unix_ms discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl); /** - * @brief Value assigned to @ref discord_bucket `busy` field in case it's - * being timed-out + * @brief Update the bucket with response header data + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param bucket NULL when bucket is first discovered + * @param key obtained from discord_ratelimiter_build_key() + * @param info informational struct containing details on the current transfer + * @note If the bucket was just discovered it will be created here. */ -#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) +void discord_ratelimiter_build(struct discord_ratelimiter *rl, + struct discord_bucket *bucket, + const char key[], + struct ua_info *info); /** @brief The Discord bucket for handling per-group ratelimits */ struct discord_bucket { @@ -383,10 +406,10 @@ void discord_bucket_try_sleep(struct discord_ratelimiter *rl, /** * @brief Try to timeout bucket for pending cooldown time * - * @param rest the handle initialized with discord_rest_init() + * @param rl the handle initialized with discord_ratelimiter_init() * @param bucket the bucket to wait on cooldown */ -void discord_bucket_try_timeout(struct discord_rest *rest, +void discord_bucket_try_timeout(struct discord_ratelimiter *rl, struct discord_bucket *b); /** @@ -420,103 +443,80 @@ void discord_bucket_add_context(struct discord_bucket *b, struct discord_context *discord_bucket_remove_context( struct discord_bucket *b); -/** @brief The ratelimiter struct for handling ratelimiting */ -struct discord_ratelimiter { - /** DISCORD_RATELIMIT logging module */ +/** @} DiscordInternalRESTRatelimit */ + +/** @brief The handle used for interfacing with Discord's REST API */ +struct discord_rest { + /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ struct logconf conf; - /** amount of bucket's routes discovered */ - int length; - /** route's cap before increase */ - int capacity; - /** - * routes matched to individual buckets - * @note datatype declared at discord-rest_ratelimit.c - */ - struct _discord_route *routes; - /** singleton bucket for requests that haven't been matched to a - * known or new bucket (i.e first time running the request) */ - struct discord_bucket *null; - /** singleton bucket for requests that are not part of any known - * ratelimiting group */ - struct discord_bucket *miss; + /** the user agent handle for performing requests */ + struct user_agent *ua; + /** store individual contexts from asynchronous requests */ + struct discord_async async; - /* client-wide ratelimiting timeout */ - struct { - /** global ratelimit */ - u64unix_ms wait_ms; - /** global rwlock */ - pthread_rwlock_t rwlock; - /** global lock */ - pthread_mutex_t lock; - } global; -}; + /** enforce ratelimiting on discovered buckets */ + struct discord_ratelimiter ratelimiter; -/** - * @brief Initialize ratelimiter handle - * - * A hashtable shall be used for storage and retrieval of discovered buckets - * @param conf pointer to @ref discord_rest logging module - * @return the ratelimiter handle - */ -struct discord_ratelimiter *discord_ratelimiter_init(struct logconf *conf); + /** max amount of retries before a failed request gives up */ + int retry_limit; +}; /** - * @brief Cleanup all buckets that have been discovered + * @brief Initialize an REST handle * - * @note pending requests will be moved to `rest.idle_contexts` - * @param rl the handle initialized with discord_ratelimiter_init() + * Structure used for interfacing with the Discord's REST API + * @param rest the REST handle to be initialized + * @param conf pointer to @ref discord logging module + * @param token the bot token */ -void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); +void discord_rest_init(struct discord_rest *rest, + struct logconf *conf, + struct ccord_szbuf_readonly *token); /** - * @brief Iterate known buckets + * @brief Free an REST handle * - * @param rl the handle initialized with discord_ratelimiter_init() * @param rest the handle initialized with discord_rest_init() - * @param iter the user callback to be called per bucket */ -void discord_ratelimiter_foreach_bucket( - struct discord_ratelimiter *rl, - struct discord_rest *rest, - void (*iter)(struct discord_rest *rest, struct discord_bucket *b)); +void discord_rest_cleanup(struct discord_rest *rest); /** - * @brief Build unique key formed from the HTTP method and endpoint - * @see https://discord.com/developers/docs/topics/rate-limits + * @brief Perform a request to Discord * - * @param[in] method the request method - * @param[out] key unique key for matching to buckets - * @param[in] endpoint_fmt the printf-like endpoint formatting string - * @param[in] args variadic arguments matched to `endpoint_fmt` + * This functions is a selector over discord_rest_run() or + * discord_rest_run_async() + * @param rest the handle initialized with discord_rest_init() + * @param req return object of request + * @param body the body sent for methods that require (ex: post), leave as + * null if unecessary + * @param method the method in opcode format of the request being sent + * @param endpoint_fmt the printf-like endpoint formatting string + * @CCORD_return + * @note if sync is set then this function will block the thread and perform it + * immediately */ -void discord_ratelimiter_build_key(enum http_method method, - char key[DISCORD_ROUTE_LEN], - const char endpoint_fmt[], - va_list args); +CCORDcode discord_rest_run(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint_fmt[], + ...); /** - * @brief Get global timeout timestamp + * @brief Check and manage on-going, pending and timed-out requests * - * @param rl the handle initialized with discord_ratelimiter_init() - * @return the most recent global timeout timestamp + * @param rest the handle initialized with discord_rest_init() + * @CCORD_return */ -u64unix_ms discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl); +CCORDcode discord_rest_async_perform(struct discord_rest *rest); /** - * @brief Update the bucket with response header data + * @brief Stop all bucket's on-going, pending and timed-out requests * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param bucket NULL when bucket is first discovered - * @param key obtained from discord_ratelimiter_build_key() - * @param info informational struct containing details on the current transfer - * @note If the bucket was just discovered it will be created here. + * The requests will be moved over to client's 'idle_contexts' queue + * @param rest the handle initialized with discord_rest_init() */ -void discord_ratelimiter_build(struct discord_ratelimiter *rl, - struct discord_bucket *bucket, - const char key[], - struct ua_info *info); - -/** @} DiscordInternalRESTRatelimit */ +void discord_rest_stop_buckets(struct discord_rest *rest); /** @} DiscordInternalREST */ @@ -1020,16 +1020,12 @@ void discord_message_commands_set_prefix(struct discord_message_commands *cmds, * @brief Read the current @ref DISCORD_EV_MESSAGE_CREATE payload and attempt * to perform its matching callback * - * @param gw the handle initialized with discord_gateway_init() - * @note used for its @ref discord_refcounter and passing as a callback - * parameter * @param cmds the handle initialized with discord_message_commands_init() * @param payload the event payload to read from * (assumes its from `MESSAGE_CREATE`) * @return `true` if the callback has been performed */ bool discord_message_commands_try_perform( - struct discord_gateway *gw, struct discord_message_commands *cmds, struct discord_gateway_payload *payload); diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index e1b0f2638..f45c0ebc4 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -140,7 +140,7 @@ discord_gateway_dispatch(struct discord_gateway *gw) switch (event) { case DISCORD_EV_MESSAGE_CREATE: - if (discord_message_commands_try_perform(gw, &client->commands, + if (discord_message_commands_try_perform(&client->commands, &gw->payload)) { return; diff --git a/src/discord-messagecommands.c b/src/discord-messagecommands.c index 1c5dd82b2..8d20af043 100644 --- a/src/discord-messagecommands.c +++ b/src/discord-messagecommands.c @@ -121,8 +121,7 @@ _discord_message_cleanup_v(struct discord *client, void *message) /** return true in case user command has been triggered */ bool -discord_message_commands_try_perform(struct discord_gateway *gw, - struct discord_message_commands *cmds, +discord_message_commands_try_perform(struct discord_message_commands *cmds, struct discord_gateway_payload *payload) { jsmnf_pair *f; @@ -134,7 +133,7 @@ discord_message_commands_try_perform(struct discord_gateway *gw, && !strncmp(cmds->prefix.start, payload->json + f->v.pos, cmds->prefix.size)) { - struct discord *client = CLIENT(gw, gw); + struct discord *client = CLIENT(cmds, commands); struct discord_message *event = calloc(1, sizeof *event); discord_ev_message callback = NULL; struct ccord_szbuf command; diff --git a/src/discord-rest.c b/src/discord-rest.c index a6418b697..9e04ec45e 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -48,7 +48,7 @@ discord_rest_init(struct discord_rest *rest, } discord_async_init(&rest->async, &rest->conf); - rest->ratelimiter = discord_ratelimiter_init(&rest->conf); + discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ } @@ -63,7 +63,7 @@ discord_rest_cleanup(struct discord_rest *rest) /* cleanup idle requests queue */ discord_async_cleanup(&rest->async); /* cleanup discovered buckets */ - discord_ratelimiter_cleanup(rest->ratelimiter); + discord_ratelimiter_cleanup(&rest->ratelimiter); } static CCORDcode _discord_rest_run_sync(struct discord_rest *rest, @@ -278,7 +278,7 @@ _discord_rest_run_sync(struct discord_rest *rest, bool retry; CCORDcode code; - b = discord_bucket_get(rest->ratelimiter, key); + b = discord_bucket_get(&rest->ratelimiter, key); conn = ua_conn_start(rest->ua); if (HTTP_MIMEPOST == method) { @@ -299,7 +299,7 @@ _discord_rest_run_sync(struct discord_rest *rest, pthread_mutex_lock(&b->lock); do { - discord_bucket_try_sleep(rest->ratelimiter, b); + discord_bucket_try_sleep(&rest->ratelimiter, b); /* perform blocking request, and check results */ switch (code = ua_conn_easy_perform(conn)) { @@ -335,7 +335,7 @@ _discord_rest_run_sync(struct discord_rest *rest, * TODO: create discord_timestamp_update() */ ws_timestamp_update(client->gw.ws); - discord_ratelimiter_build(rest->ratelimiter, b, key, &info); + discord_ratelimiter_build(&rest->ratelimiter, b, key, &info); cog_sleep_ms(wait_ms); ua_info_cleanup(&info); @@ -421,22 +421,27 @@ _discord_rest_add_request(struct discord_rest *rest, struct discord_bucket *b) } static void -_discord_rest_try_add_request(struct discord_rest *rest, +_discord_rest_try_add_request(struct discord_ratelimiter *rl, struct discord_bucket *b) { /* skip if bucket is busy performing */ if (b->busy) return; - if (!b->remaining) - discord_bucket_try_timeout(rest, b); - else if (!QUEUE_EMPTY(&b->pending_queue)) + if (!b->remaining) { + discord_bucket_try_timeout(rl, b); + } + else if (!QUEUE_EMPTY(&b->pending_queue)) { + struct discord_rest *rest = + CONTAINEROF(rl, struct discord_rest, ratelimiter); + _discord_rest_add_request(rest, b); + } } static CCORDcode _discord_rest_check_pending(struct discord_rest *rest) { - discord_ratelimiter_foreach_bucket(rest->ratelimiter, rest, + discord_ratelimiter_foreach_bucket(&rest->ratelimiter, &_discord_rest_try_add_request); /* FIXME: redundant return value (constant) */ return CCORD_OK; @@ -490,7 +495,7 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) code = info.code; - discord_ratelimiter_build(rest->ratelimiter, cxt->b, cxt->key, &info); + discord_ratelimiter_build(&rest->ratelimiter, cxt->b, cxt->key, &info); ua_info_cleanup(&info); } break; case CURLE_READ_ERROR: @@ -549,24 +554,28 @@ discord_rest_async_perform(struct discord_rest *rest) } static void -_discord_rest_stop_bucket(struct discord_rest *rest, struct discord_bucket *b) +_discord_rest_stop_bucket(struct discord_ratelimiter *rl, + struct discord_bucket *b) { + struct discord_async *async = + &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; + /* cancel busy transfer */ if (b->busy && b->busy != DISCORD_BUCKET_TIMEOUT) { struct discord_context *cxt = b->busy; b->busy = NULL; - discord_async_recycle_context(&rest->async, cxt); + discord_async_recycle_context(async, cxt); } /* cancel pending tranfers */ - QUEUE_ADD(rest->async.idle_contexts, &b->pending_queue); + QUEUE_ADD(async->idle_contexts, &b->pending_queue); QUEUE_INIT(&b->pending_queue); } void discord_rest_stop_buckets(struct discord_rest *rest) { - discord_ratelimiter_foreach_bucket(rest->ratelimiter, rest, + discord_ratelimiter_foreach_bucket(&rest->ratelimiter, &_discord_rest_stop_bucket); } diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 3ff373719..18ae115e2 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -217,7 +217,7 @@ discord_async_start_context(struct discord_async *async, /* copy bucket's key */ memcpy(cxt->key, key, sizeof(cxt->key)); /* bucket pertaining to the request */ - cxt->b = discord_bucket_get(rest->ratelimiter, key); + cxt->b = discord_bucket_get(&rest->ratelimiter, key); if (req->dispatch.data) discord_refcounter_incr(&client->refcounter, req->dispatch.data, diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index b3a0c82b7..1cc7e64bc 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -135,58 +135,59 @@ _discord_bucket_init(struct discord_ratelimiter *rl, QUEUE_INIT(&b->pending_queue); - pthread_mutex_lock(&rl->global.lock); + pthread_mutex_lock(&rl->global->lock); chash_assign(rl, key, b, RATELIMITER_TABLE); - pthread_mutex_unlock(&rl->global.lock); + pthread_mutex_unlock(&rl->global->lock); return b; } -struct discord_ratelimiter * -discord_ratelimiter_init(struct logconf *conf) +void +discord_ratelimiter_init(struct discord_ratelimiter *rl, struct logconf *conf) { struct ua_szbuf_readonly keynull = { "null", 4 }, keymiss = { "miss", 4 }; - struct discord_ratelimiter *rl = chash_init(rl, RATELIMITER_TABLE); + + __chash_init(rl, RATELIMITER_TABLE); logconf_branch(&rl->conf, conf, "DISCORD_RATELIMIT"); /* global ratelimiting resources */ - rl->global.wait_ms = 0; - if (pthread_rwlock_init(&rl->global.rwlock, NULL)) + rl->global = malloc(sizeof *rl->global); + rl->global->wait_ms = 0; + if (pthread_rwlock_init(&rl->global->rwlock, NULL)) ERR("Couldn't initialize pthread rwlock"); - if (pthread_mutex_init(&rl->global.lock, NULL)) + if (pthread_mutex_init(&rl->global->lock, NULL)) ERR("Couldn't initialize pthread mutex"); /* initialize 'singleton' buckets */ rl->null = _discord_bucket_init(rl, "null", &keynull, 1L); rl->miss = _discord_bucket_init(rl, "miss", &keymiss, LONG_MAX); - - return rl; } void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) { - pthread_rwlock_destroy(&rl->global.rwlock); - pthread_mutex_destroy(&rl->global.lock); - chash_free(rl, RATELIMITER_TABLE); + pthread_rwlock_destroy(&rl->global->rwlock); + pthread_mutex_destroy(&rl->global->lock); + free(rl->global); + + __chash_free(rl, RATELIMITER_TABLE); } void discord_ratelimiter_foreach_bucket(struct discord_ratelimiter *rl, - struct discord_rest *rest, - void (*iter)(struct discord_rest *rest, + void (*iter)(struct discord_ratelimiter *rl, struct discord_bucket *b)) { struct _discord_route *r; int i; - pthread_mutex_lock(&rl->global.lock); + pthread_mutex_lock(&rl->global->lock); for (i = 0; i < rl->capacity; ++i) { r = rl->routes + i; - if (CHASH_FILLED == r->state) (*iter)(rest, r->bucket); + if (CHASH_FILLED == r->state) (*iter)(rl, r->bucket); } - pthread_mutex_unlock(&rl->global.lock); + pthread_mutex_unlock(&rl->global->lock); } static struct discord_bucket * @@ -195,12 +196,12 @@ _discord_bucket_find(struct discord_ratelimiter *rl, const char key[]) struct discord_bucket *b = NULL; int ret; - pthread_mutex_lock(&rl->global.lock); + pthread_mutex_lock(&rl->global->lock); ret = chash_contains(rl, key, ret, RATELIMITER_TABLE); if (ret) { b = chash_lookup(rl, key, b, RATELIMITER_TABLE); } - pthread_mutex_unlock(&rl->global.lock); + pthread_mutex_unlock(&rl->global->lock); return b; } @@ -210,9 +211,9 @@ discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl) { u64unix_ms global; - pthread_rwlock_rdlock(&rl->global.rwlock); - global = rl->global.wait_ms; - pthread_rwlock_unlock(&rl->global.rwlock); + pthread_rwlock_rdlock(&rl->global->rwlock); + global = rl->global->wait_ms; + pthread_rwlock_unlock(&rl->global->rwlock); return global; } @@ -255,18 +256,18 @@ _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) } void -discord_bucket_try_timeout(struct discord_rest *rest, struct discord_bucket *b) +discord_bucket_try_timeout(struct discord_ratelimiter *rl, + struct discord_bucket *b) { - struct discord *client = CLIENT(rest, rest); + struct discord *client = CLIENT(rl, rest.ratelimiter); const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); b->busy = DISCORD_BUCKET_TIMEOUT; discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); - logconf_info(&client->rest.ratelimiter->conf, - "[%.4s] RATELIMITING (wait %" PRId64 " ms)", b->hash, - delay_ms); + logconf_info(&rl->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", + b->hash, delay_ms); } /* attempt to find a bucket associated key */ @@ -343,9 +344,9 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, if (global.size) { /* lock all buckets */ - pthread_rwlock_wrlock(&rl->global.rwlock); - rl->global.wait_ms = reset_tstamp; - pthread_rwlock_unlock(&rl->global.rwlock); + pthread_rwlock_wrlock(&rl->global->rwlock); + rl->global->wait_ms = reset_tstamp; + pthread_rwlock_unlock(&rl->global->rwlock); } else { /* lock single bucket, timeout at discord_rest_run() */ From 00209140adad0d75786807d5ee91ba452b909033 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 16 May 2022 23:18:29 -0300 Subject: [PATCH 048/118] chore(test/async.c): test discord_claim() and discord_unclaim() --- test/async.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/test/async.c b/test/async.c index f6db05162..bb89b878f 100644 --- a/test/async.c +++ b/test/async.c @@ -6,11 +6,25 @@ #include "discord.h" -struct user_cxt { +struct global_context { u64snowflake channel_id; unsigned long long counter; }; +struct local_context { + struct discord *client; + const struct discord_message *event; +}; + +void +local_context_cleanup(struct discord *client, void *data) +{ + struct local_context *cxt = data; + + discord_unclaim(client, cxt->event); + free(data); +} + void on_ready(struct discord *client, const struct discord_ready *event) { @@ -114,10 +128,10 @@ on_spam(struct discord *client, const struct discord_message *event) void send_msg(struct discord *client, void *data, const struct discord_message *msg) { - struct user_cxt *cxt = discord_get_data(client); + struct global_context *g_cxt = discord_get_data(client); char text[32]; - snprintf(text, sizeof(text), "%llu", cxt->counter); + snprintf(text, sizeof(text), "%llu", g_cxt->counter); discord_create_message(client, msg->channel_id, &(struct discord_create_message){ @@ -127,7 +141,7 @@ send_msg(struct discord *client, void *data, const struct discord_message *msg) .done = &send_msg, }); - ++cxt->counter; + ++g_cxt->counter; } void @@ -137,12 +151,12 @@ on_spam_ordered(struct discord *client, const struct discord_message *event) } void -send_err(struct discord *client, CCORDcode code, void *data) +fail_delete_channel(struct discord *client, CCORDcode code, void *data) { - struct discord_message *event = data; + struct local_context *cxt = data; discord_create_message( - client, event->channel_id, + client, cxt->event->channel_id, &(struct discord_create_message){ .content = (char *)discord_strerror(code, client), }, @@ -153,11 +167,15 @@ void on_force_error(struct discord *client, const struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123; + struct local_context *cxt = malloc(sizeof *cxt); + + cxt->event = discord_claim(client, event); discord_delete_channel(client, FAUX_CHANNEL_ID, &(struct discord_ret_channel){ - .fail = &send_err, - .data = event, + .fail = &fail_delete_channel, + .data = cxt, + .cleanup = &local_context_cleanup, }); } @@ -175,8 +193,8 @@ main(int argc, char *argv[]) struct discord *client = discord_config_init(config_file); assert(NULL != client && "Couldn't initialize client"); - struct user_cxt cxt = { 0 }; - discord_set_data(client, &cxt); + struct global_context g_cxt = { 0 }; + discord_set_data(client, &g_cxt); discord_set_on_ready(client, &on_ready); From 09aff81e72683dc60b0bed03adecc8e9432cd6de Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 17 May 2022 15:59:20 -0300 Subject: [PATCH 049/118] feat: add '.keep' field for keeping Concord's parameters and sharing between callbacks --- include/discord-internal.h | 31 +++++++++------ include/discord-request.h | 38 ++++++++++--------- ...discord-templates.h => discord-response.h} | 26 ++++++++----- include/discord.h | 2 +- src/channel.c | 12 ++++-- src/discord-refcount.c | 26 ++++++------- src/discord-rest.c | 35 ++++++++--------- src/discord-rest_async.c | 13 ++++++- 8 files changed, 109 insertions(+), 74 deletions(-) rename include/{discord-templates.h => discord-response.h} (86%) diff --git a/include/discord-internal.h b/include/discord-internal.h index 4163f711b..8d23c14aa 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -84,6 +84,7 @@ /** @brief Generic request dispatcher */ struct discord_ret_dispatch { + DISCORDT_RET_DEFAULT_FIELDS; /** `true` if may receive a datatype from response */ bool has_type; @@ -93,12 +94,13 @@ struct discord_ret_dispatch { * otherwise its UB */ union { - void (*typed)(struct discord *client, void *data, const void *ret); - void (*typeless)(struct discord *client, void *data); + void (*typed)(struct discord *client, + struct discord_response *resp, + const void *ret); + void (*typeless)(struct discord *client, + struct discord_response *resp); } done; - DISCORDT_RET_DEFAULT_FIELDS; - /** if an address is provided, then request will block the thread and * perform on-spot. On success the response object will be written to * the address. */ @@ -121,7 +123,7 @@ struct discord_ret_response { /** * @brief Macro containing @ref discord_request fields - * @note for @ref discord_context alignment purposes + * @note this exists for @ref discord_context alignment purposes */ #define DISCORD_REQUEST_FIELDS \ /** attributes set by client for request dispatch behavior */ \ @@ -151,7 +153,6 @@ struct discord_context { /** the request's bucket */ struct discord_bucket *b; /** request body handle @note buffer is kept and reused */ - struct ccord_szbuf_reusable body; /** the request's http method */ enum http_method method; @@ -302,7 +303,6 @@ struct discord_ratelimiter { * A hashtable shall be used for storage and retrieval of discovered buckets * @param rl the ratelimiter handle to be initialized * @param conf pointer to @ref discord_rest logging module - * @return the ratelimiter handle */ void discord_ratelimiter_init(struct discord_ratelimiter *rl, struct logconf *conf); @@ -878,24 +878,34 @@ void discord_refcounter_init(struct discord_refcounter *rc, */ void discord_refcounter_cleanup(struct discord_refcounter *rc); +/** + * @brief Check if `data` is stored at the reference counter + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data address to be checked + * @return `true` if data is stored + */ +bool discord_refcounter_contains(struct discord_refcounter *rc, + const void *data); + /** * @brief Claim ownership of `data` * @see discord_refcounter_unclaim() * * After ownership is claimed `data` will no longer be cleaned automatically, - * but shall be immediatelly cleaned once discord_refcounter_unclaim() is + * instead shall be cleaned only when discord_refcounter_unclaim() is * called * @param rc the handle initialized with discord_refcounter_init() * @param data the data to have its ownership claimed * @return `true` if `data` was found and claimed */ -bool discord_refcounter_claim(struct discord_refcounter *rc, void *data); +bool discord_refcounter_claim(struct discord_refcounter *rc, const void *data); /** * @brief Unclaim ownership of `data` * @see discord_refcounter_claim() * - * This function will have `data` cleanup method will be immediatelly called + * This function will have `data` cleanup method called immediately * @param rc the handle initialized with discord_refcounter_init() * @param data the data to have its ownership unclaimed * @return `true` if `data` was found, unclaimed, and free'd @@ -964,7 +974,6 @@ struct discord_message_commands { * * @param cmds the message commands handle to be initialized * @param conf pointer to @ref discord logging module - * @return the message commands handle */ void discord_message_commands_init(struct discord_message_commands *cmds, struct logconf *conf); diff --git a/include/discord-request.h b/include/discord-request.h index 8a277b0bd..5196a72dc 100644 --- a/include/discord-request.h +++ b/include/discord-request.h @@ -8,26 +8,32 @@ #ifndef DISCORD_REQUEST_H #define DISCORD_REQUEST_H +/* helper typedefs for casting */ +typedef void (*cast_done_typed)(struct discord *, + struct discord_response *, + const void *); +typedef void (*cast_init)(void *); +typedef void (*cast_cleanup)(void *); +typedef size_t (*cast_from_json)(const char *, size_t, void *); + +/* helper typedef for getting sizeof of `struct discord_ret` common fields */ +typedef struct { + DISCORD_RET_DEFAULT_FIELDS; +} discord_ret_default_fields; + #define _RET_SAFECOPY_TYPED(dest, src) \ do { \ + memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \ (dest).has_type = true; \ - (dest).done.typed = \ - (void (*)(struct discord *, void *, const void *))(src).done; \ - (dest).fail = (src).fail; \ - (dest).data = (src).data; \ - (dest).cleanup = (src).cleanup; \ - (dest).high_p = (src).high_p; \ + (dest).done.typed = (cast_done_typed)(src).done; \ (dest).sync = (src).sync; \ } while (0) #define _RET_SAFECOPY_TYPELESS(dest, src) \ do { \ + memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \ (dest).has_type = false; \ (dest).done.typeless = (src).done; \ - (dest).fail = (src).fail; \ - (dest).data = (src).data; \ - (dest).cleanup = (src).cleanup; \ - (dest).high_p = (src).high_p; \ (dest).sync = (void *)(src).sync; \ } while (0) @@ -41,10 +47,9 @@ #define DISCORD_REQ_INIT(req, type, ret) \ do { \ (req).response.size = sizeof(struct type); \ - (req).response.init = (void (*)(void *))type##_init; \ - (req).response.from_json = \ - (size_t(*)(const char *, size_t, void *))type##_from_json; \ - (req).response.cleanup = (void (*)(void *))type##_cleanup; \ + (req).response.init = (cast_init)type##_init; \ + (req).response.from_json = (cast_from_json)type##_from_json; \ + (req).response.cleanup = (cast_cleanup)type##_cleanup; \ if (ret) _RET_SAFECOPY_TYPED(req.dispatch, *ret); \ } while (0) @@ -58,9 +63,8 @@ #define DISCORD_REQ_LIST_INIT(req, type, ret) \ do { \ (req).response.size = sizeof(struct type); \ - (req).response.from_json = \ - (size_t(*)(const char *, size_t, void *))type##_from_json; \ - (req).response.cleanup = (void (*)(void *))type##_cleanup; \ + (req).response.from_json = (cast_from_json)type##_from_json; \ + (req).response.cleanup = (cast_cleanup)type##_cleanup; \ if (ret) _RET_SAFECOPY_TYPED(req.dispatch, *ret); \ } while (0) diff --git a/include/discord-templates.h b/include/discord-response.h similarity index 86% rename from include/discord-templates.h rename to include/discord-response.h index 4d2a8f3fc..beb3d91ef 100644 --- a/include/discord-templates.h +++ b/include/discord-response.h @@ -1,12 +1,18 @@ /** - * @file discord-templates.h + * @file discord-response.h * @author Cogmasters - * @brief Macro template for generating type-safe return handles for async - * requests + * @brief Generic macros for initializing a @ref discord_response and return + * handles */ -#ifndef DISCORD_TEMPLATES_H -#define DISCORD_TEMPLATES_H +#ifndef DISCORD_RESPONSE_H +#define DISCORD_RESPONSE_H + +struct discord_response { + void *data; + const void *keep; + CCORDcode code; +}; /****************************************************************************** * Templates for generating type-safe return handles for async requests @@ -14,12 +20,14 @@ #define DISCORDT_RET_DEFAULT_FIELDS \ /** optional callback to be executed on a failed request */ \ - void (*fail)(struct discord * client, CCORDcode code, void *data); \ + void (*fail)(struct discord * client, struct discord_response * resp); \ /** user arbitrary data to be passed to `done` or `fail` callbacks */ \ void *data; \ /** cleanup method to be called for `data`, once its no longer \ being referenced */ \ void (*cleanup)(struct discord * client, void *data); \ + /** Concord callback parameter the client wish to keep reference */ \ + const void *keep; \ /** if `true` then request will be prioritized over already enqueued \ requests */ \ bool high_p @@ -29,7 +37,7 @@ struct discord_ret_##_type { \ /** optional callback to be executed on a successful request */ \ void (*done)(struct discord * client, \ - void *data, \ + struct discord_response *resp, \ const struct discord_##_type *ret); \ DISCORDT_RET_DEFAULT_FIELDS; \ /** if an address is provided, then request will block the thread and \ @@ -42,7 +50,7 @@ /** @brief Request's return context */ struct discord_ret { /** optional callback to be executed on a successful request */ - void (*done)(struct discord *client, void *data); + void (*done)(struct discord *client, struct discord_response *resp); DISCORDT_RET_DEFAULT_FIELDS; /** if `true`, request will block the thread and perform on-spot */ bool sync; @@ -132,4 +140,4 @@ DISCORDT_RETURN(guild_application_command_permissions); DISCORDT_RETURN(interaction_response); /** @} DiscordAPIInteractionsReact */ -#endif /* DISCORD_TEMPLATES_H */ +#endif /* DISCORD_RESPONSE_H */ diff --git a/include/discord.h b/include/discord.h index 9068321fc..d86888391 100644 --- a/include/discord.h +++ b/include/discord.h @@ -31,7 +31,7 @@ struct discord; #ifdef CCORD_VOICE #include "discord-voice.h" #endif /* CCORD_VOICE */ -#include "discord-templates.h" +#include "discord-response.h" /** @defgroup DiscordConstants Constants * @brief Macros for constants defined by Discord diff --git a/src/channel.c b/src/channel.c index 5ba1d5816..518b26560 100644 --- a/src/channel.c +++ b/src/channel.c @@ -20,10 +20,10 @@ struct _discord_get_channel_at_pos_cxt { * discord_get_channel_at_pos() */ static void _done_get_channels(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_channels *chs) { - struct _discord_get_channel_at_pos_cxt *cxt = data; + struct _discord_get_channel_at_pos_cxt *cxt = resp->data; const struct discord_channel *found_ch = NULL; int pos; @@ -36,11 +36,15 @@ _done_get_channels(struct discord *client, } } + resp->data = cxt->ret.data; + resp->keep = cxt->ret.keep; + if (found_ch) { - if (cxt->ret.done) cxt->ret.done(client, cxt->ret.data, found_ch); + if (cxt->ret.done) cxt->ret.done(client, resp, found_ch); } else if (cxt->ret.fail) { - cxt->ret.fail(client, CCORD_BAD_PARAMETER, cxt->ret.data); + resp->code = CCORD_BAD_PARAMETER; + cxt->ret.fail(client, resp); } discord_refcounter_decr(&client->refcounter, cxt->ret.data); diff --git a/src/discord-refcount.c b/src/discord-refcount.c index 92073037e..0ae65da99 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -57,7 +57,7 @@ _discord_refvalue_cleanup(struct _discord_refvalue *value, } static struct _discord_refvalue * -_discord_refvalue_find(struct discord_refcounter *rc, void *data) +_discord_refvalue_find(struct discord_refcounter *rc, const void *data) { struct _discord_ref *ref = NULL; @@ -85,13 +85,6 @@ _discord_refvalue_init(struct discord_refcounter *rc, return _discord_refvalue_find(rc, data); } -static bool -_discord_refvalue_contains(struct discord_refcounter *rc, void *data) -{ - bool ret = chash_contains(rc, (intptr_t)data, ret, REFCOUNTER_TABLE); - return ret; -} - static void _discord_refvalue_delete(struct discord_refcounter *rc, void *data) { @@ -115,9 +108,16 @@ discord_refcounter_cleanup(struct discord_refcounter *rc) } bool -discord_refcounter_claim(struct discord_refcounter *rc, void *data) +discord_refcounter_contains(struct discord_refcounter *rc, const void *data) +{ + bool ret = chash_contains(rc, (intptr_t)data, ret, REFCOUNTER_TABLE); + return ret; +} + +bool +discord_refcounter_claim(struct discord_refcounter *rc, const void *data) { - if (_discord_refvalue_contains(rc, data)) { + if (discord_refcounter_contains(rc, data)) { struct _discord_refvalue *value = _discord_refvalue_find(rc, data); value->visits = -1; @@ -129,7 +129,7 @@ discord_refcounter_claim(struct discord_refcounter *rc, void *data) bool discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) { - if (_discord_refvalue_contains(rc, data)) { + if (discord_refcounter_contains(rc, data)) { struct _discord_refvalue *value = _discord_refvalue_find(rc, data); if (value->visits == -1) { @@ -148,7 +148,7 @@ discord_refcounter_incr(struct discord_refcounter *rc, { struct _discord_refvalue *value; - if (_discord_refvalue_contains(rc, data)) + if (discord_refcounter_contains(rc, data)) value = _discord_refvalue_find(rc, data); else value = _discord_refvalue_init(rc, data, cleanup, should_free); @@ -165,7 +165,7 @@ discord_refcounter_decr(struct discord_refcounter *rc, void *data) { struct _discord_refvalue *value = NULL; - if (_discord_refvalue_contains(rc, data)) + if (discord_refcounter_contains(rc, data)) value = _discord_refvalue_find(rc, data); if (value && value->visits != -1) { diff --git a/src/discord-rest.c b/src/discord-rest.c index 9e04ec45e..e1c251541 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -451,27 +451,31 @@ static CCORDcode _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) { struct discord *client = CLIENT(rest, rest); + struct discord_response resp; struct discord_context *cxt; int64_t wait_ms = 0LL; - CCORDcode code; bool retry; curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); + resp = (struct discord_response){ .data = cxt->dispatch.data, + .keep = cxt->dispatch.keep, + .code = CCORD_OK }; + switch (msg->data.result) { case CURLE_OK: { - struct ua_info info = { 0 }; struct ua_szbuf_readonly body; + struct ua_info info = { 0 }; ua_info_extract(cxt->conn, &info); retry = _discord_rest_get_info(rest, &info, &wait_ms); - body = ua_info_get_body(&info); - if (info.code != CCORD_OK) { - logconf_error(&client->conf, "%.*s", (int)body.size, body.start); - if (cxt->dispatch.fail) - cxt->dispatch.fail(client, info.code, cxt->dispatch.data); + resp.code = info.code; + + if (resp.code != CCORD_OK) { + logconf_error(&rest->conf, "%.*s", (int)body.size, body.start); + if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); } else if (cxt->dispatch.done.typed) { void *ret = calloc(1, cxt->response.size); @@ -484,17 +488,15 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) cxt->response.from_json(body.start, body.size, ret); if (cxt->dispatch.has_type) - cxt->dispatch.done.typed(client, cxt->dispatch.data, ret); + cxt->dispatch.done.typed(client, &resp, ret); else - cxt->dispatch.done.typeless(client, cxt->dispatch.data); + cxt->dispatch.done.typeless(client, &resp); - /* cleanup ret */ + /* cleanup ret TODO: add refcounter so that users may keep */ if (cxt->response.cleanup) cxt->response.cleanup(ret); free(ret); } - code = info.code; - discord_ratelimiter_build(&rest->ratelimiter, cxt->b, cxt->key, &info); ua_info_cleanup(&info); } break; @@ -502,17 +504,16 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) logconf_warn(&rest->conf, "Read error, will retry again"); retry = true; - code = CCORD_CURLE_INTERNAL; + resp.code = CCORD_CURLE_INTERNAL; break; default: logconf_error(&rest->conf, "(CURLE code: %d)", msg->data.result); retry = false; - code = CCORD_CURLE_INTERNAL; + resp.code = CCORD_CURLE_INTERNAL; - if (cxt->dispatch.fail) - cxt->dispatch.fail(client, code, cxt->dispatch.data); + if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); break; } @@ -522,7 +523,7 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) discord_async_recycle_context(&rest->async, cxt); - return code; + return resp.code; } CCORDcode diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 18ae115e2..5ac8631a2 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -131,13 +131,14 @@ void discord_async_recycle_context(struct discord_async *async, struct discord_context *cxt) { + struct discord_refcounter *rc = &CLIENT(async, rest.async)->refcounter; CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); curl_multi_remove_handle(async->mhandle, ehandle); if (cxt->conn) ua_conn_stop(cxt->conn); - discord_refcounter_decr(&CLIENT(async, rest.async)->refcounter, - cxt->dispatch.data); + discord_refcounter_decr(rc, (void *)cxt->dispatch.keep); + discord_refcounter_decr(rc, cxt->dispatch.data); cxt->b = NULL; cxt->body.size = 0; @@ -219,6 +220,14 @@ discord_async_start_context(struct discord_async *async, /* bucket pertaining to the request */ cxt->b = discord_bucket_get(&rest->ratelimiter, key); + if (req->dispatch.keep) { + ASSERT_S(discord_refcounter_contains(&client->refcounter, + req->dispatch.keep), + "'.keep' data must be a Concord callback parameter"); + + discord_refcounter_incr(&client->refcounter, + (void *)req->dispatch.keep, NULL, false); + } if (req->dispatch.data) discord_refcounter_incr(&client->refcounter, req->dispatch.data, req->dispatch.cleanup, false); From 21c044f7fa2f2f2dc46496e076b8f3e38b5ddf08 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 17 May 2022 15:59:37 -0300 Subject: [PATCH 050/118] chore(test): match 09aff81e --- include/discord-internal.h | 2 +- include/discord-response.h | 88 ++++++++++++++++++++------------------ test/async.c | 30 ++++++------- test/rest.c | 30 ++++++------- 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 8d23c14aa..ffce5bb89 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -84,7 +84,7 @@ /** @brief Generic request dispatcher */ struct discord_ret_dispatch { - DISCORDT_RET_DEFAULT_FIELDS; + DISCORD_RET_DEFAULT_FIELDS; /** `true` if may receive a datatype from response */ bool has_type; diff --git a/include/discord-response.h b/include/discord-response.h index beb3d91ef..bdacd9356 100644 --- a/include/discord-response.h +++ b/include/discord-response.h @@ -18,9 +18,11 @@ struct discord_response { * Templates for generating type-safe return handles for async requests ******************************************************************************/ -#define DISCORDT_RET_DEFAULT_FIELDS \ - /** optional callback to be executed on a failed request */ \ - void (*fail)(struct discord * client, struct discord_response * resp); \ +/** + * @brief Macro containing common fields for `struct discord_ret*` datatypes + * @note this exists for alignment purposes + */ +#define DISCORD_RET_DEFAULT_FIELDS \ /** user arbitrary data to be passed to `done` or `fail` callbacks */ \ void *data; \ /** cleanup method to be called for `data`, once its no longer \ @@ -30,16 +32,18 @@ struct discord_response { const void *keep; \ /** if `true` then request will be prioritized over already enqueued \ requests */ \ - bool high_p + bool high_p; \ + /** optional callback to be executed on a failed request */ \ + void (*fail)(struct discord * client, struct discord_response * resp) -#define DISCORDT_RETURN(_type) \ +#define DISCORD_RETURN(_type) \ /** @brief Request's return context */ \ struct discord_ret_##_type { \ + DISCORD_RET_DEFAULT_FIELDS; \ /** optional callback to be executed on a successful request */ \ void (*done)(struct discord * client, \ struct discord_response *resp, \ const struct discord_##_type *ret); \ - DISCORDT_RET_DEFAULT_FIELDS; \ /** if an address is provided, then request will block the thread and \ perform on-spot. \ On success the response object will be written to the address, \ @@ -49,9 +53,9 @@ struct discord_response { /** @brief Request's return context */ struct discord_ret { + DISCORD_RET_DEFAULT_FIELDS; /** optional callback to be executed on a successful request */ void (*done)(struct discord *client, struct discord_response *resp); - DISCORDT_RET_DEFAULT_FIELDS; /** if `true`, request will block the thread and perform on-spot */ bool sync; }; @@ -61,83 +65,83 @@ struct discord_ret { /** @addtogroup DiscordAPIAuditLog * @{ */ -DISCORDT_RETURN(audit_log); +DISCORD_RETURN(audit_log); /** @} DiscordAPIAuditLog */ /** @addtogroup DiscordAPIChannel * @{ */ -DISCORDT_RETURN(channel); -DISCORDT_RETURN(channels); -DISCORDT_RETURN(message); -DISCORDT_RETURN(messages); -DISCORDT_RETURN(followed_channel); -DISCORDT_RETURN(thread_members); -DISCORDT_RETURN(thread_response_body); +DISCORD_RETURN(channel); +DISCORD_RETURN(channels); +DISCORD_RETURN(message); +DISCORD_RETURN(messages); +DISCORD_RETURN(followed_channel); +DISCORD_RETURN(thread_members); +DISCORD_RETURN(thread_response_body); /** @} DiscordAPIChannel */ /** @addtogroup DiscordAPIEmoji * @{ */ -DISCORDT_RETURN(emoji); -DISCORDT_RETURN(emojis); +DISCORD_RETURN(emoji); +DISCORD_RETURN(emojis); /** @} DiscordAPIEmoji */ /** @addtogroup DiscordAPIGuild * @{ */ -DISCORDT_RETURN(guild); -DISCORDT_RETURN(guilds); -DISCORDT_RETURN(guild_preview); -DISCORDT_RETURN(guild_member); -DISCORDT_RETURN(guild_members); -DISCORDT_RETURN(ban); -DISCORDT_RETURN(bans); -DISCORDT_RETURN(role); -DISCORDT_RETURN(roles); -DISCORDT_RETURN(welcome_screen); +DISCORD_RETURN(guild); +DISCORD_RETURN(guilds); +DISCORD_RETURN(guild_preview); +DISCORD_RETURN(guild_member); +DISCORD_RETURN(guild_members); +DISCORD_RETURN(ban); +DISCORD_RETURN(bans); +DISCORD_RETURN(role); +DISCORD_RETURN(roles); +DISCORD_RETURN(welcome_screen); /** @} DiscordAPIGuild */ /** @addtogroup DiscordAPIGuildTemplate * @{ */ -DISCORDT_RETURN(guild_template); +DISCORD_RETURN(guild_template); /** @} DiscordAPIGuildTemplate */ /** @addtogroup DiscordAPIInvite * @{ */ -DISCORDT_RETURN(invite); -DISCORDT_RETURN(invites); +DISCORD_RETURN(invite); +DISCORD_RETURN(invites); /** @} DiscordAPIInvite */ /** @addtogroup DiscordAPIUser * @{ */ -DISCORDT_RETURN(user); -DISCORDT_RETURN(users); -DISCORDT_RETURN(connections); +DISCORD_RETURN(user); +DISCORD_RETURN(users); +DISCORD_RETURN(connections); /** @} DiscordAPIUser */ /** @addtogroup DiscordAPIVoice * @{ */ -DISCORDT_RETURN(voice_regions); +DISCORD_RETURN(voice_regions); /** @} DiscordAPIVoice */ /** @addtogroup DiscordAPIWebhook * @{ */ -DISCORDT_RETURN(webhook); -DISCORDT_RETURN(webhooks); +DISCORD_RETURN(webhook); +DISCORD_RETURN(webhooks); /** @} DiscordAPIWebhook */ /** @addtogroup DiscordAPIInteractionsApplicationCommand * @ingroup DiscordAPIInteractions * @{ */ -DISCORDT_RETURN(application_command); -DISCORDT_RETURN(application_commands); -DISCORDT_RETURN(application_command_permission); -DISCORDT_RETURN(application_command_permissions); -DISCORDT_RETURN(guild_application_command_permissions); +DISCORD_RETURN(application_command); +DISCORD_RETURN(application_commands); +DISCORD_RETURN(application_command_permission); +DISCORD_RETURN(application_command_permissions); +DISCORD_RETURN(guild_application_command_permissions); /** @} DiscordAPIInteractionsApplicationCommand */ /** @addtogroup DiscordAPIInteractionsReact * @ingroup DiscordAPIInteractions * @{ */ -DISCORDT_RETURN(interaction_response); +DISCORD_RETURN(interaction_response); /** @} DiscordAPIInteractionsReact */ #endif /* DISCORD_RESPONSE_H */ diff --git a/test/async.c b/test/async.c index bb89b878f..bd06de6e4 100644 --- a/test/async.c +++ b/test/async.c @@ -34,20 +34,20 @@ on_ready(struct discord *client, const struct discord_ready *event) void disconnect(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_message *msg) { - (void)data; + (void)resp; (void)msg; discord_shutdown(client); } void reconnect(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_message *msg) { - (void)data; + (void)resp; (void)msg; discord_reconnect(client, true); } @@ -96,9 +96,10 @@ on_single(struct discord *client, const struct discord_message *event) void send_batch(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_message *msg) { + (void)resp; char text[32]; for (int i = 0; i < 128; ++i) { @@ -126,8 +127,11 @@ on_spam(struct discord *client, const struct discord_message *event) } void -send_msg(struct discord *client, void *data, const struct discord_message *msg) +send_msg(struct discord *client, + struct discord_response *resp, + const struct discord_message *msg) { + (void)resp; struct global_context *g_cxt = discord_get_data(client); char text[32]; @@ -151,14 +155,14 @@ on_spam_ordered(struct discord *client, const struct discord_message *event) } void -fail_delete_channel(struct discord *client, CCORDcode code, void *data) +fail_delete_channel(struct discord *client, struct discord_response *resp) { - struct local_context *cxt = data; + const struct discord_message *event = resp->keep; discord_create_message( - client, cxt->event->channel_id, + client, event->channel_id, &(struct discord_create_message){ - .content = (char *)discord_strerror(code, client), + .content = (char *)discord_strerror(resp->code, client), }, NULL); } @@ -167,15 +171,11 @@ void on_force_error(struct discord *client, const struct discord_message *event) { const u64snowflake FAUX_CHANNEL_ID = 123; - struct local_context *cxt = malloc(sizeof *cxt); - - cxt->event = discord_claim(client, event); discord_delete_channel(client, FAUX_CHANNEL_ID, &(struct discord_ret_channel){ .fail = &fail_delete_channel, - .data = cxt, - .cleanup = &local_context_cleanup, + .keep = event, }); } diff --git a/test/rest.c b/test/rest.c index 09dbde23f..a7c5e09b4 100644 --- a/test/rest.c +++ b/test/rest.c @@ -87,26 +87,23 @@ SUITE(synchronous) } void -on_done(struct discord *client, void *data) +on_done(struct discord *client, struct discord_response *resp) { - *(CCORDcode *)data = CCORD_OK; + *(CCORDcode *)resp->data = resp->code; discord_shutdown(client); } void -on_done1(struct discord *client, void *data, const void *obj) +on_done1(struct discord *client, + struct discord_response *resp, + const void *obj) { - on_done(client, data); + (void)obj; + on_done(client, resp); } -#define DONE1_CAST(_type) void (*)(struct discord *, void *, const _type *) - -void -on_fail(struct discord *client, CCORDcode code, void *data) -{ - *(CCORDcode *)data = code; - discord_shutdown(client); -} +#define DONE1_CAST(_type) \ + void (*)(struct discord *, struct discord_response *, const _type *) TEST check_async_fetch_object(void) @@ -115,7 +112,7 @@ check_async_fetch_object(void) CCORDcode result = CCORD_OK; ret.done = (DONE1_CAST(struct discord_user))on_done1; - ret.fail = on_fail; + ret.fail = on_done; ret.data = &result; discord_get_current_user(CLIENT, &ret); @@ -132,7 +129,7 @@ check_async_fetch_array(void) CCORDcode result = CCORD_OK; ret.done = (DONE1_CAST(struct discord_guilds))on_done1; - ret.fail = on_fail; + ret.fail = on_done; ret.data = &result; discord_get_current_user_guilds(CLIENT, &ret); @@ -151,8 +148,7 @@ check_async_fetch_nothing(void *data) if (!ch_id) SKIPm("Missing channel_id from config.json"); - ret.done = on_done; - ret.fail = on_fail; + ret.fail = ret.done = on_done; ret.data = &result; discord_trigger_typing_indicator(CLIENT, ch_id, &ret); @@ -170,7 +166,7 @@ check_async_trigger_error_on_bogus_parameter(void) CCORDcode result = CCORD_OK; ret.done = (DONE1_CAST(struct discord_channel))on_done1; - ret.fail = on_fail; + ret.fail = on_done; ret.data = &result; discord_delete_channel(CLIENT, BOGUS_ID, &ret); From 308c64dc939f120f560bce96805ae7d30703b600 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 18 May 2022 14:08:09 -0300 Subject: [PATCH 051/118] refactor!(discord-refcount): move discord_refcount_incr() auto-initialization logic into discord_refcount_add_internal() and discord_refcount_add_client(), add descriptive error code for discord_refcount_incr() and discord_refcount_decr() --- core/error.h | 7 ++- include/discord-internal.h | 50 +++++++++++++----- src/discord-refcount.c | 101 +++++++++++++++++++++++-------------- 3 files changed, 107 insertions(+), 51 deletions(-) diff --git a/core/error.h b/core/error.h index 77e108742..c62a2a8a9 100644 --- a/core/error.h +++ b/core/error.h @@ -9,7 +9,8 @@ /** the error code datatype */ typedef int CCORDcode; -/** request was a success */ + +/** action was a success */ #define CCORD_OK 0 /** request wasn't succesful */ #define CCORD_HTTP_CODE -1 @@ -27,6 +28,10 @@ typedef int CCORDcode; #define CCORD_CURLM_INTERNAL -7 /** attempt to initialize globals more than once */ #define CCORD_GLOBAL_INIT -8 +/** couldn't perform action because of resource's ownership issues */ +#define CCORD_OWNERSHIP -9 +/** couldn't perform action because resource is unavailable */ +#define CCORD_UNAVAILABLE -10 /** @} ConcordError */ diff --git a/include/discord-internal.h b/include/discord-internal.h index ffce5bb89..2ea62c0bf 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -871,6 +871,35 @@ struct discord_refcounter { void discord_refcounter_init(struct discord_refcounter *rc, struct logconf *conf); +/** + * @brief Add a new internal reference to the reference counter + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data address to be referenced + * @param cleanup function for cleaning `data` resources once its + * no longer referenced + * @param should_free whether `data` cleanup should be followed by a free() + */ +void discord_refcounter_add_internal(struct discord_refcounter *rc, + void *data, + void (*cleanup)(void *data), + bool should_free); + +/** + * @brief Add a new client reference to the reference counter + * + * @param rc the handle initialized with discord_refcounter_init() + * @param data the data address to be referenced + * @param cleanup function for cleaning `data` resources once its + * no longer referenced + * @param should_free whether `data` cleanup should be followed by a free() + */ +void discord_refcounter_add_client(struct discord_refcounter *rc, + void *data, + void (*cleanup)(struct discord *client, + void *data), + bool should_free); + /** * @brief Cleanup refcounter and all user data currently held * @@ -905,7 +934,7 @@ bool discord_refcounter_claim(struct discord_refcounter *rc, const void *data); * @brief Unclaim ownership of `data` * @see discord_refcounter_claim() * - * This function will have `data` cleanup method called immediately + * This function will have `data` cleanup method be called immediately * @param rc the handle initialized with discord_refcounter_init() * @param data the data to have its ownership unclaimed * @return `true` if `data` was found, unclaimed, and free'd @@ -918,16 +947,11 @@ bool discord_refcounter_unclaim(struct discord_refcounter *rc, void *data); * * @param rc the handle initialized with discord_refcounter_init() * @param data the data to have its reference counter incremented - * @param cleanup function for cleaning `data` resources once its - * no longer referenced - * @param should_free whether `data` cleanup should be followed by a free() - * @return `true` if `data` reference count has been successfully incremented + * @retval CCORD_OK counter for `data` has been incremented + * @retval CCORD_UNAVAILABLE couldn't find a match to `data` + * @retval CCORD_OWNERSHIP `data` has been claimed by client with discord_claim() */ -bool discord_refcounter_incr(struct discord_refcounter *rc, - void *data, - void (*cleanup)(struct discord *client, - void *data), - bool should_free); +CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data); /** * @brief Decrement the reference counter for `data` @@ -937,9 +961,11 @@ bool discord_refcounter_incr(struct discord_refcounter *rc, * user-defined cleanup function * @param rc the handle initialized with discord_refcounter_init() * @param data the data to have its reference counter decremented - * @return `true` if `data` reference count has been successfully decremented + * @retval CCORD_OK counter for `data` has been decremented + * @retval CCORD_UNAVAILABLE couldn't find a match to `data` + * @retval CCORD_OWNERSHIP `data` has been claimed by client with discord_claim() */ -bool discord_refcounter_decr(struct discord_refcounter *rc, void *data); +CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data); /** @} DiscordInternalRefcount */ diff --git a/src/discord-refcount.c b/src/discord-refcount.c index 0ae65da99..f08468529 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -14,7 +14,7 @@ #define REFCOUNTER_TABLE_FREE_KEY(_key) #define REFCOUNTER_TABLE_HASH(_key, _hash) ((intptr_t)(_key)) #define REFCOUNTER_TABLE_FREE_VALUE(_value) \ - _discord_refvalue_cleanup(&_value, client) + _discord_refvalue_cleanup(rc, &_value) #define REFCOUNTER_TABLE_COMPARE(_cmp_a, _cmp_b) (_cmp_a == _cmp_b) #define REFCOUNTER_TABLE_INIT(ref, _key, _value) \ memset(&ref, 0, sizeof(ref)); \ @@ -23,11 +23,16 @@ struct _discord_refvalue { /** user arbitrary data to be retrieved at `done` or `fail` callbacks */ void *data; + /** whether cleanup expects a client parameter */ + bool expects_client; /** * cleanup for when `data` is no longer needed * @note this only has to be assigned once, it is automatically called once * `data` is no longer referenced by any callback */ - void (*cleanup)(struct discord *client, void *data); + union { + void (*client)(struct discord *client, void *data); + void (*internal)(void *data); + } cleanup; /** * `data` references count * @note if `-1` then `data` has been claimed with @@ -49,10 +54,15 @@ struct _discord_ref { }; static void -_discord_refvalue_cleanup(struct _discord_refvalue *value, - struct discord *client) +_discord_refvalue_cleanup(struct discord_refcounter *rc, + struct _discord_refvalue *value) { - if (value->cleanup) value->cleanup(client, value->data); + if (value->cleanup.client) { + if (value->expects_client) + value->cleanup.client(CLIENT(rc, refcounter), value->data); + else + value->cleanup.internal(value->data); + } if (value->should_free) free(value->data); } @@ -66,29 +76,19 @@ _discord_refvalue_find(struct discord_refcounter *rc, const void *data) return &ref->value; } -static struct _discord_refvalue * +static void _discord_refvalue_init(struct discord_refcounter *rc, void *data, - void (*cleanup)(struct discord *client, void *data), - bool should_free) + struct _discord_refvalue *init_fields) { - struct discord *client = CLIENT(rc, refcounter); - struct _discord_refvalue value = { - .data = data, - .cleanup = cleanup, - .visits = 0, - .should_free = should_free, - }; - - chash_assign(rc, (intptr_t)data, value, REFCOUNTER_TABLE); - - return _discord_refvalue_find(rc, data); + init_fields->data = data; + init_fields->visits = 1; + chash_assign(rc, (intptr_t)data, *init_fields, REFCOUNTER_TABLE); } static void _discord_refvalue_delete(struct discord_refcounter *rc, void *data) { - struct discord *client = CLIENT(rc, refcounter); chash_delete(rc, (intptr_t)data, REFCOUNTER_TABLE); } @@ -103,7 +103,6 @@ discord_refcounter_init(struct discord_refcounter *rc, struct logconf *conf) void discord_refcounter_cleanup(struct discord_refcounter *rc) { - struct discord *client = CLIENT(rc, refcounter); __chash_free(rc, REFCOUNTER_TABLE); } @@ -140,39 +139,65 @@ discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) return false; } -bool -discord_refcounter_incr(struct discord_refcounter *rc, - void *data, - void (*cleanup)(struct discord *client, void *data), - bool should_free) +void +discord_refcounter_add_internal(struct discord_refcounter *rc, + void *data, + void (*cleanup)(void *data), + bool should_free) +{ + struct _discord_refvalue init = { + .expects_client = false, + .cleanup.internal = cleanup, + .should_free = should_free, + }; + _discord_refvalue_init(rc, data, &init); +} + +void +discord_refcounter_add_client(struct discord_refcounter *rc, + void *data, + void (*cleanup)(struct discord *client, + void *data), + bool should_free) +{ + struct _discord_refvalue init = { + .expects_client = true, + .cleanup.client = cleanup, + .should_free = should_free, + }; + _discord_refvalue_init(rc, data, &init); +} + +CCORDcode +discord_refcounter_incr(struct discord_refcounter *rc, void *data) { struct _discord_refvalue *value; - if (discord_refcounter_contains(rc, data)) - value = _discord_refvalue_find(rc, data); - else - value = _discord_refvalue_init(rc, data, cleanup, should_free); + if (!discord_refcounter_contains(rc, data)) return CCORD_UNAVAILABLE; + + value = _discord_refvalue_find(rc, data); if (value->visits != -1) { ++value->visits; - return true; + return CCORD_OK; } - return false; + return CCORD_OWNERSHIP; } -bool +CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data) { struct _discord_refvalue *value = NULL; - if (discord_refcounter_contains(rc, data)) - value = _discord_refvalue_find(rc, data); + if (!discord_refcounter_contains(rc, data)) return CCORD_UNAVAILABLE; + + value = _discord_refvalue_find(rc, data); - if (value && value->visits != -1) { + if (value->visits != -1) { if (0 == --value->visits) { _discord_refvalue_delete(rc, data); } - return true; + return CCORD_OK; } - return false; + return CCORD_OWNERSHIP; } From ff62bc16deec172a1b1f0b285306f5efe9c8d920 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 18 May 2022 14:08:52 -0300 Subject: [PATCH 052/118] fix: match 308c64dc --- include/discord-request.h | 10 ++++---- src/channel.c | 10 +++++--- src/discord-gateway_dispatch.c | 26 ++++++++++----------- src/discord-messagecommands.c | 42 ++++++++++++++++++---------------- src/discord-rest.c | 38 +++++++++++++++++------------- src/discord-rest_async.c | 28 +++++++++++++++-------- 6 files changed, 86 insertions(+), 68 deletions(-) diff --git a/include/discord-request.h b/include/discord-request.h index 5196a72dc..8e6e783e2 100644 --- a/include/discord-request.h +++ b/include/discord-request.h @@ -21,7 +21,7 @@ typedef struct { DISCORD_RET_DEFAULT_FIELDS; } discord_ret_default_fields; -#define _RET_SAFECOPY_TYPED(dest, src) \ +#define _RET_COPY_TYPED(dest, src) \ do { \ memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \ (dest).has_type = true; \ @@ -29,7 +29,7 @@ typedef struct { (dest).sync = (src).sync; \ } while (0) -#define _RET_SAFECOPY_TYPELESS(dest, src) \ +#define _RET_COPY_TYPELESS(dest, src) \ do { \ memcpy(&(dest), &(src), sizeof(discord_ret_default_fields)); \ (dest).has_type = false; \ @@ -50,7 +50,7 @@ typedef struct { (req).response.init = (cast_init)type##_init; \ (req).response.from_json = (cast_from_json)type##_from_json; \ (req).response.cleanup = (cast_cleanup)type##_cleanup; \ - if (ret) _RET_SAFECOPY_TYPED(req.dispatch, *ret); \ + if (ret) _RET_COPY_TYPED(req.dispatch, *ret); \ } while (0) /** @@ -65,7 +65,7 @@ typedef struct { (req).response.size = sizeof(struct type); \ (req).response.from_json = (cast_from_json)type##_from_json; \ (req).response.cleanup = (cast_cleanup)type##_cleanup; \ - if (ret) _RET_SAFECOPY_TYPED(req.dispatch, *ret); \ + if (ret) _RET_COPY_TYPED(req.dispatch, *ret); \ } while (0) /** @@ -75,6 +75,6 @@ typedef struct { * @param ret request attributes */ #define DISCORD_REQ_BLANK_INIT(req, ret) \ - if (ret) _RET_SAFECOPY_TYPELESS(req.dispatch, *ret) + if (ret) _RET_COPY_TYPELESS(req.dispatch, *ret) #endif /* DISCORD_REQUEST_H */ diff --git a/src/channel.c b/src/channel.c index 518b26560..ff926f00c 100644 --- a/src/channel.c +++ b/src/channel.c @@ -73,9 +73,13 @@ discord_get_channel_at_pos(struct discord *client, current_ret.fail = ret->fail; current_ret.data = cxt; - if (ret->data) - discord_refcounter_incr(&client->refcounter, ret->data, ret->cleanup, - true); + if (ret->data + && CCORD_UNAVAILABLE + == discord_refcounter_incr(&client->refcounter, ret->data)) + { + discord_refcounter_add_client(&client->refcounter, ret->data, + ret->cleanup, false); + } /* TODO: fetch channel via caching, and return if results are non-existent */ diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index f45c0ebc4..5c8423139 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -126,12 +126,6 @@ static const struct { INIT(discord_webhooks_update, webhooks_update), }; -static void -_discord_gateway_dispatch_cleanup(struct discord *client, void *data) -{ - dispatch[client->gw.payload.event].cleanup(data); -} - void discord_gateway_dispatch(struct discord_gateway *gw) { @@ -141,22 +135,26 @@ discord_gateway_dispatch(struct discord_gateway *gw) switch (event) { case DISCORD_EV_MESSAGE_CREATE: if (discord_message_commands_try_perform(&client->commands, - &gw->payload)) - { + &gw->payload)) { return; } /* fall-through */ default: if (gw->cbs[event]) { - void *data = calloc(1, dispatch[event].size); + void *event_data = calloc(1, dispatch[event].size); dispatch[event].from_jsmnf(gw->payload.data, gw->payload.json, - data); + event_data); - discord_refcounter_incr(&client->refcounter, data, - _discord_gateway_dispatch_cleanup, true); - gw->cbs[event](client, data); - discord_refcounter_decr(&client->refcounter, data); + if (CCORD_UNAVAILABLE + == discord_refcounter_incr(&client->refcounter, event_data)) + { + discord_refcounter_add_internal(&client->refcounter, + event_data, + dispatch[event].cleanup, true); + } + gw->cbs[event](client, event_data); + discord_refcounter_decr(&client->refcounter, event_data); } break; case DISCORD_EV_NONE: diff --git a/src/discord-messagecommands.c b/src/discord-messagecommands.c index 8d20af043..c5eeff1c9 100644 --- a/src/discord-messagecommands.c +++ b/src/discord-messagecommands.c @@ -111,12 +111,10 @@ discord_message_commands_set_prefix(struct discord_message_commands *cmds, } static void -_discord_message_cleanup_v(struct discord *client, void *message) +_discord_message_cleanup_v(void *p_message) { - (void)client; - - discord_message_cleanup(message); - free(message); + discord_message_cleanup(p_message); + free(p_message); } /** return true in case user command has been triggered */ @@ -134,17 +132,17 @@ discord_message_commands_try_perform(struct discord_message_commands *cmds, cmds->prefix.size)) { struct discord *client = CLIENT(cmds, commands); - struct discord_message *event = calloc(1, sizeof *event); + struct discord_message *event_data = calloc(1, sizeof *event_data); discord_ev_message callback = NULL; struct ccord_szbuf command; char *tmp; - discord_message_from_jsmnf(payload->data, payload->json, event); + discord_message_from_jsmnf(payload->data, payload->json, event_data); - command.start = event->content + cmds->prefix.size; + command.start = event_data->content + cmds->prefix.size; command.size = strcspn(command.start, " \n\t\r"); - tmp = event->content; + tmp = event_data->content; /* match command to its callback */ if (!(callback = discord_message_commands_find(cmds, command.start, @@ -152,8 +150,7 @@ discord_message_commands_try_perform(struct discord_message_commands *cmds, { /* couldn't match command to callback, get fallback if available */ if (!cmds->prefix.size || !cmds->fallback) { - discord_message_cleanup(event); - free(event); + _discord_message_cleanup_v(event_data); return false; } command.size = 0; @@ -161,17 +158,22 @@ discord_message_commands_try_perform(struct discord_message_commands *cmds, } /* skip blank characters after command */ - if (event->content) { - event->content = command.start + command.size; - while (*event->content && isspace((int)event->content[0])) - ++event->content; + if (event_data->content) { + event_data->content = command.start + command.size; + while (*event_data->content + && isspace((int)event_data->content[0])) + ++event_data->content; } - discord_refcounter_incr(&client->refcounter, event, - _discord_message_cleanup_v, false); - callback(client, event); - event->content = tmp; /* retrieve original ptr */ - discord_refcounter_decr(&client->refcounter, event); + if (CCORD_UNAVAILABLE + == discord_refcounter_incr(&client->refcounter, event_data)) + { + discord_refcounter_add_internal(&client->refcounter, event_data, + _discord_message_cleanup_v, false); + } + callback(client, event_data); + event_data->content = tmp; /* retrieve original ptr */ + discord_refcounter_decr(&client->refcounter, event_data); return true; } diff --git a/src/discord-rest.c b/src/discord-rest.c index e1c251541..27e464790 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -478,23 +478,29 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); } else if (cxt->dispatch.done.typed) { - void *ret = calloc(1, cxt->response.size); - - /* initialize ret */ - if (cxt->response.init) cxt->response.init(ret); - - /* populate ret */ - if (cxt->response.from_json) - cxt->response.from_json(body.start, body.size, ret); - - if (cxt->dispatch.has_type) - cxt->dispatch.done.typed(client, &resp, ret); - else + if (!cxt->dispatch.has_type) { cxt->dispatch.done.typeless(client, &resp); - - /* cleanup ret TODO: add refcounter so that users may keep */ - if (cxt->response.cleanup) cxt->response.cleanup(ret); - free(ret); + } + else { + void *ret_data = calloc(1, cxt->response.size); + + /* initialize ret_data */ + if (cxt->response.init) cxt->response.init(ret_data); + + /* populate ret_data */ + if (cxt->response.from_json) + cxt->response.from_json(body.start, body.size, ret_data); + + if (CCORD_UNAVAILABLE + == discord_refcounter_incr(&client->refcounter, ret_data)) + { + discord_refcounter_add_internal( + &client->refcounter, ret_data, cxt->response.cleanup, + true); + } + cxt->dispatch.done.typed(client, &resp, ret_data); + discord_refcounter_decr(&client->refcounter, ret_data); + } } discord_ratelimiter_build(&rest->ratelimiter, cxt->b, cxt->key, &info); diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 5ac8631a2..5f012120a 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -137,8 +137,12 @@ discord_async_recycle_context(struct discord_async *async, curl_multi_remove_handle(async->mhandle, ehandle); if (cxt->conn) ua_conn_stop(cxt->conn); - discord_refcounter_decr(rc, (void *)cxt->dispatch.keep); - discord_refcounter_decr(rc, cxt->dispatch.data); + if (cxt->dispatch.keep) { + discord_refcounter_decr(rc, (void *)cxt->dispatch.keep); + } + if (cxt->dispatch.data) { + discord_refcounter_decr(rc, cxt->dispatch.data); + } cxt->b = NULL; cxt->body.size = 0; @@ -221,16 +225,20 @@ discord_async_start_context(struct discord_async *async, cxt->b = discord_bucket_get(&rest->ratelimiter, key); if (req->dispatch.keep) { - ASSERT_S(discord_refcounter_contains(&client->refcounter, - req->dispatch.keep), - "'.keep' data must be a Concord callback parameter"); + CCORDcode code = discord_refcounter_incr(&client->refcounter, + (void *)req->dispatch.keep); - discord_refcounter_incr(&client->refcounter, - (void *)req->dispatch.keep, NULL, false); + ASSERT_S(code == CCORD_OK, + "'.keep' data must be a Concord callback parameter"); + } + if (req->dispatch.data + && CCORD_UNAVAILABLE + == discord_refcounter_incr(&client->refcounter, + req->dispatch.data)) + { + discord_refcounter_add_client(&client->refcounter, req->dispatch.data, + req->dispatch.cleanup, false); } - if (req->dispatch.data) - discord_refcounter_incr(&client->refcounter, req->dispatch.data, - req->dispatch.cleanup, false); io_poller_curlm_enable_perform(client->io_poller, async->mhandle); From 98508a45af20a52efa81a8904d669d5a0fb49db9 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 18 May 2022 14:37:12 -0300 Subject: [PATCH 053/118] chore(examples): match #66 syntax changes --- examples/audit-log.c | 12 ++++++------ examples/channel.c | 36 ++++++++++++++++++------------------ examples/emoji.c | 24 ++++++++++++------------ examples/guild-template.c | 16 ++++++++-------- examples/guild.c | 25 +++++++++++++------------ examples/invite.c | 14 ++++++++------ examples/pin.c | 10 +++++----- examples/reaction.c | 12 ++++++------ examples/slash-commands2.c | 4 ++-- examples/voice-join.c | 30 +++++++++++++++--------------- 10 files changed, 93 insertions(+), 90 deletions(-) diff --git a/examples/audit-log.c b/examples/audit-log.c index 519c0a912..1a8dac7f6 100644 --- a/examples/audit-log.c +++ b/examples/audit-log.c @@ -58,10 +58,10 @@ log_on_guild_member_remove(struct discord *client, void done(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_audit_log *audit_log) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; if (!audit_log->audit_log_entries || !audit_log->audit_log_entries->size) { log_warn("No audit log entries found!"); @@ -80,12 +80,12 @@ done(struct discord *client, } void -fail(struct discord *client, CCORDcode code, void *data) +fail(struct discord *client, struct discord_response *resp) { - (void)data; + (void)resp; log_error("Couldn't retrieve audit log: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); } void @@ -97,7 +97,7 @@ on_audit_channel_create(struct discord *client, struct discord_ret_audit_log ret = { .done = &done, .fail = &fail, - .data = event, + .keep = event, }; struct discord_get_guild_audit_log params = { .user_id = event->author->id, diff --git a/examples/channel.c b/examples/channel.c index a9d58f0ad..2eb82b060 100644 --- a/examples/channel.c +++ b/examples/channel.c @@ -102,7 +102,7 @@ on_channel_delete_this(struct discord *client, void done_get_channel_invites(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_invites *invites) { if (!invites->size) { @@ -110,7 +110,7 @@ done_get_channel_invites(struct discord *client, return; } - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), "%d invite links created.", invites->size); @@ -119,11 +119,10 @@ done_get_channel_invites(struct discord *client, } void -fail_get_channel_invites(struct discord *client, CCORDcode code, void *data) +fail_get_channel_invites(struct discord *client, struct discord_response *resp) { - (void)data; - - log_info("Couldn't fetch invites: %s", discord_strerror(code, client)); + log_info("Couldn't fetch invites: %s", + discord_strerror(resp->code, client)); } void @@ -135,17 +134,17 @@ on_channel_get_invites(struct discord *client, struct discord_ret_invites ret = { .done = &done_get_channel_invites, .fail = &fail_get_channel_invites, - .data = event, + .keep = event, }; discord_get_channel_invites(client, event->channel_id, &ret); } void done_create_channel_invite(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_invite *invite) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "https://discord.gg/%s", invite->code); @@ -155,9 +154,10 @@ done_create_channel_invite(struct discord *client, } void -fail_create_channel_invite(struct discord *client, CCORDcode code, void *data) +fail_create_channel_invite(struct discord *client, + struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Couldn't create invite", @@ -174,17 +174,17 @@ on_channel_create_invite(struct discord *client, struct discord_ret_invite ret = { .done = &done_create_channel_invite, .fail = &fail_create_channel_invite, - .data = event, + .keep = event, }; discord_create_channel_invite(client, event->channel_id, NULL, &ret); } void done_start_thread(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_channel *thread) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[1024]; snprintf(text, sizeof(text), "Created thread <#%" PRIu64 ">", @@ -195,13 +195,13 @@ done_start_thread(struct discord *client, } void -fail_start_thread(struct discord *client, CCORDcode code, void *data) +fail_start_thread(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[1024]; snprintf(text, sizeof(text), "Couldn't create thread: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); @@ -216,7 +216,7 @@ on_channel_start_thread(struct discord *client, struct discord_ret_channel ret = { .done = &done_start_thread, .fail = &fail_start_thread, - .data = event, + .keep = event, }; if (event->message_reference) { diff --git a/examples/emoji.c b/examples/emoji.c index 94015a601..b03970e8a 100644 --- a/examples/emoji.c +++ b/examples/emoji.c @@ -25,10 +25,10 @@ on_ready(struct discord *client, const struct discord_ready *event) void done_list_guild_emojis(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_emojis *emojis) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[2000] = ""; if (!emojis->size) { @@ -67,13 +67,13 @@ done_list_guild_emojis(struct discord *client, } void -fail_list_guild_emojis(struct discord *client, CCORDcode code, void *data) +fail_list_guild_emojis(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "Couldn't fetch guild emojis: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); @@ -88,17 +88,17 @@ on_list_guild_emojis(struct discord *client, struct discord_ret_emojis ret = { .done = &done_list_guild_emojis, .fail = &fail_list_guild_emojis, - .data = event, + .keep = event, }; discord_list_guild_emojis(client, event->guild_id, &ret); } void done_get_guild_emoji(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_emoji *emoji) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), "Here you go: <%s:%s:%" PRIu64 ">", @@ -109,13 +109,13 @@ done_get_guild_emoji(struct discord *client, } void -fail_get_guild_emoji(struct discord *client, CCORDcode code, void *data) +fail_get_guild_emoji(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "Unknown emoji: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); @@ -134,7 +134,7 @@ on_get_guild_emoji(struct discord *client, const struct discord_message *event) struct discord_ret_emoji ret = { .done = &done_get_guild_emoji, .fail = &fail_get_guild_emoji, - .data = event, + .keep = event, }; discord_get_guild_emoji(client, event->guild_id, emoji_id, &ret); } diff --git a/examples/guild-template.c b/examples/guild-template.c index 2217edb49..de7dd74a8 100644 --- a/examples/guild-template.c +++ b/examples/guild-template.c @@ -27,10 +27,10 @@ on_ready(struct discord *client, const struct discord_ready *event) void done(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_guild_template *template) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), @@ -43,13 +43,13 @@ done(struct discord *client, } void -fail(struct discord *client, CCORDcode code, void *data) +fail(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[DISCORD_MAX_MESSAGE_LEN]; snprintf(text, sizeof(text), "Couldn't perform operation: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); @@ -62,7 +62,7 @@ on_get_guild_template(struct discord *client, struct discord_ret_guild_template ret = { .done = &done, .fail = &fail, - .data = event, + .keep = event, }; discord_get_guild_template(client, event->content, &ret); } @@ -74,7 +74,7 @@ on_create_guild_template(struct discord *client, struct discord_ret_guild_template ret = { .done = &done, .fail = &fail, - .data = event, + .keep = event, }; struct discord_create_guild_template params = { @@ -92,7 +92,7 @@ on_sync_guild_template(struct discord *client, struct discord_ret_guild_template ret = { .done = &done, .fail = &fail, - .data = event, + .keep = event, }; discord_sync_guild_template(client, event->guild_id, event->content, &ret); diff --git a/examples/guild.c b/examples/guild.c index 64a969a71..cfac0d744 100644 --- a/examples/guild.c +++ b/examples/guild.c @@ -124,7 +124,7 @@ on_role_member_remove(struct discord *client, void done_get_guild_roles(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_roles *roles) { char text[DISCORD_MAX_MESSAGE_LEN]; @@ -152,10 +152,10 @@ done_get_guild_roles(struct discord *client, } void -fail_get_guild_roles(struct discord *client, CCORDcode code, void *data) +fail_get_guild_roles(struct discord *client, struct discord_response *resp) { log_error("Couldn't fetch guild roles: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); } void @@ -172,18 +172,19 @@ on_role_list(struct discord *client, const struct discord_message *event) void done_get_guild_member(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_guild_member *member) { + (void)resp; log_info("Member %s (%" PRIu64 ") found!", member->user->username, member->user->id); } void -fail_get_guild_member(struct discord *client, CCORDcode code, void *data) +fail_get_guild_member(struct discord *client, struct discord_response *resp) { log_error("Couldn't fetch guild member: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); } void @@ -208,10 +209,10 @@ on_member_get(struct discord *client, const struct discord_message *event) void done_get_guild_channels(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_channels *channels) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[DISCORD_MAX_MESSAGE_LEN]; char *cur = text; @@ -238,13 +239,13 @@ done_get_guild_channels(struct discord *client, } void -fail_get_guild_channels(struct discord *client, CCORDcode code, void *data) +fail_get_guild_channels(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "Couldn't fetch guild channels: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); @@ -258,7 +259,7 @@ on_channels_get(struct discord *client, const struct discord_message *event) struct discord_ret_channels ret = { .done = &done_get_guild_channels, .fail = &fail_get_guild_channels, - .data = event, + .keep = event, }; discord_get_guild_channels(client, event->guild_id, &ret); } diff --git a/examples/invite.c b/examples/invite.c index 0bcec228d..683e8a88d 100644 --- a/examples/invite.c +++ b/examples/invite.c @@ -25,9 +25,11 @@ on_ready(struct discord *client, const struct discord_ready *event) } void -done(struct discord *client, void *data, const struct discord_invite *invite) +done(struct discord *client, + struct discord_response *resp, + const struct discord_invite *invite) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "Success: https://discord.gg/%s", @@ -38,9 +40,9 @@ done(struct discord *client, void *data, const struct discord_invite *invite) } void -fail(struct discord *client, CCORDcode code, void *data) +fail(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Couldn't perform operation." @@ -56,7 +58,7 @@ on_invite_get(struct discord *client, const struct discord_message *event) struct discord_ret_invite ret = { .done = &done, .fail = &fail, - .data = event, + .keep = event, }; struct discord_get_invite params = { @@ -74,7 +76,7 @@ on_invite_delete(struct discord *client, const struct discord_message *event) struct discord_ret_invite ret = { .done = &done, .fail = &fail, - .data = event, + .keep = event, }; discord_delete_invite(client, event->content, &ret); } diff --git a/examples/pin.c b/examples/pin.c index e658aac87..ae3dbd0d8 100644 --- a/examples/pin.c +++ b/examples/pin.c @@ -64,10 +64,10 @@ on_unpin(struct discord *client, const struct discord_message *event) void done_get_pins(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_messages *msgs) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[2000] = "No pins on channel"; char *cur = text; @@ -87,9 +87,9 @@ done_get_pins(struct discord *client, } void -fail_get_pins(struct discord *client, CCORDcode code, void *data) +fail_get_pins(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[2000] = ""; snprintf(text, sizeof(text), @@ -108,7 +108,7 @@ on_get_pins(struct discord *client, const struct discord_message *event) struct discord_ret_messages ret = { .done = &done_get_pins, .fail = &fail_get_pins, - .data = event, + .keep = event, }; discord_get_pinned_messages(client, event->channel_id, &ret); } diff --git a/examples/reaction.c b/examples/reaction.c index fbab68ab8..b8b425087 100644 --- a/examples/reaction.c +++ b/examples/reaction.c @@ -36,10 +36,10 @@ on_ready(struct discord *client, const struct discord_ready *event) void done_get_users(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_users *users) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[2000]; if (!users->size) { @@ -62,13 +62,13 @@ done_get_users(struct discord *client, } void -fail_get_users(struct discord *client, CCORDcode code, void *data) +fail_get_users(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "Couldn't fetch reactions: %s", - discord_strerror(code, client)); + discord_strerror(resp->code, client)); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); @@ -82,7 +82,7 @@ on_get_users(struct discord *client, const struct discord_message *event) struct discord_ret_users ret = { .done = &done_get_users, .fail = &fail_get_users, - .data = event, + .keep = event, }; struct discord_get_reactions params = { .limit = 25 }; diff --git a/examples/slash-commands2.c b/examples/slash-commands2.c index 137018e29..2f798a0dc 100644 --- a/examples/slash-commands2.c +++ b/examples/slash-commands2.c @@ -63,9 +63,9 @@ log_on_app_delete(struct discord *client, } void -fail_interaction_create(struct discord *client, CCORDcode code, void *data) +fail_interaction_create(struct discord *client, struct discord_response *resp) { - log_error("%s", discord_strerror(code, client)); + log_error("%s", discord_strerror(resp->code, client)); } void diff --git a/examples/voice-join.c b/examples/voice-join.c index fdbc09d55..06d2f7c8e 100644 --- a/examples/voice-join.c +++ b/examples/voice-join.c @@ -38,10 +38,10 @@ log_on_voice_state_update(struct discord *client, void done_list_voice_regions(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_voice_regions *regions) { - struct discord_message *event = data; + struct discord_message *event = resp->keep; for (int i = 0; i < regions->size; ++i) { struct discord_create_message params = { .content = @@ -51,9 +51,9 @@ done_list_voice_regions(struct discord *client, } void -fail_list_voice_regions(struct discord *client, CCORDcode code, void *data) +fail_list_voice_regions(struct discord *client, struct discord_response *resp) { - struct discord_message *event = data; + struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Could not fetch voice regions" @@ -78,10 +78,10 @@ on_list_voice_regions(struct discord *client, void done_get_vchannel_position(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_channel *vchannel) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; discord_voice_join(client, event->guild_id, vchannel->id, false, false); @@ -94,9 +94,10 @@ done_get_vchannel_position(struct discord *client, } void -fail_get_vchannel_position(struct discord *client, CCORDcode code, void *data) +fail_get_vchannel_position(struct discord *client, + struct discord_response *resp) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Invalid channel position" }; @@ -115,7 +116,7 @@ on_voice_join(struct discord *client, const struct discord_message *event) struct discord_ret_channel ret = { .done = &done_get_vchannel_position, .fail = &fail_get_vchannel_position, - .data = event, + .keep = event, }; discord_get_channel_at_pos(client, event->guild_id, @@ -125,10 +126,10 @@ on_voice_join(struct discord *client, const struct discord_message *event) void done_disconnect_guild_member(struct discord *client, - void *data, + struct discord_response *resp, const struct discord_guild_member *member) { - struct discord_message *event = data; + const struct discord_message *event = resp->keep; char text[256]; snprintf(text, sizeof(text), "<@!%" PRIu64 "> has been kicked from VC", @@ -140,10 +141,9 @@ done_disconnect_guild_member(struct discord *client, void fail_disconnect_guild_member(struct discord *client, - CCORDcode code, - void *data) + struct discord_response *resp) { - struct discord_message *event = data; + struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Couldn't disconnect user from voice channel" @@ -169,7 +169,7 @@ on_voice_kick(struct discord *client, const struct discord_message *event) struct discord_ret_guild_member ret = { .done = &done_disconnect_guild_member, .fail = &fail_disconnect_guild_member, - .data = event, + .keep = event, }; discord_disconnect_guild_member(client, event->guild_id, user_id, From 2907422d21b28b62c8e8297a6a14e4e7e690e6ca Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 18 May 2022 22:28:32 -0300 Subject: [PATCH 054/118] fix(examples/channel.c): print the correct channel --- examples/channel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/channel.c b/examples/channel.c index 2eb82b060..20bcde79b 100644 --- a/examples/channel.c +++ b/examples/channel.c @@ -187,8 +187,7 @@ done_start_thread(struct discord *client, const struct discord_message *event = resp->keep; char text[1024]; - snprintf(text, sizeof(text), "Created thread <#%" PRIu64 ">", - event->channel_id); + snprintf(text, sizeof(text), "Created thread <#%" PRIu64 ">", thread->id); struct discord_create_message params = { .content = text }; discord_create_message(client, event->channel_id, ¶ms, NULL); From 424ae3f32be2b180f3603057414321df78864c42 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 19 May 2022 16:48:15 -0300 Subject: [PATCH 055/118] wip: fixing race-conditions when using async and sync simultaneously --- include/discord-internal.h | 19 +--- src/discord-rest.c | 23 ++-- src/discord-rest_async.c | 5 +- src/discord-rest_ratelimit.c | 5 +- test/Makefile | 2 +- test/async.c | 213 ----------------------------------- test/{sync.c => racecond.c} | 167 +++++++++++++-------------- 7 files changed, 106 insertions(+), 328 deletions(-) delete mode 100644 test/async.c rename test/{sync.c => racecond.c} (66%) diff --git a/include/discord-internal.h b/include/discord-internal.h index 2ea62c0bf..a8eb957f6 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -260,12 +260,6 @@ struct discord_context *discord_async_start_context( * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ -/** - * @brief Value assigned to @ref discord_bucket `busy` field in case it's - * being timed-out - */ -#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) - /** @brief The ratelimiter struct for handling ratelimiting */ struct discord_ratelimiter { /** DISCORD_RATELIMIT logging module */ @@ -375,11 +369,8 @@ struct discord_bucket { pthread_mutex_t lock; /** pending requests */ QUEUE(struct discord_context) pending_queue; - /** - * pointer to currently performing busy request (if any) - * @note `NULL` if free or @ref DISCORD_BUCKET_TIMEOUT if being ratelimited - */ - struct discord_context *busy; + /** pointer to currently performing busy context (if asynchronous) */ + struct discord_context *performing_cxt; }; /** @@ -949,7 +940,8 @@ bool discord_refcounter_unclaim(struct discord_refcounter *rc, void *data); * @param data the data to have its reference counter incremented * @retval CCORD_OK counter for `data` has been incremented * @retval CCORD_UNAVAILABLE couldn't find a match to `data` - * @retval CCORD_OWNERSHIP `data` has been claimed by client with discord_claim() + * @retval CCORD_OWNERSHIP `data` has been claimed by client with + * discord_claim() */ CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data); @@ -963,7 +955,8 @@ CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data); * @param data the data to have its reference counter decremented * @retval CCORD_OK counter for `data` has been decremented * @retval CCORD_UNAVAILABLE couldn't find a match to `data` - * @retval CCORD_OWNERSHIP `data` has been claimed by client with discord_claim() + * @retval CCORD_OWNERSHIP `data` has been claimed by client with + * discord_claim() */ CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data); diff --git a/src/discord-rest.c b/src/discord-rest.c index 27e464790..23e2da128 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -188,8 +188,8 @@ _discord_rest_get_info(struct discord_rest *rest, if (*wait_ms < 0) *wait_ms = 0; logconf_warn(&rest->conf, - "429 %s RATELIMITING (wait: %" PRId64 " ms) : %.*s", - is_global ? "GLOBAL" : "", *wait_ms, message.len, + "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", + is_global ? "GLOBAL " : "", *wait_ms, message.len, body.start + message.pos); return true; @@ -425,7 +425,9 @@ _discord_rest_try_add_request(struct discord_ratelimiter *rl, struct discord_bucket *b) { /* skip if bucket is busy performing */ - if (b->busy) return; + if (pthread_mutex_trylock(&b->lock) != 0) { + return; + } if (!b->remaining) { discord_bucket_try_timeout(rl, b); @@ -436,11 +438,15 @@ _discord_rest_try_add_request(struct discord_ratelimiter *rl, _discord_rest_add_request(rest, b); } + else { + pthread_mutex_unlock(&b->lock); + } } static CCORDcode _discord_rest_check_pending(struct discord_rest *rest) { + /* TODO: replace foreach with a mechanism that loops only busy buckets */ discord_ratelimiter_foreach_bucket(&rest->ratelimiter, &_discord_rest_try_add_request); /* FIXME: redundant return value (constant) */ @@ -525,10 +531,12 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) } /* enqueue request for retry or recycle */ - cxt->b->busy = NULL; if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) discord_async_recycle_context(&rest->async, cxt); + cxt->b->performing_cxt = NULL; + pthread_mutex_unlock(&cxt->b->lock); + return resp.code; } @@ -568,12 +576,7 @@ _discord_rest_stop_bucket(struct discord_ratelimiter *rl, &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; /* cancel busy transfer */ - if (b->busy && b->busy != DISCORD_BUCKET_TIMEOUT) { - struct discord_context *cxt = b->busy; - - b->busy = NULL; - discord_async_recycle_context(async, cxt); - } + discord_async_recycle_context(async, b->performing_cxt); /* cancel pending tranfers */ QUEUE_ADD(async->idle_contexts, &b->pending_queue); diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 5f012120a..9033f8535 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -93,7 +93,7 @@ discord_async_add_request(struct discord_async *async, CURLMcode mcode; cxt->conn = conn; - cxt->b->busy = cxt; + cxt->b->performing_cxt = cxt; /* link 'cxt' to 'ehandle' for easy retrieval */ curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); @@ -131,6 +131,8 @@ void discord_async_recycle_context(struct discord_async *async, struct discord_context *cxt) { + if (!cxt) return; + struct discord_refcounter *rc = &CLIENT(async, rest.async)->refcounter; CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); @@ -144,7 +146,6 @@ discord_async_recycle_context(struct discord_async *async, discord_refcounter_decr(rc, cxt->dispatch.data); } - cxt->b = NULL; cxt->body.size = 0; cxt->method = 0; *cxt->endpoint = '\0'; diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 1cc7e64bc..76dbfec8a 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -251,8 +251,9 @@ _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) (void)client; struct discord_bucket *b = timer->data; - b->busy = NULL; /* bucket is no longer busy */ b->remaining = 1; + + pthread_mutex_unlock(&b->lock); } void @@ -262,8 +263,6 @@ discord_bucket_try_timeout(struct discord_ratelimiter *rl, struct discord *client = CLIENT(rl, rest.ratelimiter); const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); - b->busy = DISCORD_BUCKET_TIMEOUT; - discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); logconf_info(&rl->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", diff --git a/test/Makefile b/test/Makefile index dabc3dd1e..141e463da 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,7 @@ CORE_DIR = $(TOP)/core INCLUDE_DIR = $(TOP)/include GENCODECS_DIR = $(TOP)/gencodecs -TEST_DISCORD = rest sync async timeout +TEST_DISCORD = racecond rest timeout TEST_CORE = user-agent websockets EXES := $(TEST_DISCORD) $(TEST_GITHUB) $(TEST_CORE) diff --git a/test/async.c b/test/async.c deleted file mode 100644 index bd06de6e4..000000000 --- a/test/async.c +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include /* strcmp() */ -#include -#include - -#include "discord.h" - -struct global_context { - u64snowflake channel_id; - unsigned long long counter; -}; - -struct local_context { - struct discord *client; - const struct discord_message *event; -}; - -void -local_context_cleanup(struct discord *client, void *data) -{ - struct local_context *cxt = data; - - discord_unclaim(client, cxt->event); - free(data); -} - -void -on_ready(struct discord *client, const struct discord_ready *event) -{ - log_info("Succesfully connected to Discord as %s#%s!", - event->user->username, event->user->discriminator); -} - -void -disconnect(struct discord *client, - struct discord_response *resp, - const struct discord_message *msg) -{ - (void)resp; - (void)msg; - discord_shutdown(client); -} - -void -reconnect(struct discord *client, - struct discord_response *resp, - const struct discord_message *msg) -{ - (void)resp; - (void)msg; - discord_reconnect(client, true); -} - -void -on_disconnect(struct discord *client, const struct discord_message *event) -{ - if (event->author->bot) return; - - discord_create_message(client, event->channel_id, - &(struct discord_create_message){ - .content = "Disconnecting ...", - }, - &(struct discord_ret_message){ - .done = &disconnect, - .high_p = true, - }); -} - -void -on_reconnect(struct discord *client, const struct discord_message *event) -{ - if (event->author->bot) return; - - discord_create_message(client, event->channel_id, - &(struct discord_create_message){ - .content = "Reconnecting ...", - }, - &(struct discord_ret_message){ - .done = &reconnect, - .high_p = true, - }); -} - -void -on_single(struct discord *client, const struct discord_message *event) -{ - if (event->author->bot) return; - - discord_create_message(client, event->channel_id, - &(struct discord_create_message){ - .content = "Hello", - }, - NULL); -} - -void -send_batch(struct discord *client, - struct discord_response *resp, - const struct discord_message *msg) -{ - (void)resp; - char text[32]; - - for (int i = 0; i < 128; ++i) { - snprintf(text, sizeof(text), "%d", i); - discord_create_message(client, msg->channel_id, - &(struct discord_create_message){ - .content = text, - }, - NULL); - } - - discord_create_message(client, msg->channel_id, - &(struct discord_create_message){ - .content = "CHECKPOINT", - }, - &(struct discord_ret_message){ - .done = &send_batch, - }); -} - -void -on_spam(struct discord *client, const struct discord_message *event) -{ - send_batch(client, NULL, event); -} - -void -send_msg(struct discord *client, - struct discord_response *resp, - const struct discord_message *msg) -{ - (void)resp; - struct global_context *g_cxt = discord_get_data(client); - char text[32]; - - snprintf(text, sizeof(text), "%llu", g_cxt->counter); - - discord_create_message(client, msg->channel_id, - &(struct discord_create_message){ - .content = text, - }, - &(struct discord_ret_message){ - .done = &send_msg, - }); - - ++g_cxt->counter; -} - -void -on_spam_ordered(struct discord *client, const struct discord_message *event) -{ - send_msg(client, NULL, event); -} - -void -fail_delete_channel(struct discord *client, struct discord_response *resp) -{ - const struct discord_message *event = resp->keep; - - discord_create_message( - client, event->channel_id, - &(struct discord_create_message){ - .content = (char *)discord_strerror(resp->code, client), - }, - NULL); -} - -void -on_force_error(struct discord *client, const struct discord_message *event) -{ - const u64snowflake FAUX_CHANNEL_ID = 123; - - discord_delete_channel(client, FAUX_CHANNEL_ID, - &(struct discord_ret_channel){ - .fail = &fail_delete_channel, - .keep = event, - }); -} - -int -main(int argc, char *argv[]) -{ - const char *config_file; - if (argc > 1) - config_file = argv[1]; - else - config_file = "../config.json"; - - ccord_global_init(); - - struct discord *client = discord_config_init(config_file); - assert(NULL != client && "Couldn't initialize client"); - - struct global_context g_cxt = { 0 }; - discord_set_data(client, &g_cxt); - - discord_set_on_ready(client, &on_ready); - - discord_set_prefix(client, "!"); - discord_set_on_command(client, "disconnect", &on_disconnect); - discord_set_on_command(client, "reconnect", &on_reconnect); - discord_set_on_command(client, "single", &on_single); - discord_set_on_command(client, "spam", &on_spam); - discord_set_on_command(client, "spam-ordered", &on_spam_ordered); - discord_set_on_command(client, "force_error", &on_force_error); - - discord_run(client); - - discord_cleanup(client); - ccord_global_cleanup(); -} diff --git a/test/sync.c b/test/racecond.c similarity index 66% rename from test/sync.c rename to test/racecond.c index 4007892ba..2be17181a 100644 --- a/test/sync.c +++ b/test/racecond.c @@ -24,6 +24,26 @@ on_ready(struct discord *client, const struct discord_ready *event) event->user->username, event->user->discriminator); } +void +disconnect(struct discord *client, + struct discord_response *resp, + const struct discord_message *msg) +{ + (void)resp; + (void)msg; + discord_shutdown(client); +} + +void +reconnect(struct discord *client, + struct discord_response *resp, + const struct discord_message *msg) +{ + (void)resp; + (void)msg; + discord_reconnect(client, true); +} + void on_disconnect(struct discord *client, const struct discord_message *event) { @@ -34,10 +54,9 @@ on_disconnect(struct discord *client, const struct discord_message *event) .content = "Disconnecting ...", }, &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, + .done = &disconnect, + .high_p = true, }); - - discord_shutdown(client); } void @@ -50,14 +69,34 @@ on_reconnect(struct discord *client, const struct discord_message *event) .content = "Reconnecting ...", }, &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, + .done = &reconnect, + .high_p = true, }); +} - discord_reconnect(client, true); +void +on_single(struct discord *client, const struct discord_message *event) +{ + if (event->author->bot) return; + + discord_create_message(client, event->channel_id, + &(struct discord_create_message){ + .content = "Hello", + }, + NULL); } void -on_spam(struct discord *client, const struct discord_message *event) +on_stop_spam_sync(struct discord *client, const struct discord_message *event) +{ + pthread_mutex_lock(&g_lock); + g_keep_spamming = false; + g_thread_count = 0; + pthread_mutex_unlock(&g_lock); +} + +void +on_spam_sync(struct discord *client, const struct discord_message *event) { const unsigned threadpool_size = strtol(THREADPOOL_SIZE, NULL, 10); @@ -105,91 +144,59 @@ on_spam(struct discord *client, const struct discord_message *event) } void -on_spam_block(struct discord *client, const struct discord_message *event) +send_batch(struct discord *client, + struct discord_response *resp, + const struct discord_message *msg) { - if (event->author->bot) return; - - discord_create_message(client, event->channel_id, - &(struct discord_create_message){ - .content = "No 1", - }, - &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, - }); -} - -void -on_spam_block_continue(struct discord *client, - const struct discord_message *event) -{ - const struct discord_user *bot = discord_get_self(client); + (void)resp; char text[32]; - int number; - if (event->author->id != bot->id) return; - - sscanf(event->content, "No %d", &number); - snprintf(text, sizeof(text), "No %d", 1 + number); + for (int i = 0; i < 128; ++i) { + snprintf(text, sizeof(text), "%d", i); + discord_create_message(client, msg->channel_id, + &(struct discord_create_message){ + .content = text, + }, + NULL); + } - discord_create_message(client, event->channel_id, + discord_create_message(client, msg->channel_id, &(struct discord_create_message){ - .content = text, + .content = "CHECKPOINT", }, &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, + .done = &send_batch, }); } void -on_stop(struct discord *client, const struct discord_message *event) +on_spam_async(struct discord *client, const struct discord_message *event) { - if (event->author->bot) return; - - pthread_mutex_lock(&g_lock); - g_keep_spamming = false; - g_thread_count = 0; - pthread_mutex_unlock(&g_lock); + send_batch(client, NULL, event); } void -on_force_error(struct discord *client, const struct discord_message *event) +fail_delete_channel(struct discord *client, struct discord_response *resp) { - const u64snowflake FAUX_CHANNEL_ID = 123ULL; - CCORDcode code; - - if (event->author->bot) return; - - code = discord_delete_channel(client, FAUX_CHANNEL_ID, - &(struct discord_ret_channel){ - .sync = DISCORD_SYNC_FLAG, - }); - assert(code != CCORD_OK); + const struct discord_message *event = resp->keep; discord_create_message( client, event->channel_id, &(struct discord_create_message){ - .content = (char *)discord_strerror(code, client), + .content = (char *)discord_strerror(resp->code, client), }, - &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, - }); + NULL); } void -on_ping(struct discord *client, const struct discord_message *event) +on_force_error(struct discord *client, const struct discord_message *event) { - char text[256]; + const u64snowflake FAUX_CHANNEL_ID = 123; - if (event->author->bot) return; - - sprintf(text, "Ping: %d", discord_get_ping(client)); - - discord_create_message(client, event->channel_id, - &(struct discord_create_message){ - .content = text, - }, - &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, + discord_delete_channel(client, FAUX_CHANNEL_ID, + &(struct discord_ret_channel){ + .fail = &fail_delete_channel, + .keep = event, }); } @@ -200,7 +207,7 @@ scheduler(struct discord *client, enum discord_gateway_events event) { if (event == DISCORD_EV_MESSAGE_CREATE) { - char cmd[1024] = ""; + char cmd[DISCORD_MAX_MESSAGE_LEN] = ""; jsmntok_t *tokens = NULL; unsigned ntokens = 0; @@ -226,22 +233,10 @@ scheduler(struct discord *client, free(tokens); } - if (0 == strcmp(PREFIX "ping", cmd) - || 0 == strcmp(PREFIX "spam-block", cmd)) { - return DISCORD_EVENT_MAIN_THREAD; - } - else if (0 == strncmp("No", cmd, 2)) { - struct discord_message msg = { 0 }; - - discord_message_from_json(data, size, &msg); - on_spam_block_continue(client, &msg); - discord_message_cleanup(&msg); - - return DISCORD_EVENT_IGNORE; - } + if (0 == strcmp(PREFIX "spam_sync", cmd)) + return DISCORD_EVENT_WORKER_THREAD; } - - return DISCORD_EVENT_WORKER_THREAD; + return DISCORD_EVENT_MAIN_THREAD; } int @@ -257,10 +252,10 @@ main(int argc, char *argv[]) setenv("CCORD_THREADPOOL_QUEUE_SIZE", "128", 1); ccord_global_init(); + struct discord *client = discord_config_init(config_file); assert(NULL != client && "Couldn't initialize client"); - /* trigger event callbacks in a multi-threaded fashion */ discord_set_event_scheduler(client, &scheduler); discord_set_on_ready(client, &on_ready); @@ -268,11 +263,11 @@ main(int argc, char *argv[]) discord_set_prefix(client, PREFIX); discord_set_on_command(client, "disconnect", &on_disconnect); discord_set_on_command(client, "reconnect", &on_reconnect); - discord_set_on_command(client, "spam", &on_spam); - discord_set_on_command(client, "spam-block", &on_spam_block); - discord_set_on_command(client, "stop", &on_stop); + discord_set_on_command(client, "single", &on_single); + discord_set_on_command(client, "stop_spam_sync", &on_stop_spam_sync); + discord_set_on_command(client, "spam_sync", &on_spam_sync); + discord_set_on_command(client, "spam_async", &on_spam_async); discord_set_on_command(client, "force_error", &on_force_error); - discord_set_on_command(client, "ping", &on_ping); discord_run(client); From 64218cc81199a8c1bf7bda80ddf88244f40b0ffb Mon Sep 17 00:00:00 2001 From: Anotra Date: Thu, 19 May 2022 15:52:54 -0400 Subject: [PATCH 056/118] fix:(discord-rest.c): make discord rest requests responsive --- src/discord-rest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/discord-rest.c b/src/discord-rest.c index 23e2da128..f98a9acab 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -547,8 +547,6 @@ discord_rest_async_perform(struct discord_rest *rest) CCORDcode code; int alive = 0; - if (CCORD_OK != (code = _discord_rest_check_pending(rest))) return code; - if (CURLM_OK != (mcode = curl_multi_socket_all(rest->async.mhandle, &alive))) return CCORD_CURLM_INTERNAL; @@ -565,6 +563,8 @@ discord_rest_async_perform(struct discord_rest *rest) _discord_rest_check_action(rest, msg); } + if (CCORD_OK != (code = _discord_rest_check_pending(rest))) return code; + return CCORD_OK; } From a2f68d8cb4525cf1f7ac946c3636d9688c9e6e73 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 19 May 2022 17:39:31 -0300 Subject: [PATCH 057/118] refactor(discord-rest.c): reduce discord_rest_async_perform() --- src/discord-rest.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/discord-rest.c b/src/discord-rest.c index f98a9acab..eab793bf9 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -543,12 +543,9 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) CCORDcode discord_rest_async_perform(struct discord_rest *rest) { - CURLMcode mcode; - CCORDcode code; int alive = 0; - if (CURLM_OK - != (mcode = curl_multi_socket_all(rest->async.mhandle, &alive))) + if (CURLM_OK != curl_multi_socket_all(rest->async.mhandle, &alive)) return CCORD_CURLM_INTERNAL; /* ask for any messages/informationals from the individual transfers */ @@ -563,9 +560,7 @@ discord_rest_async_perform(struct discord_rest *rest) _discord_rest_check_action(rest, msg); } - if (CCORD_OK != (code = _discord_rest_check_pending(rest))) return code; - - return CCORD_OK; + return _discord_rest_check_pending(rest); } static void From 7096f91c3dbcb17bd5cfbff35eb6ae3ccca751d4 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 20 May 2022 15:43:45 -0300 Subject: [PATCH 058/118] wip(discord-rest): moving logic to a special REST management thread --- core/error.h | 2 + include/discord-internal.h | 41 +++-- src/discord-loop.c | 14 +- src/discord-rest.c | 281 ++++++++++++----------------------- src/discord-rest_ratelimit.c | 36 ++--- 5 files changed, 131 insertions(+), 243 deletions(-) diff --git a/core/error.h b/core/error.h index c62a2a8a9..c5a240bd8 100644 --- a/core/error.h +++ b/core/error.h @@ -32,6 +32,8 @@ typedef int CCORDcode; #define CCORD_OWNERSHIP -9 /** couldn't perform action because resource is unavailable */ #define CCORD_UNAVAILABLE -10 +/** couldn't enqueue request (queue is full) */ +#define CCORD_FULL_QUEUE -11 /** @} ConcordError */ diff --git a/include/discord-internal.h b/include/discord-internal.h index a8eb957f6..d7fb5fdc9 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -109,8 +109,6 @@ struct discord_ret_dispatch { /** @brief Attributes of response datatype */ struct discord_ret_response { - /** pointer to the datatype in memory */ - void *data; /** size of datatype in bytes */ size_t size; /** initializer function for datatype fields */ @@ -260,6 +258,12 @@ struct discord_context *discord_async_start_context( * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ +/** + * @brief Value assigned to @ref discord_bucket `pending_cxt` field in case + * it's being timed-out + */ +#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) + /** @brief The ratelimiter struct for handling ratelimiting */ struct discord_ratelimiter { /** DISCORD_RATELIMIT logging module */ @@ -333,14 +337,6 @@ void discord_ratelimiter_build_key(enum http_method method, const char endpoint_fmt[], va_list args); -/** - * @brief Get global timeout timestamp - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @return the most recent global timeout timestamp - */ -u64unix_ms discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl); - /** * @brief Update the bucket with response header data * @@ -365,12 +361,18 @@ struct discord_bucket { long remaining; /** timestamp of when cooldown timer resets */ u64unix_ms reset_tstamp; - /** synchronize ratelimiting between threads */ - pthread_mutex_t lock; /** pending requests */ QUEUE(struct discord_context) pending_queue; - /** pointer to currently performing busy context (if asynchronous) */ + /** + * pointer to context of this bucket's currently performing request + * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited + */ struct discord_context *performing_cxt; + /** wait and notify synchronous requests */ + struct { + pthread_cond_t cond; + pthread_mutex_t lock; + } sync; }; /** @@ -383,17 +385,6 @@ struct discord_bucket { u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, struct discord_bucket *bucket); -/** - * @brief Try to sleep bucket for pending cooldown time - * @note this is used for `sync` mode and **WILL** block the bucket's - * execution thread - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param bucket the bucket to wait on cooldown - */ -void discord_bucket_try_sleep(struct discord_ratelimiter *rl, - struct discord_bucket *bucket); - /** * @brief Try to timeout bucket for pending cooldown time * @@ -440,6 +431,8 @@ struct discord_context *discord_bucket_remove_context( struct discord_rest { /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ struct logconf conf; + /** threadpool that manages a single REST thread */ + struct threadpool_t *tpool; /** the user agent handle for performing requests */ struct user_agent *ua; /** store individual contexts from asynchronous requests */ diff --git a/src/discord-loop.c b/src/discord-loop.c index fb8117fc5..657f8db3f 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -93,8 +93,8 @@ discord_run(struct discord *client) next_run = (int64_t)discord_timestamp_us(client); while (1) { - int64_t poll_time = 0; int poll_result, poll_errno = 0; + int64_t poll_time = 0; now = (int64_t)discord_timestamp_us(client); @@ -110,7 +110,10 @@ discord_run(struct discord *client) now = (int64_t)discord_timestamp_us(client); if (0 == poll_result) { - if (ccord_has_sigint != 0) discord_shutdown(client); + if (ccord_has_sigint != 0) { + discord_shutdown(client); + } + if (client->on_idle) { client->on_idle(client); } @@ -142,7 +145,9 @@ discord_run(struct discord *client) if (next_run <= now) { BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); +#if 0 BREAK_ON_FAIL(code, discord_rest_async_perform(&client->rest)); +#endif /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; @@ -150,10 +155,7 @@ discord_run(struct discord *client) } /* stop all pending requests in case of connection shutdown */ - if (true == discord_gateway_end(&client->gw)) { - discord_rest_stop_buckets(&client->rest); - break; - } + if (true == discord_gateway_end(&client->gw)) break; } return code; diff --git a/src/discord-rest.c b/src/discord-rest.c index eab793bf9..75fef6254 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -4,12 +4,13 @@ #include #include "carray.h" +#include "threadpool.h" #include "discord.h" #include "discord-internal.h" static void -setopt_cb(struct ua_conn *conn, void *p_token) +_discord_rest_setopt_cb(struct ua_conn *conn, void *p_token) { struct ccord_szbuf *token = p_token; char auth[128]; @@ -26,6 +27,18 @@ setopt_cb(struct ua_conn *conn, void *p_token) #endif } +static void +_discord_rest_manager(void *p_rest) +{ + struct discord_rest *rest = p_rest; + + while (1) { + discord_rest_async_perform(rest); + } + + discord_rest_stop_buckets(rest); +} + void discord_rest_init(struct discord_rest *rest, struct logconf *conf, @@ -44,18 +57,24 @@ discord_rest_init(struct discord_rest *rest, else { /* bot client */ logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); - ua_set_opt(rest->ua, token, &setopt_cb); + ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); } discord_async_init(&rest->async, &rest->conf); discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ + + rest->tpool = threadpool_create(1, 1024, 0); + ASSERT_S(0 == threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), + "Couldn't initialize REST managagement thread"); } void discord_rest_cleanup(struct discord_rest *rest) { + /* cleanup REST managing thread */ + threadpool_destroy(rest->tpool, threadpool_graceful); /* cleanup User-Agent handle */ ua_cleanup(rest->ua); /* move pending requests to idle_contexts */ @@ -66,19 +85,12 @@ discord_rest_cleanup(struct discord_rest *rest) discord_ratelimiter_cleanup(&rest->ratelimiter); } -static CCORDcode _discord_rest_run_sync(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); - -static CCORDcode _discord_rest_run_async(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); +static CCORDcode _discord_rest_start_context(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]); /* template function for performing requests */ CCORDcode @@ -115,15 +127,7 @@ discord_rest_run(struct discord_rest *rest, discord_ratelimiter_build_key(method, key, endpoint_fmt, args); va_end(args); - if (req->dispatch.sync) { /* perform blocking request */ - if (req->dispatch.has_type && req->dispatch.sync != DISCORD_SYNC_FLAG) - req->response.data = req->dispatch.sync; - - return _discord_rest_run_sync(rest, req, body, method, endpoint, key); - } - - /* enqueue asynchronous request */ - return _discord_rest_run_async(rest, req, body, method, endpoint, key); + return _discord_rest_start_context(rest, req, body, method, endpoint, key); } /* return true if there should be a retry attempt */ @@ -202,198 +206,86 @@ _discord_rest_get_info(struct discord_rest *rest, } } -/* - * data is a `void *[2]`, where the first element is a - * `struct discord_attachment` and the second element is a - * `struct ccord_szbuf` containing the request body */ +/* enqueue a request to be executed asynchronously */ +static CCORDcode +_discord_rest_start_context(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) +{ + struct discord_context *cxt = discord_async_start_context( + &rest->async, req, body, method, endpoint, key); + + pthread_mutex_lock(&cxt->b->sync.lock); + + discord_bucket_add_context(cxt->b, cxt, cxt->dispatch.high_p); + + if (cxt->dispatch.sync) + pthread_cond_wait(&cxt->b->sync.cond, &cxt->b->sync.lock); + + pthread_mutex_unlock(&cxt->b->sync.lock); + + return CCORD_OK; +} + static void -_discord_rest_request_to_multipart(curl_mime *mime, void *data) +_discord_context_to_multipart(curl_mime *mime, void *p_cxt) { - struct discord_attachments *atchs = ((void **)data)[0]; - struct ccord_szbuf *body = ((void **)data)[1]; + struct discord_context *cxt = p_cxt; curl_mimepart *part; char name[64]; /* json part */ - if (body->start && body->size) { + if (cxt->body.start && cxt->body.size) { part = curl_mime_addpart(mime); - curl_mime_data(part, body->start, body->size); + curl_mime_data(part, cxt->body.start, cxt->body.size); curl_mime_type(part, "application/json"); curl_mime_name(part, "payload_json"); } /* attachment part */ - for (int i = 0; i < atchs->size; ++i) { + for (int i = 0; i < cxt->attachments.size; ++i) { int len = snprintf(name, sizeof(name), "files[%d]", i); ASSERT_NOT_OOB(len, sizeof(name)); - if (atchs->array[i].content) { + if (cxt->attachments.array[i].content) { part = curl_mime_addpart(mime); - curl_mime_data(part, atchs->array[i].content, - atchs->array[i].size ? atchs->array[i].size - : CURL_ZERO_TERMINATED); - curl_mime_filename(part, !atchs->array[i].filename + curl_mime_data(part, cxt->attachments.array[i].content, + cxt->attachments.array[i].size + ? cxt->attachments.array[i].size + : CURL_ZERO_TERMINATED); + curl_mime_filename(part, !cxt->attachments.array[i].filename ? "a.out" - : atchs->array[i].filename); - curl_mime_type(part, !atchs->array[i].content_type + : cxt->attachments.array[i].filename); + curl_mime_type(part, !cxt->attachments.array[i].content_type ? "application/octet-stream" - : atchs->array[i].content_type); + : cxt->attachments.array[i].content_type); curl_mime_name(part, name); } - else if (atchs->array[i].filename) { + else if (cxt->attachments.array[i].filename) { CURLcode code; /* fetch local file by the filename */ part = curl_mime_addpart(mime); - code = curl_mime_filedata(part, atchs->array[i].filename); + code = + curl_mime_filedata(part, cxt->attachments.array[i].filename); if (code != CURLE_OK) { char errbuf[256]; snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", - curl_easy_strerror(code), atchs->array[i].filename); + curl_easy_strerror(code), + cxt->attachments.array[i].filename); perror(errbuf); } - curl_mime_type(part, !atchs->array[i].content_type + curl_mime_type(part, !cxt->attachments.array[i].content_type ? "application/octet-stream" - : atchs->array[i].content_type); + : cxt->attachments.array[i].content_type); curl_mime_name(part, name); } } } -/* SYNCHRONOUS REQUEST LOGIC */ - -/* perform a blocking request */ -static CCORDcode -_discord_rest_run_sync(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - void *data[2] = { &req->attachments, body }; - struct discord_bucket *b; - struct ua_conn *conn; - int retry_attempt = 0; - bool retry; - CCORDcode code; - - b = discord_bucket_get(&rest->ratelimiter, key); - conn = ua_conn_start(rest->ua); - - if (HTTP_MIMEPOST == method) { - ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, data, &_discord_rest_request_to_multipart); - } - else { - ua_conn_add_header(conn, "Content-Type", "application/json"); - } - - ua_conn_setup(conn, &(struct ua_conn_attr){ - .method = method, - .body = body->start, - .body_size = body->size, - .endpoint = endpoint, - .base_url = NULL, - }); - - pthread_mutex_lock(&b->lock); - do { - discord_bucket_try_sleep(&rest->ratelimiter, b); - - /* perform blocking request, and check results */ - switch (code = ua_conn_easy_perform(conn)) { - case CCORD_OK: { - struct discord *client = CLIENT(rest, rest); - struct ua_szbuf_readonly resp; - struct ua_info info = { 0 }; - int64_t wait_ms = 0; - - ua_info_extract(conn, &info); - retry = _discord_rest_get_info(rest, &info, &wait_ms); - - resp = ua_info_get_body(&info); - if (info.code != CCORD_OK) { - logconf_error(&client->conf, "%.*s", (int)resp.size, - resp.start); - } - else if (req->response.data) { - /* initialize ret */ - if (req->response.init) req->response.init(req->response.data); - - /* populate ret */ - if (req->response.from_json) - req->response.from_json(resp.start, resp.size, - req->response.data); - } - - code = info.code; - - /* in the off-chance of having consecutive blocking calls, update - * timestamp used for ratelimiting - * TODO: redundant for REST-only clients - * TODO: create discord_timestamp_update() */ - ws_timestamp_update(client->gw.ws); - - discord_ratelimiter_build(&rest->ratelimiter, b, key, &info); - cog_sleep_ms(wait_ms); - - ua_info_cleanup(&info); - } break; - case CCORD_CURLE_INTERNAL: - logconf_error(&rest->conf, - "Curl internal error, will retry again"); - retry = true; - break; - default: - logconf_error(&rest->conf, "CCORD code: %d", code); - retry = false; - break; - } - - ua_conn_reset(conn); - - } while (retry && retry_attempt++ < rest->retry_limit); - pthread_mutex_unlock(&b->lock); - - /* reset conn and mark it as free to use */ - ua_conn_stop(conn); - - return code; -} - -/* ASYNCHRONOUS REQUEST LOGIC */ - -/* enqueue a request to be executed asynchronously */ -static CCORDcode -_discord_rest_run_async(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - struct discord_context *cxt = discord_async_start_context( - &rest->async, req, body, method, endpoint, key); - - discord_bucket_add_context(cxt->b, cxt, req->dispatch.high_p); - - /* FIXME: redundant return value (constant) */ - return CCORD_OK; -} - -static void -_discord_context_to_multipart(curl_mime *mime, void *p_cxt) -{ - struct discord_context *cxt = p_cxt; - void *data[2] = { &cxt->attachments, &(struct ccord_szbuf){ - cxt->body.start, - cxt->body.size, - } }; - - _discord_rest_request_to_multipart(mime, data); -} - /* add a request to libcurl's multi handle */ static CCORDcode _discord_rest_add_request(struct discord_rest *rest, struct discord_bucket *b) @@ -425,9 +317,7 @@ _discord_rest_try_add_request(struct discord_ratelimiter *rl, struct discord_bucket *b) { /* skip if bucket is busy performing */ - if (pthread_mutex_trylock(&b->lock) != 0) { - return; - } + if (b->performing_cxt) return; if (!b->remaining) { discord_bucket_try_timeout(rl, b); @@ -438,9 +328,6 @@ _discord_rest_try_add_request(struct discord_ratelimiter *rl, _discord_rest_add_request(rest, b); } - else { - pthread_mutex_unlock(&b->lock); - } } static CCORDcode @@ -483,6 +370,18 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) logconf_error(&rest->conf, "%.*s", (int)body.size, body.start); if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); } + else if (cxt->dispatch.sync) { + if (cxt->dispatch.has_type + && cxt->dispatch.sync != DISCORD_SYNC_FLAG) { + /* initialize ret */ + if (cxt->response.init) cxt->response.init(cxt->dispatch.sync); + + /* populate ret */ + if (cxt->response.from_json) + cxt->response.from_json(body.start, body.size, + cxt->dispatch.sync); + } + } else if (cxt->dispatch.done.typed) { if (!cxt->dispatch.has_type) { cxt->dispatch.done.typeless(client, &resp); @@ -531,11 +430,15 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) } /* enqueue request for retry or recycle */ + cxt->b->performing_cxt = NULL; if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) discord_async_recycle_context(&rest->async, cxt); - cxt->b->performing_cxt = NULL; - pthread_mutex_unlock(&cxt->b->lock); + if (cxt->dispatch.sync) { + pthread_mutex_lock(&cxt->b->sync.lock); + pthread_cond_signal(&cxt->b->sync.cond); + pthread_mutex_unlock(&cxt->b->sync.lock); + } return resp.code; } diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 76dbfec8a..61f019bae 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -36,7 +36,8 @@ struct _discord_route { static void _discord_bucket_cleanup(struct discord_bucket *b) { - pthread_mutex_destroy(&b->lock); + pthread_cond_destroy(&b->sync.cond); + pthread_mutex_destroy(&b->sync.lock); free(b); } @@ -130,8 +131,10 @@ _discord_bucket_init(struct discord_ratelimiter *rl, b->remaining = 1; b->limit = limit; - if (pthread_mutex_init(&b->lock, NULL)) - ERR("Couldn't initialize pthread mutex"); + ASSERT_S(!pthread_cond_init(&b->sync.cond, NULL), + "Couldn't initialize bucket's cond"); + ASSERT_S(!pthread_mutex_init(&b->sync.lock, NULL), + "Couldn't initialize bucket's mutex"); QUEUE_INIT(&b->pending_queue); @@ -206,8 +209,8 @@ _discord_bucket_find(struct discord_ratelimiter *rl, const char key[]) return b; } -u64unix_ms -discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl) +static u64unix_ms +_discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl) { u64unix_ms global; @@ -223,37 +226,20 @@ u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, struct discord_bucket *b) { - u64unix_ms global = discord_ratelimiter_get_global_wait(rl), + u64unix_ms global = _discord_ratelimiter_get_global_wait(rl), reset = (b->remaining < 1) ? b->reset_tstamp : 0ULL; return (global > reset) ? global : reset; } -void -discord_bucket_try_sleep(struct discord_ratelimiter *rl, - struct discord_bucket *b) -{ - /* sleep_ms := reset timestamp - current timestamp */ - const int64_t sleep_ms = - (int64_t)(discord_bucket_get_timeout(rl, b) - cog_timestamp_ms()); - - if (sleep_ms > 0) { - /* block thread's runtime for delay amount */ - logconf_info(&rl->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", - b->hash, sleep_ms); - cog_sleep_ms(sleep_ms); - } -} - static void _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) { (void)client; struct discord_bucket *b = timer->data; + b->performing_cxt = NULL; b->remaining = 1; - - pthread_mutex_unlock(&b->lock); } void @@ -263,6 +249,8 @@ discord_bucket_try_timeout(struct discord_ratelimiter *rl, struct discord *client = CLIENT(rl, rest.ratelimiter); const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); + b->performing_cxt = DISCORD_BUCKET_TIMEOUT; + discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); logconf_info(&rl->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", From 1e5c24cf71ae6b33de93a6775c0e357cc26e0ed5 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 15:07:33 -0400 Subject: [PATCH 059/118] refactor(discord-rest): add io_poller for REST loop --- include/discord-internal.h | 2 ++ src/discord-rest.c | 6 +++++- src/discord-rest_async.c | 11 ++++++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index d7fb5fdc9..576539728 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -437,6 +437,8 @@ struct discord_rest { struct user_agent *ua; /** store individual contexts from asynchronous requests */ struct discord_async async; + /** io_poller for rest only */ + struct io_poller *io_poller; /** enforce ratelimiting on discovered buckets */ struct discord_ratelimiter ratelimiter; diff --git a/src/discord-rest.c b/src/discord-rest.c index 75fef6254..5a721e5c9 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -33,6 +33,8 @@ _discord_rest_manager(void *p_rest) struct discord_rest *rest = p_rest; while (1) { + io_poller_poll(rest->io_poller, 1000); + io_poller_perform(rest->io_poller); discord_rest_async_perform(rest); } @@ -59,7 +61,7 @@ discord_rest_init(struct discord_rest *rest, logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); } - + rest->io_poller = io_poller_create(); discord_async_init(&rest->async, &rest->conf); discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); @@ -83,6 +85,8 @@ discord_rest_cleanup(struct discord_rest *rest) discord_async_cleanup(&rest->async); /* cleanup discovered buckets */ discord_ratelimiter_cleanup(&rest->ratelimiter); + /* cleanup REST io_poller */ + io_poller_destroy(rest->io_poller); } static CCORDcode _discord_rest_start_context(struct discord_rest *rest, diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 9033f8535..267712903 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -60,8 +60,8 @@ discord_async_init(struct discord_async *async, struct logconf *conf) QUEUE_INIT(async->idle_contexts); async->mhandle = curl_multi_init(); - io_poller_curlm_add(CLIENT(rest, rest)->io_poller, async->mhandle, - &_on_io_poller_curl, rest); + io_poller_curlm_add(rest->io_poller, async->mhandle, &_on_io_poller_curl, + rest); } void @@ -80,7 +80,8 @@ discord_async_cleanup(struct discord_async *async) free(async->idle_contexts); /* cleanup curl's multi handle */ - io_poller_curlm_del(CLIENT(async, rest.async)->io_poller, async->mhandle); + io_poller_curlm_del(CLIENT(async, rest.async)->rest.io_poller, + async->mhandle); curl_multi_cleanup(async->mhandle); } @@ -101,7 +102,7 @@ discord_async_add_request(struct discord_async *async, /* initiate libcurl transfer */ mcode = curl_multi_add_handle(async->mhandle, ehandle); - io_poller_curlm_enable_perform(CLIENT(async, rest.async)->io_poller, + io_poller_curlm_enable_perform(CLIENT(async, rest.async)->rest.io_poller, async->mhandle); return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; @@ -241,7 +242,7 @@ discord_async_start_context(struct discord_async *async, req->dispatch.cleanup, false); } - io_poller_curlm_enable_perform(client->io_poller, async->mhandle); + io_poller_curlm_enable_perform(rest->io_poller, async->mhandle); return cxt; } From 4e6a5062dd48e1b0d95b3c7b1cda7fd1140ff268 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 15:15:35 -0400 Subject: [PATCH 060/118] refactor(discord-internal.h): move discord_timer declarations --- include/discord-internal.h | 146 ++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 576539728..e05fd83b8 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -78,6 +78,79 @@ /** Route's unique key threshold length */ #define DISCORD_ROUTE_LEN 256 +/** @defgroup DiscordInternalTimer Timer API + * @brief Callback scheduling API + * @{ */ + +struct discord_timers { + priority_queue *q; + struct { + struct discord_timer *timer; + bool skip_update_phase; + } active; +}; + +/** + * @brief Prepare timers for usage + * + * @param client the client created with discord_init() + */ +void discord_timers_init(struct discord *client); + +/** + * @brief Cleanup timers and call cancel any running ones + * + * @param client the client created with discord_init() + */ +void discord_timers_cleanup(struct discord *client); + +/** + * @brief Run all timers that are due + * + * @param client the client created with discord_init() + * @param timers the timers to run + */ +void discord_timers_run(struct discord *client, struct discord_timers *timers); + +/** + * @brief Modifies or creates a timer + * + * @param client the client created with discord_init() + * @param timers the timer group to perform this operation on + * @param timer the timer that should be modified + * @return the id of the timer + */ +unsigned _discord_timer_ctl(struct discord *client, + struct discord_timers *timers, + struct discord_timer *timer); + +/** + * @brief Modifies or creates a timer + * + * @param client the client created with discord_init() + * @param timer the timer that should be modified + * @return the id of the timer + */ +unsigned discord_internal_timer_ctl(struct discord *client, + struct discord_timer *timer); + +/** + * @brief Creates a one shot timer that automatically deletes itself upon + * completion + * + * @param client the client created with discord_init() + * @param cb the callback that should be called when timer triggers + * @param data user data + * @param delay delay before timer should start in milliseconds + * @return the id of the timer + */ +unsigned discord_internal_timer(struct discord *client, + discord_ev_timer cb, + void *data, + int64_t delay); + +/** @} DiscordInternalTimer */ + /** @defgroup DiscordInternalREST REST API * @brief Wrapper to the Discord REST API * @{ */ @@ -749,79 +822,6 @@ void discord_gateway_dispatch(struct discord_gateway *gw); /** @} DiscordInternalGateway */ -/** @defgroup DiscordInternalTimer Timer API - * @brief Callback scheduling API - * @{ */ - -struct discord_timers { - priority_queue *q; - struct { - struct discord_timer *timer; - bool skip_update_phase; - } active; -}; - -/** - * @brief Prepare timers for usage - * - * @param client the client created with discord_init() - */ -void discord_timers_init(struct discord *client); - -/** - * @brief Cleanup timers and call cancel any running ones - * - * @param client the client created with discord_init() - */ -void discord_timers_cleanup(struct discord *client); - -/** - * @brief Run all timers that are due - * - * @param client the client created with discord_init() - * @param timers the timers to run - */ -void discord_timers_run(struct discord *client, struct discord_timers *timers); - -/** - * @brief Modifies or creates a timer - * - * @param client the client created with discord_init() - * @param timers the timer group to perform this operation on - * @param timer the timer that should be modified - * @return the id of the timer - */ -unsigned _discord_timer_ctl(struct discord *client, - struct discord_timers *timers, - struct discord_timer *timer); - -/** - * @brief Modifies or creates a timer - * - * @param client the client created with discord_init() - * @param timer the timer that should be modified - * @return the id of the timer - */ -unsigned discord_internal_timer_ctl(struct discord *client, - struct discord_timer *timer); - -/** - * @brief Creates a one shot timer that automatically deletes itself upon - * completion - * - * @param client the client created with discord_init() - * @param cb the callback that should be called when timer triggers - * @param data user data - * @param delay delay before timer should start in milliseconds - * @return the id of the timer - */ -unsigned discord_internal_timer(struct discord *client, - discord_ev_timer cb, - void *data, - int64_t delay); - -/** @} DiscordInternalTimer */ - /** @defgroup DiscordInternalRefcount Reference counter * @brief Handle automatic cleanup of user's data * @{ */ From d7dece5df7cc6fed957ac54b4eac20d1208619b5 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 15:36:44 -0400 Subject: [PATCH 061/118] refactor(discord-timers): move discord_timers_get_next_trigger to discord_timer.c --- include/discord-internal.h | 15 +++++++++++++++ src/discord-loop.c | 26 ++------------------------ src/discord-timer.c | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index e05fd83b8..3a1749049 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -104,6 +104,21 @@ void discord_timers_init(struct discord *client); */ void discord_timers_cleanup(struct discord *client); +/** + * @brief Get earliest trigger time from a group of timers + * + * @param timers array of timers + * @param n number of timers in array + * @param now current time + * @param max_time max time to allowed + * @return time in microseconds until next timer, or max + */ +int64_t +discord_timers_get_next_trigger(struct discord_timers *const timers[], + size_t n, + int64_t now, + int64_t max_time); + /** * @brief Run all timers that are due * diff --git a/src/discord-loop.c b/src/discord-loop.c index 657f8db3f..8a9cec68f 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -49,28 +49,6 @@ discord_set_on_cycle(struct discord *client, discord_ev_idle callback) client->on_cycle = callback; } -static inline int64_t -discord_timer_get_next_trigger(struct discord_timers *const timers[], - size_t n, - int64_t now, - int64_t max_time) -{ - if (max_time == 0) return 0; - - for (unsigned i = 0; i < n; i++) { - int64_t trigger; - if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { - if (trigger < 0) continue; - - if (trigger <= now) - max_time = 0; - else if (max_time > trigger - now) - max_time = trigger - now; - } - } - return max_time; -} - #define BREAK_ON_FAIL(code, function) \ if (CCORD_OK != (code = function)) break @@ -99,7 +77,7 @@ discord_run(struct discord *client) now = (int64_t)discord_timestamp_us(client); if (!client->on_idle) { - poll_time = discord_timer_get_next_trigger( + poll_time = discord_timers_get_next_trigger( timers, sizeof timers / sizeof *timers, now, now < next_run ? ((next_run - now)) : 0); } @@ -118,7 +96,7 @@ discord_run(struct discord *client) client->on_idle(client); } else { - int64_t sleep_time = discord_timer_get_next_trigger( + int64_t sleep_time = discord_timers_get_next_trigger( timers, sizeof timers / sizeof *timers, now, now < next_run ? ((next_run - now)) : 0); if (sleep_time > 0 && sleep_time < 1000) diff --git a/src/discord-timer.c b/src/discord-timer.c index 622bbd6cc..207cc8300 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -50,6 +50,28 @@ discord_timers_cleanup(struct discord *client) priority_queue_destroy(client->timers.internal.q); } +int64_t +discord_timers_get_next_trigger(struct discord_timers *const timers[], + size_t n, + int64_t now, + int64_t max_time) +{ + if (max_time == 0) return 0; + + for (unsigned i = 0; i < n; i++) { + int64_t trigger; + if (priority_queue_peek(timers[i]->q, &trigger, NULL)) { + if (trigger < 0) continue; + + if (trigger <= now) + max_time = 0; + else if (max_time > trigger - now) + max_time = trigger - now; + } + } + return max_time; +} + unsigned _discord_timer_ctl(struct discord *client, struct discord_timers *timers, From d721c81a957cb05d90e5e3075da54400abfd7bf1 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 15:49:15 -0400 Subject: [PATCH 062/118] refactor(discord-rest) add timer queue to discord REST thread --- include/discord-internal.h | 13 +++++++------ src/discord-rest.c | 15 ++++++++++++++- src/discord-rest_ratelimit.c | 7 ++++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 3a1749049..cd08f9ab6 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -106,18 +106,17 @@ void discord_timers_cleanup(struct discord *client); /** * @brief Get earliest trigger time from a group of timers - * + * * @param timers array of timers * @param n number of timers in array * @param now current time * @param max_time max time to allowed * @return time in microseconds until next timer, or max */ -int64_t -discord_timers_get_next_trigger(struct discord_timers *const timers[], - size_t n, - int64_t now, - int64_t max_time); +int64_t discord_timers_get_next_trigger(struct discord_timers *const timers[], + size_t n, + int64_t now, + int64_t max_time); /** * @brief Run all timers that are due @@ -527,6 +526,8 @@ struct discord_rest { struct discord_async async; /** io_poller for rest only */ struct io_poller *io_poller; + /** the timer queue for the rest thread */ + struct discord_timers timers; /** enforce ratelimiting on discovered buckets */ struct discord_ratelimiter ratelimiter; diff --git a/src/discord-rest.c b/src/discord-rest.c index 5a721e5c9..31a9b0383 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -31,9 +31,22 @@ static void _discord_rest_manager(void *p_rest) { struct discord_rest *rest = p_rest; + struct discord_timers *const timers[] = { &rest->timers }; + int64_t now, trigger; while (1) { - io_poller_poll(rest->io_poller, 1000); + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + + trigger = discord_timers_get_next_trigger(timers, 1, now, 1000000); + int poll_result = + io_poller_poll(rest->io_poller, (int)(trigger / 1000)); + + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + if (0 == poll_result) { + trigger = discord_timers_get_next_trigger(timers, 1, now, 1000000); + if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); + } + discord_timers_run(CLIENT(rest, rest), &rest->timers); io_poller_perform(rest->io_poller); discord_rest_async_perform(rest); } diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 61f019bae..fff833f8a 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -251,7 +251,12 @@ discord_bucket_try_timeout(struct discord_ratelimiter *rl, b->performing_cxt = DISCORD_BUCKET_TIMEOUT; - discord_internal_timer(client, &_discord_bucket_wake_cb, b, delay_ms); + _discord_timer_ctl( + client, &client->rest.timers, + &(struct discord_timer){ .cb = &_discord_bucket_wake_cb, + .data = b, + .delay = delay_ms, + .flags = DISCORD_TIMER_DELETE_AUTO }); logconf_info(&rl->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", b->hash, delay_ms); From a06dbc57e9d75f51c07ba4b84eeff2310d25b666 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 16:06:44 -0400 Subject: [PATCH 063/118] refactor(discord-timer): clean up init and cleanup functions in order to be more adaptable --- include/discord-internal.h | 8 +++++--- src/discord-client.c | 6 ++++-- src/discord-rest.c | 2 ++ src/discord-timer.c | 22 ++++++++-------------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index cd08f9ab6..526fba966 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -93,16 +93,18 @@ struct discord_timers { /** * @brief Prepare timers for usage * - * @param client the client created with discord_init() + * @param timers the 'struct discord_timers' to init */ -void discord_timers_init(struct discord *client); +void discord_timers_init(struct discord_timers *timers); /** * @brief Cleanup timers and call cancel any running ones * * @param client the client created with discord_init() + * @param timers the 'struct discord_timers' to cleanup */ -void discord_timers_cleanup(struct discord *client); +void discord_timers_cleanup(struct discord *client, + struct discord_timers *timers); /** * @brief Get earliest trigger time from a group of timers diff --git a/src/discord-client.c b/src/discord-client.c index 6b3c42909..668b4c7e0 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -12,7 +12,8 @@ static void _discord_init(struct discord *new_client) { ccord_global_init(); - discord_timers_init(new_client); + discord_timers_init(&new_client->timers.internal); + discord_timers_init(&new_client->timers.user); new_client->io_poller = io_poller_create(); discord_refcounter_init(&new_client->refcounter, &new_client->conf); @@ -168,7 +169,8 @@ void discord_cleanup(struct discord *client) { if (client->is_original) { - discord_timers_cleanup(client); + discord_timers_cleanup(client, &client->timers.user); + discord_timers_cleanup(client, &client->timers.internal); logconf_cleanup(&client->conf); discord_rest_cleanup(&client->rest); discord_gateway_cleanup(&client->gw); diff --git a/src/discord-rest.c b/src/discord-rest.c index 31a9b0383..2c3f9d0c4 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -74,6 +74,7 @@ discord_rest_init(struct discord_rest *rest, logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); } + discord_timers_init(&rest->timers); rest->io_poller = io_poller_create(); discord_async_init(&rest->async, &rest->conf); discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); @@ -88,6 +89,7 @@ discord_rest_init(struct discord_rest *rest, void discord_rest_cleanup(struct discord_rest *rest) { + discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); /* cleanup REST managing thread */ threadpool_destroy(rest->tpool, threadpool_graceful); /* cleanup User-Agent handle */ diff --git a/src/discord-timer.c b/src/discord-timer.c index 207cc8300..35f6e02d6 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -20,34 +20,28 @@ cmp_timers(const void *a, const void *b) } void -discord_timers_init(struct discord *client) +discord_timers_init(struct discord_timers *timers) { - client->timers.internal.q = priority_queue_create( - sizeof(int64_t), sizeof(struct discord_timer), cmp_timers, 0); - client->timers.user.q = priority_queue_create( + timers->q = priority_queue_create( sizeof(int64_t), sizeof(struct discord_timer), cmp_timers, 0); } static void -discord_timers_cancel_all(struct discord *client, priority_queue *q) +discord_timers_cancel_all(struct discord *client, struct discord_timers *timers) { struct discord_timer timer; - while ((timer.id = priority_queue_pop(q, NULL, &timer))) { + while ((timer.id = priority_queue_pop(timers->q, NULL, &timer))) { timer.flags |= DISCORD_TIMER_CANCELED; if (timer.cb) timer.cb(client, &timer); } } void -discord_timers_cleanup(struct discord *client) +discord_timers_cleanup(struct discord *client, struct discord_timers *timers) { - priority_queue_set_max_capacity(client->timers.user.q, 0); - discord_timers_cancel_all(client, client->timers.user.q); - priority_queue_destroy(client->timers.user.q); - - priority_queue_set_max_capacity(client->timers.internal.q, 0); - discord_timers_cancel_all(client, client->timers.internal.q); - priority_queue_destroy(client->timers.internal.q); + priority_queue_set_max_capacity(timers->q, 0); + discord_timers_cancel_all(client, timers); + priority_queue_destroy(timers->q); } int64_t From 3c8e3f5a6cf99664621686d85eb58d909a584d65 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 20 May 2022 17:36:57 -0300 Subject: [PATCH 064/118] wip(discord-rest): triggers pthread_cond_signal() when request is done --- src/discord-rest.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/discord-rest.c b/src/discord-rest.c index 2c3f9d0c4..e22893d80 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -450,10 +450,8 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) /* enqueue request for retry or recycle */ cxt->b->performing_cxt = NULL; - if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) + if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) { discord_async_recycle_context(&rest->async, cxt); - - if (cxt->dispatch.sync) { pthread_mutex_lock(&cxt->b->sync.lock); pthread_cond_signal(&cxt->b->sync.cond); pthread_mutex_unlock(&cxt->b->sync.lock); From 612f01ec25562b14a76c317f295a4cb5a80523a2 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 17:54:26 -0400 Subject: [PATCH 065/118] feat(io_poller): add io_poller_wakeup --- core/io_poller.c | 75 ++++++++++++++++++++++++++++++++---------------- core/io_poller.h | 8 ++++++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/core/io_poller.c b/core/io_poller.c index 6ad6e1fdc..c41d9c59e 100644 --- a/core/io_poller.c +++ b/core/io_poller.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -38,8 +40,19 @@ struct io_poller { struct io_curlm **curlm; int curlm_cap; int curlm_cnt; + + int wakeup_fds[2]; }; +static void +on_io_poller_wakeup(struct io_poller *io, + enum io_poller_events events, + void *user_data) +{ + char buf[0x1000]; + read(io->wakeup_fds[0], buf, sizeof buf); +} + struct io_poller * io_poller_create(void) { @@ -48,8 +61,18 @@ io_poller_create(void) io->cap = 0x10; io->elements = calloc(io->cap, sizeof *io->elements); io->pollfds = calloc(io->cap, sizeof *io->pollfds); - if (io->elements && io->pollfds) - return io; + if (io->elements && io->pollfds) { + if (0 == pipe(io->wakeup_fds)) { + int flags = fcntl(io->wakeup_fds[0], F_GETFL); + fcntl(io->wakeup_fds[0], F_SETFL, flags | O_NONBLOCK); + + io_poller_socket_add(io, io->wakeup_fds[0], IO_POLLER_IN, + on_io_poller_wakeup, NULL); + return io; + } + free(io->elements); + free(io->pollfds); + } free(io->elements); free(io->pollfds); free(io); @@ -60,6 +83,8 @@ io_poller_create(void) void io_poller_destroy(struct io_poller *io) { + close(io->wakeup_fds[0]); + close(io->wakeup_fds[1]); for (int i = 0; i < io->curlm_cnt; i++) { free(io->curlm[i]->fds); free(io->curlm[i]); @@ -70,6 +95,13 @@ io_poller_destroy(struct io_poller *io) free(io); } +void +io_poller_wakeup(struct io_poller *io) +{ + char buf = 0; + write(io->wakeup_fds[1], &buf, sizeof buf); +} + int io_poller_poll(struct io_poller *io, int milliseconds) { @@ -96,10 +128,8 @@ io_poller_perform(struct io_poller *io) for (int i = 0; i < io->cnt; i++) { if (io->pollfds[i].revents) { int events = 0; - if (io->pollfds[i].revents & POLLIN) - events |= IO_POLLER_IN; - if (io->pollfds[i].revents & POLLOUT) - events |= IO_POLLER_OUT; + if (io->pollfds[i].revents & POLLIN) events |= IO_POLLER_IN; + if (io->pollfds[i].revents & POLLOUT) events |= IO_POLLER_OUT; io->pollfds[i].revents = 0; struct io_poller_element *element = &io->elements[i]; element->cb(io, events, element->user_data); @@ -107,15 +137,15 @@ io_poller_perform(struct io_poller *io) } for (int i = 0; i < io->curlm_cnt; i++) { struct io_curlm *curlm = io->curlm[i]; - if (curlm->should_perform || - (-1 != curlm->timeout && now >= curlm->timeout)) { + if (curlm->should_perform + || (-1 != curlm->timeout && now >= curlm->timeout)) { curlm->should_perform = false; - int result = curlm->cb ? - curlm->cb(io, curlm->multi, curlm->user_data) : - curl_multi_socket_all(curlm->multi, &curlm->running); - - if (result != 0) - return result; + int result = + curlm->cb + ? curlm->cb(io, curlm->multi, curlm->user_data) + : curl_multi_socket_all(curlm->multi, &curlm->running); + + if (result != 0) return result; } } return 0; @@ -156,10 +186,8 @@ io_poller_socket_add(struct io_poller *io, modify: io->pollfds[index].events = 0; - if (events & IO_POLLER_IN) - io->pollfds[index].events |= POLLIN; - if (events & IO_POLLER_OUT) - io->pollfds[index].events |= POLLOUT; + if (events & IO_POLLER_IN) io->pollfds[index].events |= POLLIN; + if (events & IO_POLLER_OUT) io->pollfds[index].events |= POLLOUT; io->elements[index].cb = cb; io->elements[index].user_data = user_data; return true; @@ -241,7 +269,8 @@ curl_socket_cb( } io_curlm->fds[io_curlm->fds_cnt++] = fd; } - io_poller_socket_add(io_curlm->io_poller, fd, events, io_curl_cb, io_curlm); + io_poller_socket_add(io_curlm->io_poller, fd, events, io_curl_cb, + io_curlm); return CURLM_OK; } @@ -264,7 +293,7 @@ io_poller_curlm_add(struct io_poller *io, CURLM *multi, io_poller_curl_cb cb, void *user_data) -{ +{ struct io_curlm *io_curlm = NULL; size_t index = 0; for (; index < io->curlm_cnt; index++) { @@ -282,13 +311,12 @@ io_poller_curlm_add(struct io_poller *io, io->curlm_cap = cap; } - if (!(io_curlm = calloc(1, sizeof *io_curlm))) - return false; + if (!(io_curlm = calloc(1, sizeof *io_curlm))) return false; io->curlm[io->curlm_cnt++] = io_curlm; io_curlm->io_poller = io; io_curlm->multi = multi; io_curlm->timeout = -1; - io_curlm->should_perform = true; + io_curlm->should_perform = true; curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); curl_multi_setopt(multi, CURLMOPT_TIMERDATA, io_curlm); curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, curl_socket_cb); @@ -323,7 +351,6 @@ io_poller_curlm_del(struct io_poller *io, CURLM *multi) return false; } - bool io_poller_curlm_enable_perform(struct io_poller *io, CURLM *multi) { diff --git a/core/io_poller.h b/core/io_poller.h index c7f717e79..bc3ba5cb2 100644 --- a/core/io_poller.h +++ b/core/io_poller.h @@ -32,6 +32,14 @@ typedef void (*io_poller_cb)(struct io_poller *io, struct io_poller *io_poller_create(void); void io_poller_destroy(struct io_poller *io); +/** + * @brief wakeup the thread listening to this io_poller + * + * @param io the io_poller to wake up + */ +void +io_poller_wakeup(struct io_poller *io); + /** * @brief wait for events to be triggered * @param io the io_poller to poll on From 5777a1eb4e7366592ef7973d7f480e1cf908690d Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 18:27:13 -0400 Subject: [PATCH 066/118] refactor(discord-rest_async.c): remove useless enable_perform calls, and add wakeup for io_poller --- src/discord-rest_async.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 267712903..35569be25 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -102,9 +102,6 @@ discord_async_add_request(struct discord_async *async, /* initiate libcurl transfer */ mcode = curl_multi_add_handle(async->mhandle, ehandle); - io_poller_curlm_enable_perform(CLIENT(async, rest.async)->rest.io_poller, - async->mhandle); - return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; } @@ -242,7 +239,6 @@ discord_async_start_context(struct discord_async *async, req->dispatch.cleanup, false); } - io_poller_curlm_enable_perform(rest->io_poller, async->mhandle); - + io_poller_wakeup(rest->io_poller); return cxt; } From 02a0d91b5aec6ab1f6d6976180917a98c37f4296 Mon Sep 17 00:00:00 2001 From: Anotra Date: Fri, 20 May 2022 19:11:35 -0400 Subject: [PATCH 067/118] refactor(discord-rest.c): increase poll time to 60 seconds --- src/discord-rest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discord-rest.c b/src/discord-rest.c index e22893d80..8bae7f614 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -35,20 +35,20 @@ _discord_rest_manager(void *p_rest) int64_t now, trigger; while (1) { + discord_rest_async_perform(rest); now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); - trigger = discord_timers_get_next_trigger(timers, 1, now, 1000000); + trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); int poll_result = io_poller_poll(rest->io_poller, (int)(trigger / 1000)); now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); if (0 == poll_result) { - trigger = discord_timers_get_next_trigger(timers, 1, now, 1000000); + trigger = discord_timers_get_next_trigger(timers, 1, now, 1000); if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); } discord_timers_run(CLIENT(rest, rest), &rest->timers); io_poller_perform(rest->io_poller); - discord_rest_async_perform(rest); } discord_rest_stop_buckets(rest); From 0ed9322049737a983e0e27c671bb18f09c796fb3 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 20 May 2022 20:48:09 -0300 Subject: [PATCH 068/118] refactor(discord-rest): move REST io_poller to 'struct discord_async'; rename idle_contexts to queues->recycling and add queues->completed --- core/user-agent.c | 6 +- include/discord-internal.h | 27 ++- src/discord-rest.c | 460 ++++++++++++++++++------------------- src/discord-rest_async.c | 61 ++--- 4 files changed, 282 insertions(+), 272 deletions(-) diff --git a/core/user-agent.c b/core/user-agent.c index 6af634793..4530e07e3 100644 --- a/core/user-agent.c +++ b/core/user-agent.c @@ -522,12 +522,10 @@ ua_init(struct ua_attr *attr) void ua_cleanup(struct user_agent *ua) { - QUEUE(struct ua_conn) - *ua_queues[] = { &ua->connq->idle, &ua->connq->busy }; - size_t i; + QUEUE *const ua_queues[] = { &ua->connq->idle, &ua->connq->busy }; /* cleanup connection queues */ - for (i = 0; i < sizeof(ua_queues) / sizeof(QUEUE *); ++i) { + for (size_t i = 0; i < sizeof(ua_queues) / sizeof *ua_queues; ++i) { QUEUE(struct ua_conn) queue, *qelem; struct ua_conn *conn; diff --git a/include/discord-internal.h b/include/discord-internal.h index 526fba966..41964eb41 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -198,6 +198,8 @@ struct discord_ret_dispatch { /** @brief Attributes of response datatype */ struct discord_ret_response { + /** pointer to datatype */ + void *data; /** size of datatype in bytes */ size_t size; /** initializer function for datatype fields */ @@ -262,8 +264,19 @@ struct discord_async { struct logconf conf; /** curl_multi handle for performing asynchronous requests */ CURLM *mhandle; - /** idle request contexts */ - QUEUE(struct discord_context) * idle_contexts; + /** io_poller for rest only */ + struct io_poller *io_poller; + + /** context queues */ + struct { + /** requests contexts for recycling */ + QUEUE(struct discord_context) recycling; + /** + * finished requests contexts that are done performing and waiting for + * their callbacks to be called from the main thread + */ + QUEUE(struct discord_context) finished; + } * queues; }; /** @@ -312,8 +325,8 @@ bool discord_async_retry_context(struct discord_async *async, int64_t wait_ms); /** - * @brief Insert a @ref discord_context structure into `async.idle_contexts` - * queue for recycling + * @brief Insert a @ref discord_context structure into + * `async.queues->recycling` queue for recycling * * @param async the async handle initialized with discord_async_init() * @param cxt the request context to be recycled @@ -397,7 +410,7 @@ void discord_ratelimiter_init(struct discord_ratelimiter *rl, /** * @brief Cleanup all buckets that have been discovered * - * @note pending requests will be moved to `rest.idle_contexts` + * @note pending requests will be moved to `rest.queues->recycling` * @param rl the handle initialized with discord_ratelimiter_init() */ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); @@ -526,8 +539,6 @@ struct discord_rest { struct user_agent *ua; /** store individual contexts from asynchronous requests */ struct discord_async async; - /** io_poller for rest only */ - struct io_poller *io_poller; /** the timer queue for the rest thread */ struct discord_timers timers; @@ -590,7 +601,7 @@ CCORDcode discord_rest_async_perform(struct discord_rest *rest); /** * @brief Stop all bucket's on-going, pending and timed-out requests * - * The requests will be moved over to client's 'idle_contexts' queue + * The requests will be moved over to client's 'queues->recycling' queue * @param rest the handle initialized with discord_rest_init() */ void discord_rest_stop_buckets(struct discord_rest *rest); diff --git a/src/discord-rest.c b/src/discord-rest.c index 8bae7f614..2dbfafc7f 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -27,228 +27,6 @@ _discord_rest_setopt_cb(struct ua_conn *conn, void *p_token) #endif } -static void -_discord_rest_manager(void *p_rest) -{ - struct discord_rest *rest = p_rest; - struct discord_timers *const timers[] = { &rest->timers }; - int64_t now, trigger; - - while (1) { - discord_rest_async_perform(rest); - now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); - - trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); - int poll_result = - io_poller_poll(rest->io_poller, (int)(trigger / 1000)); - - now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); - if (0 == poll_result) { - trigger = discord_timers_get_next_trigger(timers, 1, now, 1000); - if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); - } - discord_timers_run(CLIENT(rest, rest), &rest->timers); - io_poller_perform(rest->io_poller); - } - - discord_rest_stop_buckets(rest); -} - -void -discord_rest_init(struct discord_rest *rest, - struct logconf *conf, - struct ccord_szbuf_readonly *token) -{ - struct ua_attr attr = { 0 }; - - attr.conf = conf; - rest->ua = ua_init(&attr); - ua_set_url(rest->ua, DISCORD_API_BASE_URL); - - if (!token->size) { - /* no token means a webhook-only client */ - logconf_branch(&rest->conf, conf, "DISCORD_WEBHOOK"); - } - else { - /* bot client */ - logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); - ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); - } - discord_timers_init(&rest->timers); - rest->io_poller = io_poller_create(); - discord_async_init(&rest->async, &rest->conf); - discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); - - rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ - - rest->tpool = threadpool_create(1, 1024, 0); - ASSERT_S(0 == threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), - "Couldn't initialize REST managagement thread"); -} - -void -discord_rest_cleanup(struct discord_rest *rest) -{ - discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); - /* cleanup REST managing thread */ - threadpool_destroy(rest->tpool, threadpool_graceful); - /* cleanup User-Agent handle */ - ua_cleanup(rest->ua); - /* move pending requests to idle_contexts */ - discord_rest_stop_buckets(rest); - /* cleanup idle requests queue */ - discord_async_cleanup(&rest->async); - /* cleanup discovered buckets */ - discord_ratelimiter_cleanup(&rest->ratelimiter); - /* cleanup REST io_poller */ - io_poller_destroy(rest->io_poller); -} - -static CCORDcode _discord_rest_start_context(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); - -/* template function for performing requests */ -CCORDcode -discord_rest_run(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint_fmt[], - ...) -{ - char endpoint[DISCORD_ENDPT_LEN]; - char key[DISCORD_ROUTE_LEN]; - va_list args; - int len; - - /* have it point somewhere */ - if (!req) { - static struct discord_request blank = { 0 }; - req = ␣ - } - if (!body) { - static struct ccord_szbuf blank = { 0 }; - body = ␣ - } - - /* build the endpoint string */ - va_start(args, endpoint_fmt); - len = vsnprintf(endpoint, sizeof(endpoint), endpoint_fmt, args); - ASSERT_NOT_OOB(len, sizeof(endpoint)); - va_end(args); - - /* build the bucket's key */ - va_start(args, endpoint_fmt); - discord_ratelimiter_build_key(method, key, endpoint_fmt, args); - va_end(args); - - return _discord_rest_start_context(rest, req, body, method, endpoint, key); -} - -/* return true if there should be a retry attempt */ -static bool -_discord_rest_get_info(struct discord_rest *rest, - struct ua_info *info, - int64_t *wait_ms) -{ - if (info->code != CCORD_HTTP_CODE) { - /* CCORD_OK or internal error */ - return false; - } - - switch (info->httpcode) { - case HTTP_FORBIDDEN: - case HTTP_NOT_FOUND: - case HTTP_BAD_REQUEST: - info->code = CCORD_DISCORD_JSON_CODE; - return false; - case HTTP_UNAUTHORIZED: - logconf_fatal( - &rest->conf, - "UNAUTHORIZED: Please provide a valid authentication token"); - info->code = CCORD_DISCORD_BAD_AUTH; - return false; - case HTTP_METHOD_NOT_ALLOWED: - logconf_fatal(&rest->conf, - "METHOD_NOT_ALLOWED: The server couldn't recognize the " - "received HTTP method"); - return false; - case HTTP_TOO_MANY_REQUESTS: { - struct ua_szbuf_readonly body = ua_info_get_body(info); - struct jsmnftok message = { 0 }; - double retry_after = 1.0; - bool is_global = false; - jsmn_parser parser; - jsmntok_t tokens[16]; - - jsmn_init(&parser); - if (0 < jsmn_parse(&parser, body.start, body.size, tokens, - sizeof(tokens) / sizeof *tokens)) - { - jsmnf_loader loader; - jsmnf_pair pairs[16]; - - jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, - pairs, sizeof(pairs) / sizeof *pairs)) - { - jsmnf_pair *f; - - if ((f = jsmnf_find(pairs, body.start, "global", 6))) - is_global = ('t' == body.start[f->v.pos]); - if ((f = jsmnf_find(pairs, body.start, "message", 7))) - message = f->v; - if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) - retry_after = strtod(body.start + f->v.pos, NULL); - } - } - - *wait_ms = (int64_t)(1000 * retry_after); - if (*wait_ms < 0) *wait_ms = 0; - - logconf_warn(&rest->conf, - "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", - is_global ? "GLOBAL " : "", *wait_ms, message.len, - body.start + message.pos); - - return true; - } - default: - if (info->httpcode >= 500) { /* Server Error */ - return true; - } - return false; - } -} - -/* enqueue a request to be executed asynchronously */ -static CCORDcode -_discord_rest_start_context(struct discord_rest *rest, - struct discord_request *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - struct discord_context *cxt = discord_async_start_context( - &rest->async, req, body, method, endpoint, key); - - pthread_mutex_lock(&cxt->b->sync.lock); - - discord_bucket_add_context(cxt->b, cxt, cxt->dispatch.high_p); - - if (cxt->dispatch.sync) - pthread_cond_wait(&cxt->b->sync.cond, &cxt->b->sync.lock); - - pthread_mutex_unlock(&cxt->b->sync.lock); - - return CCORD_OK; -} - static void _discord_context_to_multipart(curl_mime *mime, void *p_cxt) { @@ -359,6 +137,82 @@ _discord_rest_check_pending(struct discord_rest *rest) return CCORD_OK; } +/* return true if there should be a retry attempt */ +static bool +_discord_rest_get_info(struct discord_rest *rest, + struct ua_info *info, + int64_t *wait_ms) +{ + if (info->code != CCORD_HTTP_CODE) { + /* CCORD_OK or internal error */ + return false; + } + + switch (info->httpcode) { + case HTTP_FORBIDDEN: + case HTTP_NOT_FOUND: + case HTTP_BAD_REQUEST: + info->code = CCORD_DISCORD_JSON_CODE; + return false; + case HTTP_UNAUTHORIZED: + logconf_fatal( + &rest->conf, + "UNAUTHORIZED: Please provide a valid authentication token"); + info->code = CCORD_DISCORD_BAD_AUTH; + return false; + case HTTP_METHOD_NOT_ALLOWED: + logconf_fatal(&rest->conf, + "METHOD_NOT_ALLOWED: The server couldn't recognize the " + "received HTTP method"); + return false; + case HTTP_TOO_MANY_REQUESTS: { + struct ua_szbuf_readonly body = ua_info_get_body(info); + struct jsmnftok message = { 0 }; + double retry_after = 1.0; + bool is_global = false; + jsmn_parser parser; + jsmntok_t tokens[16]; + + jsmn_init(&parser); + if (0 < jsmn_parse(&parser, body.start, body.size, tokens, + sizeof(tokens) / sizeof *tokens)) + { + jsmnf_loader loader; + jsmnf_pair pairs[16]; + + jsmnf_init(&loader); + if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, + pairs, sizeof(pairs) / sizeof *pairs)) + { + jsmnf_pair *f; + + if ((f = jsmnf_find(pairs, body.start, "global", 6))) + is_global = ('t' == body.start[f->v.pos]); + if ((f = jsmnf_find(pairs, body.start, "message", 7))) + message = f->v; + if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) + retry_after = strtod(body.start + f->v.pos, NULL); + } + } + + *wait_ms = (int64_t)(1000 * retry_after); + if (*wait_ms < 0) *wait_ms = 0; + + logconf_warn(&rest->conf, + "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", + is_global ? "GLOBAL " : "", *wait_ms, message.len, + body.start + message.pos); + + return true; + } + default: + if (info->httpcode >= 500) { /* Server Error */ + return true; + } + return false; + } +} + static CCORDcode _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) { @@ -370,6 +224,8 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); + pthread_mutex_lock(&cxt->b->sync.lock); + resp = (struct discord_response){ .data = cxt->dispatch.data, .keep = cxt->dispatch.keep, .code = CCORD_OK }; @@ -380,9 +236,10 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) struct ua_info info = { 0 }; ua_info_extract(cxt->conn, &info); - retry = _discord_rest_get_info(rest, &info, &wait_ms); body = ua_info_get_body(&info); + retry = _discord_rest_get_info(rest, &info, &wait_ms); + resp.code = info.code; if (resp.code != CCORD_OK) { @@ -406,24 +263,27 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) cxt->dispatch.done.typeless(client, &resp); } else { - void *ret_data = calloc(1, cxt->response.size); + cxt->response.data = calloc(1, cxt->response.size); /* initialize ret_data */ - if (cxt->response.init) cxt->response.init(ret_data); + if (cxt->response.init) cxt->response.init(cxt->response.data); /* populate ret_data */ if (cxt->response.from_json) - cxt->response.from_json(body.start, body.size, ret_data); + cxt->response.from_json(body.start, body.size, + cxt->response.data); if (CCORD_UNAVAILABLE - == discord_refcounter_incr(&client->refcounter, ret_data)) + == discord_refcounter_incr(&client->refcounter, + cxt->response.data)) { discord_refcounter_add_internal( - &client->refcounter, ret_data, cxt->response.cleanup, + &client->refcounter, cxt->response.data, cxt->response.cleanup, true); } - cxt->dispatch.done.typed(client, &resp, ret_data); - discord_refcounter_decr(&client->refcounter, ret_data); + cxt->dispatch.done.typed(client, &resp, cxt->response.data); + discord_refcounter_decr(&client->refcounter, + cxt->response.data); } } @@ -452,10 +312,9 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) cxt->b->performing_cxt = NULL; if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) { discord_async_recycle_context(&rest->async, cxt); - pthread_mutex_lock(&cxt->b->sync.lock); pthread_cond_signal(&cxt->b->sync.cond); - pthread_mutex_unlock(&cxt->b->sync.lock); } + pthread_mutex_unlock(&cxt->b->sync.lock); return resp.code; } @@ -483,6 +342,141 @@ discord_rest_async_perform(struct discord_rest *rest) return _discord_rest_check_pending(rest); } +static void +_discord_rest_manager(void *p_rest) +{ + struct discord_rest *rest = p_rest; + struct discord_timers *const timers[] = { &rest->timers }; + int64_t now, trigger; + + while (1) { + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + + trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); + int poll_result = + io_poller_poll(rest->async.io_poller, (int)(trigger / 1000)); + + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + if (0 == poll_result) { + trigger = discord_timers_get_next_trigger(timers, 1, now, 1000); + if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); + } + discord_timers_run(CLIENT(rest, rest), &rest->timers); + io_poller_perform(rest->async.io_poller); + } + + discord_rest_stop_buckets(rest); +} + +void +discord_rest_init(struct discord_rest *rest, + struct logconf *conf, + struct ccord_szbuf_readonly *token) +{ + struct ua_attr attr = { 0 }; + + attr.conf = conf; + rest->ua = ua_init(&attr); + ua_set_url(rest->ua, DISCORD_API_BASE_URL); + + if (!token->size) { + /* no token means a webhook-only client */ + logconf_branch(&rest->conf, conf, "DISCORD_WEBHOOK"); + } + else { + /* bot client */ + logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); + ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); + } + discord_timers_init(&rest->timers); + discord_async_init(&rest->async, &rest->conf); + discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); + + rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ + + rest->tpool = threadpool_create(1, 1024, 0); + ASSERT_S(0 == threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), + "Couldn't initialize REST managagement thread"); +} + +void +discord_rest_cleanup(struct discord_rest *rest) +{ + discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); + /* cleanup REST managing thread */ + threadpool_destroy(rest->tpool, threadpool_graceful); + /* cleanup User-Agent handle */ + ua_cleanup(rest->ua); + /* move pending requests to queues->recycling */ + discord_rest_stop_buckets(rest); + /* cleanup context queues */ + discord_async_cleanup(&rest->async); + /* cleanup discovered buckets */ + discord_ratelimiter_cleanup(&rest->ratelimiter); +} + +/* enqueue a request to be executed asynchronously */ +static CCORDcode +_discord_rest_start_context(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) +{ + struct discord_context *cxt = discord_async_start_context( + &rest->async, req, body, method, endpoint, key); + + pthread_mutex_lock(&cxt->b->sync.lock); + + discord_bucket_add_context(cxt->b, cxt, cxt->dispatch.high_p); + + if (cxt->dispatch.sync) + pthread_cond_wait(&cxt->b->sync.cond, &cxt->b->sync.lock); + + pthread_mutex_unlock(&cxt->b->sync.lock); + + return CCORD_OK; +} + +/* template function for performing requests */ +CCORDcode +discord_rest_run(struct discord_rest *rest, + struct discord_request *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint_fmt[], + ...) +{ + char endpoint[DISCORD_ENDPT_LEN]; + char key[DISCORD_ROUTE_LEN]; + va_list args; + int len; + + /* have it point somewhere */ + if (!req) { + static struct discord_request blank = { 0 }; + req = ␣ + } + if (!body) { + static struct ccord_szbuf blank = { 0 }; + body = ␣ + } + + /* build the endpoint string */ + va_start(args, endpoint_fmt); + len = vsnprintf(endpoint, sizeof(endpoint), endpoint_fmt, args); + ASSERT_NOT_OOB(len, sizeof(endpoint)); + va_end(args); + + /* build the bucket's key */ + va_start(args, endpoint_fmt); + discord_ratelimiter_build_key(method, key, endpoint_fmt, args); + va_end(args); + + return _discord_rest_start_context(rest, req, body, method, endpoint, key); +} + static void _discord_rest_stop_bucket(struct discord_ratelimiter *rl, struct discord_bucket *b) @@ -494,7 +488,7 @@ _discord_rest_stop_bucket(struct discord_ratelimiter *rl, discord_async_recycle_context(async, b->performing_cxt); /* cancel pending tranfers */ - QUEUE_ADD(async->idle_contexts, &b->pending_queue); + QUEUE_ADD(&async->queues->recycling, &b->pending_queue); QUEUE_INIT(&b->pending_queue); } diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 35569be25..f6812708f 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -24,12 +24,13 @@ _discord_context_get(struct discord_async *async) { struct discord_context *cxt; - if (QUEUE_EMPTY(async->idle_contexts)) { /* create new context struct */ + if (QUEUE_EMPTY(&async->queues->recycling)) + { /* create new context struct */ cxt = _discord_context_init(); } - else { /* recycle a context struct from idle_contexts */ + else { /* recycle a context struct from queues->recycling */ QUEUE(struct discord_context) *qelem = - QUEUE_HEAD(async->idle_contexts); + QUEUE_HEAD(&async->queues->recycling); QUEUE_REMOVE(qelem); cxt = QUEUE_DATA(qelem, struct discord_context, entry); @@ -50,39 +51,46 @@ _on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) void discord_async_init(struct discord_async *async, struct logconf *conf) { - struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); - logconf_branch(&async->conf, conf, "DISCORD_ASYNC"); - /* idle_contexts is malloc'd to guarantee a client cloned by + /* queues are malloc'd to guarantee a client cloned by * discord_clone() will share the same queue with the original */ - async->idle_contexts = malloc(sizeof *async->idle_contexts); - QUEUE_INIT(async->idle_contexts); + async->queues = malloc(sizeof *async->queues); + QUEUE_INIT(&async->queues->recycling); + QUEUE_INIT(&async->queues->finished); async->mhandle = curl_multi_init(); - io_poller_curlm_add(rest->io_poller, async->mhandle, &_on_io_poller_curl, - rest); + async->io_poller = io_poller_create(); + io_poller_curlm_add(async->io_poller, async->mhandle, &_on_io_poller_curl, + CONTAINEROF(async, struct discord_rest, async)); } void discord_async_cleanup(struct discord_async *async) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; + QUEUE *const cxt_queues[] = { &async->queues->recycling, + &async->queues->finished }; - QUEUE_MOVE(async->idle_contexts, &queue); - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - QUEUE_REMOVE(&cxt->entry); - _discord_context_cleanup(cxt); + for (size_t i = 0; i < sizeof(cxt_queues) / sizeof *cxt_queues; ++i) { + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; + + QUEUE_MOVE(cxt_queues[i], &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); + + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + _discord_context_cleanup(cxt); + } } - free(async->idle_contexts); + free(async->queues); /* cleanup curl's multi handle */ - io_poller_curlm_del(CLIENT(async, rest.async)->rest.io_poller, - async->mhandle); + io_poller_curlm_del(async->io_poller, async->mhandle); curl_multi_cleanup(async->mhandle); + /* cleanup REST io_poller */ + io_poller_destroy(async->io_poller); } CCORDcode @@ -91,7 +99,6 @@ discord_async_add_request(struct discord_async *async, struct ua_conn *conn) { CURL *ehandle = ua_conn_get_easy_handle(conn); - CURLMcode mcode; cxt->conn = conn; cxt->b->performing_cxt = cxt; @@ -100,9 +107,9 @@ discord_async_add_request(struct discord_async *async, curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); /* initiate libcurl transfer */ - mcode = curl_multi_add_handle(async->mhandle, ehandle); - - return mcode ? CCORD_CURLM_INTERNAL : CCORD_OK; + return curl_multi_add_handle(async->mhandle, ehandle) + ? CCORD_CURLM_INTERNAL + : CCORD_OK; } bool @@ -153,7 +160,7 @@ discord_async_recycle_context(struct discord_async *async, discord_attachments_cleanup(&cxt->attachments); memset(cxt, 0, sizeof(struct discord_request)); - QUEUE_INSERT_TAIL(async->idle_contexts, &cxt->entry); + QUEUE_INSERT_TAIL(&async->queues->recycling, &cxt->entry); } /* Only the fields that are required at _discord_rest_request_to_multipart() @@ -239,6 +246,6 @@ discord_async_start_context(struct discord_async *async, req->dispatch.cleanup, false); } - io_poller_wakeup(rest->io_poller); + io_poller_wakeup(async->io_poller); return cxt; } From 44d8e14e1063f82334b4b852193fdf31f5bb67eb Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 21 May 2022 03:32:51 -0400 Subject: [PATCH 069/118] fix(io_poller.c): double free in io_poller_create --- core/io_poller.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/io_poller.c b/core/io_poller.c index c41d9c59e..17f870696 100644 --- a/core/io_poller.c +++ b/core/io_poller.c @@ -70,8 +70,6 @@ io_poller_create(void) on_io_poller_wakeup, NULL); return io; } - free(io->elements); - free(io->pollfds); } free(io->elements); free(io->pollfds); From 4903af430fda41ec3797e696f8e52b9376ecca3f Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 21 May 2022 12:18:45 -0400 Subject: [PATCH 070/118] fix(discord-rest.c): restore discord_rest_async_perform call --- src/discord-rest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/discord-rest.c b/src/discord-rest.c index 2dbfafc7f..832c55fd7 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -350,6 +350,8 @@ _discord_rest_manager(void *p_rest) int64_t now, trigger; while (1) { + discord_rest_async_perform(rest); + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); From 8663db763ea114b6697c732d21d0126ab8654474 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 21 May 2022 13:29:10 -0400 Subject: [PATCH 071/118] refactor(discord-rest.c): make shutting down REST thread possible --- src/discord-rest.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/discord-rest.c b/src/discord-rest.c index 832c55fd7..ffceafc84 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -349,25 +349,23 @@ _discord_rest_manager(void *p_rest) struct discord_timers *const timers[] = { &rest->timers }; int64_t now, trigger; - while (1) { - discord_rest_async_perform(rest); + discord_rest_async_perform(rest); - now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); - trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); - int poll_result = - io_poller_poll(rest->async.io_poller, (int)(trigger / 1000)); + trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); + int poll_result = + io_poller_poll(rest->async.io_poller, (int)(trigger / 1000)); - now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); - if (0 == poll_result) { - trigger = discord_timers_get_next_trigger(timers, 1, now, 1000); - if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); - } - discord_timers_run(CLIENT(rest, rest), &rest->timers); - io_poller_perform(rest->async.io_poller); + now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + if (0 == poll_result) { + trigger = discord_timers_get_next_trigger(timers, 1, now, 1000); + if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); } - - discord_rest_stop_buckets(rest); + discord_timers_run(CLIENT(rest, rest), &rest->timers); + io_poller_perform(rest->async.io_poller); + + threadpool_add(rest->tpool, _discord_rest_manager, rest, 0); } void @@ -404,9 +402,10 @@ discord_rest_init(struct discord_rest *rest, void discord_rest_cleanup(struct discord_rest *rest) { - discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); /* cleanup REST managing thread */ threadpool_destroy(rest->tpool, threadpool_graceful); + /* cleanup timers */ + discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); /* cleanup User-Agent handle */ ua_cleanup(rest->ua); /* move pending requests to queues->recycling */ From dec0e0a80cb5b93229dc8fbb0704494dc1242ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=BCller?= Date: Sat, 21 May 2022 14:47:26 -0300 Subject: [PATCH 072/118] V2.0.0 rest refactor wip (#67) * wip(discord-rest): move bucket's pthread_cond_t to individual 'struct discord_context' * fix(discord-rest_async.c): move io_poller_wakeup() back to discord_async_start_context() --- include/discord-internal.h | 18 +++++++++--------- src/discord-rest.c | 24 +++++++++++++----------- src/discord-rest_async.c | 13 +++++++++---- src/discord-rest_ratelimit.c | 9 ++++----- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 41964eb41..70df0253e 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -17,7 +17,7 @@ #include "jsmn.h" #include "jsmn-find.h" -#include "logconf.h" /* struct logconf */ +#include "logconf.h" #include "user-agent.h" #include "websockets.h" #include "work.h" @@ -251,11 +251,13 @@ struct discord_context { char key[DISCORD_ROUTE_LEN]; /** the connection handler assigned */ struct ua_conn *conn; - /** the request bucket's queue entry */ - QUEUE entry; /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; + /** the request bucket's queue entry */ + QUEUE entry; + /** synchronize synchronous requests */ + pthread_cond_t *cond; }; /** @brief The handle used for handling asynchronous requests */ @@ -352,7 +354,8 @@ struct discord_context *discord_async_start_context( struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); + char key[DISCORD_ROUTE_LEN], + struct discord_bucket *b); /** @} DiscordInternalRESTAsync */ @@ -470,11 +473,8 @@ struct discord_bucket { * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited */ struct discord_context *performing_cxt; - /** wait and notify synchronous requests */ - struct { - pthread_cond_t cond; - pthread_mutex_t lock; - } sync; + /** synchronize bucket */ + pthread_mutex_t lock; }; /** diff --git a/src/discord-rest.c b/src/discord-rest.c index ffceafc84..65e84fb98 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -224,8 +224,7 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); - pthread_mutex_lock(&cxt->b->sync.lock); - + pthread_mutex_lock(&cxt->b->lock); resp = (struct discord_response){ .data = cxt->dispatch.data, .keep = cxt->dispatch.keep, .code = CCORD_OK }; @@ -312,9 +311,9 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) cxt->b->performing_cxt = NULL; if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) { discord_async_recycle_context(&rest->async, cxt); - pthread_cond_signal(&cxt->b->sync.cond); + if (cxt->cond) pthread_cond_signal(cxt->cond); } - pthread_mutex_unlock(&cxt->b->sync.lock); + pthread_mutex_unlock(&cxt->b->lock); return resp.code; } @@ -425,17 +424,20 @@ _discord_rest_start_context(struct discord_rest *rest, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct discord_context *cxt = discord_async_start_context( - &rest->async, req, body, method, endpoint, key); + struct discord_bucket *b = discord_bucket_get(&rest->ratelimiter, key); + struct discord_context *cxt; - pthread_mutex_lock(&cxt->b->sync.lock); + pthread_mutex_lock(&b->lock); - discord_bucket_add_context(cxt->b, cxt, cxt->dispatch.high_p); + cxt = discord_async_start_context( + &rest->async, req, body, method, endpoint, key, b); - if (cxt->dispatch.sync) - pthread_cond_wait(&cxt->b->sync.cond, &cxt->b->sync.lock); + if (cxt->dispatch.sync) { + cxt->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; + pthread_cond_wait(cxt->cond, &b->lock); + } - pthread_mutex_unlock(&cxt->b->sync.lock); + pthread_mutex_unlock(&b->lock); return CCORD_OK; } diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index f6812708f..2778458e7 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -107,7 +107,7 @@ discord_async_add_request(struct discord_async *async, curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); /* initiate libcurl transfer */ - return curl_multi_add_handle(async->mhandle, ehandle) + return (curl_multi_add_handle(async->mhandle, ehandle) != CURLM_OK) ? CCORD_CURLM_INTERNAL : CCORD_OK; } @@ -198,7 +198,8 @@ discord_async_start_context(struct discord_async *async, struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) + char key[DISCORD_ROUTE_LEN], + struct discord_bucket *b) { struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); struct discord *client = CLIENT(rest, rest); @@ -227,8 +228,8 @@ discord_async_start_context(struct discord_async *async, memcpy(cxt->endpoint, endpoint, sizeof(cxt->endpoint)); /* copy bucket's key */ memcpy(cxt->key, key, sizeof(cxt->key)); - /* bucket pertaining to the request */ - cxt->b = discord_bucket_get(&rest->ratelimiter, key); + + cxt->cond = NULL; if (req->dispatch.keep) { CCORDcode code = discord_refcounter_incr(&client->refcounter, @@ -246,6 +247,10 @@ discord_async_start_context(struct discord_async *async, req->dispatch.cleanup, false); } + /* bucket pertaining to the request */ + discord_bucket_add_context(b, cxt, cxt->dispatch.high_p); + io_poller_wakeup(async->io_poller); + return cxt; } diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index fff833f8a..a4fc832b1 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -36,8 +36,7 @@ struct _discord_route { static void _discord_bucket_cleanup(struct discord_bucket *b) { - pthread_cond_destroy(&b->sync.cond); - pthread_mutex_destroy(&b->sync.lock); + pthread_mutex_destroy(&b->lock); free(b); } @@ -131,9 +130,7 @@ _discord_bucket_init(struct discord_ratelimiter *rl, b->remaining = 1; b->limit = limit; - ASSERT_S(!pthread_cond_init(&b->sync.cond, NULL), - "Couldn't initialize bucket's cond"); - ASSERT_S(!pthread_mutex_init(&b->sync.lock, NULL), + ASSERT_S(!pthread_mutex_init(&b->lock, NULL), "Couldn't initialize bucket's mutex"); QUEUE_INIT(&b->pending_queue); @@ -423,6 +420,8 @@ discord_bucket_add_context(struct discord_bucket *b, QUEUE_INSERT_HEAD(&b->pending_queue, &cxt->entry); else QUEUE_INSERT_TAIL(&b->pending_queue, &cxt->entry); + + cxt->b = b; } struct discord_context * From 0d4b39e7504aa56162458c1db07b843839b05327 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 21 May 2022 18:33:10 -0300 Subject: [PATCH 073/118] wip(discord-rest): finished requests queue that should trigger callbacks from the main thread --- include/discord-internal.h | 22 +++++-- src/discord-loop.c | 4 +- src/discord-rest.c | 116 +++++++++++++++++++++++-------------- src/discord-rest_async.c | 18 ++---- 4 files changed, 97 insertions(+), 63 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 70df0253e..6fcfebcad 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -251,6 +251,8 @@ struct discord_context { char key[DISCORD_ROUTE_LEN]; /** the connection handler assigned */ struct ua_conn *conn; + /** request's status code */ + CURLcode ecode; /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; @@ -533,20 +535,25 @@ struct discord_context *discord_bucket_remove_context( struct discord_rest { /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ struct logconf conf; - /** threadpool that manages a single REST thread */ - struct threadpool_t *tpool; /** the user agent handle for performing requests */ struct user_agent *ua; /** store individual contexts from asynchronous requests */ struct discord_async async; /** the timer queue for the rest thread */ struct discord_timers timers; - /** enforce ratelimiting on discovered buckets */ struct discord_ratelimiter ratelimiter; /** max amount of retries before a failed request gives up */ int retry_limit; + + /** REST thread manager */ + struct { + /** threadpool for managing a single REST thread */ + struct threadpool_t *tpool; + /** global lock */ + pthread_mutex_t lock; + } * manager; }; /** @@ -596,7 +603,7 @@ CCORDcode discord_rest_run(struct discord_rest *rest, * @param rest the handle initialized with discord_rest_init() * @CCORD_return */ -CCORDcode discord_rest_async_perform(struct discord_rest *rest); +CCORDcode discord_rest_perform(struct discord_rest *rest); /** * @brief Stop all bucket's on-going, pending and timed-out requests @@ -606,6 +613,13 @@ CCORDcode discord_rest_async_perform(struct discord_rest *rest); */ void discord_rest_stop_buckets(struct discord_rest *rest); +/** + * @brief Run pending callbacks from completed requests + * + * @param rest the handle initialized with discord_rest_init() + */ +void discord_rest_perform_callbacks(struct discord_rest *rest); + /** @} DiscordInternalREST */ /** @defgroup DiscordInternalGateway WebSockets API diff --git a/src/discord-loop.c b/src/discord-loop.c index 8a9cec68f..9b736a11c 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -123,9 +123,7 @@ discord_run(struct discord *client) if (next_run <= now) { BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); -#if 0 - BREAK_ON_FAIL(code, discord_rest_async_perform(&client->rest)); -#endif + discord_rest_perform_callbacks(&client->rest); /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; diff --git a/src/discord-rest.c b/src/discord-rest.c index 65e84fb98..898bdb79d 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -206,30 +206,23 @@ _discord_rest_get_info(struct discord_rest *rest, return true; } default: - if (info->httpcode >= 500) { /* Server Error */ - return true; - } - return false; + return info->httpcode >= 500; /* retry if Server Error */ } } -static CCORDcode -_discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) +static void +_discord_rest_run_finished(struct discord_rest *rest, + struct discord_context *cxt) { struct discord *client = CLIENT(rest, rest); - struct discord_response resp; - struct discord_context *cxt; int64_t wait_ms = 0LL; bool retry; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); - - pthread_mutex_lock(&cxt->b->lock); - resp = (struct discord_response){ .data = cxt->dispatch.data, - .keep = cxt->dispatch.keep, - .code = CCORD_OK }; + struct discord_response resp = { .data = cxt->dispatch.data, + .keep = cxt->dispatch.keep, + .code = CCORD_OK }; - switch (msg->data.result) { + switch (cxt->ecode) { case CURLE_OK: { struct ua_szbuf_readonly body; struct ua_info info = { 0 }; @@ -277,8 +270,8 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) cxt->response.data)) { discord_refcounter_add_internal( - &client->refcounter, cxt->response.data, cxt->response.cleanup, - true); + &client->refcounter, cxt->response.data, + cxt->response.cleanup, true); } cxt->dispatch.done.typed(client, &resp, cxt->response.data); discord_refcounter_decr(&client->refcounter, @@ -291,15 +284,17 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) } break; case CURLE_READ_ERROR: logconf_warn(&rest->conf, "Read error, will retry again"); - retry = true; + retry = true; resp.code = CCORD_CURLE_INTERNAL; + if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); + break; default: - logconf_error(&rest->conf, "(CURLE code: %d)", msg->data.result); - retry = false; + logconf_error(&rest->conf, "(CURLE code: %d)", cxt->ecode); + retry = false; resp.code = CCORD_CURLE_INTERNAL; if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); @@ -309,17 +304,33 @@ _discord_rest_check_action(struct discord_rest *rest, struct CURLMsg *msg) /* enqueue request for retry or recycle */ cxt->b->performing_cxt = NULL; - if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) { + if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) discord_async_recycle_context(&rest->async, cxt); - if (cxt->cond) pthread_cond_signal(cxt->cond); - } - pthread_mutex_unlock(&cxt->b->lock); +} + +void +discord_rest_perform_callbacks(struct discord_rest *rest) +{ + if (!QUEUE_EMPTY(&rest->async.queues->finished)) { + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; + + QUEUE_MOVE(&rest->async.queues->finished, &queue); + do { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); + + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + + _discord_rest_run_finished(rest, cxt); + } while (!QUEUE_EMPTY(&queue)); - return resp.code; + io_poller_wakeup(rest->async.io_poller); + } } CCORDcode -discord_rest_async_perform(struct discord_rest *rest) +discord_rest_perform(struct discord_rest *rest) { int alive = 0; @@ -332,10 +343,24 @@ discord_rest_async_perform(struct discord_rest *rest) struct CURLMsg *msg = curl_multi_info_read(rest->async.mhandle, &msgq); if (!msg) break; - if (CURLMSG_DONE != msg->msg) continue; - /* check for request action */ - _discord_rest_check_action(rest, msg); + if (CURLMSG_DONE == msg->msg) { + struct discord_context *cxt; + + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); + curl_multi_remove_handle(rest->async.mhandle, msg->easy_handle); + + cxt->ecode = msg->data.result; + + if (cxt->dispatch.sync) { + pthread_mutex_lock(&cxt->b->lock); + pthread_cond_signal(cxt->cond); + pthread_mutex_unlock(&cxt->b->lock); + } + else { + QUEUE_INSERT_TAIL(&rest->async.queues->finished, &cxt->entry); + } + } } return _discord_rest_check_pending(rest); @@ -348,7 +373,7 @@ _discord_rest_manager(void *p_rest) struct discord_timers *const timers[] = { &rest->timers }; int64_t now, trigger; - discord_rest_async_perform(rest); + discord_rest_perform(rest); now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); @@ -363,8 +388,8 @@ _discord_rest_manager(void *p_rest) } discord_timers_run(CLIENT(rest, rest), &rest->timers); io_poller_perform(rest->async.io_poller); - - threadpool_add(rest->tpool, _discord_rest_manager, rest, 0); + + threadpool_add(rest->manager->tpool, _discord_rest_manager, rest, 0); } void @@ -378,12 +403,10 @@ discord_rest_init(struct discord_rest *rest, rest->ua = ua_init(&attr); ua_set_url(rest->ua, DISCORD_API_BASE_URL); - if (!token->size) { - /* no token means a webhook-only client */ + if (!token->size) { /* no token means a webhook-only client */ logconf_branch(&rest->conf, conf, "DISCORD_WEBHOOK"); } - else { - /* bot client */ + else { /* bot client */ logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); } @@ -393,16 +416,18 @@ discord_rest_init(struct discord_rest *rest, rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ - rest->tpool = threadpool_create(1, 1024, 0); - ASSERT_S(0 == threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), - "Couldn't initialize REST managagement thread"); + rest->manager = malloc(sizeof *rest->manager); + ASSERT_S(!pthread_mutex_init(&rest->manager->lock, NULL), + "Couldn't initialize REST manager mutex"); + rest->manager->tpool = threadpool_create(1, 1024, 0); + ASSERT_S( + !threadpool_add(rest->manager->tpool, &_discord_rest_manager, rest, 0), + "Couldn't initialize REST managagement thread"); } void discord_rest_cleanup(struct discord_rest *rest) { - /* cleanup REST managing thread */ - threadpool_destroy(rest->tpool, threadpool_graceful); /* cleanup timers */ discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); /* cleanup User-Agent handle */ @@ -413,6 +438,10 @@ discord_rest_cleanup(struct discord_rest *rest) discord_async_cleanup(&rest->async); /* cleanup discovered buckets */ discord_ratelimiter_cleanup(&rest->ratelimiter); + /* cleanup REST managing thread */ + threadpool_destroy(rest->manager->tpool, threadpool_graceful); + pthread_mutex_destroy(&rest->manager->lock); + free(rest->manager); } /* enqueue a request to be executed asynchronously */ @@ -429,12 +458,13 @@ _discord_rest_start_context(struct discord_rest *rest, pthread_mutex_lock(&b->lock); - cxt = discord_async_start_context( - &rest->async, req, body, method, endpoint, key, b); + cxt = discord_async_start_context(&rest->async, req, body, method, + endpoint, key, b); if (cxt->dispatch.sync) { cxt->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; pthread_cond_wait(cxt->cond, &b->lock); + _discord_rest_run_finished(rest, cxt); } pthread_mutex_unlock(&b->lock); diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 2778458e7..efab08ad6 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -24,8 +24,7 @@ _discord_context_get(struct discord_async *async) { struct discord_context *cxt; - if (QUEUE_EMPTY(&async->queues->recycling)) - { /* create new context struct */ + if (QUEUE_EMPTY(&async->queues->recycling)) { /* new context struct */ cxt = _discord_context_init(); } else { /* recycle a context struct from queues->recycling */ @@ -45,7 +44,7 @@ _on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) { (void)io; (void)mhandle; - return discord_rest_async_perform(user_data); + return discord_rest_perform(user_data); } void @@ -121,9 +120,6 @@ discord_async_retry_context(struct discord_async *async, if (rest->retry_limit < cxt->retry_attempt++) return false; - CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); - - curl_multi_remove_handle(async->mhandle, ehandle); ua_conn_reset(cxt->conn); /* FIXME: wait_ms > 0 should be dealt with aswell */ @@ -136,12 +132,10 @@ void discord_async_recycle_context(struct discord_async *async, struct discord_context *cxt) { - if (!cxt) return; - struct discord_refcounter *rc = &CLIENT(async, rest.async)->refcounter; - CURL *ehandle = ua_conn_get_easy_handle(cxt->conn); - curl_multi_remove_handle(async->mhandle, ehandle); + if (!cxt) return; + if (cxt->conn) ua_conn_stop(cxt->conn); if (cxt->dispatch.keep) { @@ -169,12 +163,10 @@ static void _discord_attachments_dup(struct discord_attachments *dest, struct discord_attachments *src) { - int i; - if (!src->size) return; __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); - for (i = 0; i < src->size; ++i) { + for (int i = 0; i < src->size; ++i) { carray_insert(dest, i, src->array[i]); if (src->array[i].content) { dest->array[i].size = src->array[i].size From f426417736b35b8a2ed160078b1530c282bcf267 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 23 May 2022 21:35:49 -0300 Subject: [PATCH 074/118] refactor(discord-rest): ensure buckets are only handled from the REST thread, modularize and split functions --- include/discord-internal.h | 38 +-- src/discord-rest.c | 464 ++++++++++++++--------------------- src/discord-rest_async.c | 133 ++++++++-- src/discord-rest_ratelimit.c | 3 +- 4 files changed, 330 insertions(+), 308 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 6fcfebcad..404fcfa53 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -251,8 +251,13 @@ struct discord_context { char key[DISCORD_ROUTE_LEN]; /** the connection handler assigned */ struct ua_conn *conn; + /** request's status code */ - CURLcode ecode; + CCORDcode code; + /** how long to wait for in case of request being ratelimited */ + int64_t wait_ms; + /** whether this request should be retried */ + bool retry; /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; @@ -266,6 +271,8 @@ struct discord_context { struct discord_async { /** DISCORD_ASYNC logging module */ struct logconf conf; + /** the user agent handle for performing requests */ + struct user_agent *ua; /** curl_multi handle for performing asynchronous requests */ CURLM *mhandle; /** io_poller for rest only */ @@ -275,6 +282,8 @@ struct discord_async { struct { /** requests contexts for recycling */ QUEUE(struct discord_context) recycling; + /** pending requests waiting to be assigned to a bucket */ + QUEUE(struct discord_context) pending; /** * finished requests contexts that are done performing and waiting for * their callbacks to be called from the main thread @@ -290,8 +299,11 @@ struct discord_async { * asynchronously, and a queue for storing individual requests contexts * @param async the async handle to be initialized * @param conf pointer to @ref discord_rest logging module + * @param token the bot token */ -void discord_async_init(struct discord_async *async, struct logconf *conf); +void discord_async_init(struct discord_async *async, + struct logconf *conf, + struct ccord_szbuf_readonly *token); /** * @brief Free an Async handle @@ -301,18 +313,16 @@ void discord_async_init(struct discord_async *async, struct logconf *conf); void discord_async_cleanup(struct discord_async *async); /** - * @brief Kickstart the request by adding it to libcurl's request multiplexer - * (`CURLM` multi handle) + * @brief Kickstart a bucket request by adding it to libcurl's request + * multiplexer (`CURLM` multi handle) * * @param async the async handle initialized with discord_async_init() - * @param cxt the context of the request to be sent over - * @param conn the @ref ua_conn connection handle + * @param b the bucket to have a request sent over * @return CCORDcode for how the request went, @ref CCORD_CURLM_INTERNAL means * something wrong happened */ -CCORDcode discord_async_add_request(struct discord_async *async, - struct discord_context *cxt, - struct ua_conn *conn); +CCORDcode discord_async_start_bucket_request(struct discord_async *async, + struct discord_bucket *b); /** * @brief Request failed, enqueue it back to bucket's first position @@ -320,13 +330,10 @@ CCORDcode discord_async_add_request(struct discord_async *async, * * @param async the async handle initialized with discord_async_init() * @param cxt the failed request's context to be set for retry - * @param wait_ms in case of a @ref HTTP_TOO_MANY_REQUESTS, this is the - * ratelimiting time to wait for * @return `true` if request can be retried */ bool discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt, - int64_t wait_ms); + struct discord_context *cxt); /** * @brief Insert a @ref discord_context structure into @@ -356,8 +363,7 @@ struct discord_context *discord_async_start_context( struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN], - struct discord_bucket *b); + char key[DISCORD_ROUTE_LEN]); /** @} DiscordInternalRESTAsync */ @@ -535,8 +541,6 @@ struct discord_context *discord_bucket_remove_context( struct discord_rest { /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ struct logconf conf; - /** the user agent handle for performing requests */ - struct user_agent *ua; /** store individual contexts from asynchronous requests */ struct discord_async async; /** the timer queue for the rest thread */ diff --git a/src/discord-rest.c b/src/discord-rest.c index 898bdb79d..f1610d354 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -9,106 +9,6 @@ #include "discord.h" #include "discord-internal.h" -static void -_discord_rest_setopt_cb(struct ua_conn *conn, void *p_token) -{ - struct ccord_szbuf *token = p_token; - char auth[128]; - int len; - - len = snprintf(auth, sizeof(auth), "Bot %.*s", (int)token->size, - token->start); - ASSERT_NOT_OOB(len, sizeof(auth)); - - ua_conn_add_header(conn, "Authorization", auth); - -#ifdef CCORD_DEBUG_HTTP - curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); -#endif -} - -static void -_discord_context_to_multipart(curl_mime *mime, void *p_cxt) -{ - struct discord_context *cxt = p_cxt; - curl_mimepart *part; - char name[64]; - - /* json part */ - if (cxt->body.start && cxt->body.size) { - part = curl_mime_addpart(mime); - curl_mime_data(part, cxt->body.start, cxt->body.size); - curl_mime_type(part, "application/json"); - curl_mime_name(part, "payload_json"); - } - - /* attachment part */ - for (int i = 0; i < cxt->attachments.size; ++i) { - int len = snprintf(name, sizeof(name), "files[%d]", i); - ASSERT_NOT_OOB(len, sizeof(name)); - - if (cxt->attachments.array[i].content) { - part = curl_mime_addpart(mime); - curl_mime_data(part, cxt->attachments.array[i].content, - cxt->attachments.array[i].size - ? cxt->attachments.array[i].size - : CURL_ZERO_TERMINATED); - curl_mime_filename(part, !cxt->attachments.array[i].filename - ? "a.out" - : cxt->attachments.array[i].filename); - curl_mime_type(part, !cxt->attachments.array[i].content_type - ? "application/octet-stream" - : cxt->attachments.array[i].content_type); - curl_mime_name(part, name); - } - else if (cxt->attachments.array[i].filename) { - CURLcode code; - - /* fetch local file by the filename */ - part = curl_mime_addpart(mime); - code = - curl_mime_filedata(part, cxt->attachments.array[i].filename); - if (code != CURLE_OK) { - char errbuf[256]; - snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", - curl_easy_strerror(code), - cxt->attachments.array[i].filename); - perror(errbuf); - } - curl_mime_type(part, !cxt->attachments.array[i].content_type - ? "application/octet-stream" - : cxt->attachments.array[i].content_type); - curl_mime_name(part, name); - } - } -} - -/* add a request to libcurl's multi handle */ -static CCORDcode -_discord_rest_add_request(struct discord_rest *rest, struct discord_bucket *b) -{ - struct discord_context *cxt = discord_bucket_remove_context(b); - struct ua_conn *conn = ua_conn_start(rest->ua); - - if (HTTP_MIMEPOST == cxt->method) { - ua_conn_add_header(conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(conn, cxt, &_discord_context_to_multipart); - } - else { - ua_conn_add_header(conn, "Content-Type", "application/json"); - } - - ua_conn_setup(conn, &(struct ua_conn_attr){ - .method = cxt->method, - .body = cxt->body.start, - .body_size = cxt->body.size, - .endpoint = cxt->endpoint, - .base_url = NULL, - }); - - return discord_async_add_request(&rest->async, cxt, conn); -} - static void _discord_rest_try_add_request(struct discord_ratelimiter *rl, struct discord_bucket *b) @@ -120,224 +20,251 @@ _discord_rest_try_add_request(struct discord_ratelimiter *rl, discord_bucket_try_timeout(rl, b); } else if (!QUEUE_EMPTY(&b->pending_queue)) { - struct discord_rest *rest = - CONTAINEROF(rl, struct discord_rest, ratelimiter); + struct discord_async *async = + &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; - _discord_rest_add_request(rest, b); + discord_async_start_bucket_request(async, b); + } +} + +static void +_discord_rest_start_buckets(struct discord_rest *rest) +{ + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; + struct discord_bucket *b; + + QUEUE_MOVE(&rest->async.queues->pending, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); + + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + b = discord_bucket_get(&rest->ratelimiter, cxt->key); + discord_bucket_add_context(b, cxt, cxt->dispatch.high_p); } } static CCORDcode _discord_rest_check_pending(struct discord_rest *rest) { + _discord_rest_start_buckets(rest); + /* TODO: replace foreach with a mechanism that loops only busy buckets */ discord_ratelimiter_foreach_bucket(&rest->ratelimiter, &_discord_rest_try_add_request); + /* FIXME: redundant return value (constant) */ return CCORD_OK; } /* return true if there should be a retry attempt */ -static bool -_discord_rest_get_info(struct discord_rest *rest, - struct ua_info *info, - int64_t *wait_ms) +static void +_discord_rest_info_extract(struct discord_rest *rest, + struct discord_context *cxt, + struct ua_info *info) { + ua_info_extract(cxt->conn, info); + if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ - return false; + cxt->retry = false; } - - switch (info->httpcode) { - case HTTP_FORBIDDEN: - case HTTP_NOT_FOUND: - case HTTP_BAD_REQUEST: - info->code = CCORD_DISCORD_JSON_CODE; - return false; - case HTTP_UNAUTHORIZED: - logconf_fatal( - &rest->conf, - "UNAUTHORIZED: Please provide a valid authentication token"); - info->code = CCORD_DISCORD_BAD_AUTH; - return false; - case HTTP_METHOD_NOT_ALLOWED: - logconf_fatal(&rest->conf, - "METHOD_NOT_ALLOWED: The server couldn't recognize the " - "received HTTP method"); - return false; - case HTTP_TOO_MANY_REQUESTS: { - struct ua_szbuf_readonly body = ua_info_get_body(info); - struct jsmnftok message = { 0 }; - double retry_after = 1.0; - bool is_global = false; - jsmn_parser parser; - jsmntok_t tokens[16]; - - jsmn_init(&parser); - if (0 < jsmn_parse(&parser, body.start, body.size, tokens, - sizeof(tokens) / sizeof *tokens)) - { - jsmnf_loader loader; - jsmnf_pair pairs[16]; - - jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, - pairs, sizeof(pairs) / sizeof *pairs)) + else { + switch (info->httpcode) { + case HTTP_FORBIDDEN: + case HTTP_NOT_FOUND: + case HTTP_BAD_REQUEST: + info->code = CCORD_DISCORD_JSON_CODE; + cxt->retry = false; + break; + case HTTP_UNAUTHORIZED: + logconf_fatal( + &rest->conf, + "UNAUTHORIZED: Please provide a valid authentication token"); + info->code = CCORD_DISCORD_BAD_AUTH; + cxt->retry = false; + break; + case HTTP_METHOD_NOT_ALLOWED: + logconf_fatal( + &rest->conf, + "METHOD_NOT_ALLOWED: The server couldn't recognize the " + "received HTTP method"); + cxt->retry = false; + break; + case HTTP_TOO_MANY_REQUESTS: { + struct ua_szbuf_readonly body = ua_info_get_body(info); + struct jsmnftok message = { 0 }; + double retry_after = 1.0; + bool is_global = false; + jsmn_parser parser; + jsmntok_t tokens[16]; + + jsmn_init(&parser); + if (0 < jsmn_parse(&parser, body.start, body.size, tokens, + sizeof(tokens) / sizeof *tokens)) { - jsmnf_pair *f; - - if ((f = jsmnf_find(pairs, body.start, "global", 6))) - is_global = ('t' == body.start[f->v.pos]); - if ((f = jsmnf_find(pairs, body.start, "message", 7))) - message = f->v; - if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) - retry_after = strtod(body.start + f->v.pos, NULL); + jsmnf_loader loader; + jsmnf_pair pairs[16]; + + jsmnf_init(&loader); + if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, + pairs, sizeof(pairs) / sizeof *pairs)) + { + jsmnf_pair *f; + + if ((f = jsmnf_find(pairs, body.start, "global", 6))) + is_global = ('t' == body.start[f->v.pos]); + if ((f = jsmnf_find(pairs, body.start, "message", 7))) + message = f->v; + if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) + retry_after = strtod(body.start + f->v.pos, NULL); + } } - } - *wait_ms = (int64_t)(1000 * retry_after); - if (*wait_ms < 0) *wait_ms = 0; + cxt->wait_ms = (int64_t)(1000 * retry_after); + if (cxt->wait_ms < 0) cxt->wait_ms = 0; - logconf_warn(&rest->conf, - "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", - is_global ? "GLOBAL " : "", *wait_ms, message.len, - body.start + message.pos); + logconf_warn(&rest->conf, + "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", + is_global ? "GLOBAL " : "", cxt->wait_ms, message.len, + body.start + message.pos); - return true; - } - default: - return info->httpcode >= 500; /* retry if Server Error */ + cxt->retry = true; + break; + } + default: + cxt->retry = (info->httpcode >= 500); /* retry if Server Error */ + break; + } } } -static void -_discord_rest_run_finished(struct discord_rest *rest, - struct discord_context *cxt) +static CCORDcode +_discord_rest_fetch_callback(struct discord_rest *rest, + struct discord_context *cxt, + CURLcode ecode) { - struct discord *client = CLIENT(rest, rest); - int64_t wait_ms = 0LL; - bool retry; - - struct discord_response resp = { .data = cxt->dispatch.data, - .keep = cxt->dispatch.keep, - .code = CCORD_OK }; - - switch (cxt->ecode) { + switch (ecode) { case CURLE_OK: { struct ua_szbuf_readonly body; - struct ua_info info = { 0 }; + struct ua_info info; - ua_info_extract(cxt->conn, &info); + _discord_rest_info_extract(rest, cxt, &info); body = ua_info_get_body(&info); - retry = _discord_rest_get_info(rest, &info, &wait_ms); - - resp.code = info.code; - - if (resp.code != CCORD_OK) { + if (info.code != CCORD_OK) { logconf_error(&rest->conf, "%.*s", (int)body.size, body.start); - if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); - } - else if (cxt->dispatch.sync) { - if (cxt->dispatch.has_type - && cxt->dispatch.sync != DISCORD_SYNC_FLAG) { - /* initialize ret */ - if (cxt->response.init) cxt->response.init(cxt->dispatch.sync); - - /* populate ret */ - if (cxt->response.from_json) - cxt->response.from_json(body.start, body.size, - cxt->dispatch.sync); - } } - else if (cxt->dispatch.done.typed) { - if (!cxt->dispatch.has_type) { - cxt->dispatch.done.typeless(client, &resp); + else if (cxt->dispatch.has_type + && cxt->dispatch.sync != DISCORD_SYNC_FLAG) { + if (cxt->dispatch.sync) { + cxt->response.data = cxt->dispatch.sync; } else { cxt->response.data = calloc(1, cxt->response.size); + discord_refcounter_add_internal( + &CLIENT(rest, rest)->refcounter, cxt->response.data, + cxt->response.cleanup, true); + } - /* initialize ret_data */ - if (cxt->response.init) cxt->response.init(cxt->response.data); - - /* populate ret_data */ - if (cxt->response.from_json) - cxt->response.from_json(body.start, body.size, - cxt->response.data); - - if (CCORD_UNAVAILABLE - == discord_refcounter_incr(&client->refcounter, - cxt->response.data)) - { - discord_refcounter_add_internal( - &client->refcounter, cxt->response.data, - cxt->response.cleanup, true); - } - cxt->dispatch.done.typed(client, &resp, cxt->response.data); - discord_refcounter_decr(&client->refcounter, + /* initialize ret */ + if (cxt->response.init) cxt->response.init(cxt->response.data); + /* populate ret */ + if (cxt->response.from_json) + cxt->response.from_json(body.start, body.size, cxt->response.data); - } } discord_ratelimiter_build(&rest->ratelimiter, cxt->b, cxt->key, &info); ua_info_cleanup(&info); } break; case CURLE_READ_ERROR: - logconf_warn(&rest->conf, "Read error, will retry again"); + logconf_warn(&rest->conf, "%s (CURLE code: %d)", + curl_easy_strerror(ecode), ecode); - retry = true; - resp.code = CCORD_CURLE_INTERNAL; - - if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); + cxt->retry = true; + cxt->code = CCORD_CURLE_INTERNAL; break; default: - logconf_error(&rest->conf, "(CURLE code: %d)", cxt->ecode); - - retry = false; - resp.code = CCORD_CURLE_INTERNAL; + logconf_error(&rest->conf, "%s (CURLE code: %d)", + curl_easy_strerror(ecode), ecode); - if (cxt->dispatch.fail) cxt->dispatch.fail(client, &resp); + cxt->retry = false; + cxt->code = CCORD_CURLE_INTERNAL; break; } + return cxt->code; +} + +static CCORDcode +_discord_rest_run_callback(struct discord_rest *rest, + struct discord_context *cxt) +{ + struct discord *client = CLIENT(rest, rest); + struct discord_response resp = { .data = cxt->dispatch.data, + .keep = cxt->dispatch.keep, + .code = cxt->code }; + + if (cxt->code != CCORD_OK) { + cxt->dispatch.fail(client, &resp); + } + else if (cxt->dispatch.done.typed) { + if (!cxt->dispatch.has_type) { + cxt->dispatch.done.typeless(client, &resp); + } + else { + cxt->dispatch.done.typed(client, &resp, cxt->response.data); + discord_refcounter_decr(&client->refcounter, cxt->response.data); + } + } + /* enqueue request for retry or recycle */ cxt->b->performing_cxt = NULL; - if (!retry || !discord_async_retry_context(&rest->async, cxt, wait_ms)) + if (!discord_async_retry_context(&rest->async, cxt)) discord_async_recycle_context(&rest->async, cxt); + + return resp.code; } void discord_rest_perform_callbacks(struct discord_rest *rest) { - if (!QUEUE_EMPTY(&rest->async.queues->finished)) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; + if (0 == pthread_mutex_trylock(&rest->manager->lock)) { + if (!QUEUE_EMPTY(&rest->async.queues->finished)) { + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; - QUEUE_MOVE(&rest->async.queues->finished, &queue); - do { - qelem = QUEUE_HEAD(&queue); - QUEUE_REMOVE(qelem); + QUEUE_MOVE(&rest->async.queues->finished, &queue); + do { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); + cxt = QUEUE_DATA(qelem, struct discord_context, entry); - _discord_rest_run_finished(rest, cxt); - } while (!QUEUE_EMPTY(&queue)); + _discord_rest_run_callback(rest, cxt); + } while (!QUEUE_EMPTY(&queue)); - io_poller_wakeup(rest->async.io_poller); + io_poller_wakeup(rest->async.io_poller); + } + pthread_mutex_unlock(&rest->manager->lock); } } CCORDcode discord_rest_perform(struct discord_rest *rest) { + CCORDcode code; int alive = 0; if (CURLM_OK != curl_multi_socket_all(rest->async.mhandle, &alive)) return CCORD_CURLM_INTERNAL; /* ask for any messages/informationals from the individual transfers */ + pthread_mutex_lock(&rest->manager->lock); while (1) { int msgq = 0; struct CURLMsg *msg = curl_multi_info_read(rest->async.mhandle, &msgq); @@ -350,20 +277,19 @@ discord_rest_perform(struct discord_rest *rest) curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); curl_multi_remove_handle(rest->async.mhandle, msg->easy_handle); - cxt->ecode = msg->data.result; - - if (cxt->dispatch.sync) { - pthread_mutex_lock(&cxt->b->lock); + _discord_rest_fetch_callback(rest, cxt, msg->data.result); + if (cxt->dispatch.sync) pthread_cond_signal(cxt->cond); - pthread_mutex_unlock(&cxt->b->lock); - } - else { + else QUEUE_INSERT_TAIL(&rest->async.queues->finished, &cxt->entry); - } } } - return _discord_rest_check_pending(rest); + code = _discord_rest_check_pending(rest); + + pthread_mutex_unlock(&rest->manager->lock); + + return code; } static void @@ -397,21 +323,13 @@ discord_rest_init(struct discord_rest *rest, struct logconf *conf, struct ccord_szbuf_readonly *token) { - struct ua_attr attr = { 0 }; - - attr.conf = conf; - rest->ua = ua_init(&attr); - ua_set_url(rest->ua, DISCORD_API_BASE_URL); - - if (!token->size) { /* no token means a webhook-only client */ + if (!token->size) logconf_branch(&rest->conf, conf, "DISCORD_WEBHOOK"); - } - else { /* bot client */ + else logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); - ua_set_opt(rest->ua, token, &_discord_rest_setopt_cb); - } + discord_timers_init(&rest->timers); - discord_async_init(&rest->async, &rest->conf); + discord_async_init(&rest->async, &rest->conf, token); discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ @@ -428,20 +346,18 @@ discord_rest_init(struct discord_rest *rest, void discord_rest_cleanup(struct discord_rest *rest) { - /* cleanup timers */ + /* cleanup REST managing thread */ + threadpool_destroy(rest->manager->tpool, threadpool_graceful); + pthread_mutex_destroy(&rest->manager->lock); discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); - /* cleanup User-Agent handle */ - ua_cleanup(rest->ua); + free(rest->manager); + /* move pending requests to queues->recycling */ discord_rest_stop_buckets(rest); /* cleanup context queues */ discord_async_cleanup(&rest->async); /* cleanup discovered buckets */ discord_ratelimiter_cleanup(&rest->ratelimiter); - /* cleanup REST managing thread */ - threadpool_destroy(rest->manager->tpool, threadpool_graceful); - pthread_mutex_destroy(&rest->manager->lock); - free(rest->manager); } /* enqueue a request to be executed asynchronously */ @@ -453,23 +369,23 @@ _discord_rest_start_context(struct discord_rest *rest, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { - struct discord_bucket *b = discord_bucket_get(&rest->ratelimiter, key); struct discord_context *cxt; + CCORDcode code = CCORD_OK; - pthread_mutex_lock(&b->lock); + pthread_mutex_lock(&rest->manager->lock); cxt = discord_async_start_context(&rest->async, req, body, method, - endpoint, key, b); + endpoint, key); if (cxt->dispatch.sync) { cxt->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; - pthread_cond_wait(cxt->cond, &b->lock); - _discord_rest_run_finished(rest, cxt); + pthread_cond_wait(cxt->cond, &rest->manager->lock); + code = _discord_rest_run_callback(rest, cxt); } - pthread_mutex_unlock(&b->lock); + pthread_mutex_unlock(&rest->manager->lock); - return CCORD_OK; + return code; } /* template function for performing requests */ diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index efab08ad6..39beab061 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -40,27 +40,53 @@ _discord_context_get(struct discord_async *async) } static int -_on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) +_discord_on_rest_perform(struct io_poller *io, CURLM *mhandle, void *p_rest) { (void)io; (void)mhandle; - return discord_rest_perform(user_data); + return discord_rest_perform(p_rest); +} + +static void +_discord_on_curl_setopt(struct ua_conn *conn, void *p_token) +{ + struct ccord_szbuf *token = p_token; + char auth[128]; + int len; + + len = snprintf(auth, sizeof(auth), "Bot %.*s", (int)token->size, + token->start); + ASSERT_NOT_OOB(len, sizeof(auth)); + + ua_conn_add_header(conn, "Authorization", auth); + +#ifdef CCORD_DEBUG_HTTP + curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); +#endif } void -discord_async_init(struct discord_async *async, struct logconf *conf) +discord_async_init(struct discord_async *async, + struct logconf *conf, + struct ccord_szbuf_readonly *token) { logconf_branch(&async->conf, conf, "DISCORD_ASYNC"); + async->ua = ua_init(&(struct ua_attr){ .conf = conf }); + ua_set_url(async->ua, DISCORD_API_BASE_URL); + ua_set_opt(async->ua, token, &_discord_on_curl_setopt); + /* queues are malloc'd to guarantee a client cloned by * discord_clone() will share the same queue with the original */ async->queues = malloc(sizeof *async->queues); QUEUE_INIT(&async->queues->recycling); + QUEUE_INIT(&async->queues->pending); QUEUE_INIT(&async->queues->finished); async->mhandle = curl_multi_init(); async->io_poller = io_poller_create(); - io_poller_curlm_add(async->io_poller, async->mhandle, &_on_io_poller_curl, + io_poller_curlm_add(async->io_poller, async->mhandle, + &_discord_on_rest_perform, CONTAINEROF(async, struct discord_rest, async)); } @@ -68,6 +94,7 @@ void discord_async_cleanup(struct discord_async *async) { QUEUE *const cxt_queues[] = { &async->queues->recycling, + &async->queues->pending, &async->queues->finished }; for (size_t i = 0; i < sizeof(cxt_queues) / sizeof *cxt_queues; ++i) { @@ -90,17 +117,92 @@ discord_async_cleanup(struct discord_async *async) curl_multi_cleanup(async->mhandle); /* cleanup REST io_poller */ io_poller_destroy(async->io_poller); + /* cleanup User-Agent handle */ + ua_cleanup(async->ua); +} + +static void +_discord_context_to_multipart(curl_mime *mime, void *p_cxt) +{ + struct discord_context *cxt = p_cxt; + curl_mimepart *part; + char name[64]; + + /* json part */ + if (cxt->body.start && cxt->body.size) { + part = curl_mime_addpart(mime); + curl_mime_data(part, cxt->body.start, cxt->body.size); + curl_mime_type(part, "application/json"); + curl_mime_name(part, "payload_json"); + } + + /* attachment part */ + for (int i = 0; i < cxt->attachments.size; ++i) { + int len = snprintf(name, sizeof(name), "files[%d]", i); + ASSERT_NOT_OOB(len, sizeof(name)); + + if (cxt->attachments.array[i].content) { + part = curl_mime_addpart(mime); + curl_mime_data(part, cxt->attachments.array[i].content, + cxt->attachments.array[i].size + ? cxt->attachments.array[i].size + : CURL_ZERO_TERMINATED); + curl_mime_filename(part, !cxt->attachments.array[i].filename + ? "a.out" + : cxt->attachments.array[i].filename); + curl_mime_type(part, !cxt->attachments.array[i].content_type + ? "application/octet-stream" + : cxt->attachments.array[i].content_type); + curl_mime_name(part, name); + } + else if (cxt->attachments.array[i].filename) { + CURLcode code; + + /* fetch local file by the filename */ + part = curl_mime_addpart(mime); + code = + curl_mime_filedata(part, cxt->attachments.array[i].filename); + if (code != CURLE_OK) { + char errbuf[256]; + snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", + curl_easy_strerror(code), + cxt->attachments.array[i].filename); + perror(errbuf); + } + curl_mime_type(part, !cxt->attachments.array[i].content_type + ? "application/octet-stream" + : cxt->attachments.array[i].content_type); + curl_mime_name(part, name); + } + } } CCORDcode -discord_async_add_request(struct discord_async *async, - struct discord_context *cxt, - struct ua_conn *conn) +discord_async_start_bucket_request(struct discord_async *async, + struct discord_bucket *b) { - CURL *ehandle = ua_conn_get_easy_handle(conn); + struct discord_context *cxt = discord_bucket_remove_context(b); + CURL *ehandle; + + b->performing_cxt = cxt; + cxt->conn = ua_conn_start(async->ua); + ehandle = ua_conn_get_easy_handle(cxt->conn); + + if (HTTP_MIMEPOST == cxt->method) { + ua_conn_add_header(cxt->conn, "Content-Type", "multipart/form-data"); + ua_conn_set_mime(cxt->conn, cxt, &_discord_context_to_multipart); + } + else { + ua_conn_add_header(cxt->conn, "Content-Type", "application/json"); + } - cxt->conn = conn; - cxt->b->performing_cxt = cxt; + ua_conn_setup(cxt->conn, &(struct ua_conn_attr){ + .method = cxt->method, + .body = cxt->body.start, + .body_size = cxt->body.size, + .endpoint = cxt->endpoint, + .base_url = NULL, + }); /* link 'cxt' to 'ehandle' for easy retrieval */ curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); @@ -113,17 +215,17 @@ discord_async_add_request(struct discord_async *async, bool discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt, - int64_t wait_ms) + struct discord_context *cxt) { struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); + if (!cxt->retry) return false; if (rest->retry_limit < cxt->retry_attempt++) return false; ua_conn_reset(cxt->conn); /* FIXME: wait_ms > 0 should be dealt with aswell */ - if (wait_ms <= 0) discord_bucket_add_context(cxt->b, cxt, true); + if (cxt->wait_ms <= 0) discord_bucket_add_context(cxt->b, cxt, true); return true; } @@ -190,8 +292,7 @@ discord_async_start_context(struct discord_async *async, struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN], - struct discord_bucket *b) + char key[DISCORD_ROUTE_LEN]) { struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); struct discord *client = CLIENT(rest, rest); @@ -240,7 +341,7 @@ discord_async_start_context(struct discord_async *async, } /* bucket pertaining to the request */ - discord_bucket_add_context(b, cxt, cxt->dispatch.high_p); + QUEUE_INSERT_TAIL(&rest->async.queues->pending, &cxt->entry); io_poller_wakeup(async->io_poller); diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index a4fc832b1..8bf90e58a 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -244,8 +244,9 @@ discord_bucket_try_timeout(struct discord_ratelimiter *rl, struct discord_bucket *b) { struct discord *client = CLIENT(rl, rest.ratelimiter); - const int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); + int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); + if (delay_ms < 0) delay_ms = 0; b->performing_cxt = DISCORD_BUCKET_TIMEOUT; _discord_timer_ctl( From 8f3359fa812ee82b47edc1fa71535e28878ac8b7 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 24 May 2022 14:55:53 -0300 Subject: [PATCH 075/118] refactor(discord-rest): tidy up codebase by moving functions and structs to their appropriate 'discord-rest' module * fix(discord-rest): race-conditions from cleanup ordering --- include/discord-internal.h | 36 ++++++----- src/discord-client.c | 6 +- src/discord-rest.c | 113 +++++++++-------------------------- src/discord-rest_async.c | 59 +++++++++++++----- src/discord-rest_ratelimit.c | 21 ++++++- 5 files changed, 111 insertions(+), 124 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 404fcfa53..255ddb289 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -278,6 +278,9 @@ struct discord_async { /** io_poller for rest only */ struct io_poller *io_poller; + /** max amount of retries before a failed request gives up */ + int retry_limit; + /** context queues */ struct { /** requests contexts for recycling */ @@ -325,25 +328,13 @@ CCORDcode discord_async_start_bucket_request(struct discord_async *async, struct discord_bucket *b); /** - * @brief Request failed, enqueue it back to bucket's first position - * for next attempt + * @brief Cancel an on-going request and move it to the recycle queue * * @param async the async handle initialized with discord_async_init() - * @param cxt the failed request's context to be set for retry - * @return `true` if request can be retried + * @param cxt the on-going request to be canceled */ -bool discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt); - -/** - * @brief Insert a @ref discord_context structure into - * `async.queues->recycling` queue for recycling - * - * @param async the async handle initialized with discord_async_init() - * @param cxt the request context to be recycled - */ -void discord_async_recycle_context(struct discord_async *async, - struct discord_context *cxt); +void discord_async_cancel_context(struct discord_async *async, + struct discord_context *cxt); /** * @brief Start request's context @@ -365,6 +356,16 @@ struct discord_context *discord_async_start_context( char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]); +/** + * @brief Run callback from a finished request + * + * @param async the async handle initialized with discord_async_init() + * @param cxt the finished request + * @CCORD_return + */ +CCORDcode discord_async_run_context_callback(struct discord_async *async, + struct discord_context *cxt); + /** @} DiscordInternalRESTAsync */ /** @defgroup DiscordInternalRESTRatelimit Ratelimiting @@ -548,9 +549,6 @@ struct discord_rest { /** enforce ratelimiting on discovered buckets */ struct discord_ratelimiter ratelimiter; - /** max amount of retries before a failed request gives up */ - int retry_limit; - /** REST thread manager */ struct { /** threadpool for managing a single REST thread */ diff --git a/src/discord-client.c b/src/discord-client.c index 668b4c7e0..6af867e82 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -169,9 +169,6 @@ void discord_cleanup(struct discord *client) { if (client->is_original) { - discord_timers_cleanup(client, &client->timers.user); - discord_timers_cleanup(client, &client->timers.internal); - logconf_cleanup(&client->conf); discord_rest_cleanup(&client->rest); discord_gateway_cleanup(&client->gw); discord_user_cleanup(&client->self); @@ -181,6 +178,9 @@ discord_cleanup(struct discord *client) #ifdef CCORD_VOICE discord_voice_connections_cleanup(client); #endif + discord_timers_cleanup(client, &client->timers.user); + discord_timers_cleanup(client, &client->timers.internal); + logconf_cleanup(&client->conf); } else { _discord_clone_cleanup(client); diff --git a/src/discord-rest.c b/src/discord-rest.c index f1610d354..831fa04c6 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -9,24 +9,6 @@ #include "discord.h" #include "discord-internal.h" -static void -_discord_rest_try_add_request(struct discord_ratelimiter *rl, - struct discord_bucket *b) -{ - /* skip if bucket is busy performing */ - if (b->performing_cxt) return; - - if (!b->remaining) { - discord_bucket_try_timeout(rl, b); - } - else if (!QUEUE_EMPTY(&b->pending_queue)) { - struct discord_async *async = - &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; - - discord_async_start_bucket_request(async, b); - } -} - static void _discord_rest_start_buckets(struct discord_rest *rest) { @@ -45,8 +27,26 @@ _discord_rest_start_buckets(struct discord_rest *rest) } } +static void +_discord_rest_try_add_request(struct discord_ratelimiter *rl, + struct discord_bucket *b) +{ + /* skip if bucket is already busy performing */ + if (b->performing_cxt) return; + + if (!b->remaining) { + discord_bucket_try_timeout(rl, b); + } + else if (!QUEUE_EMPTY(&b->pending_queue)) { + struct discord_async *async = + &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; + + discord_async_start_bucket_request(async, b); + } +} + static CCORDcode -_discord_rest_check_pending(struct discord_rest *rest) +_discord_rest_start_pending(struct discord_rest *rest) { _discord_rest_start_buckets(rest); @@ -66,8 +66,7 @@ _discord_rest_info_extract(struct discord_rest *rest, { ua_info_extract(cxt->conn, info); - if (info->code != CCORD_HTTP_CODE) { - /* CCORD_OK or internal error */ + if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ cxt->retry = false; } else { @@ -200,36 +199,6 @@ _discord_rest_fetch_callback(struct discord_rest *rest, return cxt->code; } -static CCORDcode -_discord_rest_run_callback(struct discord_rest *rest, - struct discord_context *cxt) -{ - struct discord *client = CLIENT(rest, rest); - struct discord_response resp = { .data = cxt->dispatch.data, - .keep = cxt->dispatch.keep, - .code = cxt->code }; - - if (cxt->code != CCORD_OK) { - cxt->dispatch.fail(client, &resp); - } - else if (cxt->dispatch.done.typed) { - if (!cxt->dispatch.has_type) { - cxt->dispatch.done.typeless(client, &resp); - } - else { - cxt->dispatch.done.typed(client, &resp, cxt->response.data); - discord_refcounter_decr(&client->refcounter, cxt->response.data); - } - } - - /* enqueue request for retry or recycle */ - cxt->b->performing_cxt = NULL; - if (!discord_async_retry_context(&rest->async, cxt)) - discord_async_recycle_context(&rest->async, cxt); - - return resp.code; -} - void discord_rest_perform_callbacks(struct discord_rest *rest) { @@ -241,11 +210,8 @@ discord_rest_perform_callbacks(struct discord_rest *rest) QUEUE_MOVE(&rest->async.queues->finished, &queue); do { qelem = QUEUE_HEAD(&queue); - QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - - _discord_rest_run_callback(rest, cxt); + discord_async_run_context_callback(&rest->async, cxt); } while (!QUEUE_EMPTY(&queue)); io_poller_wakeup(rest->async.io_poller); @@ -285,7 +251,7 @@ discord_rest_perform(struct discord_rest *rest) } } - code = _discord_rest_check_pending(rest); + code = _discord_rest_start_pending(rest); pthread_mutex_unlock(&rest->manager->lock); @@ -332,8 +298,6 @@ discord_rest_init(struct discord_rest *rest, discord_async_init(&rest->async, &rest->conf, token); discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); - rest->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ - rest->manager = malloc(sizeof *rest->manager); ASSERT_S(!pthread_mutex_init(&rest->manager->lock, NULL), "Couldn't initialize REST manager mutex"); @@ -349,15 +313,14 @@ discord_rest_cleanup(struct discord_rest *rest) /* cleanup REST managing thread */ threadpool_destroy(rest->manager->tpool, threadpool_graceful); pthread_mutex_destroy(&rest->manager->lock); - discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); free(rest->manager); - /* move pending requests to queues->recycling */ - discord_rest_stop_buckets(rest); - /* cleanup context queues */ - discord_async_cleanup(&rest->async); /* cleanup discovered buckets */ + discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); discord_ratelimiter_cleanup(&rest->ratelimiter); + + /* cleanup context queues */ + discord_async_cleanup(&rest->async); } /* enqueue a request to be executed asynchronously */ @@ -380,7 +343,7 @@ _discord_rest_start_context(struct discord_rest *rest, if (cxt->dispatch.sync) { cxt->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; pthread_cond_wait(cxt->cond, &rest->manager->lock); - code = _discord_rest_run_callback(rest, cxt); + code = discord_async_run_context_callback(&rest->async, cxt); } pthread_mutex_unlock(&rest->manager->lock); @@ -425,25 +388,3 @@ discord_rest_run(struct discord_rest *rest, return _discord_rest_start_context(rest, req, body, method, endpoint, key); } - -static void -_discord_rest_stop_bucket(struct discord_ratelimiter *rl, - struct discord_bucket *b) -{ - struct discord_async *async = - &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; - - /* cancel busy transfer */ - discord_async_recycle_context(async, b->performing_cxt); - - /* cancel pending tranfers */ - QUEUE_ADD(&async->queues->recycling, &b->pending_queue); - QUEUE_INIT(&b->pending_queue); -} - -void -discord_rest_stop_buckets(struct discord_rest *rest) -{ - discord_ratelimiter_foreach_bucket(&rest->ratelimiter, - &_discord_rest_stop_bucket); -} diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 39beab061..953c84fee 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -27,7 +27,7 @@ _discord_context_get(struct discord_async *async) if (QUEUE_EMPTY(&async->queues->recycling)) { /* new context struct */ cxt = _discord_context_init(); } - else { /* recycle a context struct from queues->recycling */ + else { /* fetch a context struct from queues->recycling */ QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&async->queues->recycling); @@ -88,6 +88,8 @@ discord_async_init(struct discord_async *async, io_poller_curlm_add(async->io_poller, async->mhandle, &_discord_on_rest_perform, CONTAINEROF(async, struct discord_rest, async)); + + async->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ } void @@ -213,15 +215,14 @@ discord_async_start_bucket_request(struct discord_async *async, : CCORD_OK; } -bool -discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt) +static bool +_discord_async_retry_context(struct discord_async *async, + struct discord_context *cxt) { - struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); - if (!cxt->retry) return false; - if (rest->retry_limit < cxt->retry_attempt++) return false; + if (async->retry_limit < cxt->retry_attempt++) return false; + cxt->b->performing_cxt = NULL; ua_conn_reset(cxt->conn); /* FIXME: wait_ms > 0 should be dealt with aswell */ @@ -231,22 +232,19 @@ discord_async_retry_context(struct discord_async *async, } void -discord_async_recycle_context(struct discord_async *async, - struct discord_context *cxt) +discord_async_cancel_context(struct discord_async *async, + struct discord_context *cxt) { struct discord_refcounter *rc = &CLIENT(async, rest.async)->refcounter; - if (!cxt) return; - if (cxt->conn) ua_conn_stop(cxt->conn); - if (cxt->dispatch.keep) { + if (cxt->dispatch.keep) discord_refcounter_decr(rc, (void *)cxt->dispatch.keep); - } - if (cxt->dispatch.data) { + if (cxt->dispatch.data) discord_refcounter_decr(rc, cxt->dispatch.data); - } + cxt->b->performing_cxt = NULL; cxt->body.size = 0; cxt->method = 0; *cxt->endpoint = '\0'; @@ -256,9 +254,40 @@ discord_async_recycle_context(struct discord_async *async, discord_attachments_cleanup(&cxt->attachments); memset(cxt, 0, sizeof(struct discord_request)); + QUEUE_REMOVE(&cxt->entry); + QUEUE_INIT(&cxt->entry); QUEUE_INSERT_TAIL(&async->queues->recycling, &cxt->entry); } +CCORDcode +discord_async_run_context_callback(struct discord_async *async, + struct discord_context *cxt) +{ + struct discord *client = CLIENT(async, rest.async); + struct discord_response resp = { .data = cxt->dispatch.data, + .keep = cxt->dispatch.keep, + .code = cxt->code }; + + if (cxt->code != CCORD_OK) { + cxt->dispatch.fail(client, &resp); + } + else if (cxt->dispatch.done.typed) { + if (!cxt->dispatch.has_type) { + cxt->dispatch.done.typeless(client, &resp); + } + else { + cxt->dispatch.done.typed(client, &resp, cxt->response.data); + discord_refcounter_decr(&client->refcounter, cxt->response.data); + } + } + + /* enqueue request for retry or recycle */ + if (!_discord_async_retry_context(async, cxt)) + discord_async_cancel_context(async, cxt); + + return resp.code; +} + /* Only the fields that are required at _discord_rest_request_to_multipart() * are duplicated */ static void diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 8bf90e58a..0b5b80157 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -164,9 +164,27 @@ discord_ratelimiter_init(struct discord_ratelimiter *rl, struct logconf *conf) rl->miss = _discord_bucket_init(rl, "miss", &keymiss, LONG_MAX); } +static void +_discord_bucket_cancel(struct discord_ratelimiter *rl, + struct discord_bucket *b) +{ + struct discord_async *async = + &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; + + /* cancel busy transfer */ + if (b->performing_cxt) + discord_async_cancel_context(async, b->performing_cxt); + + /* move pending tranfers to recycling */ + QUEUE_ADD(&async->queues->recycling, &b->pending_queue); + QUEUE_INIT(&b->pending_queue); +} + void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) { + discord_ratelimiter_foreach_bucket(rl, &_discord_bucket_cancel); + pthread_rwlock_destroy(&rl->global->rwlock); pthread_mutex_destroy(&rl->global->lock); free(rl->global); @@ -417,11 +435,12 @@ discord_bucket_add_context(struct discord_bucket *b, struct discord_context *cxt, bool high_priority) { + QUEUE_REMOVE(&cxt->entry); + QUEUE_INIT(&cxt->entry); if (high_priority) QUEUE_INSERT_HEAD(&b->pending_queue, &cxt->entry); else QUEUE_INSERT_TAIL(&b->pending_queue, &cxt->entry); - cxt->b = b; } From 79c0393d38a5f39e341721264b02f1c98bd59189 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 26 May 2022 14:43:11 -0300 Subject: [PATCH 076/118] refactor: replace 'discord_request' references with 'discord_attributes' --- include/discord-internal.h | 57 ++++----- include/discord-request.h | 45 ++++---- src/application_command.c | 96 ++++++++-------- src/audit_log.c | 6 +- src/channel.c | 230 ++++++++++++++++++------------------- src/discord-client.c | 14 +-- src/discord-rest.c | 114 +++++++++++------- src/discord-rest_async.c | 71 ++++-------- src/emoji.c | 30 ++--- src/gateway.c | 26 ++--- src/guild.c | 192 +++++++++++++++---------------- src/guild_template.c | 18 +-- src/interaction.c | 56 ++++----- src/invite.c | 12 +- src/user.c | 49 ++++---- src/voice.c | 6 +- src/webhook.c | 82 ++++++------- 17 files changed, 555 insertions(+), 549 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 255ddb289..0c5aacc5a 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -211,10 +211,10 @@ struct discord_ret_response { }; /** - * @brief Macro containing @ref discord_request fields + * @brief Macro containing @ref discord_attributes fields * @note this exists for @ref discord_context alignment purposes */ -#define DISCORD_REQUEST_FIELDS \ +#define DISCORD_ATTRIBUTES_FIELDS \ /** attributes set by client for request dispatch behavior */ \ struct discord_ret_dispatch dispatch; \ /** information for parsing response into a datatype (if possible) */ \ @@ -223,8 +223,8 @@ struct discord_ret_response { struct discord_attachments attachments /** @brief Request to be performed */ -struct discord_request { - DISCORD_REQUEST_FIELDS; +struct discord_attributes { + DISCORD_ATTRIBUTES_FIELDS; }; /** @defgroup DiscordInternalRESTAsync Async request's handling @@ -234,10 +234,11 @@ struct discord_request { /** * @brief Context of individual requests that are scheduled to run * asynchronously - * @note its fields are aligned with @ref discord_request + * @note this struct **SHOULD NOT** be handled from the `REST` manager thread + * @note its fields are aligned with @ref discord_attributes */ struct discord_context { - DISCORD_REQUEST_FIELDS; + DISCORD_ATTRIBUTES_FIELDS; /** the request's bucket */ struct discord_bucket *b; @@ -269,7 +270,7 @@ struct discord_context { /** @brief The handle used for handling asynchronous requests */ struct discord_async { - /** DISCORD_ASYNC logging module */ + /** `DISCORD_ASYNC` logging module */ struct logconf conf; /** the user agent handle for performing requests */ struct user_agent *ua; @@ -328,7 +329,18 @@ CCORDcode discord_async_start_bucket_request(struct discord_async *async, struct discord_bucket *b); /** - * @brief Cancel an on-going request and move it to the recycle queue + * @brief Check if request is expected to be retried and move it to its + * bucket's queue + * + * @param async the async handle initialized with discord_async_init() + * @param cxt the on-going request to be canceled + * @return `true` if request has been enqueued for retry + */ +bool discord_async_retry_context(struct discord_async *async, + struct discord_context *cxt); + +/** + * @brief Mark request as canceled and move it to the recycle queue * * @param async the async handle initialized with discord_async_init() * @param cxt the on-going request to be canceled @@ -350,22 +362,12 @@ void discord_async_cancel_context(struct discord_async *async, */ struct discord_context *discord_async_start_context( struct discord_async *async, - struct discord_request *req, + struct discord_attributes *req, struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]); -/** - * @brief Run callback from a finished request - * - * @param async the async handle initialized with discord_async_init() - * @param cxt the finished request - * @CCORD_return - */ -CCORDcode discord_async_run_context_callback(struct discord_async *async, - struct discord_context *cxt); - /** @} DiscordInternalRESTAsync */ /** @defgroup DiscordInternalRESTRatelimit Ratelimiting @@ -378,9 +380,12 @@ CCORDcode discord_async_run_context_callback(struct discord_async *async, */ #define DISCORD_BUCKET_TIMEOUT (void *)(0xf) -/** @brief The ratelimiter struct for handling ratelimiting */ +/** + * @brief The ratelimiter struct for handling ratelimiting + * @note this struct **SHOULD** only be handled from the `REST` manager thread + */ struct discord_ratelimiter { - /** DISCORD_RATELIMIT logging module */ + /** `DISCORD_RATELIMIT` logging module */ struct logconf conf; /** amount of bucket's routes discovered */ int length; @@ -540,7 +545,7 @@ struct discord_context *discord_bucket_remove_context( /** @brief The handle used for interfacing with Discord's REST API */ struct discord_rest { - /** DISCORD_HTTP or DISCORD_WEBHOOK logging module */ + /** `DISCORD_HTTP` or `DISCORD_WEBHOOK` logging module */ struct logconf conf; /** store individual contexts from asynchronous requests */ struct discord_async async; @@ -593,7 +598,7 @@ void discord_rest_cleanup(struct discord_rest *rest); * immediately */ CCORDcode discord_rest_run(struct discord_rest *rest, - struct discord_request *req, + struct discord_attributes *req, struct ccord_szbuf *body, enum http_method method, char endpoint_fmt[], @@ -663,7 +668,7 @@ struct discord_gateway_payload { /** @brief The handle used for interfacing with Discord's Gateway API */ struct discord_gateway { - /** DISCORD_GATEWAY logging module */ + /** `DISCORD_GATEWAY` logging module */ struct logconf conf; /** the websockets handle that connects to Discord */ struct websockets *ws; @@ -879,7 +884,7 @@ void discord_gateway_dispatch(struct discord_gateway *gw); * more callbacks expecting the data */ struct discord_refcounter { - /** DISCORD_REFCOUNT logging module */ + /** `DISCORD_REFCOUNT` logging module */ struct logconf conf; /** amount of individual user's data held for automatic cleanup */ int length; @@ -1011,7 +1016,7 @@ CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data); * @see discord_set_on_command() */ struct discord_message_commands { - /** DISCORD_MESSAGE_COMMANDS logging module */ + /** `DISCORD_MESSAGE_COMMANDS` logging module */ struct logconf conf; /** the prefix expected for every command */ struct ccord_szbuf prefix; diff --git a/include/discord-request.h b/include/discord-request.h index 8e6e783e2..4348b7fbf 100644 --- a/include/discord-request.h +++ b/include/discord-request.h @@ -1,8 +1,8 @@ /** * @file discord-request.h - * @ingroup DiscordInternal + * @ingroup DiscordInternalREST * @author Cogmasters - * @brief Generic macros for initializing a @ref discord_request + * @brief Generic macros for initializing a @ref discord_attributes */ #ifndef DISCORD_REQUEST_H @@ -40,41 +40,42 @@ typedef struct { /** * @brief Helper for setting attributes for a specs-generated return struct * - * @param req request handler to be initialized + * @param attr attributes handler to be initialized * @param type datatype of the struct - * @param ret request attributes + * @param ret dispatch attributes */ -#define DISCORD_REQ_INIT(req, type, ret) \ +#define DISCORD_ATTR_INIT(attr, type, ret) \ do { \ - (req).response.size = sizeof(struct type); \ - (req).response.init = (cast_init)type##_init; \ - (req).response.from_json = (cast_from_json)type##_from_json; \ - (req).response.cleanup = (cast_cleanup)type##_cleanup; \ - if (ret) _RET_COPY_TYPED(req.dispatch, *ret); \ + (attr).response.size = sizeof(struct type); \ + (attr).response.init = (cast_init)type##_init; \ + (attr).response.from_json = (cast_from_json)type##_from_json; \ + (attr).response.cleanup = (cast_cleanup)type##_cleanup; \ + if (ret) _RET_COPY_TYPED(attr.dispatch, *ret); \ } while (0) /** * @brief Helper for setting attributes for a specs-generated list * - * @param req request handler to be initialized + * @param attr attributes handler to be initialized * @param type datatype of the list - * @param ret request attributes + * @param ret dispatch attributes */ -#define DISCORD_REQ_LIST_INIT(req, type, ret) \ +#define DISCORD_ATTR_LIST_INIT(attr, type, ret) \ do { \ - (req).response.size = sizeof(struct type); \ - (req).response.from_json = (cast_from_json)type##_from_json; \ - (req).response.cleanup = (cast_cleanup)type##_cleanup; \ - if (ret) _RET_COPY_TYPED(req.dispatch, *ret); \ + (attr).response.size = sizeof(struct type); \ + (attr).response.from_json = (cast_from_json)type##_from_json; \ + (attr).response.cleanup = (cast_cleanup)type##_cleanup; \ + if (ret) _RET_COPY_TYPED(attr.dispatch, *ret); \ } while (0) /** - * @brief Helper for setting request attributes expecting no response + * @brief Helper for setting attributes for attruests that doensn't expect a + * response object * - * @param req request handler to be initialized - * @param ret request attributes + * @param attr attributes handler to be initialized + * @param ret dispatch attributes */ -#define DISCORD_REQ_BLANK_INIT(req, ret) \ - if (ret) _RET_COPY_TYPELESS(req.dispatch, *ret) +#define DISCORD_ATTR_BLANK_INIT(attr, ret) \ + if (ret) _RET_COPY_TYPELESS(attr.dispatch, *ret) #endif /* DISCORD_REQUEST_H */ diff --git a/src/application_command.c b/src/application_command.c index 88c465e34..9e2aa2b45 100644 --- a/src/application_command.c +++ b/src/application_command.c @@ -12,13 +12,13 @@ discord_get_global_application_commands( u64snowflake application_id, struct discord_ret_application_commands *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_application_commands, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/applications/%" PRIu64 "/commands", application_id); } @@ -30,7 +30,7 @@ discord_create_global_application_command( struct discord_create_global_application_command *params, struct discord_ret_application_command *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -40,13 +40,13 @@ discord_create_global_application_command( CCORD_EXPECT(client, NOT_EMPTY_STR(params->description), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_application_command, ret); + DISCORD_ATTR_INIT(attr, discord_application_command, ret); body.size = discord_create_global_application_command_to_json( buf, sizeof(buf), params); body.start = buf; - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/applications/%" PRIu64 "/commands", application_id); } @@ -58,14 +58,14 @@ discord_get_global_application_command( u64snowflake command_id, struct discord_ret_application_command *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, command_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_application_command, ret); + DISCORD_ATTR_INIT(attr, discord_application_command, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/applications/%" PRIu64 "/commands/%" PRIu64, application_id, command_id); } @@ -78,7 +78,7 @@ discord_edit_global_application_command( struct discord_edit_global_application_command *params, struct discord_ret_application_command *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -89,9 +89,9 @@ discord_edit_global_application_command( buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_application_command, ret); + DISCORD_ATTR_INIT(attr, discord_application_command, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/applications/%" PRIu64 "/commands/%" PRIu64, application_id, command_id); } @@ -102,14 +102,14 @@ discord_delete_global_application_command(struct discord *client, u64snowflake command_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, command_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/applications/%" PRIu64 "/commands/%" PRIu64, application_id, command_id); } @@ -121,7 +121,7 @@ discord_bulk_overwrite_global_application_command( struct discord_application_commands *params, struct discord_ret_application_commands *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[8192]; @@ -131,9 +131,9 @@ discord_bulk_overwrite_global_application_command( body.size = discord_application_commands_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_application_commands, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/applications/%" PRIu64 "/commands", application_id); } @@ -145,14 +145,14 @@ discord_get_guild_application_commands( u64snowflake guild_id, struct discord_ret_application_commands *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_application_commands, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands", application_id, guild_id); @@ -166,7 +166,7 @@ discord_create_guild_application_command( struct discord_create_guild_application_command *params, struct discord_ret_application_command *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -181,9 +181,9 @@ discord_create_guild_application_command( buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_application_command, ret); + DISCORD_ATTR_INIT(attr, discord_application_command, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands", application_id, guild_id); @@ -197,15 +197,15 @@ discord_get_guild_application_command( u64snowflake command_id, struct discord_ret_application_command *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, command_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_application_command, ret); + DISCORD_ATTR_INIT(attr, discord_application_command, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/%" PRIu64, application_id, guild_id, command_id); @@ -220,7 +220,7 @@ discord_edit_guild_application_command( struct discord_edit_guild_application_command *params, struct discord_ret_application_command *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -232,9 +232,9 @@ discord_edit_guild_application_command( buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_application_command, ret); + DISCORD_ATTR_INIT(attr, discord_application_command, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/%" PRIu64, application_id, guild_id, command_id); @@ -247,15 +247,15 @@ discord_delete_guild_application_command(struct discord *client, u64snowflake command_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, command_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/%" PRIu64, application_id, guild_id, command_id); @@ -269,7 +269,7 @@ discord_bulk_overwrite_guild_application_command( struct discord_application_commands *params, struct discord_ret_application_commands *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[8192]; @@ -280,9 +280,9 @@ discord_bulk_overwrite_guild_application_command( body.size = discord_application_commands_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_LIST_INIT(req, discord_application_commands, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_application_commands, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands", application_id, guild_id); @@ -295,14 +295,14 @@ discord_get_guild_application_command_permissions( u64snowflake guild_id, struct discord_ret_guild_application_command_permissions *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_application_command_permissions, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_application_command_permissions, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/permissions", application_id, guild_id); @@ -316,15 +316,15 @@ discord_get_application_command_permissions( u64snowflake command_id, struct discord_ret_application_command_permission *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, command_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_application_command_permission, ret); + DISCORD_ATTR_INIT(attr, discord_application_command_permission, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/%" PRIu64 "/permissions", application_id, guild_id, command_id); @@ -339,7 +339,7 @@ discord_edit_application_command_permissions( struct discord_edit_application_command_permissions *params, struct discord_ret_application_command_permission *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[8192]; @@ -351,9 +351,9 @@ discord_edit_application_command_permissions( buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_application_command_permission, ret); + DISCORD_ATTR_INIT(attr, discord_application_command_permission, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/%" PRIu64 "/permissions", application_id, guild_id, command_id); @@ -367,7 +367,7 @@ discord_batch_edit_application_command_permissions( struct discord_guild_application_command_permissions *params, struct discord_ret_guild_application_command_permissions *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[8192]; @@ -379,9 +379,9 @@ discord_batch_edit_application_command_permissions( buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_LIST_INIT(req, discord_application_command_permissions, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_application_command_permissions, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/applications/%" PRIu64 "/guilds/%" PRIu64 "/commands/permissions", application_id, guild_id); diff --git a/src/audit_log.c b/src/audit_log.c index d46e827ef..58fd10c07 100644 --- a/src/audit_log.c +++ b/src/audit_log.c @@ -12,7 +12,7 @@ discord_get_guild_audit_log(struct discord *client, struct discord_get_guild_audit_log *params, struct discord_ret_audit_log *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -45,9 +45,9 @@ discord_get_guild_audit_log(struct discord *client, } } - DISCORD_REQ_INIT(req, discord_audit_log, ret); + DISCORD_ATTR_INIT(attr, discord_audit_log, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/audit-logs%s", guild_id, query); } diff --git a/src/channel.c b/src/channel.c index ff926f00c..e2873178d 100644 --- a/src/channel.c +++ b/src/channel.c @@ -95,13 +95,13 @@ discord_get_channel(struct discord *client, u64snowflake channel_id, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64, channel_id); } @@ -111,7 +111,7 @@ discord_modify_channel(struct discord *client, struct discord_modify_channel *params, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -121,9 +121,9 @@ discord_modify_channel(struct discord *client, body.size = discord_modify_channel_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/channels/%" PRIu64, channel_id); } @@ -132,13 +132,13 @@ discord_delete_channel(struct discord *client, u64snowflake channel_id, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64, channel_id); } @@ -148,7 +148,7 @@ discord_get_channel_messages(struct discord *client, struct discord_get_channel_messages *params, struct discord_ret_messages *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); @@ -181,9 +181,9 @@ discord_get_channel_messages(struct discord *client, } } - DISCORD_REQ_LIST_INIT(req, discord_messages, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_messages, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/messages%s%s", channel_id, *query ? "?" : "", query); } @@ -194,14 +194,14 @@ discord_get_channel_message(struct discord *client, u64snowflake message_id, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/messages/%" PRIu64, channel_id, message_id); } @@ -212,7 +212,7 @@ discord_create_message(struct discord *client, struct discord_create_message *params, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -225,15 +225,15 @@ discord_create_message(struct discord *client, if (params->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->attachments; + attr.attachments = *params->attachments; } else { method = HTTP_POST; } - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/channels/%" PRIu64 "/messages", channel_id); } @@ -243,14 +243,14 @@ discord_crosspost_message(struct discord *client, u64snowflake message_id, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_POST, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_POST, "/channels/%" PRIu64 "/messages/%" PRIu64 "/crosspost", channel_id, message_id); @@ -264,7 +264,7 @@ discord_create_reaction(struct discord *client, const char emoji_name[], struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char *pct_emoji_name; char emoji_endpoint[256]; CCORDcode code; @@ -281,9 +281,9 @@ discord_create_reaction(struct discord *client, else snprintf(emoji_endpoint, sizeof(emoji_endpoint), "%s", pct_emoji_name); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - code = discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + code = discord_rest_run(&client->rest, &attr, NULL, HTTP_PUT, "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions/%s/@me", channel_id, message_id, emoji_endpoint); @@ -301,7 +301,7 @@ discord_delete_own_reaction(struct discord *client, const char emoji_name[], struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char *pct_emoji_name; char emoji_endpoint[256]; CCORDcode code; @@ -318,9 +318,9 @@ discord_delete_own_reaction(struct discord *client, else snprintf(emoji_endpoint, sizeof(emoji_endpoint), "%s", pct_emoji_name); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - code = discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + code = discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions/%s/@me", channel_id, message_id, emoji_endpoint); @@ -339,7 +339,7 @@ discord_delete_user_reaction(struct discord *client, const char emoji_name[], struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char *pct_emoji_name; char emoji_endpoint[256]; CCORDcode code; @@ -357,9 +357,9 @@ discord_delete_user_reaction(struct discord *client, else snprintf(emoji_endpoint, sizeof(emoji_endpoint), "%s", pct_emoji_name); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - code = discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + code = discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions/%s/%" PRIu64, channel_id, message_id, emoji_endpoint, user_id); @@ -378,7 +378,7 @@ discord_get_reactions(struct discord *client, struct discord_get_reactions *params, struct discord_ret_users *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char emoji_endpoint[256]; char query[1024] = ""; char *pct_emoji_name; @@ -417,9 +417,9 @@ discord_get_reactions(struct discord *client, else snprintf(emoji_endpoint, sizeof(emoji_endpoint), "%s", pct_emoji_name); - DISCORD_REQ_LIST_INIT(req, discord_users, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_users, ret); - code = discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + code = discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions/%s%s", channel_id, message_id, emoji_endpoint, query); @@ -435,14 +435,14 @@ discord_delete_all_reactions(struct discord *client, u64snowflake message_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions", channel_id, message_id); @@ -456,7 +456,7 @@ discord_delete_all_reactions_for_emoji(struct discord *client, const char emoji_name[], struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char *pct_emoji_name; char emoji_endpoint[256]; CCORDcode code; @@ -473,9 +473,9 @@ discord_delete_all_reactions_for_emoji(struct discord *client, else snprintf(emoji_endpoint, sizeof(emoji_endpoint), "%s", pct_emoji_name); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - code = discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + code = discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/messages/%" PRIu64 "/reactions/%s", channel_id, message_id, emoji_endpoint); @@ -492,7 +492,7 @@ discord_edit_message(struct discord *client, struct discord_edit_message *params, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[16384]; /**< @todo dynamic buffer */ @@ -503,9 +503,9 @@ discord_edit_message(struct discord *client, body.size = discord_edit_message_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/channels/%" PRIu64 "/messages/%" PRIu64, channel_id, message_id); } @@ -516,14 +516,14 @@ discord_delete_message(struct discord *client, u64snowflake message_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/messages/%" PRIu64, channel_id, message_id); } @@ -535,7 +535,7 @@ discord_bulk_delete_messages(struct discord *client, struct snowflakes *messages, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; u64unix_ms now = discord_timestamp(client); struct ccord_szbuf body; char buf[4096] = ""; @@ -558,9 +558,9 @@ discord_bulk_delete_messages(struct discord *client, CCORD_EXPECT(client, buf != NULL, CCORD_BAD_JSON, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/channels/%" PRIu64 "/messages/bulk-delete", channel_id); } @@ -573,7 +573,7 @@ discord_edit_channel_permissions( struct discord_edit_channel_permissions *params, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -585,9 +585,9 @@ discord_edit_channel_permissions( discord_edit_channel_permissions_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/channels/%" PRIu64 "/permissions/%" PRIu64, channel_id, overwrite_id); } @@ -597,13 +597,13 @@ discord_get_channel_invites(struct discord *client, u64snowflake channel_id, struct discord_ret_invites *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_invites, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_invites, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/invites", channel_id); } @@ -613,7 +613,7 @@ discord_create_channel_invite(struct discord *client, struct discord_create_channel_invite *params, struct discord_ret_invite *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024] = "{}"; size_t len = 2; @@ -626,9 +626,9 @@ discord_create_channel_invite(struct discord *client, body.start = buf; body.size = len; - DISCORD_REQ_INIT(req, discord_invite, ret); + DISCORD_ATTR_INIT(attr, discord_invite, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/channels/%" PRIu64 "/invites", channel_id); } @@ -638,14 +638,14 @@ discord_delete_channel_permission(struct discord *client, u64snowflake overwrite_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, overwrite_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/permissions/%" PRIu64, channel_id, overwrite_id); } @@ -656,7 +656,7 @@ discord_follow_news_channel(struct discord *client, struct discord_follow_news_channel *params, struct discord_ret_followed_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[256]; /* should be more than enough for this */ @@ -668,9 +668,9 @@ discord_follow_news_channel(struct discord *client, body.size = discord_follow_news_channel_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/channels/%" PRIu64 "/followers", channel_id); } @@ -679,13 +679,13 @@ discord_trigger_typing_indicator(struct discord *client, u64snowflake channel_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_POST, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_POST, "/channels/%" PRIu64 "/typing", channel_id); } @@ -694,13 +694,13 @@ discord_get_pinned_messages(struct discord *client, u64snowflake channel_id, struct discord_ret_messages *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_messages, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_messages, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/pins", channel_id); } @@ -710,14 +710,14 @@ discord_pin_message(struct discord *client, u64snowflake message_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_PUT, "/channels/%" PRIu64 "/pins/%" PRIu64, channel_id, message_id); } @@ -728,14 +728,14 @@ discord_unpin_message(struct discord *client, u64snowflake message_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/pins/%" PRIu64, channel_id, message_id); } @@ -747,7 +747,7 @@ discord_group_dm_add_recipient(struct discord *client, struct discord_group_dm_add_recipient *params, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -759,9 +759,9 @@ discord_group_dm_add_recipient(struct discord *client, discord_group_dm_add_recipient_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/channels/%" PRIu64 "/recipients/%" PRIu64, channel_id, user_id); } @@ -772,14 +772,14 @@ discord_group_dm_remove_recipient(struct discord *client, u64snowflake user_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/recipients/%" PRIu64, channel_id, user_id); } @@ -792,7 +792,7 @@ discord_start_thread_with_message( struct discord_start_thread_with_message *params, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -804,9 +804,9 @@ discord_start_thread_with_message( discord_start_thread_with_message_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/channels/%" PRIu64 "/messages/%" PRIu64 "/threads", channel_id, message_id); @@ -819,7 +819,7 @@ discord_start_thread_without_message( struct discord_start_thread_without_message *params, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -830,9 +830,9 @@ discord_start_thread_without_message( discord_start_thread_without_message_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/channels/%" PRIu64 "/threads", channel_id); } @@ -841,13 +841,13 @@ discord_join_thread(struct discord *client, u64snowflake channel_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_PUT, "/channels/%" PRIu64 "/thread-members/@me", channel_id); } @@ -858,14 +858,14 @@ discord_add_thread_member(struct discord *client, u64snowflake user_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_PUT, "/channels/%" PRIu64 "/thread-members/" PRIu64, channel_id, user_id); } @@ -875,13 +875,13 @@ discord_leave_thread(struct discord *client, u64snowflake channel_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/thread-members/@me", channel_id); } @@ -892,14 +892,14 @@ discord_remove_thread_member(struct discord *client, u64snowflake user_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/channels/%" PRIu64 "/thread-members/" PRIu64, channel_id, user_id); } @@ -909,13 +909,13 @@ discord_list_thread_members(struct discord *client, u64snowflake channel_id, struct discord_ret_thread_members *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_thread_members, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_thread_members, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/thread-members", channel_id); } @@ -925,13 +925,13 @@ discord_list_active_threads(struct discord *client, u64snowflake channel_id, struct discord_ret_thread_response_body *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_thread_response_body, ret); + DISCORD_ATTR_INIT(attr, discord_thread_response_body, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/threads/active", channel_id); } @@ -944,7 +944,7 @@ discord_list_public_archived_threads( int limit, struct discord_ret_thread_response_body *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; int offset = 0; @@ -961,9 +961,9 @@ discord_list_public_archived_threads( ASSERT_NOT_OOB(offset, sizeof(query)); } - DISCORD_REQ_INIT(req, discord_thread_response_body, ret); + DISCORD_ATTR_INIT(attr, discord_thread_response_body, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/threads/archived/public%s%s", channel_id, *query ? "?" : "", query); @@ -977,7 +977,7 @@ discord_list_private_archived_threads( int limit, struct discord_ret_thread_response_body *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; int offset = 0; @@ -994,9 +994,9 @@ discord_list_private_archived_threads( ASSERT_NOT_OOB(offset, sizeof(query)); } - DISCORD_REQ_INIT(req, discord_thread_response_body, ret); + DISCORD_ATTR_INIT(attr, discord_thread_response_body, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/threads/archived/private%s%s", channel_id, *query ? "?" : "", query); @@ -1010,7 +1010,7 @@ discord_list_joined_private_archived_threads( int limit, struct discord_ret_thread_response_body *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; int offset = 0; @@ -1027,9 +1027,9 @@ discord_list_joined_private_archived_threads( ASSERT_NOT_OOB(offset, sizeof(query)); } - DISCORD_REQ_INIT(req, discord_thread_response_body, ret); + DISCORD_ATTR_INIT(attr, discord_thread_response_body, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/users/@me/threads/archived/private%s%s", channel_id, *query ? "?" : "", query); diff --git a/src/discord-client.c b/src/discord-client.c index 6af867e82..5a88a50ec 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -168,23 +168,23 @@ _discord_clone_cleanup(struct discord *client) void discord_cleanup(struct discord *client) { - if (client->is_original) { + if (!client->is_original) { + _discord_clone_cleanup(client); + } + else { discord_rest_cleanup(&client->rest); discord_gateway_cleanup(&client->gw); - discord_user_cleanup(&client->self); - io_poller_destroy(client->io_poller); - discord_refcounter_cleanup(&client->refcounter); discord_message_commands_cleanup(&client->commands); #ifdef CCORD_VOICE discord_voice_connections_cleanup(client); #endif + discord_user_cleanup(&client->self); + io_poller_destroy(client->io_poller); + discord_refcounter_cleanup(&client->refcounter); discord_timers_cleanup(client, &client->timers.user); discord_timers_cleanup(client, &client->timers.internal); logconf_cleanup(&client->conf); } - else { - _discord_clone_cleanup(client); - } free(client); } diff --git a/src/discord-rest.c b/src/discord-rest.c index 831fa04c6..984fe88b7 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -9,24 +9,6 @@ #include "discord.h" #include "discord-internal.h" -static void -_discord_rest_start_buckets(struct discord_rest *rest) -{ - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; - struct discord_bucket *b; - - QUEUE_MOVE(&rest->async.queues->pending, &queue); - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - QUEUE_REMOVE(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - b = discord_bucket_get(&rest->ratelimiter, cxt->key); - discord_bucket_add_context(b, cxt, cxt->dispatch.high_p); - } -} - static void _discord_rest_try_add_request(struct discord_ratelimiter *rl, struct discord_bucket *b) @@ -48,7 +30,20 @@ _discord_rest_try_add_request(struct discord_ratelimiter *rl, static CCORDcode _discord_rest_start_pending(struct discord_rest *rest) { - _discord_rest_start_buckets(rest); + QUEUE(struct discord_context) queue, *qelem; + struct discord_context *cxt; + struct discord_bucket *b; + + /* match pending contexts to their buckets */ + QUEUE_MOVE(&rest->async.queues->pending, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); + + cxt = QUEUE_DATA(qelem, struct discord_context, entry); + b = discord_bucket_get(&rest->ratelimiter, cxt->key); + discord_bucket_add_context(b, cxt, cxt->dispatch.high_p); + } /* TODO: replace foreach with a mechanism that loops only busy buckets */ discord_ratelimiter_foreach_bucket(&rest->ratelimiter, @@ -139,10 +134,12 @@ _discord_rest_info_extract(struct discord_rest *rest, } } +/* parse request response and prepare callback that should be triggered + * at _discord_rest_trigger_response() */ static CCORDcode -_discord_rest_fetch_callback(struct discord_rest *rest, - struct discord_context *cxt, - CURLcode ecode) +_discord_rest_select_response(struct discord_rest *rest, + struct discord_context *cxt, + CURLcode ecode) { switch (ecode) { case CURLE_OK: { @@ -199,6 +196,35 @@ _discord_rest_fetch_callback(struct discord_rest *rest, return cxt->code; } +static CCORDcode +_discord_rest_trigger_response(struct discord_rest *rest, + struct discord_context *cxt) +{ + struct discord *client = CLIENT(rest, rest); + struct discord_response resp = { .data = cxt->dispatch.data, + .keep = cxt->dispatch.keep, + .code = cxt->code }; + + if (cxt->code != CCORD_OK) { + cxt->dispatch.fail(client, &resp); + } + else if (cxt->dispatch.done.typed) { + if (!cxt->dispatch.has_type) { + cxt->dispatch.done.typeless(client, &resp); + } + else { + cxt->dispatch.done.typed(client, &resp, cxt->response.data); + discord_refcounter_decr(&client->refcounter, cxt->response.data); + } + } + + /* enqueue request for retry or recycle */ + if (!discord_async_retry_context(&rest->async, cxt)) + discord_async_cancel_context(&rest->async, cxt); + + return resp.code; +} + void discord_rest_perform_callbacks(struct discord_rest *rest) { @@ -211,7 +237,7 @@ discord_rest_perform_callbacks(struct discord_rest *rest) do { qelem = QUEUE_HEAD(&queue); cxt = QUEUE_DATA(qelem, struct discord_context, entry); - discord_async_run_context_callback(&rest->async, cxt); + _discord_rest_trigger_response(rest, cxt); } while (!QUEUE_EMPTY(&queue)); io_poller_wakeup(rest->async.io_poller); @@ -243,7 +269,7 @@ discord_rest_perform(struct discord_rest *rest) curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); curl_multi_remove_handle(rest->async.mhandle, msg->easy_handle); - _discord_rest_fetch_callback(rest, cxt, msg->data.result); + _discord_rest_select_response(rest, cxt, msg->data.result); if (cxt->dispatch.sync) pthread_cond_signal(cxt->cond); else @@ -252,7 +278,6 @@ discord_rest_perform(struct discord_rest *rest) } code = _discord_rest_start_pending(rest); - pthread_mutex_unlock(&rest->manager->lock); return code; @@ -261,24 +286,26 @@ discord_rest_perform(struct discord_rest *rest) static void _discord_rest_manager(void *p_rest) { + struct discord *client = CLIENT(p_rest, rest); struct discord_rest *rest = p_rest; + struct discord_timers *const timers[] = { &rest->timers }; int64_t now, trigger; + int poll_result; discord_rest_perform(rest); - now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + now = (int64_t)discord_timestamp_us(client); trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); - int poll_result = - io_poller_poll(rest->async.io_poller, (int)(trigger / 1000)); + poll_result = io_poller_poll(rest->async.io_poller, (int)(trigger / 1000)); - now = (int64_t)discord_timestamp_us(CLIENT(rest, rest)); + now = (int64_t)discord_timestamp_us(client); if (0 == poll_result) { trigger = discord_timers_get_next_trigger(timers, 1, now, 1000); if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); } - discord_timers_run(CLIENT(rest, rest), &rest->timers); + discord_timers_run(client, &rest->timers); io_poller_perform(rest->async.io_poller); threadpool_add(rest->manager->tpool, _discord_rest_manager, rest, 0); @@ -326,24 +353,27 @@ discord_rest_cleanup(struct discord_rest *rest) /* enqueue a request to be executed asynchronously */ static CCORDcode _discord_rest_start_context(struct discord_rest *rest, - struct discord_request *req, + struct discord_attributes *attr, struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], char key[DISCORD_ROUTE_LEN]) { struct discord_context *cxt; - CCORDcode code = CCORD_OK; + CCORDcode code; pthread_mutex_lock(&rest->manager->lock); - cxt = discord_async_start_context(&rest->async, req, body, method, + cxt = discord_async_start_context(&rest->async, attr, body, method, endpoint, key); - if (cxt->dispatch.sync) { + if (!cxt->dispatch.sync) { + code = CCORD_OK; + } + else { cxt->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; pthread_cond_wait(cxt->cond, &rest->manager->lock); - code = discord_async_run_context_callback(&rest->async, cxt); + code = _discord_rest_trigger_response(rest, cxt); } pthread_mutex_unlock(&rest->manager->lock); @@ -354,21 +384,20 @@ _discord_rest_start_context(struct discord_rest *rest, /* template function for performing requests */ CCORDcode discord_rest_run(struct discord_rest *rest, - struct discord_request *req, + struct discord_attributes *attr, struct ccord_szbuf *body, enum http_method method, char endpoint_fmt[], ...) { - char endpoint[DISCORD_ENDPT_LEN]; - char key[DISCORD_ROUTE_LEN]; + char endpoint[DISCORD_ENDPT_LEN], key[DISCORD_ROUTE_LEN]; va_list args; int len; /* have it point somewhere */ - if (!req) { - static struct discord_request blank = { 0 }; - req = ␣ + if (!attr) { + static struct discord_attributes blank = { 0 }; + attr = ␣ } if (!body) { static struct ccord_szbuf blank = { 0 }; @@ -386,5 +415,6 @@ discord_rest_run(struct discord_rest *rest, discord_ratelimiter_build_key(method, key, endpoint_fmt, args); va_end(args); - return _discord_rest_start_context(rest, req, body, method, endpoint, key); + return _discord_rest_start_context(rest, attr, body, method, endpoint, + key); } diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c index 953c84fee..976172724 100644 --- a/src/discord-rest_async.c +++ b/src/discord-rest_async.c @@ -215,12 +215,12 @@ discord_async_start_bucket_request(struct discord_async *async, : CCORD_OK; } -static bool -_discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt) +bool +discord_async_retry_context(struct discord_async *async, + struct discord_context *cxt) { - if (!cxt->retry) return false; - if (async->retry_limit < cxt->retry_attempt++) return false; + if (!cxt->retry || cxt->retry_attempt++ >= async->retry_limit) + return false; cxt->b->performing_cxt = NULL; ua_conn_reset(cxt->conn); @@ -241,8 +241,7 @@ discord_async_cancel_context(struct discord_async *async, if (cxt->dispatch.keep) discord_refcounter_decr(rc, (void *)cxt->dispatch.keep); - if (cxt->dispatch.data) - discord_refcounter_decr(rc, cxt->dispatch.data); + if (cxt->dispatch.data) discord_refcounter_decr(rc, cxt->dispatch.data); cxt->b->performing_cxt = NULL; cxt->body.size = 0; @@ -252,50 +251,18 @@ discord_async_cancel_context(struct discord_async *async, cxt->conn = NULL; cxt->retry_attempt = 0; discord_attachments_cleanup(&cxt->attachments); - memset(cxt, 0, sizeof(struct discord_request)); + memset(cxt, 0, sizeof(struct discord_attributes)); QUEUE_REMOVE(&cxt->entry); QUEUE_INIT(&cxt->entry); QUEUE_INSERT_TAIL(&async->queues->recycling, &cxt->entry); } -CCORDcode -discord_async_run_context_callback(struct discord_async *async, - struct discord_context *cxt) -{ - struct discord *client = CLIENT(async, rest.async); - struct discord_response resp = { .data = cxt->dispatch.data, - .keep = cxt->dispatch.keep, - .code = cxt->code }; - - if (cxt->code != CCORD_OK) { - cxt->dispatch.fail(client, &resp); - } - else if (cxt->dispatch.done.typed) { - if (!cxt->dispatch.has_type) { - cxt->dispatch.done.typeless(client, &resp); - } - else { - cxt->dispatch.done.typed(client, &resp, cxt->response.data); - discord_refcounter_decr(&client->refcounter, cxt->response.data); - } - } - - /* enqueue request for retry or recycle */ - if (!_discord_async_retry_context(async, cxt)) - discord_async_cancel_context(async, cxt); - - return resp.code; -} - -/* Only the fields that are required at _discord_rest_request_to_multipart() - * are duplicated */ +/* Only fields required at _discord_context_to_multipart() are duplicated */ static void _discord_attachments_dup(struct discord_attachments *dest, struct discord_attachments *src) { - if (!src->size) return; - __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); for (int i = 0; i < src->size; ++i) { carray_insert(dest, i, src->array[i]); @@ -317,7 +284,7 @@ _discord_attachments_dup(struct discord_attachments *dest, struct discord_context * discord_async_start_context(struct discord_async *async, - struct discord_request *req, + struct discord_attributes *attr, struct ccord_szbuf *body, enum http_method method, char endpoint[DISCORD_ENDPT_LEN], @@ -329,8 +296,10 @@ discord_async_start_context(struct discord_async *async, cxt->method = method; - memcpy(cxt, req, sizeof *req); - _discord_attachments_dup(&cxt->attachments, &req->attachments); + memcpy(cxt, attr, sizeof *attr); + + if (attr->attachments.size) + _discord_attachments_dup(&cxt->attachments, &attr->attachments); if (body) { /* copy request body */ @@ -353,23 +322,23 @@ discord_async_start_context(struct discord_async *async, cxt->cond = NULL; - if (req->dispatch.keep) { + if (attr->dispatch.keep) { CCORDcode code = discord_refcounter_incr(&client->refcounter, - (void *)req->dispatch.keep); + (void *)attr->dispatch.keep); ASSERT_S(code == CCORD_OK, "'.keep' data must be a Concord callback parameter"); } - if (req->dispatch.data + if (attr->dispatch.data && CCORD_UNAVAILABLE == discord_refcounter_incr(&client->refcounter, - req->dispatch.data)) + attr->dispatch.data)) { - discord_refcounter_add_client(&client->refcounter, req->dispatch.data, - req->dispatch.cleanup, false); + discord_refcounter_add_client(&client->refcounter, attr->dispatch.data, + attr->dispatch.cleanup, false); } - /* bucket pertaining to the request */ + /* context will be assigned to its bucket at the REST thread */ QUEUE_INSERT_TAIL(&rest->async.queues->pending, &cxt->entry); io_poller_wakeup(async->io_poller); diff --git a/src/emoji.c b/src/emoji.c index fafc44bae..1997aa216 100644 --- a/src/emoji.c +++ b/src/emoji.c @@ -11,13 +11,13 @@ discord_list_guild_emojis(struct discord *client, u64snowflake guild_id, struct discord_ret_emojis *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_emojis, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_emojis, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/emojis", guild_id); } @@ -27,14 +27,14 @@ discord_get_guild_emoji(struct discord *client, u64snowflake emoji_id, struct discord_ret_emoji *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, emoji_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_emoji, ret); + DISCORD_ATTR_INIT(attr, discord_emoji, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, emoji_id); } @@ -45,7 +45,7 @@ discord_create_guild_emoji(struct discord *client, struct discord_create_guild_emoji *params, struct discord_ret_emoji *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[2048]; @@ -55,9 +55,9 @@ discord_create_guild_emoji(struct discord *client, body.size = discord_create_guild_emoji_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_emoji, ret); + DISCORD_ATTR_INIT(attr, discord_emoji, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/guilds/%" PRIu64 "/emojis", guild_id); } @@ -68,7 +68,7 @@ discord_modify_guild_emoji(struct discord *client, struct discord_modify_guild_emoji *params, struct discord_ret_emoji *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[2048]; @@ -79,9 +79,9 @@ discord_modify_guild_emoji(struct discord *client, body.size = discord_modify_guild_emoji_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_emoji, ret); + DISCORD_ATTR_INIT(attr, discord_emoji, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, emoji_id); } @@ -92,14 +92,14 @@ discord_delete_guild_emoji(struct discord *client, u64snowflake emoji_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, emoji_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64 "/emojis/%" PRIu64, guild_id, emoji_id); } diff --git a/src/gateway.c b/src/gateway.c index 901234de8..ff216b612 100644 --- a/src/gateway.c +++ b/src/gateway.c @@ -16,7 +16,7 @@ discord_disconnect_guild_member(struct discord *client, u64snowflake user_id, struct discord_ret_guild_member *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[128]; jsonb b; @@ -36,9 +36,9 @@ discord_disconnect_guild_member(struct discord *client, body.start = buf; body.size = b.pos; - DISCORD_REQ_INIT(req, discord_guild_member, ret); + DISCORD_ATTR_INIT(attr, discord_guild_member, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, user_id); } @@ -57,28 +57,28 @@ _ccord_szbuf_from_json(const char str[], size_t len, void *p_buf) CCORDcode discord_get_gateway(struct discord *client, struct ccord_szbuf *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); - req.response.from_json = &_ccord_szbuf_from_json; - req.dispatch.has_type = true; - req.dispatch.sync = ret; + attr.response.from_json = &_ccord_szbuf_from_json; + attr.dispatch.has_type = true; + attr.dispatch.sync = ret; - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, "/gateway"); + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/gateway"); } CCORDcode discord_get_gateway_bot(struct discord *client, struct ccord_szbuf *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); - req.response.from_json = &_ccord_szbuf_from_json; - req.dispatch.has_type = true; - req.dispatch.sync = ret; + attr.response.from_json = &_ccord_szbuf_from_json; + attr.dispatch.has_type = true; + attr.dispatch.sync = ret; - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/gateway/bot"); } diff --git a/src/guild.c b/src/guild.c index e52384448..d0b5c02dc 100644 --- a/src/guild.c +++ b/src/guild.c @@ -11,7 +11,7 @@ discord_create_guild(struct discord *client, struct discord_create_guild *params, struct discord_ret_guild *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -20,9 +20,9 @@ discord_create_guild(struct discord *client, body.size = discord_create_guild_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild, ret); + DISCORD_ATTR_INIT(attr, discord_guild, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, "/guilds"); + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/guilds"); } CCORDcode @@ -30,13 +30,13 @@ discord_get_guild(struct discord *client, u64snowflake guild_id, struct discord_ret_guild *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_guild, ret); + DISCORD_ATTR_INIT(attr, discord_guild, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64, guild_id); } @@ -45,13 +45,13 @@ discord_get_guild_preview(struct discord *client, u64snowflake guild_id, struct discord_ret_guild_preview *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_guild_preview, ret); + DISCORD_ATTR_INIT(attr, discord_guild_preview, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/preview", guild_id); } @@ -61,7 +61,7 @@ discord_modify_guild(struct discord *client, struct discord_modify_guild *params, struct discord_ret_guild *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -71,9 +71,9 @@ discord_modify_guild(struct discord *client, body.size = discord_modify_guild_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild, ret); + DISCORD_ATTR_INIT(attr, discord_guild, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64, guild_id); } @@ -82,13 +82,13 @@ discord_delete_guild(struct discord *client, u64snowflake guild_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64, guild_id); } @@ -97,13 +97,13 @@ discord_get_guild_channels(struct discord *client, u64snowflake guild_id, struct discord_ret_channels *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_channels, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_channels, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/channels", guild_id); } @@ -113,7 +113,7 @@ discord_create_guild_channel(struct discord *client, struct discord_create_guild_channel *params, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[2048]; @@ -123,9 +123,9 @@ discord_create_guild_channel(struct discord *client, body.size = discord_create_guild_channel_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/guilds/%" PRIu64 "/channels", guild_id); } @@ -136,7 +136,7 @@ discord_modify_guild_channel_positions( struct discord_modify_guild_channel_positions *params, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -147,9 +147,9 @@ discord_modify_guild_channel_positions( buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/channels", guild_id); } @@ -159,14 +159,14 @@ discord_get_guild_member(struct discord *client, u64snowflake user_id, struct discord_ret_guild_member *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_guild_member, ret); + DISCORD_ATTR_INIT(attr, discord_guild_member, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, user_id); } @@ -177,7 +177,7 @@ discord_list_guild_members(struct discord *client, struct discord_list_guild_members *params, struct discord_ret_guild_members *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -198,9 +198,9 @@ discord_list_guild_members(struct discord *client, } } - DISCORD_REQ_LIST_INIT(req, discord_guild_members, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_guild_members, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/members%s%s", guild_id, *query ? "?" : "", query); } @@ -211,7 +211,7 @@ discord_search_guild_members(struct discord *client, struct discord_search_guild_members *params, struct discord_ret_guild_members *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; char query[1024] = ""; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); @@ -236,9 +236,9 @@ discord_search_guild_members(struct discord *client, } } - DISCORD_REQ_LIST_INIT(req, discord_guild_members, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_guild_members, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/members/search%s%s", guild_id, *query ? "?" : "", query); } @@ -250,7 +250,7 @@ discord_add_guild_member(struct discord *client, struct discord_add_guild_member *params, struct discord_ret_guild_member *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -263,9 +263,9 @@ discord_add_guild_member(struct discord *client, body.size = discord_add_guild_member_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild_member, ret); + DISCORD_ATTR_INIT(attr, discord_guild_member, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, user_id); } @@ -277,7 +277,7 @@ discord_modify_guild_member(struct discord *client, struct discord_modify_guild_member *params, struct discord_ret_guild_member *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[2048]; @@ -288,9 +288,9 @@ discord_modify_guild_member(struct discord *client, body.size = discord_modify_guild_member_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild_member, ret); + DISCORD_ATTR_INIT(attr, discord_guild_member, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, user_id); } @@ -300,7 +300,7 @@ discord_modify_current_member(struct discord *client, struct discord_modify_current_member *params, struct discord_ret_guild_member *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[512]; @@ -312,9 +312,9 @@ discord_modify_current_member(struct discord *client, discord_modify_current_member_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild_member, ret); + DISCORD_ATTR_INIT(attr, discord_guild_member, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/members/@me", guild_id); } CCORDcode @@ -324,7 +324,7 @@ discord_modify_current_user_nick( struct discord_modify_current_user_nick *params, struct discord_ret_guild_member *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[512]; @@ -340,9 +340,9 @@ discord_modify_current_user_nick( discord_modify_current_user_nick_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild_member, ret); + DISCORD_ATTR_INIT(attr, discord_guild_member, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/members/@me/nick", guild_id); } @@ -353,15 +353,15 @@ discord_add_guild_member_role(struct discord *client, u64snowflake role_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, role_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_PUT, "/guilds/%" PRIu64 "/members/%" PRIu64 "/roles/%" PRIu64, guild_id, user_id, role_id); @@ -374,15 +374,15 @@ discord_remove_guild_member_role(struct discord *client, u64snowflake role_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, role_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64 "/members/%" PRIu64 "/roles/%" PRIu64, guild_id, user_id, role_id); @@ -394,14 +394,14 @@ discord_remove_guild_member(struct discord *client, u64snowflake user_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64 "/members/%" PRIu64, guild_id, user_id); } @@ -411,13 +411,13 @@ discord_get_guild_bans(struct discord *client, u64snowflake guild_id, struct discord_ret_bans *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_bans, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_bans, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/bans", guild_id); } @@ -427,14 +427,14 @@ discord_get_guild_ban(struct discord *client, u64snowflake user_id, struct discord_ret_ban *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_ban, ret); + DISCORD_ATTR_INIT(attr, discord_ban, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, user_id); } @@ -446,7 +446,7 @@ discord_create_guild_ban(struct discord *client, struct discord_create_guild_ban *params, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[256]; @@ -461,9 +461,9 @@ discord_create_guild_ban(struct discord *client, body.size = discord_create_guild_ban_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PUT, "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, user_id); } @@ -473,14 +473,14 @@ discord_remove_guild_ban(struct discord *client, u64snowflake user_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64 "/bans/%" PRIu64, guild_id, user_id); } @@ -490,13 +490,13 @@ discord_get_guild_roles(struct discord *client, u64snowflake guild_id, struct discord_ret_roles *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_roles, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_roles, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/roles", guild_id); } @@ -506,7 +506,7 @@ discord_create_guild_role(struct discord *client, struct discord_create_guild_role *params, struct discord_ret_role *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -515,9 +515,9 @@ discord_create_guild_role(struct discord *client, body.size = discord_create_guild_role_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_role, ret); + DISCORD_ATTR_INIT(attr, discord_role, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/guilds/%" PRIu64 "/roles", guild_id); } @@ -528,7 +528,7 @@ discord_modify_guild_role_positions( struct discord_modify_guild_role_positions *params, struct discord_ret_roles *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096]; @@ -539,9 +539,9 @@ discord_modify_guild_role_positions( discord_modify_guild_role_positions_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_LIST_INIT(req, discord_roles, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_roles, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/roles", guild_id); } @@ -552,7 +552,7 @@ discord_modify_guild_role(struct discord *client, struct discord_modify_guild_role *params, struct discord_ret_role *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[2048] = "{}"; size_t len = 2; @@ -566,9 +566,9 @@ discord_modify_guild_role(struct discord *client, body.size = len; body.start = buf; - DISCORD_REQ_INIT(req, discord_role, ret); + DISCORD_ATTR_INIT(attr, discord_role, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/guilds/%" PRIu64 "/roles/%" PRIu64, guild_id, role_id); } @@ -579,14 +579,14 @@ discord_delete_guild_role(struct discord *client, u64snowflake role_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, role_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64 "/roles/%" PRIu64, guild_id, role_id); } @@ -597,7 +597,7 @@ discord_begin_guild_prune(struct discord *client, struct discord_begin_guild_prune *params, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[4096] = "{}"; size_t len = 2; @@ -610,9 +610,9 @@ discord_begin_guild_prune(struct discord *client, body.size = len; body.start = buf; - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/guilds/%" PRIu64 "/prune", guild_id); } @@ -621,13 +621,13 @@ discord_get_guild_invites(struct discord *client, u64snowflake guild_id, struct discord_ret_invites *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_invites, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_invites, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/invites", guild_id); } @@ -637,14 +637,14 @@ discord_delete_guild_integrations(struct discord *client, u64snowflake integration_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, integration_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/guilds/%" PRIu64 "/integrations/%" PRIu64, guild_id, integration_id); } @@ -654,13 +654,13 @@ discord_get_guild_vanity_url(struct discord *client, u64snowflake guild_id, struct discord_ret_invite *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_invite, ret); + DISCORD_ATTR_INIT(attr, discord_invite, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/vanity-url", guild_id); } @@ -669,12 +669,12 @@ discord_get_guild_welcome_screen(struct discord *client, u64snowflake guild_id, struct discord_ret_welcome_screen *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_welcome_screen, ret); + DISCORD_ATTR_INIT(attr, discord_welcome_screen, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/welcome-screen", guild_id); } diff --git a/src/guild_template.c b/src/guild_template.c index 7baa8b700..ef027d44f 100644 --- a/src/guild_template.c +++ b/src/guild_template.c @@ -11,13 +11,13 @@ discord_get_guild_template(struct discord *client, char *code, struct discord_ret_guild_template *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, NOT_EMPTY_STR(code), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_guild_template, ret); + DISCORD_ATTR_INIT(attr, discord_guild_template, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/templates/%s", code); } @@ -27,7 +27,7 @@ discord_create_guild_template(struct discord *client, struct discord_create_guild_template *params, struct discord_ret_guild_template *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[256]; @@ -37,9 +37,9 @@ discord_create_guild_template(struct discord *client, discord_create_guild_template_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_guild_template, ret); + DISCORD_ATTR_INIT(attr, discord_guild_template, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/guilds/%" PRIu64 "/templates", guild_id); } @@ -49,13 +49,13 @@ discord_sync_guild_template(struct discord *client, char *code, struct discord_ret_guild_template *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_guild_template, ret); + DISCORD_ATTR_INIT(attr, discord_guild_template, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_PUT, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_PUT, "/guilds/%" PRIu64 "/templates/%s", guild_id, code); } diff --git a/src/interaction.c b/src/interaction.c index 37ec23736..501ad7667 100644 --- a/src/interaction.c +++ b/src/interaction.c @@ -14,7 +14,7 @@ discord_create_interaction_response( struct discord_interaction_response *params, struct discord_ret_interaction_response *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[4096]; @@ -29,15 +29,15 @@ discord_create_interaction_response( if (params->data && params->data->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->data->attachments; + attr.attachments = *params->data->attachments; } else { method = HTTP_POST; } - DISCORD_REQ_INIT(req, discord_interaction_response, ret); + DISCORD_ATTR_INIT(attr, discord_interaction_response, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/interactions/%" PRIu64 "/%s/callback", interaction_id, interaction_token); } @@ -49,15 +49,15 @@ discord_get_original_interaction_response( const char interaction_token[], struct discord_ret_interaction_response *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(interaction_token), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_interaction_response, ret); + DISCORD_ATTR_INIT(attr, discord_interaction_response, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/webhooks/%" PRIu64 "/%s/messages/@original", application_id, interaction_token); } @@ -70,7 +70,7 @@ discord_edit_original_interaction_response( struct discord_edit_original_interaction_response *params, struct discord_ret_interaction_response *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -86,15 +86,15 @@ discord_edit_original_interaction_response( if (params->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->attachments; + attr.attachments = *params->attachments; } else { method = HTTP_PATCH; } - DISCORD_REQ_INIT(req, discord_interaction_response, ret); + DISCORD_ATTR_INIT(attr, discord_interaction_response, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/webhooks/%" PRIu64 "/%s/messages/@original", application_id, interaction_token); } @@ -105,15 +105,15 @@ discord_delete_original_interaction_response(struct discord *client, const char interaction_token[], struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(interaction_token), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/webhooks/%" PRIu64 "/%s/messages/@original", application_id, interaction_token); } @@ -125,7 +125,7 @@ discord_create_followup_message(struct discord *client, struct discord_create_followup_message *params, struct discord_ret_webhook *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -148,15 +148,15 @@ discord_create_followup_message(struct discord *client, if (params->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->attachments; + attr.attachments = *params->attachments; } else { method = HTTP_POST; } - DISCORD_REQ_INIT(req, discord_webhook, ret); + DISCORD_ATTR_INIT(attr, discord_webhook, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/webhooks/%" PRIu64 "/%s%s%s", application_id, interaction_token, *query ? "?" : "", query); } @@ -168,16 +168,16 @@ discord_get_followup_message(struct discord *client, u64snowflake message_id, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(interaction_token), CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/webhooks/%" PRIu64 "/%s/%" PRIu64, application_id, interaction_token, message_id); } @@ -190,7 +190,7 @@ discord_edit_followup_message(struct discord *client, struct discord_edit_followup_message *params, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -207,15 +207,15 @@ discord_edit_followup_message(struct discord *client, if (params->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->attachments; + attr.attachments = *params->attachments; } else { method = HTTP_PATCH; } - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, application_id, interaction_token, message_id); } @@ -227,16 +227,16 @@ discord_delete_followup_message(struct discord *client, u64snowflake message_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, application_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(interaction_token), CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, application_id, interaction_token, message_id); } diff --git a/src/invite.c b/src/invite.c index b246aea45..e5539a376 100644 --- a/src/invite.c +++ b/src/invite.c @@ -12,7 +12,7 @@ discord_get_invite(struct discord *client, struct discord_get_invite *params, struct discord_ret_invite *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -22,9 +22,9 @@ discord_get_invite(struct discord *client, body.size = discord_get_invite_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_invite, ret); + DISCORD_ATTR_INIT(attr, discord_invite, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_GET, + return discord_rest_run(&client->rest, &attr, &body, HTTP_GET, "/invites/%s", invite_code); } @@ -33,12 +33,12 @@ discord_delete_invite(struct discord *client, char *invite_code, struct discord_ret_invite *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, NOT_EMPTY_STR(invite_code), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_invite, ret); + DISCORD_ATTR_INIT(attr, discord_invite, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/invites/%s", invite_code); } diff --git a/src/user.c b/src/user.c index 1311f37e1..8121e5f52 100644 --- a/src/user.c +++ b/src/user.c @@ -9,11 +9,12 @@ CCORDcode discord_get_current_user(struct discord *client, struct discord_ret_user *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; - DISCORD_REQ_INIT(req, discord_user, ret); + DISCORD_ATTR_INIT(attr, discord_user, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, "/users/@me"); + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, + "/users/@me"); } CCORDcode @@ -21,13 +22,13 @@ discord_get_user(struct discord *client, u64snowflake user_id, struct discord_ret_user *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, user_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_user, ret); + DISCORD_ATTR_INIT(attr, discord_user, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/users/%" PRIu64, user_id); } @@ -36,7 +37,7 @@ discord_modify_current_user(struct discord *client, struct discord_modify_current_user *params, struct discord_ret_user *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -45,9 +46,9 @@ discord_modify_current_user(struct discord *client, body.size = discord_modify_current_user_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_user, ret); + DISCORD_ATTR_INIT(attr, discord_user, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/users/@me"); } @@ -55,11 +56,11 @@ CCORDcode discord_get_current_user_guilds(struct discord *client, struct discord_ret_guilds *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; - DISCORD_REQ_LIST_INIT(req, discord_guilds, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_guilds, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/users/@me/guilds"); } @@ -68,14 +69,14 @@ discord_leave_guild(struct discord *client, u64snowflake guild_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body = { "{}", 2 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, &body, HTTP_DELETE, "/users/@me/guilds/%" PRIu64, guild_id); } @@ -84,7 +85,7 @@ discord_create_dm(struct discord *client, struct discord_create_dm *params, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[128]; @@ -93,9 +94,9 @@ discord_create_dm(struct discord *client, body.size = discord_create_dm_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/users/@me/channels"); } @@ -104,7 +105,7 @@ discord_create_group_dm(struct discord *client, struct discord_create_group_dm *params, struct discord_ret_channel *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -116,9 +117,9 @@ discord_create_group_dm(struct discord *client, body.size = discord_create_group_dm_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_channel, ret); + DISCORD_ATTR_INIT(attr, discord_channel, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/users/@me/channels"); } @@ -126,10 +127,10 @@ CCORDcode discord_get_user_connections(struct discord *client, struct discord_ret_connections *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; - DISCORD_REQ_LIST_INIT(req, discord_connections, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_connections, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/users/@me/connections"); } diff --git a/src/voice.c b/src/voice.c index 9694e3e3c..e0d006c56 100644 --- a/src/voice.c +++ b/src/voice.c @@ -10,10 +10,10 @@ CCORDcode discord_list_voice_regions(struct discord *client, struct discord_ret_voice_regions *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; - DISCORD_REQ_LIST_INIT(req, discord_voice_regions, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_voice_regions, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/voice/regions"); } diff --git a/src/webhook.c b/src/webhook.c index baa812389..8b60f150b 100644 --- a/src/webhook.c +++ b/src/webhook.c @@ -12,7 +12,7 @@ discord_create_webhook(struct discord *client, struct discord_create_webhook *params, struct discord_ret_webhook *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -23,9 +23,9 @@ discord_create_webhook(struct discord *client, body.size = discord_create_webhook_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_webhook, ret); + DISCORD_ATTR_INIT(attr, discord_webhook, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_POST, + return discord_rest_run(&client->rest, &attr, &body, HTTP_POST, "/channels/%" PRIu64 "/webhooks", channel_id); } @@ -34,13 +34,13 @@ discord_get_channel_webhooks(struct discord *client, u64snowflake channel_id, struct discord_ret_webhooks *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, channel_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_webhooks, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_webhooks, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/channels/%" PRIu64 "/webhooks", channel_id); } @@ -49,13 +49,13 @@ discord_get_guild_webhooks(struct discord *client, u64snowflake guild_id, struct discord_ret_webhooks *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_LIST_INIT(req, discord_webhooks, ret); + DISCORD_ATTR_LIST_INIT(attr, discord_webhooks, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/guilds/%" PRIu64 "/webhooks", guild_id); } @@ -64,13 +64,13 @@ discord_get_webhook(struct discord *client, u64snowflake webhook_id, struct discord_ret_webhook *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_webhook, ret); + DISCORD_ATTR_INIT(attr, discord_webhook, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/webhooks/%" PRIu64, webhook_id); } @@ -80,15 +80,15 @@ discord_get_webhook_with_token(struct discord *client, const char webhook_token[], struct discord_ret_webhook *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(webhook_token), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_webhook, ret); + DISCORD_ATTR_INIT(attr, discord_webhook, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/webhooks/%" PRIu64 "/%s", webhook_id, webhook_token); } @@ -99,7 +99,7 @@ discord_modify_webhook(struct discord *client, struct discord_modify_webhook *params, struct discord_ret_webhook *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -108,9 +108,9 @@ discord_modify_webhook(struct discord *client, body.size = discord_modify_webhook_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_webhook, ret); + DISCORD_ATTR_INIT(attr, discord_webhook, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/webhooks/%" PRIu64, webhook_id); } @@ -122,7 +122,7 @@ discord_modify_webhook_with_token( struct discord_modify_webhook_with_token *params, struct discord_ret_webhook *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; char buf[1024]; @@ -134,9 +134,9 @@ discord_modify_webhook_with_token( discord_modify_webhook_with_token_to_json(buf, sizeof(buf), params); body.start = buf; - DISCORD_REQ_INIT(req, discord_webhook, ret); + DISCORD_ATTR_INIT(attr, discord_webhook, ret); - return discord_rest_run(&client->rest, &req, &body, HTTP_PATCH, + return discord_rest_run(&client->rest, &attr, &body, HTTP_PATCH, "/webhooks/%" PRIu64 "/%s", webhook_id, webhook_token); } @@ -146,13 +146,13 @@ discord_delete_webhook(struct discord *client, u64snowflake webhook_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/webhooks/%" PRIu64, webhook_id); } @@ -162,15 +162,15 @@ discord_delete_webhook_with_token(struct discord *client, const char webhook_token[], struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(webhook_token), CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/webhooks/%" PRIu64 "/%s", webhook_id, webhook_token); } @@ -182,7 +182,7 @@ discord_execute_webhook(struct discord *client, struct discord_execute_webhook *params, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -210,15 +210,15 @@ discord_execute_webhook(struct discord *client, if (params->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->attachments; + attr.attachments = *params->attachments; } else { method = HTTP_POST; } - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/webhooks/%" PRIu64 "/%s%s%s", webhook_id, webhook_token, *query ? "?" : "", query); } @@ -230,16 +230,16 @@ discord_get_webhook_message(struct discord *client, u64snowflake message_id, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(webhook_token), CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_GET, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_GET, "/webhooks/%" PRIu64 "/%s/%" PRIu64, webhook_id, webhook_token, message_id); } @@ -252,7 +252,7 @@ discord_edit_webhook_message(struct discord *client, struct discord_edit_webhook_message *params, struct discord_ret_message *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; struct ccord_szbuf body; enum http_method method; char buf[16384]; /**< @todo dynamic buffer */ @@ -268,15 +268,15 @@ discord_edit_webhook_message(struct discord *client, if (params->attachments) { method = HTTP_MIMEPOST; - req.attachments = *params->attachments; + attr.attachments = *params->attachments; } else { method = HTTP_PATCH; } - DISCORD_REQ_INIT(req, discord_message, ret); + DISCORD_ATTR_INIT(attr, discord_message, ret); - return discord_rest_run(&client->rest, &req, &body, method, + return discord_rest_run(&client->rest, &attr, &body, method, "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, webhook_id, webhook_token, message_id); } @@ -288,16 +288,16 @@ discord_delete_webhook_message(struct discord *client, u64snowflake message_id, struct discord_ret *ret) { - struct discord_request req = { 0 }; + struct discord_attributes attr = { 0 }; CCORD_EXPECT(client, webhook_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, NOT_EMPTY_STR(webhook_token), CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, message_id != 0, CCORD_BAD_PARAMETER, ""); - DISCORD_REQ_BLANK_INIT(req, ret); + DISCORD_ATTR_BLANK_INIT(attr, ret); - return discord_rest_run(&client->rest, &req, NULL, HTTP_DELETE, + return discord_rest_run(&client->rest, &attr, NULL, HTTP_DELETE, "/webhooks/%" PRIu64 "/%s/messages/%" PRIu64, webhook_id, webhook_token, message_id); } From ec8d5ad8960e827837b86b7132ac8db93496de98 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 26 May 2022 16:35:06 -0300 Subject: [PATCH 077/118] refactor(discord-rest): rename discord-rest_async.c -> discord-rest_request.c * refactor(discord-rest_request): rename 'struct discord_async' -> 'struct discord_requestor', 'struct discord_request' -> 'struct discord_request', 'struct discord_context' -> 'struct discord_request' * refactor(discord-rest_request): move every discord-rest.c logic that handles any of the discord-rest_request.c structures --- Makefile | 2 +- include/discord-internal.h | 146 ++++---- src/discord-loop.c | 2 +- src/discord-rest.c | 310 +---------------- src/discord-rest_async.c | 347 ------------------- src/discord-rest_ratelimit.c | 45 ++- src/discord-rest_request.c | 635 +++++++++++++++++++++++++++++++++++ 7 files changed, 739 insertions(+), 748 deletions(-) delete mode 100644 src/discord-rest_async.c create mode 100644 src/discord-rest_request.c diff --git a/Makefile b/Makefile index 4df987b40..c0e4594e2 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-refcount.o \ $(SRC_DIR)/discord-rest.o \ - $(SRC_DIR)/discord-rest_async.o \ + $(SRC_DIR)/discord-rest_request.o \ $(SRC_DIR)/discord-rest_ratelimit.o \ $(SRC_DIR)/discord-client.o \ $(SRC_DIR)/discord-loop.o \ diff --git a/include/discord-internal.h b/include/discord-internal.h index 0c5aacc5a..dcc51bd69 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -167,6 +167,10 @@ unsigned discord_internal_timer(struct discord *client, /** @} DiscordInternalTimer */ +/** @defgroup DiscordInternalRESTRequest Request's handling + * @brief Store, manage and dispatch individual requests + * @{ */ + /** @defgroup DiscordInternalREST REST API * @brief Wrapper to the Discord REST API * @{ */ @@ -212,7 +216,7 @@ struct discord_ret_response { /** * @brief Macro containing @ref discord_attributes fields - * @note this exists for @ref discord_context alignment purposes + * @note this exists for @ref discord_request alignment purposes */ #define DISCORD_ATTRIBUTES_FIELDS \ /** attributes set by client for request dispatch behavior */ \ @@ -227,17 +231,12 @@ struct discord_attributes { DISCORD_ATTRIBUTES_FIELDS; }; -/** @defgroup DiscordInternalRESTAsync Async request's handling - * @brief Store contexts of individual asynchronous requests - * @{ */ - /** - * @brief Context of individual requests that are scheduled to run - * asynchronously + * @brief Individual requests that are scheduled to run asynchronously * @note this struct **SHOULD NOT** be handled from the `REST` manager thread * @note its fields are aligned with @ref discord_attributes */ -struct discord_context { +struct discord_request { DISCORD_ATTRIBUTES_FIELDS; /** the request's bucket */ @@ -269,8 +268,8 @@ struct discord_context { }; /** @brief The handle used for handling asynchronous requests */ -struct discord_async { - /** `DISCORD_ASYNC` logging module */ +struct discord_requestor { + /** `DISCORD_REQUEST` logging module */ struct logconf conf; /** the user agent handle for performing requests */ struct user_agent *ua; @@ -282,100 +281,100 @@ struct discord_async { /** max amount of retries before a failed request gives up */ int retry_limit; - /** context queues */ + /** request queues */ struct { - /** requests contexts for recycling */ - QUEUE(struct discord_context) recycling; + /** requests for recycling */ + QUEUE(struct discord_request) recycling; /** pending requests waiting to be assigned to a bucket */ - QUEUE(struct discord_context) pending; + QUEUE(struct discord_request) pending; /** - * finished requests contexts that are done performing and waiting for + * finished requests that are done performing and waiting for * their callbacks to be called from the main thread */ - QUEUE(struct discord_context) finished; + QUEUE(struct discord_request) finished; } * queues; }; /** - * @brief Initialize an Async handle + * @brief Initialize the request handler * * This shall initialize a `CURLM` multi handle for performing requests - * asynchronously, and a queue for storing individual requests contexts - * @param async the async handle to be initialized + * asynchronously, and a queue for storing individual requests + * @param rqtor the requestor handle to be initialized * @param conf pointer to @ref discord_rest logging module * @param token the bot token */ -void discord_async_init(struct discord_async *async, - struct logconf *conf, - struct ccord_szbuf_readonly *token); +void discord_requestor_init(struct discord_requestor *rqtor, + struct logconf *conf, + struct ccord_szbuf_readonly *token); /** - * @brief Free an Async handle + * @brief Free the request handler * - * @param async the handle initialized with discord_async_init() + * @param rqtor the handle initialized with discord_requestor_init() */ -void discord_async_cleanup(struct discord_async *async); +void discord_requestor_cleanup(struct discord_requestor *rqtor); /** - * @brief Kickstart a bucket request by adding it to libcurl's request - * multiplexer (`CURLM` multi handle) + * @brief Check for and start pending bucket's requests * - * @param async the async handle initialized with discord_async_init() - * @param b the bucket to have a request sent over - * @return CCORDcode for how the request went, @ref CCORD_CURLM_INTERNAL means - * something wrong happened + * @param rqtor the handle initialized with discord_requestor_init() + * @CCORD_return */ -CCORDcode discord_async_start_bucket_request(struct discord_async *async, - struct discord_bucket *b); +CCORDcode discord_requestor_start_pending(struct discord_requestor *rqtor); /** - * @brief Check if request is expected to be retried and move it to its - * bucket's queue + * @brief Poll for request's completion * - * @param async the async handle initialized with discord_async_init() - * @param cxt the on-going request to be canceled - * @return `true` if request has been enqueued for retry + * @param rqtor the handle initialized with discord_requestor_init() + * @CCORD_return */ -bool discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt); +CCORDcode discord_requestor_info_read(struct discord_requestor *rqtor); /** - * @brief Mark request as canceled and move it to the recycle queue + * @brief Run pending callbacks from completed requests * - * @param async the async handle initialized with discord_async_init() - * @param cxt the on-going request to be canceled + * @param req the request containing preliminary information for its dispatch */ -void discord_async_cancel_context(struct discord_async *async, - struct discord_context *cxt); +void discord_requestor_dispatch_responses(struct discord_requestor *rqtor); /** - * @brief Start request's context + * @brief Mark request as canceled and move it to the recycling queue * - * @param async the async handle initialized with discord_async_init() + * @param rqtor the requestor handle initialized with discord_requestor_init() + * @param req the on-going request to be canceled + */ +void discord_request_cancel(struct discord_requestor *rqtor, + struct discord_request *req); + +/** + * @brief Begin a new request + * + * The returned request automatically be performed from the `REST` thread + * @param rqtor the requestor handle initialized with discord_requestor_init() * @param req the request containing preliminary information for its dispatch * and response's parsing * @param body the request's body * @param method the request's HTTP method * @param endpoint the request's endpoint * @param key the request bucket's group for ratelimiting - * @return the initialized request context + * @CCORD_return */ -struct discord_context *discord_async_start_context( - struct discord_async *async, - struct discord_attributes *req, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]); +CCORDcode discord_request_begin(struct discord_requestor *rqtor, + struct discord_attributes *req, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]); -/** @} DiscordInternalRESTAsync */ +/** @} DiscordInternalRESTRequest */ /** @defgroup DiscordInternalRESTRatelimit Ratelimiting * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ /** - * @brief Value assigned to @ref discord_bucket `pending_cxt` field in case + * @brief Value assigned to @ref discord_bucket `performing_req` field in case * it's being timed-out */ #define DISCORD_BUCKET_TIMEOUT (void *)(0xf) @@ -481,12 +480,12 @@ struct discord_bucket { /** timestamp of when cooldown timer resets */ u64unix_ms reset_tstamp; /** pending requests */ - QUEUE(struct discord_context) pending_queue; + QUEUE(struct discord_request) pending_queue; /** - * pointer to context of this bucket's currently performing request + * pointer to this bucket's currently performing request * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited */ - struct discord_context *performing_cxt; + struct discord_request *performing_req; /** synchronize bucket */ pthread_mutex_t lock; }; @@ -521,24 +520,24 @@ struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, const char key[]); /** - * @brief Insert request's context into bucket's pending queue + * @brief Insert request into bucket's pending queue * * @param b the bucket to insert the request to - * @param cxt the request context obtained via discord_async_start_context() + * @param req the request obtained via discord_requestor_start_request() * @param high_priority if high priority then request shall be prioritized over * already enqueued requests */ -void discord_bucket_add_context(struct discord_bucket *b, - struct discord_context *cxt, +void discord_bucket_add_request(struct discord_bucket *b, + struct discord_request *req, bool high_priority); /** - * @brief Remove head request's context from bucket's pending queue + * @brief Remove head request from bucket's pending queue * * @param b the bucket to fetch the request from - * @return the request's context + * @return the request */ -struct discord_context *discord_bucket_remove_context( +struct discord_request *discord_bucket_remove_request( struct discord_bucket *b); /** @} DiscordInternalRESTRatelimit */ @@ -547,8 +546,8 @@ struct discord_context *discord_bucket_remove_context( struct discord_rest { /** `DISCORD_HTTP` or `DISCORD_WEBHOOK` logging module */ struct logconf conf; - /** store individual contexts from asynchronous requests */ - struct discord_async async; + /** the requests handler */ + struct discord_requestor requestor; /** the timer queue for the rest thread */ struct discord_timers timers; /** enforce ratelimiting on discovered buckets */ @@ -586,7 +585,7 @@ void discord_rest_cleanup(struct discord_rest *rest); * @brief Perform a request to Discord * * This functions is a selector over discord_rest_run() or - * discord_rest_run_async() + * discord_rest_run_requestor() * @param rest the handle initialized with discord_rest_init() * @param req return object of request * @param body the body sent for methods that require (ex: post), leave as @@ -620,13 +619,6 @@ CCORDcode discord_rest_perform(struct discord_rest *rest); */ void discord_rest_stop_buckets(struct discord_rest *rest); -/** - * @brief Run pending callbacks from completed requests - * - * @param rest the handle initialized with discord_rest_init() - */ -void discord_rest_perform_callbacks(struct discord_rest *rest); - /** @} DiscordInternalREST */ /** @defgroup DiscordInternalGateway WebSockets API diff --git a/src/discord-loop.c b/src/discord-loop.c index 9b736a11c..802183d7c 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -123,7 +123,7 @@ discord_run(struct discord *client) if (next_run <= now) { BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); - discord_rest_perform_callbacks(&client->rest); + discord_requestor_dispatch_responses(&client->rest.requestor); /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; diff --git a/src/discord-rest.c b/src/discord-rest.c index 984fe88b7..03730ee76 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -9,275 +9,17 @@ #include "discord.h" #include "discord-internal.h" -static void -_discord_rest_try_add_request(struct discord_ratelimiter *rl, - struct discord_bucket *b) -{ - /* skip if bucket is already busy performing */ - if (b->performing_cxt) return; - - if (!b->remaining) { - discord_bucket_try_timeout(rl, b); - } - else if (!QUEUE_EMPTY(&b->pending_queue)) { - struct discord_async *async = - &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; - - discord_async_start_bucket_request(async, b); - } -} - -static CCORDcode -_discord_rest_start_pending(struct discord_rest *rest) -{ - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; - struct discord_bucket *b; - - /* match pending contexts to their buckets */ - QUEUE_MOVE(&rest->async.queues->pending, &queue); - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - QUEUE_REMOVE(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - b = discord_bucket_get(&rest->ratelimiter, cxt->key); - discord_bucket_add_context(b, cxt, cxt->dispatch.high_p); - } - - /* TODO: replace foreach with a mechanism that loops only busy buckets */ - discord_ratelimiter_foreach_bucket(&rest->ratelimiter, - &_discord_rest_try_add_request); - - /* FIXME: redundant return value (constant) */ - return CCORD_OK; -} - -/* return true if there should be a retry attempt */ -static void -_discord_rest_info_extract(struct discord_rest *rest, - struct discord_context *cxt, - struct ua_info *info) -{ - ua_info_extract(cxt->conn, info); - - if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ - cxt->retry = false; - } - else { - switch (info->httpcode) { - case HTTP_FORBIDDEN: - case HTTP_NOT_FOUND: - case HTTP_BAD_REQUEST: - info->code = CCORD_DISCORD_JSON_CODE; - cxt->retry = false; - break; - case HTTP_UNAUTHORIZED: - logconf_fatal( - &rest->conf, - "UNAUTHORIZED: Please provide a valid authentication token"); - info->code = CCORD_DISCORD_BAD_AUTH; - cxt->retry = false; - break; - case HTTP_METHOD_NOT_ALLOWED: - logconf_fatal( - &rest->conf, - "METHOD_NOT_ALLOWED: The server couldn't recognize the " - "received HTTP method"); - cxt->retry = false; - break; - case HTTP_TOO_MANY_REQUESTS: { - struct ua_szbuf_readonly body = ua_info_get_body(info); - struct jsmnftok message = { 0 }; - double retry_after = 1.0; - bool is_global = false; - jsmn_parser parser; - jsmntok_t tokens[16]; - - jsmn_init(&parser); - if (0 < jsmn_parse(&parser, body.start, body.size, tokens, - sizeof(tokens) / sizeof *tokens)) - { - jsmnf_loader loader; - jsmnf_pair pairs[16]; - - jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, - pairs, sizeof(pairs) / sizeof *pairs)) - { - jsmnf_pair *f; - - if ((f = jsmnf_find(pairs, body.start, "global", 6))) - is_global = ('t' == body.start[f->v.pos]); - if ((f = jsmnf_find(pairs, body.start, "message", 7))) - message = f->v; - if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) - retry_after = strtod(body.start + f->v.pos, NULL); - } - } - - cxt->wait_ms = (int64_t)(1000 * retry_after); - if (cxt->wait_ms < 0) cxt->wait_ms = 0; - - logconf_warn(&rest->conf, - "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", - is_global ? "GLOBAL " : "", cxt->wait_ms, message.len, - body.start + message.pos); - - cxt->retry = true; - break; - } - default: - cxt->retry = (info->httpcode >= 500); /* retry if Server Error */ - break; - } - } -} - -/* parse request response and prepare callback that should be triggered - * at _discord_rest_trigger_response() */ -static CCORDcode -_discord_rest_select_response(struct discord_rest *rest, - struct discord_context *cxt, - CURLcode ecode) -{ - switch (ecode) { - case CURLE_OK: { - struct ua_szbuf_readonly body; - struct ua_info info; - - _discord_rest_info_extract(rest, cxt, &info); - body = ua_info_get_body(&info); - - if (info.code != CCORD_OK) { - logconf_error(&rest->conf, "%.*s", (int)body.size, body.start); - } - else if (cxt->dispatch.has_type - && cxt->dispatch.sync != DISCORD_SYNC_FLAG) { - if (cxt->dispatch.sync) { - cxt->response.data = cxt->dispatch.sync; - } - else { - cxt->response.data = calloc(1, cxt->response.size); - discord_refcounter_add_internal( - &CLIENT(rest, rest)->refcounter, cxt->response.data, - cxt->response.cleanup, true); - } - - /* initialize ret */ - if (cxt->response.init) cxt->response.init(cxt->response.data); - /* populate ret */ - if (cxt->response.from_json) - cxt->response.from_json(body.start, body.size, - cxt->response.data); - } - - discord_ratelimiter_build(&rest->ratelimiter, cxt->b, cxt->key, &info); - ua_info_cleanup(&info); - } break; - case CURLE_READ_ERROR: - logconf_warn(&rest->conf, "%s (CURLE code: %d)", - curl_easy_strerror(ecode), ecode); - - cxt->retry = true; - cxt->code = CCORD_CURLE_INTERNAL; - - break; - default: - logconf_error(&rest->conf, "%s (CURLE code: %d)", - curl_easy_strerror(ecode), ecode); - - cxt->retry = false; - cxt->code = CCORD_CURLE_INTERNAL; - - break; - } - - return cxt->code; -} - -static CCORDcode -_discord_rest_trigger_response(struct discord_rest *rest, - struct discord_context *cxt) -{ - struct discord *client = CLIENT(rest, rest); - struct discord_response resp = { .data = cxt->dispatch.data, - .keep = cxt->dispatch.keep, - .code = cxt->code }; - - if (cxt->code != CCORD_OK) { - cxt->dispatch.fail(client, &resp); - } - else if (cxt->dispatch.done.typed) { - if (!cxt->dispatch.has_type) { - cxt->dispatch.done.typeless(client, &resp); - } - else { - cxt->dispatch.done.typed(client, &resp, cxt->response.data); - discord_refcounter_decr(&client->refcounter, cxt->response.data); - } - } - - /* enqueue request for retry or recycle */ - if (!discord_async_retry_context(&rest->async, cxt)) - discord_async_cancel_context(&rest->async, cxt); - - return resp.code; -} - -void -discord_rest_perform_callbacks(struct discord_rest *rest) -{ - if (0 == pthread_mutex_trylock(&rest->manager->lock)) { - if (!QUEUE_EMPTY(&rest->async.queues->finished)) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; - - QUEUE_MOVE(&rest->async.queues->finished, &queue); - do { - qelem = QUEUE_HEAD(&queue); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - _discord_rest_trigger_response(rest, cxt); - } while (!QUEUE_EMPTY(&queue)); - - io_poller_wakeup(rest->async.io_poller); - } - pthread_mutex_unlock(&rest->manager->lock); - } -} - CCORDcode discord_rest_perform(struct discord_rest *rest) { CCORDcode code; - int alive = 0; - if (CURLM_OK != curl_multi_socket_all(rest->async.mhandle, &alive)) - return CCORD_CURLM_INTERNAL; - - /* ask for any messages/informationals from the individual transfers */ pthread_mutex_lock(&rest->manager->lock); - while (1) { - int msgq = 0; - struct CURLMsg *msg = curl_multi_info_read(rest->async.mhandle, &msgq); - if (!msg) break; - - if (CURLMSG_DONE == msg->msg) { - struct discord_context *cxt; - - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cxt); - curl_multi_remove_handle(rest->async.mhandle, msg->easy_handle); - - _discord_rest_select_response(rest, cxt, msg->data.result); - if (cxt->dispatch.sync) - pthread_cond_signal(cxt->cond); - else - QUEUE_INSERT_TAIL(&rest->async.queues->finished, &cxt->entry); - } - } + /* ask for any messages/informationals from the individual transfers */ + discord_requestor_info_read(&rest->requestor); + code = discord_requestor_start_pending(&rest->requestor); - code = _discord_rest_start_pending(rest); pthread_mutex_unlock(&rest->manager->lock); return code; @@ -298,7 +40,8 @@ _discord_rest_manager(void *p_rest) now = (int64_t)discord_timestamp_us(client); trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); - poll_result = io_poller_poll(rest->async.io_poller, (int)(trigger / 1000)); + poll_result = + io_poller_poll(rest->requestor.io_poller, (int)(trigger / 1000)); now = (int64_t)discord_timestamp_us(client); if (0 == poll_result) { @@ -306,7 +49,7 @@ _discord_rest_manager(void *p_rest) if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); } discord_timers_run(client, &rest->timers); - io_poller_perform(rest->async.io_poller); + io_poller_perform(rest->requestor.io_poller); threadpool_add(rest->manager->tpool, _discord_rest_manager, rest, 0); } @@ -322,7 +65,7 @@ discord_rest_init(struct discord_rest *rest, logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); discord_timers_init(&rest->timers); - discord_async_init(&rest->async, &rest->conf, token); + discord_requestor_init(&rest->requestor, &rest->conf, token); discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); rest->manager = malloc(sizeof *rest->manager); @@ -346,39 +89,8 @@ discord_rest_cleanup(struct discord_rest *rest) discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); discord_ratelimiter_cleanup(&rest->ratelimiter); - /* cleanup context queues */ - discord_async_cleanup(&rest->async); -} - -/* enqueue a request to be executed asynchronously */ -static CCORDcode -_discord_rest_start_context(struct discord_rest *rest, - struct discord_attributes *attr, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - struct discord_context *cxt; - CCORDcode code; - - pthread_mutex_lock(&rest->manager->lock); - - cxt = discord_async_start_context(&rest->async, attr, body, method, - endpoint, key); - - if (!cxt->dispatch.sync) { - code = CCORD_OK; - } - else { - cxt->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; - pthread_cond_wait(cxt->cond, &rest->manager->lock); - code = _discord_rest_trigger_response(rest, cxt); - } - - pthread_mutex_unlock(&rest->manager->lock); - - return code; + /* cleanup request queues */ + discord_requestor_cleanup(&rest->requestor); } /* template function for performing requests */ @@ -415,6 +127,6 @@ discord_rest_run(struct discord_rest *rest, discord_ratelimiter_build_key(method, key, endpoint_fmt, args); va_end(args); - return _discord_rest_start_context(rest, attr, body, method, endpoint, - key); + return discord_request_begin(&rest->requestor, attr, body, method, + endpoint, key); } diff --git a/src/discord-rest_async.c b/src/discord-rest_async.c deleted file mode 100644 index 976172724..000000000 --- a/src/discord-rest_async.c +++ /dev/null @@ -1,347 +0,0 @@ -#include -#include -#include - -#include "discord.h" -#include "discord-internal.h" - -static struct discord_context * -_discord_context_init(void) -{ - return calloc(1, sizeof(struct discord_context)); -} - -static void -_discord_context_cleanup(struct discord_context *cxt) -{ - discord_attachments_cleanup(&cxt->attachments); - if (cxt->body.start) free(cxt->body.start); - free(cxt); -} - -static struct discord_context * -_discord_context_get(struct discord_async *async) -{ - struct discord_context *cxt; - - if (QUEUE_EMPTY(&async->queues->recycling)) { /* new context struct */ - cxt = _discord_context_init(); - } - else { /* fetch a context struct from queues->recycling */ - QUEUE(struct discord_context) *qelem = - QUEUE_HEAD(&async->queues->recycling); - - QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - } - QUEUE_INIT(&cxt->entry); - - return cxt; -} - -static int -_discord_on_rest_perform(struct io_poller *io, CURLM *mhandle, void *p_rest) -{ - (void)io; - (void)mhandle; - return discord_rest_perform(p_rest); -} - -static void -_discord_on_curl_setopt(struct ua_conn *conn, void *p_token) -{ - struct ccord_szbuf *token = p_token; - char auth[128]; - int len; - - len = snprintf(auth, sizeof(auth), "Bot %.*s", (int)token->size, - token->start); - ASSERT_NOT_OOB(len, sizeof(auth)); - - ua_conn_add_header(conn, "Authorization", auth); - -#ifdef CCORD_DEBUG_HTTP - curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); -#endif -} - -void -discord_async_init(struct discord_async *async, - struct logconf *conf, - struct ccord_szbuf_readonly *token) -{ - logconf_branch(&async->conf, conf, "DISCORD_ASYNC"); - - async->ua = ua_init(&(struct ua_attr){ .conf = conf }); - ua_set_url(async->ua, DISCORD_API_BASE_URL); - ua_set_opt(async->ua, token, &_discord_on_curl_setopt); - - /* queues are malloc'd to guarantee a client cloned by - * discord_clone() will share the same queue with the original */ - async->queues = malloc(sizeof *async->queues); - QUEUE_INIT(&async->queues->recycling); - QUEUE_INIT(&async->queues->pending); - QUEUE_INIT(&async->queues->finished); - - async->mhandle = curl_multi_init(); - async->io_poller = io_poller_create(); - io_poller_curlm_add(async->io_poller, async->mhandle, - &_discord_on_rest_perform, - CONTAINEROF(async, struct discord_rest, async)); - - async->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ -} - -void -discord_async_cleanup(struct discord_async *async) -{ - QUEUE *const cxt_queues[] = { &async->queues->recycling, - &async->queues->pending, - &async->queues->finished }; - - for (size_t i = 0; i < sizeof(cxt_queues) / sizeof *cxt_queues; ++i) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; - - QUEUE_MOVE(cxt_queues[i], &queue); - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - QUEUE_REMOVE(qelem); - - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - _discord_context_cleanup(cxt); - } - } - free(async->queues); - - /* cleanup curl's multi handle */ - io_poller_curlm_del(async->io_poller, async->mhandle); - curl_multi_cleanup(async->mhandle); - /* cleanup REST io_poller */ - io_poller_destroy(async->io_poller); - /* cleanup User-Agent handle */ - ua_cleanup(async->ua); -} - -static void -_discord_context_to_multipart(curl_mime *mime, void *p_cxt) -{ - struct discord_context *cxt = p_cxt; - curl_mimepart *part; - char name[64]; - - /* json part */ - if (cxt->body.start && cxt->body.size) { - part = curl_mime_addpart(mime); - curl_mime_data(part, cxt->body.start, cxt->body.size); - curl_mime_type(part, "application/json"); - curl_mime_name(part, "payload_json"); - } - - /* attachment part */ - for (int i = 0; i < cxt->attachments.size; ++i) { - int len = snprintf(name, sizeof(name), "files[%d]", i); - ASSERT_NOT_OOB(len, sizeof(name)); - - if (cxt->attachments.array[i].content) { - part = curl_mime_addpart(mime); - curl_mime_data(part, cxt->attachments.array[i].content, - cxt->attachments.array[i].size - ? cxt->attachments.array[i].size - : CURL_ZERO_TERMINATED); - curl_mime_filename(part, !cxt->attachments.array[i].filename - ? "a.out" - : cxt->attachments.array[i].filename); - curl_mime_type(part, !cxt->attachments.array[i].content_type - ? "application/octet-stream" - : cxt->attachments.array[i].content_type); - curl_mime_name(part, name); - } - else if (cxt->attachments.array[i].filename) { - CURLcode code; - - /* fetch local file by the filename */ - part = curl_mime_addpart(mime); - code = - curl_mime_filedata(part, cxt->attachments.array[i].filename); - if (code != CURLE_OK) { - char errbuf[256]; - snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", - curl_easy_strerror(code), - cxt->attachments.array[i].filename); - perror(errbuf); - } - curl_mime_type(part, !cxt->attachments.array[i].content_type - ? "application/octet-stream" - : cxt->attachments.array[i].content_type); - curl_mime_name(part, name); - } - } -} - -CCORDcode -discord_async_start_bucket_request(struct discord_async *async, - struct discord_bucket *b) -{ - struct discord_context *cxt = discord_bucket_remove_context(b); - CURL *ehandle; - - b->performing_cxt = cxt; - cxt->conn = ua_conn_start(async->ua); - ehandle = ua_conn_get_easy_handle(cxt->conn); - - if (HTTP_MIMEPOST == cxt->method) { - ua_conn_add_header(cxt->conn, "Content-Type", "multipart/form-data"); - ua_conn_set_mime(cxt->conn, cxt, &_discord_context_to_multipart); - } - else { - ua_conn_add_header(cxt->conn, "Content-Type", "application/json"); - } - - ua_conn_setup(cxt->conn, &(struct ua_conn_attr){ - .method = cxt->method, - .body = cxt->body.start, - .body_size = cxt->body.size, - .endpoint = cxt->endpoint, - .base_url = NULL, - }); - - /* link 'cxt' to 'ehandle' for easy retrieval */ - curl_easy_setopt(ehandle, CURLOPT_PRIVATE, cxt); - - /* initiate libcurl transfer */ - return (curl_multi_add_handle(async->mhandle, ehandle) != CURLM_OK) - ? CCORD_CURLM_INTERNAL - : CCORD_OK; -} - -bool -discord_async_retry_context(struct discord_async *async, - struct discord_context *cxt) -{ - if (!cxt->retry || cxt->retry_attempt++ >= async->retry_limit) - return false; - - cxt->b->performing_cxt = NULL; - ua_conn_reset(cxt->conn); - - /* FIXME: wait_ms > 0 should be dealt with aswell */ - if (cxt->wait_ms <= 0) discord_bucket_add_context(cxt->b, cxt, true); - - return true; -} - -void -discord_async_cancel_context(struct discord_async *async, - struct discord_context *cxt) -{ - struct discord_refcounter *rc = &CLIENT(async, rest.async)->refcounter; - - if (cxt->conn) ua_conn_stop(cxt->conn); - - if (cxt->dispatch.keep) - discord_refcounter_decr(rc, (void *)cxt->dispatch.keep); - if (cxt->dispatch.data) discord_refcounter_decr(rc, cxt->dispatch.data); - - cxt->b->performing_cxt = NULL; - cxt->body.size = 0; - cxt->method = 0; - *cxt->endpoint = '\0'; - *cxt->key = '\0'; - cxt->conn = NULL; - cxt->retry_attempt = 0; - discord_attachments_cleanup(&cxt->attachments); - memset(cxt, 0, sizeof(struct discord_attributes)); - - QUEUE_REMOVE(&cxt->entry); - QUEUE_INIT(&cxt->entry); - QUEUE_INSERT_TAIL(&async->queues->recycling, &cxt->entry); -} - -/* Only fields required at _discord_context_to_multipart() are duplicated */ -static void -_discord_attachments_dup(struct discord_attachments *dest, - struct discord_attachments *src) -{ - __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); - for (int i = 0; i < src->size; ++i) { - carray_insert(dest, i, src->array[i]); - if (src->array[i].content) { - dest->array[i].size = src->array[i].size - ? src->array[i].size - : strlen(src->array[i].content) + 1; - - dest->array[i].content = malloc(dest->array[i].size); - memcpy(dest->array[i].content, src->array[i].content, - dest->array[i].size); - } - if (src->array[i].filename) - dest->array[i].filename = strdup(src->array[i].filename); - if (src->array[i].content_type) - dest->array[i].content_type = strdup(src->array[i].content_type); - } -} - -struct discord_context * -discord_async_start_context(struct discord_async *async, - struct discord_attributes *attr, - struct ccord_szbuf *body, - enum http_method method, - char endpoint[DISCORD_ENDPT_LEN], - char key[DISCORD_ROUTE_LEN]) -{ - struct discord_rest *rest = CONTAINEROF(async, struct discord_rest, async); - struct discord *client = CLIENT(rest, rest); - struct discord_context *cxt = _discord_context_get(async); - - cxt->method = method; - - memcpy(cxt, attr, sizeof *attr); - - if (attr->attachments.size) - _discord_attachments_dup(&cxt->attachments, &attr->attachments); - - if (body) { - /* copy request body */ - if (body->size > cxt->body.realsize) { - /* needs to increase buffer size */ - void *tmp = realloc(cxt->body.start, body->size); - ASSERT_S(tmp != NULL, "Out of memory"); - - cxt->body.start = tmp; - cxt->body.realsize = body->size; - } - memcpy(cxt->body.start, body->start, body->size); - cxt->body.size = body->size; - } - - /* copy endpoint over to cxt */ - memcpy(cxt->endpoint, endpoint, sizeof(cxt->endpoint)); - /* copy bucket's key */ - memcpy(cxt->key, key, sizeof(cxt->key)); - - cxt->cond = NULL; - - if (attr->dispatch.keep) { - CCORDcode code = discord_refcounter_incr(&client->refcounter, - (void *)attr->dispatch.keep); - - ASSERT_S(code == CCORD_OK, - "'.keep' data must be a Concord callback parameter"); - } - if (attr->dispatch.data - && CCORD_UNAVAILABLE - == discord_refcounter_incr(&client->refcounter, - attr->dispatch.data)) - { - discord_refcounter_add_client(&client->refcounter, attr->dispatch.data, - attr->dispatch.cleanup, false); - } - - /* context will be assigned to its bucket at the REST thread */ - QUEUE_INSERT_TAIL(&rest->async.queues->pending, &cxt->entry); - - io_poller_wakeup(async->io_poller); - - return cxt; -} diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 0b5b80157..704c7a2eb 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -168,15 +168,14 @@ static void _discord_bucket_cancel(struct discord_ratelimiter *rl, struct discord_bucket *b) { - struct discord_async *async = - &CONTAINEROF(rl, struct discord_rest, ratelimiter)->async; + struct discord_requestor *rqtor = + &CONTAINEROF(rl, struct discord_rest, ratelimiter)->requestor; /* cancel busy transfer */ - if (b->performing_cxt) - discord_async_cancel_context(async, b->performing_cxt); + if (b->performing_req) discord_request_cancel(rqtor, b->performing_req); /* move pending tranfers to recycling */ - QUEUE_ADD(&async->queues->recycling, &b->pending_queue); + QUEUE_ADD(&rqtor->queues->recycling, &b->pending_queue); QUEUE_INIT(&b->pending_queue); } @@ -253,7 +252,7 @@ _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) (void)client; struct discord_bucket *b = timer->data; - b->performing_cxt = NULL; + b->performing_req = NULL; b->remaining = 1; } @@ -265,7 +264,7 @@ discord_bucket_try_timeout(struct discord_ratelimiter *rl, int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); if (delay_ms < 0) delay_ms = 0; - b->performing_cxt = DISCORD_BUCKET_TIMEOUT; + b->performing_req = DISCORD_BUCKET_TIMEOUT; _discord_timer_ctl( client, &client->rest.timers, @@ -393,8 +392,8 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, struct discord_bucket *b, const char key[]) { - QUEUE(struct discord_context) queue, *qelem; - struct discord_context *cxt; + QUEUE(struct discord_request) queue, *qelem; + struct discord_request *req; QUEUE_MOVE(&rl->null->pending_queue, &queue); QUEUE_INIT(&rl->null->pending_queue); @@ -403,10 +402,10 @@ _discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, qelem = QUEUE_HEAD(&queue); QUEUE_REMOVE(qelem); - cxt = QUEUE_DATA(qelem, struct discord_context, entry); - if (0 == strcmp(cxt->key, key)) { + req = QUEUE_DATA(qelem, struct discord_request, entry); + if (0 == strcmp(req->key, key)) { QUEUE_INSERT_TAIL(&b->pending_queue, qelem); - cxt->b = b; + req->b = b; } else { QUEUE_INSERT_TAIL(&rl->null->pending_queue, qelem); @@ -431,25 +430,25 @@ discord_ratelimiter_build(struct discord_ratelimiter *rl, } void -discord_bucket_add_context(struct discord_bucket *b, - struct discord_context *cxt, +discord_bucket_add_request(struct discord_bucket *b, + struct discord_request *req, bool high_priority) { - QUEUE_REMOVE(&cxt->entry); - QUEUE_INIT(&cxt->entry); + QUEUE_REMOVE(&req->entry); + QUEUE_INIT(&req->entry); if (high_priority) - QUEUE_INSERT_HEAD(&b->pending_queue, &cxt->entry); + QUEUE_INSERT_HEAD(&b->pending_queue, &req->entry); else - QUEUE_INSERT_TAIL(&b->pending_queue, &cxt->entry); - cxt->b = b; + QUEUE_INSERT_TAIL(&b->pending_queue, &req->entry); + req->b = b; } -struct discord_context * -discord_bucket_remove_context(struct discord_bucket *b) +struct discord_request * +discord_bucket_remove_request(struct discord_bucket *b) { - QUEUE(struct discord_context) *qelem = QUEUE_HEAD(&b->pending_queue); + QUEUE(struct discord_request) *qelem = QUEUE_HEAD(&b->pending_queue); QUEUE_REMOVE(qelem); QUEUE_INIT(qelem); - return QUEUE_DATA(qelem, struct discord_context, entry); + return QUEUE_DATA(qelem, struct discord_request, entry); } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c new file mode 100644 index 000000000..35926650f --- /dev/null +++ b/src/discord-rest_request.c @@ -0,0 +1,635 @@ +#include +#include +#include + +#include "discord.h" +#include "discord-internal.h" + +static struct discord_request * +_discord_request_init(void) +{ + return calloc(1, sizeof(struct discord_request)); +} + +static void +_discord_request_cleanup(struct discord_request *req) +{ + discord_attachments_cleanup(&req->attachments); + if (req->body.start) free(req->body.start); + free(req); +} + +static int +_discord_on_rest_perform(struct io_poller *io, CURLM *mhandle, void *p_rest) +{ + (void)io; + (void)mhandle; + return discord_rest_perform(p_rest); +} + +static void +_discord_on_curl_setopt(struct ua_conn *conn, void *p_token) +{ + struct ccord_szbuf *token = p_token; + char auth[128]; + int len; + + len = snprintf(auth, sizeof(auth), "Bot %.*s", (int)token->size, + token->start); + ASSERT_NOT_OOB(len, sizeof(auth)); + + ua_conn_add_header(conn, "Authorization", auth); + +#ifdef CCORD_DEBUG_HTTP + curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); +#endif +} + +void +discord_requestor_init(struct discord_requestor *rqtor, + struct logconf *conf, + struct ccord_szbuf_readonly *token) +{ + logconf_branch(&rqtor->conf, conf, "DISCORD_REQUEST"); + + rqtor->ua = ua_init(&(struct ua_attr){ .conf = conf }); + ua_set_url(rqtor->ua, DISCORD_API_BASE_URL); + ua_set_opt(rqtor->ua, token, &_discord_on_curl_setopt); + + /* queues are malloc'd to guarantee a client cloned by + * discord_clone() will share the same queue with the original */ + rqtor->queues = malloc(sizeof *rqtor->queues); + QUEUE_INIT(&rqtor->queues->recycling); + QUEUE_INIT(&rqtor->queues->pending); + QUEUE_INIT(&rqtor->queues->finished); + + rqtor->mhandle = curl_multi_init(); + rqtor->io_poller = io_poller_create(); + io_poller_curlm_add(rqtor->io_poller, rqtor->mhandle, + &_discord_on_rest_perform, + CONTAINEROF(rqtor, struct discord_rest, requestor)); + + rqtor->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ +} + +void +discord_requestor_cleanup(struct discord_requestor *rqtor) +{ + QUEUE *const req_queues[] = { &rqtor->queues->recycling, + &rqtor->queues->pending, + &rqtor->queues->finished }; + + for (size_t i = 0; i < sizeof(req_queues) / sizeof *req_queues; ++i) { + QUEUE(struct discord_request) queue, *qelem; + struct discord_request *req; + + QUEUE_MOVE(req_queues[i], &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); + + req = QUEUE_DATA(qelem, struct discord_request, entry); + _discord_request_cleanup(req); + } + } + free(rqtor->queues); + + /* cleanup curl's multi handle */ + io_poller_curlm_del(rqtor->io_poller, rqtor->mhandle); + curl_multi_cleanup(rqtor->mhandle); + /* cleanup REST io_poller */ + io_poller_destroy(rqtor->io_poller); + /* cleanup User-Agent handle */ + ua_cleanup(rqtor->ua); +} + +static void +_discord_request_to_multipart(curl_mime *mime, void *p_req) +{ + struct discord_request *req = p_req; + curl_mimepart *part; + char name[64]; + + /* json part */ + if (req->body.start && req->body.size) { + part = curl_mime_addpart(mime); + curl_mime_data(part, req->body.start, req->body.size); + curl_mime_type(part, "application/json"); + curl_mime_name(part, "payload_json"); + } + + /* attachment part */ + for (int i = 0; i < req->attachments.size; ++i) { + int len = snprintf(name, sizeof(name), "files[%d]", i); + ASSERT_NOT_OOB(len, sizeof(name)); + + if (req->attachments.array[i].content) { + part = curl_mime_addpart(mime); + curl_mime_data(part, req->attachments.array[i].content, + req->attachments.array[i].size + ? req->attachments.array[i].size + : CURL_ZERO_TERMINATED); + curl_mime_filename(part, !req->attachments.array[i].filename + ? "a.out" + : req->attachments.array[i].filename); + curl_mime_type(part, !req->attachments.array[i].content_type + ? "application/octet-stream" + : req->attachments.array[i].content_type); + curl_mime_name(part, name); + } + else if (req->attachments.array[i].filename) { + CURLcode code; + + /* fetch local file by the filename */ + part = curl_mime_addpart(mime); + code = + curl_mime_filedata(part, req->attachments.array[i].filename); + if (code != CURLE_OK) { + char errbuf[256]; + snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", + curl_easy_strerror(code), + req->attachments.array[i].filename); + perror(errbuf); + } + curl_mime_type(part, !req->attachments.array[i].content_type + ? "application/octet-stream" + : req->attachments.array[i].content_type); + curl_mime_name(part, name); + } + } +} + +static void +_discord_request_info_extract(struct discord_requestor *rqtor, + struct discord_request *req, + struct ua_info *info) +{ + ua_info_extract(req->conn, info); + + if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ + req->retry = false; + } + else { + switch (info->httpcode) { + case HTTP_FORBIDDEN: + case HTTP_NOT_FOUND: + case HTTP_BAD_REQUEST: + info->code = CCORD_DISCORD_JSON_CODE; + req->retry = false; + break; + case HTTP_UNAUTHORIZED: + logconf_fatal( + &rqtor->conf, + "UNAUTHORIZED: Please provide a valid authentication token"); + info->code = CCORD_DISCORD_BAD_AUTH; + req->retry = false; + break; + case HTTP_METHOD_NOT_ALLOWED: + logconf_fatal( + &rqtor->conf, + "METHOD_NOT_ALLOWED: The server couldn't recognize the " + "received HTTP method"); + req->retry = false; + break; + case HTTP_TOO_MANY_REQUESTS: { + struct ua_szbuf_readonly body = ua_info_get_body(info); + struct jsmnftok message = { 0 }; + double retry_after = 1.0; + bool is_global = false; + jsmn_parser parser; + jsmntok_t tokens[16]; + + jsmn_init(&parser); + if (0 < jsmn_parse(&parser, body.start, body.size, tokens, + sizeof(tokens) / sizeof *tokens)) + { + jsmnf_loader loader; + jsmnf_pair pairs[16]; + + jsmnf_init(&loader); + if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, + pairs, sizeof(pairs) / sizeof *pairs)) + { + jsmnf_pair *f; + + if ((f = jsmnf_find(pairs, body.start, "global", 6))) + is_global = ('t' == body.start[f->v.pos]); + if ((f = jsmnf_find(pairs, body.start, "message", 7))) + message = f->v; + if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) + retry_after = strtod(body.start + f->v.pos, NULL); + } + } + + req->wait_ms = (int64_t)(1000 * retry_after); + if (req->wait_ms < 0) req->wait_ms = 0; + + logconf_warn(&rqtor->conf, + "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", + is_global ? "GLOBAL " : "", req->wait_ms, message.len, + body.start + message.pos); + + req->retry = true; + break; + } + default: + req->retry = (info->httpcode >= 500); /* retry if Server Error */ + break; + } + } +} + +/** + * @brief If request can be retried then it will be moved back to its + * bucket's queue + * @note this **MUST** be called only after discord_request_info_extract() + * + * @param rqtor the requestor handle initialized with discord_requestor_init() + * @param req the request to be checked for retry + * @return `true` if request has been enqueued for retry + */ +static bool +_discord_request_retry(struct discord_requestor *rqtor, + struct discord_request *req) +{ + if (!req->retry || req->retry_attempt++ >= rqtor->retry_limit) + return false; + + req->b->performing_req = NULL; + ua_conn_reset(req->conn); + + /* FIXME: wait_ms > 0 should be dealt with aswell */ + if (req->wait_ms <= 0) discord_bucket_add_request(req->b, req, true); + + return true; +} + +void +discord_request_cancel(struct discord_requestor *rqtor, + struct discord_request *req) +{ + struct discord_refcounter *rc = &CLIENT(rqtor, rest.requestor)->refcounter; + + if (req->conn) ua_conn_stop(req->conn); + + if (req->dispatch.keep) + discord_refcounter_decr(rc, (void *)req->dispatch.keep); + if (req->dispatch.data) discord_refcounter_decr(rc, req->dispatch.data); + + req->b->performing_req = NULL; + req->body.size = 0; + req->method = 0; + *req->endpoint = '\0'; + *req->key = '\0'; + req->conn = NULL; + req->retry_attempt = 0; + discord_attachments_cleanup(&req->attachments); + memset(req, 0, sizeof(struct discord_attributes)); + + QUEUE_REMOVE(&req->entry); + QUEUE_INIT(&req->entry); + QUEUE_INSERT_TAIL(&rqtor->queues->recycling, &req->entry); +} + +static CCORDcode +_discord_request_dispatch_response(struct discord_requestor *rqtor, + struct discord_request *req) +{ + struct discord *client = CLIENT(rqtor, rest.requestor); + struct discord_response resp = { .data = req->dispatch.data, + .keep = req->dispatch.keep, + .code = req->code }; + + if (req->code != CCORD_OK) { + req->dispatch.fail(client, &resp); + } + else if (req->dispatch.done.typed) { + if (!req->dispatch.has_type) { + req->dispatch.done.typeless(client, &resp); + } + else { + req->dispatch.done.typed(client, &resp, req->response.data); + discord_refcounter_decr(&client->refcounter, req->response.data); + } + } + + /* enqueue request for retry or recycle */ + if (!_discord_request_retry(rqtor, req)) + discord_request_cancel(rqtor, req); + + return resp.code; +} + +void +discord_requestor_dispatch_responses(struct discord_requestor *rqtor) +{ + struct discord_rest *rest = + CONTAINEROF(rqtor, struct discord_rest, requestor); + + if (0 == pthread_mutex_trylock(&rest->manager->lock)) { + if (!QUEUE_EMPTY(&rqtor->queues->finished)) { + QUEUE(struct discord_request) queue, *qelem; + struct discord_request *req; + + QUEUE_MOVE(&rqtor->queues->finished, &queue); + do { + qelem = QUEUE_HEAD(&queue); + req = QUEUE_DATA(qelem, struct discord_request, entry); + _discord_request_dispatch_response(rqtor, req); + } while (!QUEUE_EMPTY(&queue)); + + io_poller_wakeup(rqtor->io_poller); + } + pthread_mutex_unlock(&rest->manager->lock); + } +} + +/* parse request response and prepare callback that should be triggered + * at _discord_rest_run_request_callback() */ +CCORDcode +discord_requestor_info_read(struct discord_requestor *rqtor) +{ + CCORDcode code; + int alive = 0; + + if (CURLM_OK != curl_multi_socket_all(rqtor->mhandle, &alive)) + return CCORD_CURLM_INTERNAL; + + while (1) { + int msgq = 0; + struct CURLMsg *msg = curl_multi_info_read(rqtor->mhandle, &msgq); + + if (!msg) break; + + if (CURLMSG_DONE == msg->msg) { + const CURLcode ecode = msg->data.result; + struct discord_request *req; + + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &req); + curl_multi_remove_handle(rqtor->mhandle, msg->easy_handle); + + switch (ecode) { + case CURLE_OK: { + struct discord_ratelimiter *rl = + &CONTAINEROF(rqtor, struct discord_rest, requestor) + ->ratelimiter; + + struct ua_szbuf_readonly body; + struct ua_info info; + + _discord_request_info_extract(rqtor, req, &info); + body = ua_info_get_body(&info); + + if (info.code != CCORD_OK) { + logconf_error(&rqtor->conf, "%.*s", (int)body.size, + body.start); + } + else if (req->dispatch.has_type + && req->dispatch.sync != DISCORD_SYNC_FLAG) { + if (req->dispatch.sync) { + req->response.data = req->dispatch.sync; + } + else { + req->response.data = calloc(1, req->response.size); + discord_refcounter_add_internal( + &CLIENT(rqtor, rest.requestor)->refcounter, + req->response.data, req->response.cleanup, true); + } + + /* initialize ret */ + if (req->response.init) + req->response.init(req->response.data); + /* populate ret */ + if (req->response.from_json) + req->response.from_json(body.start, body.size, + req->response.data); + } + + discord_ratelimiter_build(rl, req->b, req->key, &info); + ua_info_cleanup(&info); + } break; + case CURLE_READ_ERROR: + logconf_warn(&rqtor->conf, "%s (CURLE code: %d)", + curl_easy_strerror(ecode), ecode); + + req->retry = true; + req->code = CCORD_CURLE_INTERNAL; + + break; + default: + logconf_error(&rqtor->conf, "%s (CURLE code: %d)", + curl_easy_strerror(ecode), ecode); + + req->retry = false; + req->code = CCORD_CURLE_INTERNAL; + + break; + } + + code = req->code; + + if (req->dispatch.sync) + pthread_cond_signal(req->cond); + else + QUEUE_INSERT_TAIL(&rqtor->queues->finished, &req->entry); + } + } + + return code; +} + +static void +_discord_request_try_begin(struct discord_ratelimiter *rl, + struct discord_bucket *b) +{ + /* skip if bucket is already busy performing */ + if (b->performing_req) return; + + if (!b->remaining) { + discord_bucket_try_timeout(rl, b); + } + else if (!QUEUE_EMPTY(&b->pending_queue)) { + struct discord_requestor *rqtor = + &CONTAINEROF(rl, struct discord_rest, ratelimiter)->requestor; + + struct discord_request *req = discord_bucket_remove_request(b); + CURL *ehandle; + + b->performing_req = req; + req->conn = ua_conn_start(rqtor->ua); + ehandle = ua_conn_get_easy_handle(req->conn); + + if (HTTP_MIMEPOST == req->method) { + ua_conn_add_header(req->conn, "Content-Type", + "multipart/form-data"); + ua_conn_set_mime(req->conn, req, &_discord_request_to_multipart); + } + else { + ua_conn_add_header(req->conn, "Content-Type", "application/json"); + } + + ua_conn_setup(req->conn, &(struct ua_conn_attr){ + .method = req->method, + .body = req->body.start, + .body_size = req->body.size, + .endpoint = req->endpoint, + .base_url = NULL, + }); + + /* link 'req' to 'ehandle' for easy retrieval */ + curl_easy_setopt(ehandle, CURLOPT_PRIVATE, req); + + /* initiate libcurl transfer */ + curl_multi_add_handle(rqtor->mhandle, ehandle); + } +} + +CCORDcode +discord_requestor_start_pending(struct discord_requestor *rqtor) +{ + struct discord_ratelimiter *rl = + &CONTAINEROF(rqtor, struct discord_rest, requestor)->ratelimiter; + + QUEUE(struct discord_request) queue, *qelem; + struct discord_request *req; + struct discord_bucket *b; + + /* match pending requests to their buckets */ + QUEUE_MOVE(&rqtor->queues->pending, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + QUEUE_REMOVE(qelem); + + req = QUEUE_DATA(qelem, struct discord_request, entry); + b = discord_bucket_get(rl, req->key); + discord_bucket_add_request(b, req, req->dispatch.high_p); + } + + /* TODO: replace foreach with a mechanism that loops only busy buckets */ + discord_ratelimiter_foreach_bucket(rl, &_discord_request_try_begin); + + /* FIXME: redundant return value (constant) */ + return CCORD_OK; +} + +/* Only fields required at _discord_request_to_multipart() are duplicated */ +static void +_discord_attachments_dup(struct discord_attachments *dest, + struct discord_attachments *src) +{ + __carray_init(dest, (size_t)src->size, struct discord_attachment, , ); + for (int i = 0; i < src->size; ++i) { + carray_insert(dest, i, src->array[i]); + if (src->array[i].content) { + dest->array[i].size = src->array[i].size + ? src->array[i].size + : strlen(src->array[i].content) + 1; + + dest->array[i].content = malloc(dest->array[i].size); + memcpy(dest->array[i].content, src->array[i].content, + dest->array[i].size); + } + if (src->array[i].filename) + dest->array[i].filename = strdup(src->array[i].filename); + if (src->array[i].content_type) + dest->array[i].content_type = strdup(src->array[i].content_type); + } +} + +static struct discord_request * +_discord_request_get(struct discord_requestor *rqtor) +{ + struct discord_request *req; + + if (QUEUE_EMPTY(&rqtor->queues->recycling)) { /* new request struct */ + req = _discord_request_init(); + } + else { /* fetch a request struct from queues->recycling */ + QUEUE(struct discord_request) *qelem = + QUEUE_HEAD(&rqtor->queues->recycling); + + QUEUE_REMOVE(qelem); + req = QUEUE_DATA(qelem, struct discord_request, entry); + } + QUEUE_INIT(&req->entry); + + return req; +} + +CCORDcode +discord_request_begin(struct discord_requestor *rqtor, + struct discord_attributes *attr, + struct ccord_szbuf *body, + enum http_method method, + char endpoint[DISCORD_ENDPT_LEN], + char key[DISCORD_ROUTE_LEN]) +{ + struct discord_rest *rest = + CONTAINEROF(rqtor, struct discord_rest, requestor); + struct discord *client = CLIENT(rest, rest); + struct discord_request *req = _discord_request_get(rqtor); + CCORDcode code; + + pthread_mutex_lock(&rest->manager->lock); + + req->method = method; + memcpy(req, attr, sizeof *attr); + + if (attr->attachments.size) + _discord_attachments_dup(&req->attachments, &attr->attachments); + + if (body) { + /* copy request body */ + if (body->size > req->body.realsize) { + /* needs to increase buffer size */ + void *tmp = realloc(req->body.start, body->size); + ASSERT_S(tmp != NULL, "Out of memory"); + + req->body.start = tmp; + req->body.realsize = body->size; + } + memcpy(req->body.start, body->start, body->size); + req->body.size = body->size; + } + + /* copy endpoint over to req */ + memcpy(req->endpoint, endpoint, sizeof(req->endpoint)); + /* copy bucket's key */ + memcpy(req->key, key, sizeof(req->key)); + + req->cond = NULL; + + if (attr->dispatch.keep) { + code = discord_refcounter_incr(&client->refcounter, + (void *)attr->dispatch.keep); + + ASSERT_S(code == CCORD_OK, + "'.keep' data must be a Concord callback parameter"); + } + if (attr->dispatch.data + && CCORD_UNAVAILABLE + == discord_refcounter_incr(&client->refcounter, + attr->dispatch.data)) + { + discord_refcounter_add_client(&client->refcounter, attr->dispatch.data, + attr->dispatch.cleanup, false); + } + + /* request will be assigned to its bucket at the REST thread */ + QUEUE_INSERT_TAIL(&rest->requestor.queues->pending, &req->entry); + + io_poller_wakeup(rqtor->io_poller); + + if (!req->dispatch.sync) { + code = CCORD_OK; + } + else { + req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; + pthread_cond_wait(req->cond, &rest->manager->lock); + code = _discord_request_dispatch_response(rqtor, req); + } + + pthread_mutex_unlock(&rest->manager->lock); + + return code; +} From 22ddd815b9675db269429127fc1644cfb9f2faff Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 26 May 2022 16:50:40 -0300 Subject: [PATCH 078/118] refactor: move 'struct discord_ratelimiter' field to 'struct discord_requestor' --- include/discord-internal.h | 355 ++++++++++++++++++----------------- src/discord-rest.c | 4 - src/discord-rest_ratelimit.c | 4 +- src/discord-rest_request.c | 23 +-- 4 files changed, 192 insertions(+), 194 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index dcc51bd69..483946e1e 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -167,14 +167,188 @@ unsigned discord_internal_timer(struct discord *client, /** @} DiscordInternalTimer */ +/** @defgroup DiscordInternalREST REST API + * @brief Wrapper to the Discord REST API + * @{ */ + /** @defgroup DiscordInternalRESTRequest Request's handling * @brief Store, manage and dispatch individual requests * @{ */ -/** @defgroup DiscordInternalREST REST API - * @brief Wrapper to the Discord REST API +/** @defgroup DiscordInternalRESTRequestRatelimit Ratelimiting + * @brief Enforce ratelimiting per the official Discord Documentation * @{ */ +/** + * @brief Value assigned to @ref discord_bucket `performing_req` field in case + * it's being timed-out + */ +#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) + +/** + * @brief The ratelimiter struct for handling ratelimiting + * @note this struct **SHOULD** only be handled from the `REST` manager thread + */ +struct discord_ratelimiter { + /** `DISCORD_RATELIMIT` logging module */ + struct logconf conf; + /** amount of bucket's routes discovered */ + int length; + /** route's cap before increase */ + int capacity; + /** + * routes matched to individual buckets + * @note datatype declared at discord-rest_ratelimit.c + */ + struct _discord_route *routes; + /** singleton bucket for requests that haven't been matched to a + * known or new bucket (i.e first time running the request) */ + struct discord_bucket *null; + /** singleton bucket for requests that are not part of any known + * ratelimiting group */ + struct discord_bucket *miss; + + /* client-wide ratelimiting timeout */ + struct { + /** global ratelimit */ + u64unix_ms wait_ms; + /** global rwlock */ + pthread_rwlock_t rwlock; + /** global lock */ + pthread_mutex_t lock; + } * global; +}; + +/** + * @brief Initialize ratelimiter handle + * + * A hashtable shall be used for storage and retrieval of discovered buckets + * @param rl the ratelimiter handle to be initialized + * @param conf pointer to @ref discord_rest logging module + */ +void discord_ratelimiter_init(struct discord_ratelimiter *rl, + struct logconf *conf); + +/** + * @brief Cleanup all buckets that have been discovered + * + * @note pending requests will be moved to `rest.queues->recycling` + * @param rl the handle initialized with discord_ratelimiter_init() + */ +void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); + +/** + * @brief Iterate known buckets + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param iter the user callback to be called per bucket + */ +void discord_ratelimiter_foreach_bucket( + struct discord_ratelimiter *rl, + void (*iter)(struct discord_ratelimiter *rl, struct discord_bucket *b)); + +/** + * @brief Build unique key formed from the HTTP method and endpoint + * @see https://discord.com/developers/docs/topics/rate-limits + * + * @param[in] method the request method + * @param[out] key unique key for matching to buckets + * @param[in] endpoint_fmt the printf-like endpoint formatting string + * @param[in] args variadic arguments matched to `endpoint_fmt` + */ +void discord_ratelimiter_build_key(enum http_method method, + char key[DISCORD_ROUTE_LEN], + const char endpoint_fmt[], + va_list args); + +/** + * @brief Update the bucket with response header data + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param bucket NULL when bucket is first discovered + * @param key obtained from discord_ratelimiter_build_key() + * @param info informational struct containing details on the current transfer + * @note If the bucket was just discovered it will be created here. + */ +void discord_ratelimiter_build(struct discord_ratelimiter *rl, + struct discord_bucket *bucket, + const char key[], + struct ua_info *info); + +/** @brief The Discord bucket for handling per-group ratelimits */ +struct discord_bucket { + /** the hash associated with the bucket's ratelimiting group */ + char hash[64]; + /** maximum connections this bucket can handle before ratelimit */ + long limit; + /** connections this bucket can do before waiting for cooldown */ + long remaining; + /** timestamp of when cooldown timer resets */ + u64unix_ms reset_tstamp; + /** pending requests */ + QUEUE(struct discord_request) pending_queue; + /** + * pointer to this bucket's currently performing request + * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited + */ + struct discord_request *performing_req; + /** synchronize bucket */ + pthread_mutex_t lock; +}; + +/** + * @brief Return bucket timeout timestamp + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param bucket the bucket to be checked for time out + * @return the timeout timestamp + */ +u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, + struct discord_bucket *bucket); + +/** + * @brief Try to timeout bucket for pending cooldown time + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param bucket the bucket to wait on cooldown + */ +void discord_bucket_try_timeout(struct discord_ratelimiter *rl, + struct discord_bucket *b); + +/** + * @brief Get a `struct discord_bucket` assigned to `key` + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param key obtained from discord_ratelimiter_build_key() + * @return bucket matched to `key` + */ +struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, + const char key[]); + +/** + * @brief Insert request into bucket's pending queue + * + * @param b the bucket to insert the request to + * @param req the request obtained via discord_requestor_start_request() + * @param high_priority if high priority then request shall be prioritized over + * already enqueued requests + */ +void discord_bucket_add_request(struct discord_bucket *b, + struct discord_request *req, + bool high_priority); + +/** + * @brief Remove head request from bucket's pending queue + * + * @param b the bucket to fetch the request from + * @return the request + */ +struct discord_request *discord_bucket_remove_request( + struct discord_bucket *b); + +/** @} DiscordInternalRESTRequestRatelimit */ + + /** @brief Generic request dispatcher */ struct discord_ret_dispatch { DISCORD_RET_DEFAULT_FIELDS; @@ -277,6 +451,8 @@ struct discord_requestor { CURLM *mhandle; /** io_poller for rest only */ struct io_poller *io_poller; + /** enforce Discord's ratelimiting for requests */ + struct discord_ratelimiter ratelimiter; /** max amount of retries before a failed request gives up */ int retry_limit; @@ -369,179 +545,6 @@ CCORDcode discord_request_begin(struct discord_requestor *rqtor, /** @} DiscordInternalRESTRequest */ -/** @defgroup DiscordInternalRESTRatelimit Ratelimiting - * @brief Enforce ratelimiting per the official Discord Documentation - * @{ */ - -/** - * @brief Value assigned to @ref discord_bucket `performing_req` field in case - * it's being timed-out - */ -#define DISCORD_BUCKET_TIMEOUT (void *)(0xf) - -/** - * @brief The ratelimiter struct for handling ratelimiting - * @note this struct **SHOULD** only be handled from the `REST` manager thread - */ -struct discord_ratelimiter { - /** `DISCORD_RATELIMIT` logging module */ - struct logconf conf; - /** amount of bucket's routes discovered */ - int length; - /** route's cap before increase */ - int capacity; - /** - * routes matched to individual buckets - * @note datatype declared at discord-rest_ratelimit.c - */ - struct _discord_route *routes; - /** singleton bucket for requests that haven't been matched to a - * known or new bucket (i.e first time running the request) */ - struct discord_bucket *null; - /** singleton bucket for requests that are not part of any known - * ratelimiting group */ - struct discord_bucket *miss; - - /* client-wide ratelimiting timeout */ - struct { - /** global ratelimit */ - u64unix_ms wait_ms; - /** global rwlock */ - pthread_rwlock_t rwlock; - /** global lock */ - pthread_mutex_t lock; - } * global; -}; - -/** - * @brief Initialize ratelimiter handle - * - * A hashtable shall be used for storage and retrieval of discovered buckets - * @param rl the ratelimiter handle to be initialized - * @param conf pointer to @ref discord_rest logging module - */ -void discord_ratelimiter_init(struct discord_ratelimiter *rl, - struct logconf *conf); - -/** - * @brief Cleanup all buckets that have been discovered - * - * @note pending requests will be moved to `rest.queues->recycling` - * @param rl the handle initialized with discord_ratelimiter_init() - */ -void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); - -/** - * @brief Iterate known buckets - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param iter the user callback to be called per bucket - */ -void discord_ratelimiter_foreach_bucket( - struct discord_ratelimiter *rl, - void (*iter)(struct discord_ratelimiter *rl, struct discord_bucket *b)); - -/** - * @brief Build unique key formed from the HTTP method and endpoint - * @see https://discord.com/developers/docs/topics/rate-limits - * - * @param[in] method the request method - * @param[out] key unique key for matching to buckets - * @param[in] endpoint_fmt the printf-like endpoint formatting string - * @param[in] args variadic arguments matched to `endpoint_fmt` - */ -void discord_ratelimiter_build_key(enum http_method method, - char key[DISCORD_ROUTE_LEN], - const char endpoint_fmt[], - va_list args); - -/** - * @brief Update the bucket with response header data - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param bucket NULL when bucket is first discovered - * @param key obtained from discord_ratelimiter_build_key() - * @param info informational struct containing details on the current transfer - * @note If the bucket was just discovered it will be created here. - */ -void discord_ratelimiter_build(struct discord_ratelimiter *rl, - struct discord_bucket *bucket, - const char key[], - struct ua_info *info); - -/** @brief The Discord bucket for handling per-group ratelimits */ -struct discord_bucket { - /** the hash associated with the bucket's ratelimiting group */ - char hash[64]; - /** maximum connections this bucket can handle before ratelimit */ - long limit; - /** connections this bucket can do before waiting for cooldown */ - long remaining; - /** timestamp of when cooldown timer resets */ - u64unix_ms reset_tstamp; - /** pending requests */ - QUEUE(struct discord_request) pending_queue; - /** - * pointer to this bucket's currently performing request - * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited - */ - struct discord_request *performing_req; - /** synchronize bucket */ - pthread_mutex_t lock; -}; - -/** - * @brief Return bucket timeout timestamp - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param bucket the bucket to be checked for time out - * @return the timeout timestamp - */ -u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, - struct discord_bucket *bucket); - -/** - * @brief Try to timeout bucket for pending cooldown time - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param bucket the bucket to wait on cooldown - */ -void discord_bucket_try_timeout(struct discord_ratelimiter *rl, - struct discord_bucket *b); - -/** - * @brief Get a `struct discord_bucket` assigned to `key` - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param key obtained from discord_ratelimiter_build_key() - * @return bucket matched to `key` - */ -struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, - const char key[]); - -/** - * @brief Insert request into bucket's pending queue - * - * @param b the bucket to insert the request to - * @param req the request obtained via discord_requestor_start_request() - * @param high_priority if high priority then request shall be prioritized over - * already enqueued requests - */ -void discord_bucket_add_request(struct discord_bucket *b, - struct discord_request *req, - bool high_priority); - -/** - * @brief Remove head request from bucket's pending queue - * - * @param b the bucket to fetch the request from - * @return the request - */ -struct discord_request *discord_bucket_remove_request( - struct discord_bucket *b); - -/** @} DiscordInternalRESTRatelimit */ - /** @brief The handle used for interfacing with Discord's REST API */ struct discord_rest { /** `DISCORD_HTTP` or `DISCORD_WEBHOOK` logging module */ @@ -550,8 +553,6 @@ struct discord_rest { struct discord_requestor requestor; /** the timer queue for the rest thread */ struct discord_timers timers; - /** enforce ratelimiting on discovered buckets */ - struct discord_ratelimiter ratelimiter; /** REST thread manager */ struct { diff --git a/src/discord-rest.c b/src/discord-rest.c index 03730ee76..d93f5b9f5 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -66,7 +66,6 @@ discord_rest_init(struct discord_rest *rest, discord_timers_init(&rest->timers); discord_requestor_init(&rest->requestor, &rest->conf, token); - discord_ratelimiter_init(&rest->ratelimiter, &rest->conf); rest->manager = malloc(sizeof *rest->manager); ASSERT_S(!pthread_mutex_init(&rest->manager->lock, NULL), @@ -84,11 +83,8 @@ discord_rest_cleanup(struct discord_rest *rest) threadpool_destroy(rest->manager->tpool, threadpool_graceful); pthread_mutex_destroy(&rest->manager->lock); free(rest->manager); - /* cleanup discovered buckets */ discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); - discord_ratelimiter_cleanup(&rest->ratelimiter); - /* cleanup request queues */ discord_requestor_cleanup(&rest->requestor); } diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 704c7a2eb..2c2409b8d 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -169,7 +169,7 @@ _discord_bucket_cancel(struct discord_ratelimiter *rl, struct discord_bucket *b) { struct discord_requestor *rqtor = - &CONTAINEROF(rl, struct discord_rest, ratelimiter)->requestor; + CONTAINEROF(rl, struct discord_requestor, ratelimiter); /* cancel busy transfer */ if (b->performing_req) discord_request_cancel(rqtor, b->performing_req); @@ -260,7 +260,7 @@ void discord_bucket_try_timeout(struct discord_ratelimiter *rl, struct discord_bucket *b) { - struct discord *client = CLIENT(rl, rest.ratelimiter); + struct discord *client = CLIENT(rl, rest.requestor.ratelimiter); int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); if (delay_ms < 0) delay_ms = 0; diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 35926650f..97bdf5f66 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -70,6 +70,8 @@ discord_requestor_init(struct discord_requestor *rqtor, CONTAINEROF(rqtor, struct discord_rest, requestor)); rqtor->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ + + discord_ratelimiter_init(&rqtor->ratelimiter, &rqtor->conf); } void @@ -79,6 +81,10 @@ discord_requestor_cleanup(struct discord_requestor *rqtor) &rqtor->queues->pending, &rqtor->queues->finished }; + /* cleanup ratelimiting handle */ + discord_ratelimiter_cleanup(&rqtor->ratelimiter); + + /* cleanup request structs */ for (size_t i = 0; i < sizeof(req_queues) / sizeof *req_queues; ++i) { QUEUE(struct discord_request) queue, *qelem; struct discord_request *req; @@ -370,10 +376,6 @@ discord_requestor_info_read(struct discord_requestor *rqtor) switch (ecode) { case CURLE_OK: { - struct discord_ratelimiter *rl = - &CONTAINEROF(rqtor, struct discord_rest, requestor) - ->ratelimiter; - struct ua_szbuf_readonly body; struct ua_info info; @@ -405,7 +407,8 @@ discord_requestor_info_read(struct discord_requestor *rqtor) req->response.data); } - discord_ratelimiter_build(rl, req->b, req->key, &info); + discord_ratelimiter_build(&rqtor->ratelimiter, req->b, + req->key, &info); ua_info_cleanup(&info); } break; case CURLE_READ_ERROR: @@ -450,7 +453,7 @@ _discord_request_try_begin(struct discord_ratelimiter *rl, } else if (!QUEUE_EMPTY(&b->pending_queue)) { struct discord_requestor *rqtor = - &CONTAINEROF(rl, struct discord_rest, ratelimiter)->requestor; + CONTAINEROF(rl, struct discord_requestor, ratelimiter); struct discord_request *req = discord_bucket_remove_request(b); CURL *ehandle; @@ -487,9 +490,6 @@ _discord_request_try_begin(struct discord_ratelimiter *rl, CCORDcode discord_requestor_start_pending(struct discord_requestor *rqtor) { - struct discord_ratelimiter *rl = - &CONTAINEROF(rqtor, struct discord_rest, requestor)->ratelimiter; - QUEUE(struct discord_request) queue, *qelem; struct discord_request *req; struct discord_bucket *b; @@ -501,12 +501,13 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) QUEUE_REMOVE(qelem); req = QUEUE_DATA(qelem, struct discord_request, entry); - b = discord_bucket_get(rl, req->key); + b = discord_bucket_get(&rqtor->ratelimiter, req->key); discord_bucket_add_request(b, req, req->dispatch.high_p); } /* TODO: replace foreach with a mechanism that loops only busy buckets */ - discord_ratelimiter_foreach_bucket(rl, &_discord_request_try_begin); + discord_ratelimiter_foreach_bucket(&rqtor->ratelimiter, + &_discord_request_try_begin); /* FIXME: redundant return value (constant) */ return CCORD_OK; From 1845d53dcec809c7e1638dcf4018744dc95bd9f5 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 26 May 2022 17:23:09 -0300 Subject: [PATCH 079/118] refactor(discord-rest): move io_poller from 'struct discord_requestor' to 'struct discord_rest' --- include/discord-internal.h | 24 +++++++++++------------ src/discord-rest.c | 39 +++++++++++++++++++------------------- src/discord-rest_request.c | 32 +++++++++++++++---------------- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 483946e1e..87d7041a9 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -348,7 +348,6 @@ struct discord_request *discord_bucket_remove_request( /** @} DiscordInternalRESTRequestRatelimit */ - /** @brief Generic request dispatcher */ struct discord_ret_dispatch { DISCORD_RET_DEFAULT_FIELDS; @@ -449,8 +448,6 @@ struct discord_requestor { struct user_agent *ua; /** curl_multi handle for performing asynchronous requests */ CURLM *mhandle; - /** io_poller for rest only */ - struct io_poller *io_poller; /** enforce Discord's ratelimiting for requests */ struct discord_ratelimiter ratelimiter; @@ -545,7 +542,12 @@ CCORDcode discord_request_begin(struct discord_requestor *rqtor, /** @} DiscordInternalRESTRequest */ -/** @brief The handle used for interfacing with Discord's REST API */ +/** + * @brief The handle used for interfacing with Discord's REST API + * + * This handle will manage the special REST thread where requests are performed + * in + */ struct discord_rest { /** `DISCORD_HTTP` or `DISCORD_WEBHOOK` logging module */ struct logconf conf; @@ -553,14 +555,12 @@ struct discord_rest { struct discord_requestor requestor; /** the timer queue for the rest thread */ struct discord_timers timers; - - /** REST thread manager */ - struct { - /** threadpool for managing a single REST thread */ - struct threadpool_t *tpool; - /** global lock */ - pthread_mutex_t lock; - } * manager; + /** poller for REST requests */ + struct io_poller *io_poller; + /** threadpool for managing the REST thread */ + struct threadpool_t *tpool; + /** global lock */ + pthread_mutex_t *g_lock; }; /** diff --git a/src/discord-rest.c b/src/discord-rest.c index d93f5b9f5..ad56e6040 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -14,13 +14,13 @@ discord_rest_perform(struct discord_rest *rest) { CCORDcode code; - pthread_mutex_lock(&rest->manager->lock); + pthread_mutex_lock(rest->g_lock); /* ask for any messages/informationals from the individual transfers */ discord_requestor_info_read(&rest->requestor); code = discord_requestor_start_pending(&rest->requestor); - pthread_mutex_unlock(&rest->manager->lock); + pthread_mutex_unlock(rest->g_lock); return code; } @@ -40,8 +40,7 @@ _discord_rest_manager(void *p_rest) now = (int64_t)discord_timestamp_us(client); trigger = discord_timers_get_next_trigger(timers, 1, now, 60000000); - poll_result = - io_poller_poll(rest->requestor.io_poller, (int)(trigger / 1000)); + poll_result = io_poller_poll(rest->io_poller, (int)(trigger / 1000)); now = (int64_t)discord_timestamp_us(client); if (0 == poll_result) { @@ -49,9 +48,9 @@ _discord_rest_manager(void *p_rest) if (trigger > 0 && trigger < 1000) cog_sleep_us((long)trigger); } discord_timers_run(client, &rest->timers); - io_poller_perform(rest->requestor.io_poller); + io_poller_perform(rest->io_poller); - threadpool_add(rest->manager->tpool, _discord_rest_manager, rest, 0); + threadpool_add(rest->tpool, _discord_rest_manager, rest, 0); } void @@ -64,29 +63,31 @@ discord_rest_init(struct discord_rest *rest, else logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); + rest->tpool = threadpool_create(1, 1024, 0); + ASSERT_S(!threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), + "Couldn't initialize REST managagement thread"); + rest->io_poller = io_poller_create(); + rest->g_lock = malloc(sizeof *rest->g_lock); + ASSERT_S(!pthread_mutex_init(rest->g_lock, NULL), + "Couldn't initialize REST manager mutex"); + discord_timers_init(&rest->timers); discord_requestor_init(&rest->requestor, &rest->conf, token); - - rest->manager = malloc(sizeof *rest->manager); - ASSERT_S(!pthread_mutex_init(&rest->manager->lock, NULL), - "Couldn't initialize REST manager mutex"); - rest->manager->tpool = threadpool_create(1, 1024, 0); - ASSERT_S( - !threadpool_add(rest->manager->tpool, &_discord_rest_manager, rest, 0), - "Couldn't initialize REST managagement thread"); } void discord_rest_cleanup(struct discord_rest *rest) { - /* cleanup REST managing thread */ - threadpool_destroy(rest->manager->tpool, threadpool_graceful); - pthread_mutex_destroy(&rest->manager->lock); - free(rest->manager); + /* cleanup REST managing thread and its global lock */ + threadpool_destroy(rest->tpool, threadpool_graceful); + pthread_mutex_destroy(rest->g_lock); + free(rest->g_lock); /* cleanup discovered buckets */ discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); - /* cleanup request queues */ + /* cleanup requests */ discord_requestor_cleanup(&rest->requestor); + /* cleanup REST poller */ + io_poller_destroy(rest->io_poller); } /* template function for performing requests */ diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 97bdf5f66..80f7121ad 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -50,6 +50,9 @@ discord_requestor_init(struct discord_requestor *rqtor, struct logconf *conf, struct ccord_szbuf_readonly *token) { + struct discord_rest *rest = + CONTAINEROF(rqtor, struct discord_rest, requestor); + logconf_branch(&rqtor->conf, conf, "DISCORD_REQUEST"); rqtor->ua = ua_init(&(struct ua_attr){ .conf = conf }); @@ -64,10 +67,8 @@ discord_requestor_init(struct discord_requestor *rqtor, QUEUE_INIT(&rqtor->queues->finished); rqtor->mhandle = curl_multi_init(); - rqtor->io_poller = io_poller_create(); - io_poller_curlm_add(rqtor->io_poller, rqtor->mhandle, - &_discord_on_rest_perform, - CONTAINEROF(rqtor, struct discord_rest, requestor)); + io_poller_curlm_add(rest->io_poller, rqtor->mhandle, + &_discord_on_rest_perform, rest); rqtor->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ @@ -77,6 +78,8 @@ discord_requestor_init(struct discord_requestor *rqtor, void discord_requestor_cleanup(struct discord_requestor *rqtor) { + struct discord_rest *rest = + CONTAINEROF(rqtor, struct discord_rest, requestor); QUEUE *const req_queues[] = { &rqtor->queues->recycling, &rqtor->queues->pending, &rqtor->queues->finished }; @@ -101,10 +104,8 @@ discord_requestor_cleanup(struct discord_requestor *rqtor) free(rqtor->queues); /* cleanup curl's multi handle */ - io_poller_curlm_del(rqtor->io_poller, rqtor->mhandle); + io_poller_curlm_del(rest->io_poller, rqtor->mhandle); curl_multi_cleanup(rqtor->mhandle); - /* cleanup REST io_poller */ - io_poller_destroy(rqtor->io_poller); /* cleanup User-Agent handle */ ua_cleanup(rqtor->ua); } @@ -332,7 +333,7 @@ discord_requestor_dispatch_responses(struct discord_requestor *rqtor) struct discord_rest *rest = CONTAINEROF(rqtor, struct discord_rest, requestor); - if (0 == pthread_mutex_trylock(&rest->manager->lock)) { + if (0 == pthread_mutex_trylock(rest->g_lock)) { if (!QUEUE_EMPTY(&rqtor->queues->finished)) { QUEUE(struct discord_request) queue, *qelem; struct discord_request *req; @@ -344,9 +345,9 @@ discord_requestor_dispatch_responses(struct discord_requestor *rqtor) _discord_request_dispatch_response(rqtor, req); } while (!QUEUE_EMPTY(&queue)); - io_poller_wakeup(rqtor->io_poller); + io_poller_wakeup(rest->io_poller); } - pthread_mutex_unlock(&rest->manager->lock); + pthread_mutex_unlock(rest->g_lock); } } @@ -571,7 +572,7 @@ discord_request_begin(struct discord_requestor *rqtor, struct discord_request *req = _discord_request_get(rqtor); CCORDcode code; - pthread_mutex_lock(&rest->manager->lock); + pthread_mutex_lock(rest->g_lock); req->method = method; memcpy(req, attr, sizeof *attr); @@ -617,20 +618,19 @@ discord_request_begin(struct discord_requestor *rqtor, } /* request will be assigned to its bucket at the REST thread */ - QUEUE_INSERT_TAIL(&rest->requestor.queues->pending, &req->entry); - - io_poller_wakeup(rqtor->io_poller); + QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); + io_poller_wakeup(rest->io_poller); if (!req->dispatch.sync) { code = CCORD_OK; } else { req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; - pthread_cond_wait(req->cond, &rest->manager->lock); + pthread_cond_wait(req->cond, rest->g_lock); code = _discord_request_dispatch_response(rqtor, req); } - pthread_mutex_unlock(&rest->manager->lock); + pthread_mutex_unlock(rest->g_lock); return code; } From 5e4996b6d24ef7e3f7f64ca4a918a8a6c5d957e9 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Thu, 26 May 2022 17:25:15 -0300 Subject: [PATCH 080/118] fix(discord-rest): field initialization ordering --- src/discord-rest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discord-rest.c b/src/discord-rest.c index ad56e6040..90e7d48d2 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -63,13 +63,13 @@ discord_rest_init(struct discord_rest *rest, else logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); - rest->tpool = threadpool_create(1, 1024, 0); - ASSERT_S(!threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), - "Couldn't initialize REST managagement thread"); rest->io_poller = io_poller_create(); rest->g_lock = malloc(sizeof *rest->g_lock); ASSERT_S(!pthread_mutex_init(rest->g_lock, NULL), "Couldn't initialize REST manager mutex"); + rest->tpool = threadpool_create(1, 1024, 0); + ASSERT_S(!threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), + "Couldn't initialize REST managagement thread"); discord_timers_init(&rest->timers); discord_requestor_init(&rest->requestor, &rest->conf, token); From f6ecf3f4c220051295c4a1a3c7e12690d911a464 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 27 May 2022 16:40:53 -0300 Subject: [PATCH 081/118] fix(discord-rest): move discord_request_dispatch_responses() outside of 1sec enforced delay block, add wakeup to main thread's poll --- src/discord-loop.c | 3 +-- src/discord-rest.c | 5 ++--- src/discord-rest_request.c | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index 802183d7c..e18d6102a 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -121,10 +121,9 @@ discord_run(struct discord *client) BREAK_ON_FAIL(code, io_poller_perform(client->io_poller)); + discord_requestor_dispatch_responses(&client->rest.requestor); if (next_run <= now) { BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); - discord_requestor_dispatch_responses(&client->rest.requestor); - /* enforce a min 1 sec delay between runs */ next_run = now + 1000000; } diff --git a/src/discord-rest.c b/src/discord-rest.c index 90e7d48d2..2225e4813 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -15,13 +15,12 @@ discord_rest_perform(struct discord_rest *rest) CCORDcode code; pthread_mutex_lock(rest->g_lock); - - /* ask for any messages/informationals from the individual transfers */ discord_requestor_info_read(&rest->requestor); code = discord_requestor_start_pending(&rest->requestor); - pthread_mutex_unlock(rest->g_lock); + io_poller_wakeup(CLIENT(rest, rest)->io_poller); + return code; } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 80f7121ad..83d727cb7 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -362,6 +362,7 @@ discord_requestor_info_read(struct discord_requestor *rqtor) if (CURLM_OK != curl_multi_socket_all(rqtor->mhandle, &alive)) return CCORD_CURLM_INTERNAL; + /* ask for any messages/informationals from the individual transfers */ while (1) { int msgq = 0; struct CURLMsg *msg = curl_multi_info_read(rqtor->mhandle, &msgq); From 95c12775a5aa6355554b2a29bd7cbe8421239c11 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 27 May 2022 18:34:42 -0300 Subject: [PATCH 082/118] refactor(discord-refcount.c): add locks and make discord_refcounter_contains() private --- include/discord-internal.h | 12 +--- src/discord-refcount.c | 113 +++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 53 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 87d7041a9..efe6ae60d 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -888,6 +888,8 @@ struct discord_refcounter { * @note datatype declared at discord-refcount.c */ struct _discord_ref *refs; + /** global lock */ + pthread_mutex_t *g_lock; }; /** @@ -936,16 +938,6 @@ void discord_refcounter_add_client(struct discord_refcounter *rc, */ void discord_refcounter_cleanup(struct discord_refcounter *rc); -/** - * @brief Check if `data` is stored at the reference counter - * - * @param rc the handle initialized with discord_refcounter_init() - * @param data the data address to be checked - * @return `true` if data is stored - */ -bool discord_refcounter_contains(struct discord_refcounter *rc, - const void *data); - /** * @brief Claim ownership of `data` * @see discord_refcounter_unclaim() diff --git a/src/discord-refcount.c b/src/discord-refcount.c index f08468529..c52203e0b 100644 --- a/src/discord-refcount.c +++ b/src/discord-refcount.c @@ -69,10 +69,8 @@ _discord_refvalue_cleanup(struct discord_refcounter *rc, static struct _discord_refvalue * _discord_refvalue_find(struct discord_refcounter *rc, const void *data) { - struct _discord_ref *ref = NULL; - - ref = chash_lookup_bucket(rc, (intptr_t)data, ref, REFCOUNTER_TABLE); - + struct _discord_ref *ref = + chash_lookup_bucket(rc, (intptr_t)data, ref, REFCOUNTER_TABLE); return &ref->value; } @@ -95,19 +93,25 @@ _discord_refvalue_delete(struct discord_refcounter *rc, void *data) void discord_refcounter_init(struct discord_refcounter *rc, struct logconf *conf) { + logconf_branch(&rc->conf, conf, "DISCORD_REFCOUNT"); + __chash_init(rc, REFCOUNTER_TABLE); - logconf_branch(&rc->conf, conf, "DISCORD_REFCOUNT"); + rc->g_lock = malloc(sizeof *rc->g_lock); + ASSERT_S(!pthread_mutex_init(rc->g_lock, NULL), + "Couldn't initialize refcounter mutex"); } void discord_refcounter_cleanup(struct discord_refcounter *rc) { __chash_free(rc, REFCOUNTER_TABLE); + pthread_mutex_destroy(rc->g_lock); + free(rc->g_lock); } -bool -discord_refcounter_contains(struct discord_refcounter *rc, const void *data) +static bool +_discord_refcounter_contains(struct discord_refcounter *rc, const void *data) { bool ret = chash_contains(rc, (intptr_t)data, ret, REFCOUNTER_TABLE); return ret; @@ -116,27 +120,37 @@ discord_refcounter_contains(struct discord_refcounter *rc, const void *data) bool discord_refcounter_claim(struct discord_refcounter *rc, const void *data) { - if (discord_refcounter_contains(rc, data)) { + bool ret = false; + + pthread_mutex_lock(rc->g_lock); + if (_discord_refcounter_contains(rc, data)) { struct _discord_refvalue *value = _discord_refvalue_find(rc, data); value->visits = -1; - return true; + ret = true; } - return false; + pthread_mutex_unlock(rc->g_lock); + + return ret; } bool discord_refcounter_unclaim(struct discord_refcounter *rc, void *data) { - if (discord_refcounter_contains(rc, data)) { + bool ret = false; + + pthread_mutex_lock(rc->g_lock); + if (_discord_refcounter_contains(rc, data)) { struct _discord_refvalue *value = _discord_refvalue_find(rc, data); if (value->visits == -1) { _discord_refvalue_delete(rc, data); - return true; + ret = true; } } - return false; + pthread_mutex_unlock(rc->g_lock); + + return ret; } void @@ -145,12 +159,14 @@ discord_refcounter_add_internal(struct discord_refcounter *rc, void (*cleanup)(void *data), bool should_free) { - struct _discord_refvalue init = { - .expects_client = false, - .cleanup.internal = cleanup, - .should_free = should_free, - }; - _discord_refvalue_init(rc, data, &init); + pthread_mutex_lock(rc->g_lock); + _discord_refvalue_init(rc, data, + &(struct _discord_refvalue){ + .expects_client = false, + .cleanup.internal = cleanup, + .should_free = should_free, + }); + pthread_mutex_unlock(rc->g_lock); } void @@ -160,44 +176,55 @@ discord_refcounter_add_client(struct discord_refcounter *rc, void *data), bool should_free) { - struct _discord_refvalue init = { - .expects_client = true, - .cleanup.client = cleanup, - .should_free = should_free, - }; - _discord_refvalue_init(rc, data, &init); + pthread_mutex_lock(rc->g_lock); + _discord_refvalue_init(rc, data, + &(struct _discord_refvalue){ + .expects_client = true, + .cleanup.client = cleanup, + .should_free = should_free, + }); + pthread_mutex_unlock(rc->g_lock); } CCORDcode discord_refcounter_incr(struct discord_refcounter *rc, void *data) { - struct _discord_refvalue *value; - - if (!discord_refcounter_contains(rc, data)) return CCORD_UNAVAILABLE; + CCORDcode code = CCORD_OWNERSHIP; - value = _discord_refvalue_find(rc, data); + pthread_mutex_lock(rc->g_lock); + if (!_discord_refcounter_contains(rc, data)) { + code = CCORD_UNAVAILABLE; + } + else { + struct _discord_refvalue *value = _discord_refvalue_find(rc, data); - if (value->visits != -1) { - ++value->visits; - return CCORD_OK; + if (value->visits != -1) { + ++value->visits; + code = CCORD_OK; + } } - return CCORD_OWNERSHIP; + pthread_mutex_unlock(rc->g_lock); + return code; } CCORDcode discord_refcounter_decr(struct discord_refcounter *rc, void *data) { - struct _discord_refvalue *value = NULL; + CCORDcode code = CCORD_OWNERSHIP; - if (!discord_refcounter_contains(rc, data)) return CCORD_UNAVAILABLE; - - value = _discord_refvalue_find(rc, data); - - if (value->visits != -1) { - if (0 == --value->visits) { - _discord_refvalue_delete(rc, data); + pthread_mutex_lock(rc->g_lock); + if (!_discord_refcounter_contains(rc, data)) { + code = CCORD_UNAVAILABLE; + } + else { + struct _discord_refvalue *value = _discord_refvalue_find(rc, data); + if (value->visits != -1) { + if (0 == --value->visits) { + _discord_refvalue_delete(rc, data); + } + code = CCORD_OK; } - return CCORD_OK; } - return CCORD_OWNERSHIP; + pthread_mutex_unlock(rc->g_lock); + return code; } From 3e5081a50948b8ebb92d68f6956044974dc94fca Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 27 May 2022 19:15:29 -0300 Subject: [PATCH 083/118] fix(discord-rest_request.c): lock queues that interface between REST and separate threads --- include/discord-internal.h | 12 +++++- src/discord-rest.c | 17 +++------ src/discord-rest_ratelimit.c | 8 ++-- src/discord-rest_request.c | 72 ++++++++++++++++++++++++++---------- 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index efe6ae60d..32b377132 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -466,6 +466,16 @@ struct discord_requestor { */ QUEUE(struct discord_request) finished; } * queues; + + /** queue locks */ + struct { + /** recycling queue lock */ + pthread_mutex_t recycling; + /** pending queue lock */ + pthread_mutex_t pending; + /** finished queue lock */ + pthread_mutex_t finished; + } *qlocks; }; /** @@ -559,8 +569,6 @@ struct discord_rest { struct io_poller *io_poller; /** threadpool for managing the REST thread */ struct threadpool_t *tpool; - /** global lock */ - pthread_mutex_t *g_lock; }; /** diff --git a/src/discord-rest.c b/src/discord-rest.c index 2225e4813..372cae257 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -14,11 +14,8 @@ discord_rest_perform(struct discord_rest *rest) { CCORDcode code; - pthread_mutex_lock(rest->g_lock); discord_requestor_info_read(&rest->requestor); code = discord_requestor_start_pending(&rest->requestor); - pthread_mutex_unlock(rest->g_lock); - io_poller_wakeup(CLIENT(rest, rest)->io_poller); return code; @@ -63,24 +60,20 @@ discord_rest_init(struct discord_rest *rest, logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); rest->io_poller = io_poller_create(); - rest->g_lock = malloc(sizeof *rest->g_lock); - ASSERT_S(!pthread_mutex_init(rest->g_lock, NULL), - "Couldn't initialize REST manager mutex"); - rest->tpool = threadpool_create(1, 1024, 0); - ASSERT_S(!threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), - "Couldn't initialize REST managagement thread"); discord_timers_init(&rest->timers); discord_requestor_init(&rest->requestor, &rest->conf, token); + + rest->tpool = threadpool_create(1, 1024, 0); + ASSERT_S(!threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), + "Couldn't initialize REST managagement thread"); } void discord_rest_cleanup(struct discord_rest *rest) { - /* cleanup REST managing thread and its global lock */ + /* cleanup REST managing thread */ threadpool_destroy(rest->tpool, threadpool_graceful); - pthread_mutex_destroy(rest->g_lock); - free(rest->g_lock); /* cleanup discovered buckets */ discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); /* cleanup requests */ diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 2c2409b8d..2f70b324c 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -154,10 +154,10 @@ discord_ratelimiter_init(struct discord_ratelimiter *rl, struct logconf *conf) /* global ratelimiting resources */ rl->global = malloc(sizeof *rl->global); rl->global->wait_ms = 0; - if (pthread_rwlock_init(&rl->global->rwlock, NULL)) - ERR("Couldn't initialize pthread rwlock"); - if (pthread_mutex_init(&rl->global->lock, NULL)) - ERR("Couldn't initialize pthread mutex"); + ASSERT_S(!pthread_rwlock_init(&rl->global->rwlock, NULL), + "Couldn't initialize ratelimiter rwlock"); + ASSERT_S(!pthread_mutex_init(&rl->global->lock, NULL), + "Couldn't initialize ratelimiter mutex"); /* initialize 'singleton' buckets */ rl->null = _discord_bucket_init(rl, "null", &keynull, 1L); diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 83d727cb7..205d5b1e5 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -66,6 +66,14 @@ discord_requestor_init(struct discord_requestor *rqtor, QUEUE_INIT(&rqtor->queues->pending); QUEUE_INIT(&rqtor->queues->finished); + rqtor->qlocks = malloc(sizeof *rqtor->qlocks); + ASSERT_S(!pthread_mutex_init(&rqtor->qlocks->recycling, NULL), + "Couldn't initialize requestor's recycling queue mutex"); + ASSERT_S(!pthread_mutex_init(&rqtor->qlocks->pending, NULL), + "Couldn't initialize requestor's pending queue mutex"); + ASSERT_S(!pthread_mutex_init(&rqtor->qlocks->finished, NULL), + "Couldn't initialize requestor's finished queue mutex"); + rqtor->mhandle = curl_multi_init(); io_poller_curlm_add(rest->io_poller, rqtor->mhandle, &_discord_on_rest_perform, rest); @@ -87,7 +95,7 @@ discord_requestor_cleanup(struct discord_requestor *rqtor) /* cleanup ratelimiting handle */ discord_ratelimiter_cleanup(&rqtor->ratelimiter); - /* cleanup request structs */ + /* cleanup queues */ for (size_t i = 0; i < sizeof(req_queues) / sizeof *req_queues; ++i) { QUEUE(struct discord_request) queue, *qelem; struct discord_request *req; @@ -103,6 +111,12 @@ discord_requestor_cleanup(struct discord_requestor *rqtor) } free(rqtor->queues); + /* cleanup queue locks */ + pthread_mutex_destroy(&rqtor->qlocks->recycling); + pthread_mutex_destroy(&rqtor->qlocks->pending); + pthread_mutex_destroy(&rqtor->qlocks->finished); + free(rqtor->qlocks); + /* cleanup curl's multi handle */ io_poller_curlm_del(rest->io_poller, rqtor->mhandle); curl_multi_cleanup(rqtor->mhandle); @@ -175,21 +189,22 @@ _discord_request_info_extract(struct discord_requestor *rqtor, if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ req->retry = false; + req->code = info->code; } else { switch (info->httpcode) { case HTTP_FORBIDDEN: case HTTP_NOT_FOUND: case HTTP_BAD_REQUEST: - info->code = CCORD_DISCORD_JSON_CODE; req->retry = false; + req->code = CCORD_DISCORD_JSON_CODE; break; case HTTP_UNAUTHORIZED: logconf_fatal( &rqtor->conf, "UNAUTHORIZED: Please provide a valid authentication token"); - info->code = CCORD_DISCORD_BAD_AUTH; req->retry = false; + req->code = CCORD_DISCORD_BAD_AUTH; break; case HTTP_METHOD_NOT_ALLOWED: logconf_fatal( @@ -197,6 +212,7 @@ _discord_request_info_extract(struct discord_requestor *rqtor, "METHOD_NOT_ALLOWED: The server couldn't recognize the " "received HTTP method"); req->retry = false; + req->code = info->code; break; case HTTP_TOO_MANY_REQUESTS: { struct ua_szbuf_readonly body = ua_info_get_body(info); @@ -237,10 +253,12 @@ _discord_request_info_extract(struct discord_requestor *rqtor, body.start + message.pos); req->retry = true; + req->code = info->code; break; } default: req->retry = (info->httpcode >= 500); /* retry if Server Error */ + req->code = info->code; break; } } @@ -330,11 +348,11 @@ _discord_request_dispatch_response(struct discord_requestor *rqtor, void discord_requestor_dispatch_responses(struct discord_requestor *rqtor) { - struct discord_rest *rest = - CONTAINEROF(rqtor, struct discord_rest, requestor); - - if (0 == pthread_mutex_trylock(rest->g_lock)) { + if (0 == pthread_mutex_trylock(&rqtor->qlocks->finished)) { if (!QUEUE_EMPTY(&rqtor->queues->finished)) { + struct discord_rest *rest = + CONTAINEROF(rqtor, struct discord_rest, requestor); + QUEUE(struct discord_request) queue, *qelem; struct discord_request *req; @@ -347,7 +365,7 @@ discord_requestor_dispatch_responses(struct discord_requestor *rqtor) io_poller_wakeup(rest->io_poller); } - pthread_mutex_unlock(rest->g_lock); + pthread_mutex_unlock(&rqtor->qlocks->finished); } } @@ -411,6 +429,7 @@ discord_requestor_info_read(struct discord_requestor *rqtor) discord_ratelimiter_build(&rqtor->ratelimiter, req->b, req->key, &info); + ua_info_cleanup(&info); } break; case CURLE_READ_ERROR: @@ -433,10 +452,12 @@ discord_requestor_info_read(struct discord_requestor *rqtor) code = req->code; + pthread_mutex_lock(&rqtor->qlocks->finished); if (req->dispatch.sync) pthread_cond_signal(req->cond); else QUEUE_INSERT_TAIL(&rqtor->queues->finished, &req->entry); + pthread_mutex_unlock(&rqtor->qlocks->finished); } } @@ -496,6 +517,7 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) struct discord_request *req; struct discord_bucket *b; + pthread_mutex_lock(&rqtor->qlocks->pending); /* match pending requests to their buckets */ QUEUE_MOVE(&rqtor->queues->pending, &queue); while (!QUEUE_EMPTY(&queue)) { @@ -506,6 +528,7 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) b = discord_bucket_get(&rqtor->ratelimiter, req->key); discord_bucket_add_request(b, req, req->dispatch.high_p); } + pthread_mutex_unlock(&rqtor->qlocks->pending); /* TODO: replace foreach with a mechanism that loops only busy buckets */ discord_ratelimiter_foreach_bucket(&rqtor->ratelimiter, @@ -544,6 +567,7 @@ _discord_request_get(struct discord_requestor *rqtor) { struct discord_request *req; + pthread_mutex_lock(&rqtor->qlocks->recycling); if (QUEUE_EMPTY(&rqtor->queues->recycling)) { /* new request struct */ req = _discord_request_init(); } @@ -554,6 +578,8 @@ _discord_request_get(struct discord_requestor *rqtor) QUEUE_REMOVE(qelem); req = QUEUE_DATA(qelem, struct discord_request, entry); } + pthread_mutex_unlock(&rqtor->qlocks->recycling); + QUEUE_INIT(&req->entry); return req; @@ -570,10 +596,9 @@ discord_request_begin(struct discord_requestor *rqtor, struct discord_rest *rest = CONTAINEROF(rqtor, struct discord_rest, requestor); struct discord *client = CLIENT(rest, rest); - struct discord_request *req = _discord_request_get(rqtor); - CCORDcode code; - pthread_mutex_lock(rest->g_lock); + struct discord_request *req = _discord_request_get(rqtor); + CCORDcode code = CCORD_OK; req->method = method; memcpy(req, attr, sizeof *attr); @@ -618,20 +643,27 @@ discord_request_begin(struct discord_requestor *rqtor, attr->dispatch.cleanup, false); } - /* request will be assigned to its bucket at the REST thread */ - QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); - - io_poller_wakeup(rest->io_poller); if (!req->dispatch.sync) { - code = CCORD_OK; + pthread_mutex_lock(&rqtor->qlocks->pending); + QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); + pthread_mutex_unlock(&rqtor->qlocks->pending); + io_poller_wakeup(rest->io_poller); } - else { + else { /* wait for request's completion if sync mode is active */ req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; - pthread_cond_wait(req->cond, rest->g_lock); + + pthread_mutex_lock(&rqtor->qlocks->finished); + + pthread_mutex_lock(&rqtor->qlocks->pending); + QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); + pthread_mutex_unlock(&rqtor->qlocks->pending); + io_poller_wakeup(rest->io_poller); + + pthread_cond_wait(req->cond, &rqtor->qlocks->finished); code = _discord_request_dispatch_response(rqtor, req); - } - pthread_mutex_unlock(rest->g_lock); + pthread_mutex_unlock(&rqtor->qlocks->finished); + } return code; } From f1f1b85e6617146e7e4d58fa716088b1aa53df37 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 27 May 2022 19:58:52 -0300 Subject: [PATCH 084/118] refactor(discord-rest_ratelimit.c): remove all ratelimiting-specific mutexes, no longer needed since its only run at the REST thread --- include/discord-internal.h | 13 +----- src/discord-rest.c | 1 + src/discord-rest_ratelimit.c | 87 ++++++++---------------------------- src/discord-rest_request.c | 40 +++++++---------- 4 files changed, 39 insertions(+), 102 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 32b377132..aedea5b85 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -208,15 +208,8 @@ struct discord_ratelimiter { * ratelimiting group */ struct discord_bucket *miss; - /* client-wide ratelimiting timeout */ - struct { - /** global ratelimit */ - u64unix_ms wait_ms; - /** global rwlock */ - pthread_rwlock_t rwlock; - /** global lock */ - pthread_mutex_t lock; - } * global; + /* client-wide global ratelimiting */ + u64unix_ms *global_wait_ms; }; /** @@ -292,8 +285,6 @@ struct discord_bucket { * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited */ struct discord_request *performing_req; - /** synchronize bucket */ - pthread_mutex_t lock; }; /** diff --git a/src/discord-rest.c b/src/discord-rest.c index 372cae257..8bde36d86 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -73,6 +73,7 @@ void discord_rest_cleanup(struct discord_rest *rest) { /* cleanup REST managing thread */ + io_poller_wakeup(rest->io_poller); threadpool_destroy(rest->tpool, threadpool_graceful); /* cleanup discovered buckets */ discord_timers_cleanup(CLIENT(rest, rest), &rest->timers); diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 2f70b324c..573cee16a 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -17,7 +17,7 @@ #define RATELIMITER_TABLE_BUCKET struct _discord_route #define RATELIMITER_TABLE_FREE_KEY(_key) #define RATELIMITER_TABLE_HASH(_key, _hash) chash_string_hash(_key, _hash) -#define RATELIMITER_TABLE_FREE_VALUE(_value) _discord_bucket_cleanup(_value) +#define RATELIMITER_TABLE_FREE_VALUE(_value) free(_value) #define RATELIMITER_TABLE_COMPARE(_cmp_a, _cmp_b) \ chash_string_compare(_cmp_a, _cmp_b) #define RATELIMITER_TABLE_INIT(route, _key, _value) \ @@ -33,13 +33,6 @@ struct _discord_route { int state; }; -static void -_discord_bucket_cleanup(struct discord_bucket *b) -{ - pthread_mutex_destroy(&b->lock); - free(b); -} - #define KEY_PUSH(key, len, ...) \ do { \ *len += snprintf(key + *len, DISCORD_ROUTE_LEN - (size_t)*len, \ @@ -130,14 +123,9 @@ _discord_bucket_init(struct discord_ratelimiter *rl, b->remaining = 1; b->limit = limit; - ASSERT_S(!pthread_mutex_init(&b->lock, NULL), - "Couldn't initialize bucket's mutex"); - QUEUE_INIT(&b->pending_queue); - pthread_mutex_lock(&rl->global->lock); chash_assign(rl, key, b, RATELIMITER_TABLE); - pthread_mutex_unlock(&rl->global->lock); return b; } @@ -151,13 +139,8 @@ discord_ratelimiter_init(struct discord_ratelimiter *rl, struct logconf *conf) logconf_branch(&rl->conf, conf, "DISCORD_RATELIMIT"); - /* global ratelimiting resources */ - rl->global = malloc(sizeof *rl->global); - rl->global->wait_ms = 0; - ASSERT_S(!pthread_rwlock_init(&rl->global->rwlock, NULL), - "Couldn't initialize ratelimiter rwlock"); - ASSERT_S(!pthread_mutex_init(&rl->global->lock, NULL), - "Couldn't initialize ratelimiter mutex"); + /* global ratelimiting */ + rl->global_wait_ms = calloc(1, sizeof *rl->global_wait_ms); /* initialize 'singleton' buckets */ rl->null = _discord_bucket_init(rl, "null", &keynull, 1L); @@ -183,11 +166,7 @@ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) { discord_ratelimiter_foreach_bucket(rl, &_discord_bucket_cancel); - - pthread_rwlock_destroy(&rl->global->rwlock); - pthread_mutex_destroy(&rl->global->lock); - free(rl->global); - + free(rl->global_wait_ms); __chash_free(rl, RATELIMITER_TABLE); } @@ -196,54 +175,31 @@ discord_ratelimiter_foreach_bucket(struct discord_ratelimiter *rl, void (*iter)(struct discord_ratelimiter *rl, struct discord_bucket *b)) { - struct _discord_route *r; - int i; - - pthread_mutex_lock(&rl->global->lock); - for (i = 0; i < rl->capacity; ++i) { - r = rl->routes + i; + for (int i = 0; i < rl->capacity; ++i) { + struct _discord_route *r = rl->routes + i; if (CHASH_FILLED == r->state) (*iter)(rl, r->bucket); } - pthread_mutex_unlock(&rl->global->lock); } static struct discord_bucket * _discord_bucket_find(struct discord_ratelimiter *rl, const char key[]) { struct discord_bucket *b = NULL; - int ret; + int ret = chash_contains(rl, key, ret, RATELIMITER_TABLE); - pthread_mutex_lock(&rl->global->lock); - ret = chash_contains(rl, key, ret, RATELIMITER_TABLE); if (ret) { b = chash_lookup(rl, key, b, RATELIMITER_TABLE); } - pthread_mutex_unlock(&rl->global->lock); - return b; } -static u64unix_ms -_discord_ratelimiter_get_global_wait(struct discord_ratelimiter *rl) -{ - u64unix_ms global; - - pthread_rwlock_rdlock(&rl->global->rwlock); - global = rl->global->wait_ms; - pthread_rwlock_unlock(&rl->global->rwlock); - - return global; -} - /* return ratelimit timeout timestamp for this bucket */ u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, struct discord_bucket *b) { - u64unix_ms global = _discord_ratelimiter_get_global_wait(rl), - reset = (b->remaining < 1) ? b->reset_tstamp : 0ULL; - - return (global > reset) ? global : reset; + u64unix_ms reset = (b->remaining < 1) ? b->reset_tstamp : 0ULL; + return (*rl->global_wait_ms > reset) ? *rl->global_wait_ms : reset; } static void @@ -266,12 +222,13 @@ discord_bucket_try_timeout(struct discord_ratelimiter *rl, if (delay_ms < 0) delay_ms = 0; b->performing_req = DISCORD_BUCKET_TIMEOUT; - _discord_timer_ctl( - client, &client->rest.timers, - &(struct discord_timer){ .cb = &_discord_bucket_wake_cb, - .data = b, - .delay = delay_ms, - .flags = DISCORD_TIMER_DELETE_AUTO }); + _discord_timer_ctl(client, &client->rest.timers, + &(struct discord_timer){ + .cb = &_discord_bucket_wake_cb, + .data = b, + .delay = delay_ms, + .flags = DISCORD_TIMER_DELETE_AUTO, + }); logconf_info(&rl->conf, "[%.4s] RATELIMITING (wait %" PRId64 " ms)", b->hash, delay_ms); @@ -349,16 +306,10 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, u64unix_ms reset_tstamp = now + (u64unix_ms)(1000 * strtod(reset_after.start, NULL)); - if (global.size) { - /* lock all buckets */ - pthread_rwlock_wrlock(&rl->global->rwlock); - rl->global->wait_ms = reset_tstamp; - pthread_rwlock_unlock(&rl->global->rwlock); - } - else { - /* lock single bucket, timeout at discord_rest_run() */ + if (global.size) /* lock all buckets */ + *rl->global_wait_ms = reset_tstamp; + else /* lock single bucket, timeout at discord_rest_run() */ b->reset_tstamp = reset_tstamp; - } } else if (reset.size) { struct ua_szbuf_readonly date = ua_info_get_header(info, "date"); diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 205d5b1e5..fd62b47aa 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -452,12 +452,16 @@ discord_requestor_info_read(struct discord_requestor *rqtor) code = req->code; - pthread_mutex_lock(&rqtor->qlocks->finished); - if (req->dispatch.sync) + if (req->dispatch.sync) { + pthread_mutex_lock(&rqtor->qlocks->pending); pthread_cond_signal(req->cond); - else + pthread_mutex_unlock(&rqtor->qlocks->pending); + } + else { + pthread_mutex_lock(&rqtor->qlocks->finished); QUEUE_INSERT_TAIL(&rqtor->queues->finished, &req->entry); - pthread_mutex_unlock(&rqtor->qlocks->finished); + pthread_mutex_unlock(&rqtor->qlocks->finished); + } } } @@ -625,8 +629,6 @@ discord_request_begin(struct discord_requestor *rqtor, /* copy bucket's key */ memcpy(req->key, key, sizeof(req->key)); - req->cond = NULL; - if (attr->dispatch.keep) { code = discord_refcounter_incr(&client->refcounter, (void *)attr->dispatch.keep); @@ -643,27 +645,19 @@ discord_request_begin(struct discord_requestor *rqtor, attr->dispatch.cleanup, false); } - if (!req->dispatch.sync) { - pthread_mutex_lock(&rqtor->qlocks->pending); - QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); - pthread_mutex_unlock(&rqtor->qlocks->pending); - io_poller_wakeup(rest->io_poller); - } - else { /* wait for request's completion if sync mode is active */ - req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; - - pthread_mutex_lock(&rqtor->qlocks->finished); + pthread_mutex_lock(&rqtor->qlocks->pending); + QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); + io_poller_wakeup(rest->io_poller); - pthread_mutex_lock(&rqtor->qlocks->pending); - QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); - pthread_mutex_unlock(&rqtor->qlocks->pending); - io_poller_wakeup(rest->io_poller); + /* wait for request's completion if sync mode is active */ + if (req->dispatch.sync) { + req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; + pthread_cond_wait(req->cond, &rqtor->qlocks->pending); + req->cond = NULL; - pthread_cond_wait(req->cond, &rqtor->qlocks->finished); code = _discord_request_dispatch_response(rqtor, req); - - pthread_mutex_unlock(&rqtor->qlocks->finished); } + pthread_mutex_unlock(&rqtor->qlocks->pending); return code; } From 6029e6dacda18c5f4c7fd16f389da17676a22944 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Fri, 27 May 2022 22:51:07 -0300 Subject: [PATCH 085/118] fix(discord-rest_request): add missing recycling queue lock --- src/discord-rest_ratelimit.c | 2 ++ src/discord-rest_request.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 573cee16a..c1106c3da 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -158,7 +158,9 @@ _discord_bucket_cancel(struct discord_ratelimiter *rl, if (b->performing_req) discord_request_cancel(rqtor, b->performing_req); /* move pending tranfers to recycling */ + pthread_mutex_lock(&rqtor->qlocks->recycling); QUEUE_ADD(&rqtor->queues->recycling, &b->pending_queue); + pthread_mutex_unlock(&rqtor->qlocks->recycling); QUEUE_INIT(&b->pending_queue); } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index fd62b47aa..242e8cc53 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -313,7 +313,9 @@ discord_request_cancel(struct discord_requestor *rqtor, QUEUE_REMOVE(&req->entry); QUEUE_INIT(&req->entry); + pthread_mutex_lock(&rqtor->qlocks->recycling); QUEUE_INSERT_TAIL(&rqtor->queues->recycling, &req->entry); + pthread_mutex_unlock(&rqtor->qlocks->recycling); } static CCORDcode From 9b34c9cfaa36cac45a5653a89ecf9670f7464de6 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sat, 28 May 2022 12:36:47 -0300 Subject: [PATCH 086/118] fix(discord-rest_request): no need to lock entire code chunk once queue has been moved --- include/discord-internal.h | 8 -------- src/discord-rest.c | 20 +++++++++++++----- src/discord-rest_request.c | 42 +++++++++++++++----------------------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index aedea5b85..98cef5710 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -603,14 +603,6 @@ CCORDcode discord_rest_run(struct discord_rest *rest, char endpoint_fmt[], ...); -/** - * @brief Check and manage on-going, pending and timed-out requests - * - * @param rest the handle initialized with discord_rest_init() - * @CCORD_return - */ -CCORDcode discord_rest_perform(struct discord_rest *rest); - /** * @brief Stop all bucket's on-going, pending and timed-out requests * diff --git a/src/discord-rest.c b/src/discord-rest.c index 8bde36d86..e401ae22d 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -9,8 +9,8 @@ #include "discord.h" #include "discord-internal.h" -CCORDcode -discord_rest_perform(struct discord_rest *rest) +static CCORDcode +_discord_rest_perform(struct discord_rest *rest) { CCORDcode code; @@ -31,7 +31,7 @@ _discord_rest_manager(void *p_rest) int64_t now, trigger; int poll_result; - discord_rest_perform(rest); + _discord_rest_perform(rest); now = (int64_t)discord_timestamp_us(client); @@ -49,6 +49,14 @@ _discord_rest_manager(void *p_rest) threadpool_add(rest->tpool, _discord_rest_manager, rest, 0); } +static int +_discord_on_rest_perform(struct io_poller *io, CURLM *mhandle, void *p_rest) +{ + (void)io; + (void)mhandle; + return _discord_rest_perform(p_rest); +} + void discord_rest_init(struct discord_rest *rest, struct logconf *conf, @@ -59,10 +67,12 @@ discord_rest_init(struct discord_rest *rest, else logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); - rest->io_poller = io_poller_create(); - discord_timers_init(&rest->timers); + + rest->io_poller = io_poller_create(); discord_requestor_init(&rest->requestor, &rest->conf, token); + io_poller_curlm_add(rest->io_poller, rest->requestor.mhandle, + &_discord_on_rest_perform, rest); rest->tpool = threadpool_create(1, 1024, 0); ASSERT_S(!threadpool_add(rest->tpool, &_discord_rest_manager, rest, 0), diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 242e8cc53..bb248b01e 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -19,14 +19,6 @@ _discord_request_cleanup(struct discord_request *req) free(req); } -static int -_discord_on_rest_perform(struct io_poller *io, CURLM *mhandle, void *p_rest) -{ - (void)io; - (void)mhandle; - return discord_rest_perform(p_rest); -} - static void _discord_on_curl_setopt(struct ua_conn *conn, void *p_token) { @@ -50,9 +42,6 @@ discord_requestor_init(struct discord_requestor *rqtor, struct logconf *conf, struct ccord_szbuf_readonly *token) { - struct discord_rest *rest = - CONTAINEROF(rqtor, struct discord_rest, requestor); - logconf_branch(&rqtor->conf, conf, "DISCORD_REQUEST"); rqtor->ua = ua_init(&(struct ua_attr){ .conf = conf }); @@ -75,9 +64,6 @@ discord_requestor_init(struct discord_requestor *rqtor, "Couldn't initialize requestor's finished queue mutex"); rqtor->mhandle = curl_multi_init(); - io_poller_curlm_add(rest->io_poller, rqtor->mhandle, - &_discord_on_rest_perform, rest); - rqtor->retry_limit = 3; /* FIXME: shouldn't be a hard limit */ discord_ratelimiter_init(&rqtor->ratelimiter, &rqtor->conf); @@ -351,23 +337,23 @@ void discord_requestor_dispatch_responses(struct discord_requestor *rqtor) { if (0 == pthread_mutex_trylock(&rqtor->qlocks->finished)) { - if (!QUEUE_EMPTY(&rqtor->queues->finished)) { + QUEUE(struct discord_request) queue; + QUEUE_MOVE(&rqtor->queues->finished, &queue); + pthread_mutex_unlock(&rqtor->qlocks->finished); + + if (!QUEUE_EMPTY(&queue)) { struct discord_rest *rest = CONTAINEROF(rqtor, struct discord_rest, requestor); - - QUEUE(struct discord_request) queue, *qelem; + QUEUE(struct discord_request) * qelem; struct discord_request *req; - QUEUE_MOVE(&rqtor->queues->finished, &queue); do { qelem = QUEUE_HEAD(&queue); req = QUEUE_DATA(qelem, struct discord_request, entry); _discord_request_dispatch_response(rqtor, req); } while (!QUEUE_EMPTY(&queue)); - io_poller_wakeup(rest->io_poller); } - pthread_mutex_unlock(&rqtor->qlocks->finished); } } @@ -429,6 +415,8 @@ discord_requestor_info_read(struct discord_requestor *rqtor) req->response.data); } + /** FIXME: bucket should be recycled if it was matched with an + * invalid endpoint */ discord_ratelimiter_build(&rqtor->ratelimiter, req->b, req->key, &info); @@ -524,8 +512,10 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) struct discord_bucket *b; pthread_mutex_lock(&rqtor->qlocks->pending); - /* match pending requests to their buckets */ QUEUE_MOVE(&rqtor->queues->pending, &queue); + pthread_mutex_unlock(&rqtor->qlocks->pending); + + /* match pending requests to their buckets */ while (!QUEUE_EMPTY(&queue)) { qelem = QUEUE_HEAD(&queue); QUEUE_REMOVE(qelem); @@ -534,7 +524,6 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) b = discord_bucket_get(&rqtor->ratelimiter, req->key); discord_bucket_add_request(b, req, req->dispatch.high_p); } - pthread_mutex_unlock(&rqtor->qlocks->pending); /* TODO: replace foreach with a mechanism that loops only busy buckets */ discord_ratelimiter_foreach_bucket(&rqtor->ratelimiter, @@ -650,16 +639,17 @@ discord_request_begin(struct discord_requestor *rqtor, pthread_mutex_lock(&rqtor->qlocks->pending); QUEUE_INSERT_TAIL(&rqtor->queues->pending, &req->entry); io_poller_wakeup(rest->io_poller); - - /* wait for request's completion if sync mode is active */ - if (req->dispatch.sync) { + if (!req->dispatch.sync) { + pthread_mutex_unlock(&rqtor->qlocks->pending); + } + else { /* wait for request's completion if sync mode is active */ req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; pthread_cond_wait(req->cond, &rqtor->qlocks->pending); req->cond = NULL; + pthread_mutex_unlock(&rqtor->qlocks->pending); code = _discord_request_dispatch_response(rqtor, req); } - pthread_mutex_unlock(&rqtor->qlocks->pending); return code; } From 3692562f2be35fd8e5f64ec2d6c07d01c1303108 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 28 May 2022 22:57:38 -0400 Subject: [PATCH 087/118] refactor(discord_gateway): use timer to determine when the next ping is --- include/discord-internal.h | 2 + src/discord-gateway.c | 5 +++ src/discord-gateway_dispatch.c | 75 ++++++++++++++++------------------ 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 98cef5710..17aa09d49 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -674,6 +674,8 @@ struct discord_gateway { u64unix_ms event; /** latency obtained from HEARTBEAT and HEARTBEAT_ACK interval */ int ping_ms; + /** timer id for ping timer */ + unsigned ping_timer; /** ping rwlock */ pthread_rwlock_t rwlock; } * timer; diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 9f54ddcc2..acb3bf970 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -538,6 +538,11 @@ discord_gateway_init(struct discord_gateway *gw, void discord_gateway_cleanup(struct discord_gateway *gw) { + if (gw->timer->ping_timer) + discord_internal_timer_ctl( + CLIENT(gw, gw), + &(struct discord_timer){ .id = gw->timer->ping_timer, + .flags = DISCORD_TIMER_DELETE }); /* cleanup WebSockets handle */ io_poller_curlm_del(CLIENT(gw, gw)->io_poller, gw->mhandle); curl_multi_cleanup(gw->mhandle); diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index 5c8423139..9f6a905f8 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -21,42 +21,31 @@ static const struct { /** event's cleanup */ void (*cleanup)(void *); } dispatch[] = { - [DISCORD_EV_READY] = - INIT(discord_ready, ready), + [DISCORD_EV_READY] = INIT(discord_ready, ready), [DISCORD_EV_APPLICATION_COMMAND_CREATE] = INIT(discord_application_command, application_command_create), [DISCORD_EV_APPLICATION_COMMAND_UPDATE] = INIT(discord_application_command, application_command_update), [DISCORD_EV_APPLICATION_COMMAND_DELETE] = INIT(discord_application_command, application_command_delete), - [DISCORD_EV_CHANNEL_CREATE] = - INIT(discord_channel, channel_create), - [DISCORD_EV_CHANNEL_UPDATE] = - INIT(discord_channel, channel_update), - [DISCORD_EV_CHANNEL_DELETE] = - INIT(discord_channel, channel_delete), + [DISCORD_EV_CHANNEL_CREATE] = INIT(discord_channel, channel_create), + [DISCORD_EV_CHANNEL_UPDATE] = INIT(discord_channel, channel_update), + [DISCORD_EV_CHANNEL_DELETE] = INIT(discord_channel, channel_delete), [DISCORD_EV_CHANNEL_PINS_UPDATE] = INIT(discord_channel_pins_update, channel_pins_update), - [DISCORD_EV_THREAD_CREATE] = - INIT(discord_channel, thread_create), - [DISCORD_EV_THREAD_UPDATE] = - INIT(discord_channel, thread_update), - [DISCORD_EV_THREAD_DELETE] = - INIT(discord_channel, thread_delete), + [DISCORD_EV_THREAD_CREATE] = INIT(discord_channel, thread_create), + [DISCORD_EV_THREAD_UPDATE] = INIT(discord_channel, thread_update), + [DISCORD_EV_THREAD_DELETE] = INIT(discord_channel, thread_delete), [DISCORD_EV_THREAD_LIST_SYNC] = INIT(discord_thread_list_sync, thread_list_sync), [DISCORD_EV_THREAD_MEMBER_UPDATE] = INIT(discord_thread_member, thread_member_update), [DISCORD_EV_THREAD_MEMBERS_UPDATE] = INIT(discord_thread_members_update, thread_members_update), - [DISCORD_EV_GUILD_CREATE] = - INIT(discord_guild, guild_create), - [DISCORD_EV_GUILD_UPDATE] = - INIT(discord_guild, guild_update), - [DISCORD_EV_GUILD_DELETE] = - INIT(discord_guild, guild_delete), - [DISCORD_EV_GUILD_BAN_ADD] = - INIT(discord_guild_ban_add, guild_ban_add), + [DISCORD_EV_GUILD_CREATE] = INIT(discord_guild, guild_create), + [DISCORD_EV_GUILD_UPDATE] = INIT(discord_guild, guild_update), + [DISCORD_EV_GUILD_DELETE] = INIT(discord_guild, guild_delete), + [DISCORD_EV_GUILD_BAN_ADD] = INIT(discord_guild_ban_add, guild_ban_add), [DISCORD_EV_GUILD_BAN_REMOVE] = INIT(discord_guild_ban_remove, guild_ban_remove), [DISCORD_EV_GUILD_EMOJIS_UPDATE] = @@ -85,16 +74,11 @@ static const struct { INIT(discord_integration_delete, integration_delete), [DISCORD_EV_INTERACTION_CREATE] = INIT(discord_interaction, interaction_create), - [DISCORD_EV_INVITE_CREATE] = - INIT(discord_invite_create, invite_create), - [DISCORD_EV_INVITE_DELETE] = - INIT(discord_invite_delete, invite_delete), - [DISCORD_EV_MESSAGE_CREATE] = - INIT(discord_message, message_create), - [DISCORD_EV_MESSAGE_UPDATE] = - INIT(discord_message, message_update), - [DISCORD_EV_MESSAGE_DELETE] = - INIT(discord_message_delete, message_delete), + [DISCORD_EV_INVITE_CREATE] = INIT(discord_invite_create, invite_create), + [DISCORD_EV_INVITE_DELETE] = INIT(discord_invite_delete, invite_delete), + [DISCORD_EV_MESSAGE_CREATE] = INIT(discord_message, message_create), + [DISCORD_EV_MESSAGE_UPDATE] = INIT(discord_message, message_update), + [DISCORD_EV_MESSAGE_DELETE] = INIT(discord_message_delete, message_delete), [DISCORD_EV_MESSAGE_DELETE_BULK] = INIT(discord_message_delete_bulk, message_delete_bulk), [DISCORD_EV_MESSAGE_REACTION_ADD] = @@ -103,9 +87,8 @@ static const struct { INIT(discord_message_reaction_remove, message_reaction_remove), [DISCORD_EV_MESSAGE_REACTION_REMOVE_ALL] = INIT(discord_message_reaction_remove_all, message_reaction_remove_all), - [DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI] = - INIT(discord_message_reaction_remove_emoji, - message_reaction_remove_emoji), + [DISCORD_EV_MESSAGE_REACTION_REMOVE_EMOJI] = INIT( + discord_message_reaction_remove_emoji, message_reaction_remove_emoji), [DISCORD_EV_PRESENCE_UPDATE] = INIT(discord_presence_update, presence_update), [DISCORD_EV_STAGE_INSTANCE_CREATE] = @@ -114,10 +97,8 @@ static const struct { INIT(discord_stage_instance, stage_instance_update), [DISCORD_EV_STAGE_INSTANCE_DELETE] = INIT(discord_stage_instance, stage_instance_delete), - [DISCORD_EV_TYPING_START] = - INIT(discord_typing_start, typing_start), - [DISCORD_EV_USER_UPDATE] = - INIT(discord_user, user_update), + [DISCORD_EV_TYPING_START] = INIT(discord_typing_start, typing_start), + [DISCORD_EV_USER_UPDATE] = INIT(discord_user, user_update), [DISCORD_EV_VOICE_STATE_UPDATE] = INIT(discord_voice_state, voice_state_update), [DISCORD_EV_VOICE_SERVER_UPDATE] = @@ -252,6 +233,18 @@ discord_gateway_send_resume(struct discord_gateway *gw, } } +static void +on_ping_timer_cb(struct discord *client, struct discord_timer *timer) +{ + (void)client; + struct discord_gateway *gw = timer->data; + if (~timer->flags & DISCORD_TIMER_CANCELED) { + discord_gateway_perform(gw); + timer->interval = (int64_t)gw->timer->interval; + timer->repeat = 1; + } +} + /* send heartbeat pulse to websockets server in order * to maintain connection alive */ void @@ -281,6 +274,10 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) b.pos, info.loginfo.counter + 1); /* update heartbeat timestamp */ gw->timer->hbeat = gw->timer->now; + if (!gw->timer->ping_timer) + gw->timer->ping_timer = + discord_internal_timer(CLIENT(gw, gw), on_ping_timer_cb, gw, + (int64_t)gw->timer->interval); } else { logconf_info( From 38d5086201fb06fd2b995d84c6c5cc744196d0c9 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 28 May 2022 23:02:34 -0400 Subject: [PATCH 088/118] refactor(discord-loop.c): eliminate next_run from the main loop allowing the client to truly idle --- src/discord-loop.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/discord-loop.c b/src/discord-loop.c index e18d6102a..f8bb793ea 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -63,13 +63,12 @@ discord_run(struct discord *client) { struct discord_timers *const timers[] = { &client->timers.internal, &client->timers.user }; - int64_t next_run, now; + int64_t now; CCORDcode code; while (1) { BREAK_ON_FAIL(code, discord_gateway_start(&client->gw)); - next_run = (int64_t)discord_timestamp_us(client); while (1) { int poll_result, poll_errno = 0; int64_t poll_time = 0; @@ -78,8 +77,7 @@ discord_run(struct discord *client) if (!client->on_idle) { poll_time = discord_timers_get_next_trigger( - timers, sizeof timers / sizeof *timers, now, - now < next_run ? ((next_run - now)) : 0); + timers, sizeof timers / sizeof *timers, now, 60000000); } CALL_IO_POLLER_POLL(poll_errno, poll_result, client->io_poller, @@ -97,8 +95,7 @@ discord_run(struct discord *client) } else { int64_t sleep_time = discord_timers_get_next_trigger( - timers, sizeof timers / sizeof *timers, now, - now < next_run ? ((next_run - now)) : 0); + timers, sizeof timers / sizeof *timers, now, 1000); if (sleep_time > 0 && sleep_time < 1000) cog_sleep_us(sleep_time); } @@ -122,11 +119,6 @@ discord_run(struct discord *client) BREAK_ON_FAIL(code, io_poller_perform(client->io_poller)); discord_requestor_dispatch_responses(&client->rest.requestor); - if (next_run <= now) { - BREAK_ON_FAIL(code, discord_gateway_perform(&client->gw)); - /* enforce a min 1 sec delay between runs */ - next_run = now + 1000000; - } } /* stop all pending requests in case of connection shutdown */ From 8279e09e4ed4ce60febe2fd7ee39babb4ca62ce1 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sat, 28 May 2022 23:29:28 -0400 Subject: [PATCH 089/118] fix(discord-gateway_dispatch.c): determine correct interval for next heartbeat --- src/discord-gateway_dispatch.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index 9f6a905f8..c8908a6a6 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -240,7 +240,9 @@ on_ping_timer_cb(struct discord *client, struct discord_timer *timer) struct discord_gateway *gw = timer->data; if (~timer->flags & DISCORD_TIMER_CANCELED) { discord_gateway_perform(gw); - timer->interval = (int64_t)gw->timer->interval; + const u64unix_ms next_hb = gw->timer->hbeat + gw->timer->interval; + timer->interval = (int64_t)(next_hb - discord_timestamp(client)); + if (timer->interval < 0) timer->interval = 0; timer->repeat = 1; } } From c89e7821ae277ecd1d7f43d96a3c663bc15e3183 Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 29 May 2022 03:53:28 -0400 Subject: [PATCH 090/118] fix(discord-gateway_dispatch.c): used signed int for difference in _on_ping_timer_cb --- src/discord-gateway_dispatch.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index c8908a6a6..15c3ea77f 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -241,8 +241,9 @@ on_ping_timer_cb(struct discord *client, struct discord_timer *timer) if (~timer->flags & DISCORD_TIMER_CANCELED) { discord_gateway_perform(gw); const u64unix_ms next_hb = gw->timer->hbeat + gw->timer->interval; - timer->interval = (int64_t)(next_hb - discord_timestamp(client)); - if (timer->interval < 0) timer->interval = 0; + timer->interval = + (int64_t)(next_hb) - (int64_t)discord_timestamp(client); + if (timer->interval < 1) timer->interval = 1; timer->repeat = 1; } } From 4995d71bfae0c4fad2a119c2db8ff31076db2d4c Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 29 May 2022 21:52:09 -0300 Subject: [PATCH 091/118] feat: add discord_bucket_request_selector(), discord_bucket_request_unselect() to iterate only over buckets that have pending requests --- include/discord-internal.h | 89 +++++++++------- src/discord-rest_ratelimit.c | 200 +++++++++++++++++++++-------------- src/discord-rest_request.c | 90 +++++++--------- 3 files changed, 213 insertions(+), 166 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 17aa09d49..ff71ed952 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -180,7 +180,7 @@ unsigned discord_internal_timer(struct discord *client, * @{ */ /** - * @brief Value assigned to @ref discord_bucket `performing_req` field in case + * @brief Value assigned to @ref discord_bucket `busy_req` field in case * it's being timed-out */ #define DISCORD_BUCKET_TIMEOUT (void *)(0xf) @@ -210,6 +210,12 @@ struct discord_ratelimiter { /* client-wide global ratelimiting */ u64unix_ms *global_wait_ms; + + /** bucket queues */ + struct { + /** buckets that are currently pending (have pending requests) */ + QUEUE(struct discord_bucket) pending; + } queues; }; /** @@ -230,16 +236,6 @@ void discord_ratelimiter_init(struct discord_ratelimiter *rl, */ void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl); -/** - * @brief Iterate known buckets - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param iter the user callback to be called per bucket - */ -void discord_ratelimiter_foreach_bucket( - struct discord_ratelimiter *rl, - void (*iter)(struct discord_ratelimiter *rl, struct discord_bucket *b)); - /** * @brief Build unique key formed from the HTTP method and endpoint * @see https://discord.com/developers/docs/topics/rate-limits @@ -274,17 +270,24 @@ struct discord_bucket { char hash[64]; /** maximum connections this bucket can handle before ratelimit */ long limit; - /** connections this bucket can do before waiting for cooldown */ + /** connections this bucket can do before pending for cooldown */ long remaining; /** timestamp of when cooldown timer resets */ u64unix_ms reset_tstamp; - /** pending requests */ - QUEUE(struct discord_request) pending_queue; + /** - * pointer to this bucket's currently performing request + * pointer to this bucket's currently busy request * @note @ref DISCORD_BUCKET_TIMEOUT if bucket is being ratelimited */ - struct discord_request *performing_req; + struct discord_request *busy_req; + + /** request queues */ + struct { + /** next requests queue */ + QUEUE(struct discord_request) next; + } queues; + /** entry for @ref discord_ratelimiter pending buckets queue */ + QUEUE entry; }; /** @@ -297,15 +300,6 @@ struct discord_bucket { u64unix_ms discord_bucket_get_timeout(struct discord_ratelimiter *rl, struct discord_bucket *bucket); -/** - * @brief Try to timeout bucket for pending cooldown time - * - * @param rl the handle initialized with discord_ratelimiter_init() - * @param bucket the bucket to wait on cooldown - */ -void discord_bucket_try_timeout(struct discord_ratelimiter *rl, - struct discord_bucket *b); - /** * @brief Get a `struct discord_bucket` assigned to `key` * @@ -317,25 +311,44 @@ struct discord_bucket *discord_bucket_get(struct discord_ratelimiter *rl, const char key[]); /** - * @brief Insert request into bucket's pending queue + * @brief Insert into bucket's next requests queue * + * @param rl the handle initialized with discord_ratelimiter_init() * @param b the bucket to insert the request to - * @param req the request obtained via discord_requestor_start_request() + * @param req the request to be inserted to bucket * @param high_priority if high priority then request shall be prioritized over * already enqueued requests */ -void discord_bucket_add_request(struct discord_bucket *b, - struct discord_request *req, - bool high_priority); +void discord_bucket_insert(struct discord_ratelimiter *rl, + struct discord_bucket *b, + struct discord_request *req, + bool high_priority); /** - * @brief Remove head request from bucket's pending queue + * @brief Iterate and select next requests + * @note discord_bucket_unselect() must be called once bucket's current request + * is done and its next one should be selected * - * @param b the bucket to fetch the request from - * @return the request + * @param rl the handle initialized with discord_ratelimiter_init() + * @param data user arbitrary data + * @param iter the user callback to be called per bucket */ -struct discord_request *discord_bucket_remove_request( - struct discord_bucket *b); +void discord_bucket_request_selector( + struct discord_ratelimiter *rl, + void *data, + void (*iter)(void *data, struct discord_request *req)); + +/** + * @brief Unselect a request provided at discord_ratelimiter_request_selector() + * @note counterpart to discord_ratelimiter_request_selector() + * + * @param rl the handle initialized with discord_ratelimiter_init() + * @param b the request's bucket + * @param req the request to unslect + */ +void discord_bucket_request_unselect(struct discord_ratelimiter *rl, + struct discord_bucket *b, + struct discord_request *req); /** @} DiscordInternalRESTRequestRatelimit */ @@ -425,10 +438,10 @@ struct discord_request { /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; - /** the request bucket's queue entry */ - QUEUE entry; /** synchronize synchronous requests */ pthread_cond_t *cond; + /** entry for @ref discord_ratelimitor and @ref discord_bucket queues */ + QUEUE entry; }; /** @brief The handle used for handling asynchronous requests */ @@ -466,7 +479,7 @@ struct discord_requestor { pthread_mutex_t pending; /** finished queue lock */ pthread_mutex_t finished; - } *qlocks; + } * qlocks; }; /** diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index c1106c3da..0e78fa545 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -57,7 +57,6 @@ discord_ratelimiter_build_key(enum http_method method, KEY_PUSH(key, &keylen, "%d", method); do { u64snowflake id_arg = 0ULL; - size_t i; curr += 1 + currlen; currlen = strcspn(curr, "/"); @@ -66,7 +65,7 @@ discord_ratelimiter_build_key(enum http_method method, if (0 == strncmp(prev, "reactions", 9)) break; /* consume variadic arguments */ - for (i = 0; i < currlen; ++i) { + for (size_t i = 0; i < currlen; ++i) { if ('%' == curr[i]) { const char *type = &curr[i + 1]; @@ -93,12 +92,9 @@ discord_ratelimiter_build_key(enum http_method method, if (0 == strncmp(curr, "%" PRIu64, currlen) && (0 == strncmp(prev, "channels", 8) || 0 == strncmp(prev, "guilds", 6))) - { KEY_PUSH(key, &keylen, "%" PRIu64, id_arg); - } - else { + else KEY_PUSH(key, &keylen, "%.*s", (int)currlen, curr); - } prev = curr; @@ -117,13 +113,13 @@ _discord_bucket_init(struct discord_ratelimiter *rl, struct discord_bucket *b = calloc(1, sizeof *b); int len = snprintf(b->hash, sizeof(b->hash), "%.*s", (int)hash->size, hash->start); - ASSERT_NOT_OOB(len, sizeof(b->hash)); b->remaining = 1; b->limit = limit; - QUEUE_INIT(&b->pending_queue); + QUEUE_INIT(&b->queues.next); + QUEUE_INIT(&b->entry); chash_assign(rl, key, b, RATELIMITER_TABLE); @@ -145,42 +141,41 @@ discord_ratelimiter_init(struct discord_ratelimiter *rl, struct logconf *conf) /* initialize 'singleton' buckets */ rl->null = _discord_bucket_init(rl, "null", &keynull, 1L); rl->miss = _discord_bucket_init(rl, "miss", &keymiss, LONG_MAX); + + /* initialize bucket queues */ + QUEUE_INIT(&rl->queues.pending); } +/* cancel all pending and busy requests from a bucket */ static void -_discord_bucket_cancel(struct discord_ratelimiter *rl, - struct discord_bucket *b) +_discord_bucket_cancel_all(struct discord_ratelimiter *rl, + struct discord_bucket *b) { struct discord_requestor *rqtor = CONTAINEROF(rl, struct discord_requestor, ratelimiter); /* cancel busy transfer */ - if (b->performing_req) discord_request_cancel(rqtor, b->performing_req); + if (b->busy_req) discord_request_cancel(rqtor, b->busy_req); /* move pending tranfers to recycling */ pthread_mutex_lock(&rqtor->qlocks->recycling); - QUEUE_ADD(&rqtor->queues->recycling, &b->pending_queue); + QUEUE_ADD(&rqtor->queues->recycling, &b->queues.next); pthread_mutex_unlock(&rqtor->qlocks->recycling); - QUEUE_INIT(&b->pending_queue); + QUEUE_INIT(&b->queues.next); } void discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) { - discord_ratelimiter_foreach_bucket(rl, &_discord_bucket_cancel); - free(rl->global_wait_ms); - __chash_free(rl, RATELIMITER_TABLE); -} - -void -discord_ratelimiter_foreach_bucket(struct discord_ratelimiter *rl, - void (*iter)(struct discord_ratelimiter *rl, - struct discord_bucket *b)) -{ + /* iterate and cleanup known buckets */ for (int i = 0; i < rl->capacity; ++i) { struct _discord_route *r = rl->routes + i; - if (CHASH_FILLED == r->state) (*iter)(rl, r->bucket); + if (CHASH_FILLED == r->state) { + _discord_bucket_cancel_all(rl, r->bucket); + } } + free(rl->global_wait_ms); + __chash_free(rl, RATELIMITER_TABLE); } static struct discord_bucket * @@ -210,19 +205,19 @@ _discord_bucket_wake_cb(struct discord *client, struct discord_timer *timer) (void)client; struct discord_bucket *b = timer->data; - b->performing_req = NULL; + b->busy_req = NULL; b->remaining = 1; } -void -discord_bucket_try_timeout(struct discord_ratelimiter *rl, - struct discord_bucket *b) +static void +_discord_bucket_try_timeout(struct discord_ratelimiter *rl, + struct discord_bucket *b) { struct discord *client = CLIENT(rl, rest.requestor.ratelimiter); int64_t delay_ms = (int64_t)(b->reset_tstamp - cog_timestamp_ms()); if (delay_ms < 0) delay_ms = 0; - b->performing_req = DISCORD_BUCKET_TIMEOUT; + b->busy_req = DISCORD_BUCKET_TIMEOUT; _discord_timer_ctl(client, &client->rest.timers, &(struct discord_timer){ @@ -245,14 +240,32 @@ discord_bucket_get(struct discord_ratelimiter *rl, const char key[]) if (NULL != (b = _discord_bucket_find(rl, key))) { logconf_trace(&rl->conf, "[%.4s] Found a bucket match for '%s'!", b->hash, key); - - return b; } + else { + b = rl->null; + logconf_trace(&rl->conf, "[null] Couldn't match known buckets to '%s'", + key); + } + return b; +} - logconf_trace(&rl->conf, "[null] Couldn't match known buckets to '%s'", - key); +/* check if successive requests made from a `null` singleton bucket can be + * matched to another bucket */ +static void +_discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, + struct discord_bucket *b, + const char key[]) +{ + QUEUE(struct discord_request) queue, *qelem; + struct discord_request *req; - return rl->null; + QUEUE_MOVE(&rl->null->queues.next, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + req = QUEUE_DATA(qelem, struct discord_request, entry); + if (strcmp(req->key, key) != 0) b = rl->null; + discord_bucket_insert(rl, b, req, false); + } } static struct discord_bucket * @@ -262,7 +275,6 @@ _discord_ratelimiter_get_match(struct discord_ratelimiter *rl, { struct discord_bucket *b; - /* create bucket if it doesn't exist yet */ if (NULL == (b = _discord_bucket_find(rl, key))) { struct ua_szbuf_readonly hash = ua_info_get_header(info, "x-ratelimit-bucket"); @@ -270,7 +282,7 @@ _discord_ratelimiter_get_match(struct discord_ratelimiter *rl, if (!hash.size) { /* bucket is not part of a ratelimiting group */ b = rl->miss; } - else { + else { /* create bucket if it doesn't exist yet */ struct ua_szbuf_readonly limit = ua_info_get_header(info, "x-ratelimit-limit"); long _limit = @@ -282,6 +294,8 @@ _discord_ratelimiter_get_match(struct discord_ratelimiter *rl, logconf_debug(&rl->conf, "[%.4s] Match '%s' to bucket", b->hash, key); + _discord_ratelimiter_null_filter(rl, b, key); + return b; } @@ -338,34 +352,6 @@ _discord_bucket_populate(struct discord_ratelimiter *rl, b->hash, b->remaining, b->reset_tstamp); } -/* in case of asynchronous requests, check if successive requests made from a - * `null` singleton bucket can be matched to another bucket */ -static void -_discord_ratelimiter_null_filter(struct discord_ratelimiter *rl, - struct discord_bucket *b, - const char key[]) -{ - QUEUE(struct discord_request) queue, *qelem; - struct discord_request *req; - - QUEUE_MOVE(&rl->null->pending_queue, &queue); - QUEUE_INIT(&rl->null->pending_queue); - - while (!QUEUE_EMPTY(&queue)) { - qelem = QUEUE_HEAD(&queue); - QUEUE_REMOVE(qelem); - - req = QUEUE_DATA(qelem, struct discord_request, entry); - if (0 == strcmp(req->key, key)) { - QUEUE_INSERT_TAIL(&b->pending_queue, qelem); - req->b = b; - } - else { - QUEUE_INSERT_TAIL(&rl->null->pending_queue, qelem); - } - } -} - /* attempt to create and/or update bucket's values */ void discord_ratelimiter_build(struct discord_ratelimiter *rl, @@ -374,34 +360,92 @@ discord_ratelimiter_build(struct discord_ratelimiter *rl, struct ua_info *info) { /* try to match to existing, or create new bucket */ - if (b == rl->null) { - b = _discord_ratelimiter_get_match(rl, key, info); - _discord_ratelimiter_null_filter(rl, b, key); - } + if (b == rl->null) b = _discord_ratelimiter_get_match(rl, key, info); /* populate bucket with response header values */ _discord_bucket_populate(rl, b, info); } void -discord_bucket_add_request(struct discord_bucket *b, - struct discord_request *req, - bool high_priority) +discord_bucket_insert(struct discord_ratelimiter *rl, + struct discord_bucket *b, + struct discord_request *req, + bool high_priority) { QUEUE_REMOVE(&req->entry); - QUEUE_INIT(&req->entry); if (high_priority) - QUEUE_INSERT_HEAD(&b->pending_queue, &req->entry); + QUEUE_INSERT_HEAD(&b->queues.next, &req->entry); else - QUEUE_INSERT_TAIL(&b->pending_queue, &req->entry); + QUEUE_INSERT_TAIL(&b->queues.next, &req->entry); + + /* add bucket to ratelimiter pending buckets queue (if not already in) */ + if (QUEUE_EMPTY(&b->entry)) + QUEUE_INSERT_HEAD(&rl->queues.pending, &b->entry); + req->b = b; } -struct discord_request * -discord_bucket_remove_request(struct discord_bucket *b) +static void +_discord_bucket_pop(struct discord_bucket *b) { - QUEUE(struct discord_request) *qelem = QUEUE_HEAD(&b->pending_queue); + QUEUE(struct discord_request) *qelem = QUEUE_HEAD(&b->queues.next); QUEUE_REMOVE(qelem); QUEUE_INIT(qelem); - return QUEUE_DATA(qelem, struct discord_request, entry); + b->busy_req = QUEUE_DATA(qelem, struct discord_request, entry); + if (b->busy_req->b == NULL) abort(); +} + +void +discord_bucket_request_selector(struct discord_ratelimiter *rl, + void *data, + void (*iter)(void *data, + struct discord_request *req)) +{ + QUEUE(struct discord_bucket) queue, *qelem; + struct discord_bucket *b; + + /* loop through each pending buckets and enqueue next requests */ + QUEUE_MOVE(&rl->queues.pending, &queue); + while (!QUEUE_EMPTY(&queue)) { + qelem = QUEUE_HEAD(&queue); + b = QUEUE_DATA(qelem, struct discord_bucket, entry); + + QUEUE_REMOVE(qelem); + if (b->busy_req) { + QUEUE_INSERT_TAIL(&rl->queues.pending, qelem); + continue; + } + if (!b->remaining) { + _discord_bucket_try_timeout(rl, b); + QUEUE_INSERT_TAIL(&rl->queues.pending, qelem); + continue; + } + + _discord_bucket_pop(b); + (*iter)(data, b->busy_req); + + /* if bucket has no pending requests then remove it from + * ratelimiter pending buckets queue */ + if (QUEUE_EMPTY(&b->queues.next)) + QUEUE_INIT(qelem); + else /* otherwise move it back to pending buckets queue */ + QUEUE_INSERT_TAIL(&rl->queues.pending, qelem); + } +} + +void +discord_bucket_request_unselect(struct discord_ratelimiter *rl, + struct discord_bucket *b, + struct discord_request *req) +{ + (void)rl; + ASSERT_S(req == b->busy_req, + "Attempt to unlock a bucket with a non-busy request"); + + if (!req->retry && QUEUE_EMPTY(&b->queues.next)) { + QUEUE_REMOVE(&b->entry); + QUEUE_INIT(&b->entry); + } + b->busy_req = NULL; + req->b = NULL; } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index bb248b01e..97f8d745d 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -266,11 +266,11 @@ _discord_request_retry(struct discord_requestor *rqtor, if (!req->retry || req->retry_attempt++ >= rqtor->retry_limit) return false; - req->b->performing_req = NULL; ua_conn_reset(req->conn); /* FIXME: wait_ms > 0 should be dealt with aswell */ - if (req->wait_ms <= 0) discord_bucket_add_request(req->b, req, true); + if (req->wait_ms <= 0) + discord_bucket_insert(&rqtor->ratelimiter, req->b, req, true); return true; } @@ -281,13 +281,16 @@ discord_request_cancel(struct discord_requestor *rqtor, { struct discord_refcounter *rc = &CLIENT(rqtor, rest.requestor)->refcounter; - if (req->conn) ua_conn_stop(req->conn); - - if (req->dispatch.keep) + if (req->conn) { + ua_conn_stop(req->conn); + } + if (req->dispatch.keep) { discord_refcounter_decr(rc, (void *)req->dispatch.keep); - if (req->dispatch.data) discord_refcounter_decr(rc, req->dispatch.data); + } + if (req->dispatch.data) { + discord_refcounter_decr(rc, req->dispatch.data); + } - req->b->performing_req = NULL; req->body.size = 0; req->method = 0; *req->endpoint = '\0'; @@ -298,7 +301,6 @@ discord_request_cancel(struct discord_requestor *rqtor, memset(req, 0, sizeof(struct discord_attributes)); QUEUE_REMOVE(&req->entry); - QUEUE_INIT(&req->entry); pthread_mutex_lock(&rqtor->qlocks->recycling); QUEUE_INSERT_TAIL(&rqtor->queues->recycling, &req->entry); pthread_mutex_unlock(&rqtor->qlocks->recycling); @@ -314,7 +316,7 @@ _discord_request_dispatch_response(struct discord_requestor *rqtor, .code = req->code }; if (req->code != CCORD_OK) { - req->dispatch.fail(client, &resp); + if (req->dispatch.fail) req->dispatch.fail(client, &resp); } else if (req->dispatch.done.typed) { if (!req->dispatch.has_type) { @@ -330,6 +332,8 @@ _discord_request_dispatch_response(struct discord_requestor *rqtor, if (!_discord_request_retry(rqtor, req)) discord_request_cancel(rqtor, req); + discord_bucket_request_unselect(&rqtor->ratelimiter, req->b, req); + return resp.code; } @@ -459,49 +463,35 @@ discord_requestor_info_read(struct discord_requestor *rqtor) } static void -_discord_request_try_begin(struct discord_ratelimiter *rl, - struct discord_bucket *b) +_discord_request_send(void *p_rqtor, struct discord_request *req) { - /* skip if bucket is already busy performing */ - if (b->performing_req) return; - - if (!b->remaining) { - discord_bucket_try_timeout(rl, b); - } - else if (!QUEUE_EMPTY(&b->pending_queue)) { - struct discord_requestor *rqtor = - CONTAINEROF(rl, struct discord_requestor, ratelimiter); + struct discord_requestor *rqtor = p_rqtor; + CURL *ehandle; - struct discord_request *req = discord_bucket_remove_request(b); - CURL *ehandle; + req->conn = ua_conn_start(rqtor->ua); + ehandle = ua_conn_get_easy_handle(req->conn); - b->performing_req = req; - req->conn = ua_conn_start(rqtor->ua); - ehandle = ua_conn_get_easy_handle(req->conn); - - if (HTTP_MIMEPOST == req->method) { - ua_conn_add_header(req->conn, "Content-Type", - "multipart/form-data"); - ua_conn_set_mime(req->conn, req, &_discord_request_to_multipart); - } - else { - ua_conn_add_header(req->conn, "Content-Type", "application/json"); - } + if (HTTP_MIMEPOST == req->method) { + ua_conn_add_header(req->conn, "Content-Type", "multipart/form-data"); + ua_conn_set_mime(req->conn, req, &_discord_request_to_multipart); + } + else { + ua_conn_add_header(req->conn, "Content-Type", "application/json"); + } - ua_conn_setup(req->conn, &(struct ua_conn_attr){ - .method = req->method, - .body = req->body.start, - .body_size = req->body.size, - .endpoint = req->endpoint, - .base_url = NULL, - }); + ua_conn_setup(req->conn, &(struct ua_conn_attr){ + .method = req->method, + .body = req->body.start, + .body_size = req->body.size, + .endpoint = req->endpoint, + .base_url = NULL, + }); - /* link 'req' to 'ehandle' for easy retrieval */ - curl_easy_setopt(ehandle, CURLOPT_PRIVATE, req); + /* link 'req' to 'ehandle' for easy retrieval */ + curl_easy_setopt(ehandle, CURLOPT_PRIVATE, req); - /* initiate libcurl transfer */ - curl_multi_add_handle(rqtor->mhandle, ehandle); - } + /* initiate libcurl transfer */ + curl_multi_add_handle(rqtor->mhandle, ehandle); } CCORDcode @@ -522,12 +512,12 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) req = QUEUE_DATA(qelem, struct discord_request, entry); b = discord_bucket_get(&rqtor->ratelimiter, req->key); - discord_bucket_add_request(b, req, req->dispatch.high_p); + discord_bucket_insert(&rqtor->ratelimiter, b, req, + req->dispatch.high_p); } - /* TODO: replace foreach with a mechanism that loops only busy buckets */ - discord_ratelimiter_foreach_bucket(&rqtor->ratelimiter, - &_discord_request_try_begin); + discord_bucket_request_selector(&rqtor->ratelimiter, rqtor, + &_discord_request_send); /* FIXME: redundant return value (constant) */ return CCORD_OK; From aafd4c97807224d7e7638e9ce49b059114cf31d1 Mon Sep 17 00:00:00 2001 From: Anotra Date: Mon, 30 May 2022 05:37:04 -0400 Subject: [PATCH 092/118] fix(discord-timer): delete function should skip callback --- src/discord-timer.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/discord-timer.c b/src/discord-timer.c index 35f6e02d6..40f2689b4 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -27,7 +27,8 @@ discord_timers_init(struct discord_timers *timers) } static void -discord_timers_cancel_all(struct discord *client, struct discord_timers *timers) +discord_timers_cancel_all(struct discord *client, + struct discord_timers *timers) { struct discord_timer timer; while ((timer.id = priority_queue_pop(timers->q, NULL, &timer))) { @@ -109,12 +110,10 @@ _discord_timer_ctl(struct discord *client, } #define TIMER_TRY_DELETE \ - do { \ - if (timer.flags & DISCORD_TIMER_DELETE) { \ - priority_queue_del(timers->q, timer.id); \ - continue; \ - } \ - } while (0) + if (timer.flags & DISCORD_TIMER_DELETE) { \ + priority_queue_del(timers->q, timer.id); \ + continue; \ + } void discord_timers_run(struct discord *client, struct discord_timers *timers) From 7a348ad16160be82dc85cda655f32321604a9fee Mon Sep 17 00:00:00 2001 From: Anotra Date: Mon, 30 May 2022 08:11:29 -0400 Subject: [PATCH 093/118] refactor(discord-timer): cleanup discord_timers_run --- src/discord-timer.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/discord-timer.c b/src/discord-timer.c index 40f2689b4..41c1b002c 100644 --- a/src/discord-timer.c +++ b/src/discord-timer.c @@ -112,6 +112,7 @@ _discord_timer_ctl(struct discord *client, #define TIMER_TRY_DELETE \ if (timer.flags & DISCORD_TIMER_DELETE) { \ priority_queue_del(timers->q, timer.id); \ + timers->active.skip_update_phase = false; \ continue; \ } @@ -124,6 +125,7 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) struct discord_timer timer; timers->active.timer = &timer; + timers->active.skip_update_phase = false; for (int64_t trigger, max_iterations = 100000; (timer.id = priority_queue_peek(timers->q, &trigger, &timer)) && max_iterations > 0; @@ -133,20 +135,23 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) if ((max_iterations & 0x1F) == 0) { now = (int64_t)discord_timestamp_us(client); // break if we've spent too much time running timers - if (now - start_time > 3000) break; + if (now - start_time > 10000) break; } // no timers to run if (trigger > now || trigger == -1) break; - if (~timer.flags & DISCORD_TIMER_CANCELED) TIMER_TRY_DELETE; + if (~timer.flags & DISCORD_TIMER_CANCELED) { + TIMER_TRY_DELETE; - if (timer.repeat > 0 && ~timer.flags & DISCORD_TIMER_CANCELED) - timer.repeat--; + if (timer.repeat > 0) timer.repeat--; + } - timers->active.skip_update_phase = false; if (timer.cb) timer.cb(client, &timer); - if (timers->active.skip_update_phase) continue; + if (timers->active.skip_update_phase) { + timers->active.skip_update_phase = false; + continue; + } if ((timer.repeat == 0 || timer.flags & DISCORD_TIMER_CANCELED) && (timer.flags & DISCORD_TIMER_DELETE_AUTO)) @@ -157,16 +162,14 @@ discord_timers_run(struct discord *client, struct discord_timers *timers) TIMER_TRY_DELETE; int64_t next = -1; - if (timer.repeat != 0 && timer.delay != -1 + if (timer.delay != -1 && timer.interval >= 0 && timer.repeat != 0 && ~timer.flags & DISCORD_TIMER_CANCELED) { - if (timer.interval >= 0) { - next = ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) ? trigger - : now) - + ((timer.flags & DISCORD_TIMER_MICROSECONDS) - ? timer.interval - : timer.interval * 1000); - } + next = + ((timer.flags & DISCORD_TIMER_INTERVAL_FIXED) ? trigger : now) + + ((timer.flags & DISCORD_TIMER_MICROSECONDS) + ? timer.interval + : timer.interval * 1000); } timer.flags &= DISCORD_TIMER_ALLOWED_FLAGS; priority_queue_update(timers->q, timer.id, &next, &timer); From 3fef95ebde6162e96a3af0220a55347d5c64d89e Mon Sep 17 00:00:00 2001 From: Anotra Date: Mon, 30 May 2022 09:01:58 -0400 Subject: [PATCH 094/118] fix: sigint --- src/discord-gateway.c | 4 ++++ src/discord-loop.c | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/discord-gateway.c b/src/discord-gateway.c index acb3bf970..4024c8708 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -274,6 +274,7 @@ on_invalid_session(struct discord_gateway *gw) gw->session->retry.enable = true; ws_close(gw->ws, opcode, reason, SIZE_MAX); + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); } static void @@ -287,6 +288,7 @@ on_reconnect(struct discord_gateway *gw) ws_close(gw->ws, (enum ws_close_reason)DISCORD_GATEWAY_CLOSE_REASON_RECONNECT, reason, sizeof(reason)); + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); } static void @@ -792,6 +794,7 @@ discord_gateway_shutdown(struct discord_gateway *gw) gw->session->status = DISCORD_SESSION_SHUTDOWN; ws_close(gw->ws, WS_CLOSE_REASON_NORMAL, reason, sizeof(reason)); + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); } void @@ -812,4 +815,5 @@ discord_gateway_reconnect(struct discord_gateway *gw, bool resume) } ws_close(gw->ws, opcode, reason, sizeof(reason)); + io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); } diff --git a/src/discord-loop.c b/src/discord-loop.c index f8bb793ea..efbcb7409 100644 --- a/src/discord-loop.c +++ b/src/discord-loop.c @@ -84,11 +84,8 @@ discord_run(struct discord *client) poll_time / 1000); now = (int64_t)discord_timestamp_us(client); - + if (0 == poll_result) { - if (ccord_has_sigint != 0) { - discord_shutdown(client); - } if (client->on_idle) { client->on_idle(client); @@ -110,6 +107,7 @@ discord_run(struct discord *client) CALL_IO_POLLER_POLL(poll_errno, poll_result, client->io_poller, 0); + if (ccord_has_sigint != 0) discord_shutdown(client); if (-1 == poll_result) { /* TODO: handle poll error here */ /* use poll_errno instead of errno */ From 8789ac2f8f1c5b4dff8041b490de69f9ede4c031 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 30 May 2022 12:56:16 -0300 Subject: [PATCH 095/118] refactor(discord-gateway): better document timers, move heartbeat logic from discord_gateway_perform() --- include/discord-internal.h | 43 ++++++++++++++++++++++--------- src/discord-gateway.c | 47 +++++++++++++--------------------- src/discord-gateway_dispatch.c | 36 +++++++++++++++----------- src/discord-rest_ratelimit.c | 5 ++-- 4 files changed, 72 insertions(+), 59 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index ff71ed952..f571c87cb 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -674,21 +674,40 @@ struct discord_gateway { /** timers kept for synchronization */ struct { - /** fixed interval between heartbeats */ - u64unix_ms interval; - /** last heartbeat pulse timestamp */ - u64unix_ms hbeat; - /** Gateway's concept of "now" */ + /** + * Gateway's concept of "now" + * @note updated at discord_gateway_perform() + */ u64unix_ms now; - /** timestamp of last succesful identify request */ - u64unix_ms identify; - /** timestamp of last succesful event timestamp in ms - * (resets every 60s) */ + /** + * fixed interval between heartbeats + * @note obtained at `HELLO` + */ + u64unix_ms hbeat_interval; + /** + * last heartbeat pulse timestamp + * @note first sent at `READY` and `RESUME`, then updated every + * `hbeat_interval` + */ + u64unix_ms hbeat_last; + /** + * timestamp of last succesful identify request + * @note updated at discord_gateway_send_identify() + */ + u64unix_ms identify_last; + /** + * timestamp of last succesful event + * @note resets every 60s + */ u64unix_ms event; - /** latency obtained from HEARTBEAT and HEARTBEAT_ACK interval */ + /** timer id for heartbeat timer */ + unsigned hbeat_timer; + + /** + * latency obtained from `HEARTBEAT` and `HEARTBEAT_ACK` response + * interval + */ int ping_ms; - /** timer id for ping timer */ - unsigned ping_timer; /** ping rwlock */ pthread_rwlock_t rwlock; } * timer; diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 4024c8708..e4d1f1f18 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -67,12 +67,12 @@ on_hello(struct discord_gateway *gw) { jsmnf_pair *f; - gw->timer->interval = 0; - gw->timer->hbeat = gw->timer->now; + gw->timer->hbeat_interval = 0; if ((f = jsmnf_find(gw->payload.data, gw->payload.json, "heartbeat_interval", 18))) - gw->timer->interval = strtoull(gw->payload.json + f->v.pos, NULL, 10); + gw->timer->hbeat_interval = + strtoull(gw->payload.json + f->v.pos, NULL, 10); if (gw->session->status & DISCORD_SESSION_RESUMABLE) discord_gateway_send_resume(gw, &(struct discord_resume){ @@ -186,13 +186,13 @@ on_dispatch(struct discord_gateway *gw) /* XXX: this should only apply for user dispatched payloads? */ #if 0 /* Ratelimit check */ - if (gw->timer->now - gw->timer->event < 60000) { + if (gw->timer->now - gw->timer->event_last < 60000) { ++gw->session->event_count; ASSERT_S(gw->session->event_count < 120, "Reach event dispatch threshold (120 every 60 seconds)"); } else { - gw->timer->event = gw->timer->now; + gw->timer->event_last = gw->timer->now; gw->session->event_count = 0; } #endif @@ -296,7 +296,7 @@ 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); + gw->timer->ping_ms = (int)(gw->timer->now - gw->timer->hbeat_last); pthread_rwlock_unlock(&gw->timer->rwlock); logconf_trace(&gw->conf, "PING: %d ms", gw->timer->ping_ms); @@ -477,11 +477,11 @@ default_scheduler_cb(struct discord *a, } static int -on_io_poller_curl(struct io_poller *io, CURLM *mhandle, void *user_data) +_discord_on_gateway_perform(struct io_poller *io, CURLM *mhandle, void *p_gw) { (void)io; (void)mhandle; - return discord_gateway_perform(user_data); + return discord_gateway_perform(p_gw); } void @@ -505,7 +505,8 @@ discord_gateway_init(struct discord_gateway *gw, /* Web-Sockets handler */ gw->mhandle = curl_multi_init(); - io_poller_curlm_add(client->io_poller, gw->mhandle, on_io_poller_curl, gw); + io_poller_curlm_add(client->io_poller, gw->mhandle, + _discord_on_gateway_perform, gw); gw->ws = ws_init(&cbs, gw->mhandle, &attr); logconf_branch(&gw->conf, conf, "DISCORD_GATEWAY"); @@ -540,11 +541,12 @@ discord_gateway_init(struct discord_gateway *gw, void discord_gateway_cleanup(struct discord_gateway *gw) { - if (gw->timer->ping_timer) - discord_internal_timer_ctl( - CLIENT(gw, gw), - &(struct discord_timer){ .id = gw->timer->ping_timer, - .flags = DISCORD_TIMER_DELETE }); + if (gw->timer->hbeat_timer) + discord_internal_timer_ctl(CLIENT(gw, gw), + &(struct discord_timer){ + .id = gw->timer->hbeat_timer, + .flags = DISCORD_TIMER_DELETE, + }); /* cleanup WebSockets handle */ io_poller_curlm_del(CLIENT(gw, gw)->io_poller, gw->mhandle); curl_multi_cleanup(gw->mhandle); @@ -613,9 +615,9 @@ static int _ws_curl_debug_trace( CURL *handle, curl_infotype type, char *data, size_t size, void *userp) { - const char *text; (void)handle; (void)userp; + const char *text; switch (type) { case CURLINFO_TEXT: @@ -765,22 +767,9 @@ discord_gateway_end(struct discord_gateway *gw) CCORDcode discord_gateway_perform(struct discord_gateway *gw) { - /* check for pending transfer, exit on failure */ + /* check for pending transfer, exit if not running */ if (!ws_multi_socket_run(gw->ws, &gw->timer->now)) return CCORD_DISCORD_CONNECTION; - - /* client is in the process of shutting down */ - if (gw->session->status & DISCORD_SESSION_SHUTDOWN) return CCORD_OK; - - /* client is in the process of connecting */ - if (!gw->session->is_ready) return CCORD_OK; - - /* check if timespan since first pulse is greater than - * minimum heartbeat interval required */ - if (gw->timer->interval < gw->timer->now - gw->timer->hbeat) { - discord_gateway_send_heartbeat(gw, gw->payload.seq); - } - return CCORD_OK; } diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index 15c3ea77f..dc30a5f42 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -155,7 +155,7 @@ discord_gateway_send_identify(struct discord_gateway *gw, jsonb b; /* Ratelimit check */ - if (gw->timer->now - gw->timer->identify < 5) { + if (gw->timer->now - gw->timer->identify_last < 5) { ++gw->session->concurrent; VASSERT_S(gw->session->concurrent < gw->session->start_limit.max_concurrency, @@ -184,8 +184,9 @@ discord_gateway_send_identify(struct discord_gateway *gw, "SEND", ANSI_FG_BRIGHT_GREEN) " IDENTIFY (%d bytes) [@@@_%zu_@@@]", b.pos, info.loginfo.counter + 1); + /* get timestamp for this identify */ - gw->timer->identify = gw->timer->now; + gw->timer->identify_last = gw->timer->now; } else { logconf_info( @@ -234,13 +235,22 @@ discord_gateway_send_resume(struct discord_gateway *gw, } static void -on_ping_timer_cb(struct discord *client, struct discord_timer *timer) +_discord_on_heartbeat_timeout(struct discord *client, + struct discord_timer *timer) { (void)client; struct discord_gateway *gw = timer->data; + if (~timer->flags & DISCORD_TIMER_CANCELED) { - discord_gateway_perform(gw); - const u64unix_ms next_hb = gw->timer->hbeat + gw->timer->interval; + if (CCORD_OK == discord_gateway_perform(gw) + && ~gw->session->status & DISCORD_SESSION_SHUTDOWN + && gw->session->is_ready) + { + discord_gateway_send_heartbeat(gw, gw->payload.seq); + } + const u64unix_ms next_hb = + gw->timer->hbeat_last + gw->timer->hbeat_interval; + timer->interval = (int64_t)(next_hb) - (int64_t)discord_timestamp(client); if (timer->interval < 1) timer->interval = 1; @@ -275,12 +285,13 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) "SEND", ANSI_FG_BRIGHT_GREEN) " HEARTBEAT (%d bytes) [@@@_%zu_@@@]", b.pos, info.loginfo.counter + 1); + /* update heartbeat timestamp */ - gw->timer->hbeat = gw->timer->now; - if (!gw->timer->ping_timer) - gw->timer->ping_timer = - discord_internal_timer(CLIENT(gw, gw), on_ping_timer_cb, gw, - (int64_t)gw->timer->interval); + gw->timer->hbeat_last = gw->timer->now; + if (!gw->timer->hbeat_timer) + gw->timer->hbeat_timer = discord_internal_timer( + CLIENT(gw, gw), _discord_on_heartbeat_timeout, gw, + (int64_t)gw->timer->hbeat_interval); } else { logconf_info( @@ -316,8 +327,6 @@ discord_gateway_send_request_guild_members( ANSICOLOR("SEND", ANSI_FG_BRIGHT_GREEN) " REQUEST_GUILD_MEMBERS " "(%d bytes) [@@@_%zu_@@@]", b.pos, info.loginfo.counter + 1); - /* update heartbeat timestamp */ - gw->timer->hbeat = gw->timer->now; } else { logconf_info( @@ -357,9 +366,6 @@ discord_gateway_send_update_voice_state( "(%d bytes): %s channels [@@@_%zu_@@@]", b.pos, event->channel_id ? "join" : "leave", info.loginfo.counter + 1); - - /* update heartbeat timestamp */ - gw->timer->hbeat = gw->timer->now; } else { logconf_info( diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 0e78fa545..1f6d20dbe 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -385,14 +385,13 @@ discord_bucket_insert(struct discord_ratelimiter *rl, } static void -_discord_bucket_pop(struct discord_bucket *b) +_discord_bucket_request_select(struct discord_bucket *b) { QUEUE(struct discord_request) *qelem = QUEUE_HEAD(&b->queues.next); QUEUE_REMOVE(qelem); QUEUE_INIT(qelem); b->busy_req = QUEUE_DATA(qelem, struct discord_request, entry); - if (b->busy_req->b == NULL) abort(); } void @@ -421,7 +420,7 @@ discord_bucket_request_selector(struct discord_ratelimiter *rl, continue; } - _discord_bucket_pop(b); + _discord_bucket_request_select(b); (*iter)(data, b->busy_req); /* if bucket has no pending requests then remove it from From 51f77004b6f158f0c8e58530c2230f5ff05ec791 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 30 May 2022 13:14:54 -0300 Subject: [PATCH 096/118] refactor(discord-gateway): 'hbeat_interval' is not a timestamp, change its type u64unix_ms -> int64_t --- include/discord-internal.h | 10 +++++----- src/discord-gateway.c | 4 +--- src/discord-gateway_dispatch.c | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index f571c87cb..6f61dd4a9 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -674,16 +674,16 @@ struct discord_gateway { /** timers kept for synchronization */ struct { + /** + * fixed milliseconds interval between heartbeats + * @note obtained at `HELLO` + */ + int64_t hbeat_interval; /** * Gateway's concept of "now" * @note updated at discord_gateway_perform() */ u64unix_ms now; - /** - * fixed interval between heartbeats - * @note obtained at `HELLO` - */ - u64unix_ms hbeat_interval; /** * last heartbeat pulse timestamp * @note first sent at `READY` and `RESUME`, then updated every diff --git a/src/discord-gateway.c b/src/discord-gateway.c index e4d1f1f18..823572a82 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -67,12 +67,10 @@ on_hello(struct discord_gateway *gw) { jsmnf_pair *f; - gw->timer->hbeat_interval = 0; - if ((f = jsmnf_find(gw->payload.data, gw->payload.json, "heartbeat_interval", 18))) gw->timer->hbeat_interval = - strtoull(gw->payload.json + f->v.pos, NULL, 10); + strtoll(gw->payload.json + f->v.pos, NULL, 10); if (gw->session->status & DISCORD_SESSION_RESUMABLE) discord_gateway_send_resume(gw, &(struct discord_resume){ diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index dc30a5f42..a2bf8a6ed 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -249,7 +249,7 @@ _discord_on_heartbeat_timeout(struct discord *client, discord_gateway_send_heartbeat(gw, gw->payload.seq); } const u64unix_ms next_hb = - gw->timer->hbeat_last + gw->timer->hbeat_interval; + gw->timer->hbeat_last + (u64unix_ms)gw->timer->hbeat_interval; timer->interval = (int64_t)(next_hb) - (int64_t)discord_timestamp(client); @@ -291,7 +291,7 @@ discord_gateway_send_heartbeat(struct discord_gateway *gw, int seq) if (!gw->timer->hbeat_timer) gw->timer->hbeat_timer = discord_internal_timer( CLIENT(gw, gw), _discord_on_heartbeat_timeout, gw, - (int64_t)gw->timer->hbeat_interval); + gw->timer->hbeat_interval); } else { logconf_info( From 4727e0e516e5ba8167d0b09a82edc6e23fbeafaf Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Mon, 30 May 2022 14:35:23 -0300 Subject: [PATCH 097/118] fix(discord-gateway): session couldn't be resumed because it was missing the bot token from the payload * refactor: replace bot's 'token' field type with a 'char *' --- include/discord-internal.h | 10 ++++----- src/discord-client.c | 44 +++++++++++++++----------------------- src/discord-gateway.c | 25 ++++++++-------------- src/discord-rest.c | 4 ++-- src/discord-rest_request.c | 11 +++------- 5 files changed, 36 insertions(+), 58 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 6f61dd4a9..bc0aeaf55 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -493,7 +493,7 @@ struct discord_requestor { */ void discord_requestor_init(struct discord_requestor *rqtor, struct logconf *conf, - struct ccord_szbuf_readonly *token); + const char token[]); /** * @brief Free the request handler @@ -585,7 +585,7 @@ struct discord_rest { */ void discord_rest_init(struct discord_rest *rest, struct logconf *conf, - struct ccord_szbuf_readonly *token); + const char token[]); /** * @brief Free an REST handle @@ -690,7 +690,7 @@ struct discord_gateway { * `hbeat_interval` */ u64unix_ms hbeat_last; - /** + /** * timestamp of last succesful identify request * @note updated at discord_gateway_send_identify() */ @@ -777,7 +777,7 @@ struct discord_gateway { */ void discord_gateway_init(struct discord_gateway *gw, struct logconf *conf, - struct ccord_szbuf_readonly *token); + const char token[]); /** * @brief Free a Gateway handle @@ -1126,7 +1126,7 @@ struct discord { /** whether this is the original client or a clone */ bool is_original; /** the bot token */ - struct ccord_szbuf_readonly token; + char *token; /** the io poller for listening to file descriptors */ struct io_poller *io_poller; diff --git a/src/discord-client.c b/src/discord-client.c index 5a88a50ec..85e4d00a9 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -19,24 +19,20 @@ _discord_init(struct discord *new_client) discord_refcounter_init(&new_client->refcounter, &new_client->conf); discord_message_commands_init(&new_client->commands, &new_client->conf); - discord_rest_init(&new_client->rest, &new_client->conf, - &new_client->token); + discord_rest_init(&new_client->rest, &new_client->conf, new_client->token); discord_gateway_init(&new_client->gw, &new_client->conf, - &new_client->token); + new_client->token); #ifdef CCORD_VOICE discord_voice_connections_init(new_client); #endif - /* fetch the client user structure */ - if (new_client->token.size) { - struct discord_ret_user ret = { 0 }; - CCORDcode code; - - ret.sync = &new_client->self; - code = discord_get_current_user(new_client, &ret); + if (new_client->token) { /* fetch client's user structure */ + CCORDcode code = + discord_get_current_user(new_client, &(struct discord_ret_user){ + .sync = &new_client->self, + }); ASSERT_S(CCORD_OK == code, "Couldn't fetch client's user object"); } - new_client->is_original = true; } @@ -50,8 +46,7 @@ discord_init(const char token[]) /* silence terminal input by default */ logconf_set_quiet(&new_client->conf, true); - new_client->token.start = (char *)token; - new_client->token.size = token ? cog_str_bounds_check(token, 128) : 0; + if (token && *token) cog_strndup(token, strlen(token), &new_client->token); _discord_init(new_client); @@ -61,9 +56,8 @@ discord_init(const char token[]) struct discord * discord_config_init(const char config_file[]) { - char *path[2] = { "discord", "" }; + struct ccord_szbuf_readonly field; struct discord *new_client; - struct logconf_field field; FILE *fp; fp = fopen(config_file, "rb"); @@ -75,21 +69,16 @@ discord_config_init(const char config_file[]) fclose(fp); - path[1] = "token"; - field = logconf_get_field(&new_client->conf, path, - sizeof(path) / sizeof *path); - if (!strncmp("YOUR-BOT-TOKEN", field.start, field.size)) - memset(&new_client->token, 0, sizeof(new_client->token)); - else { - new_client->token.start = field.start; - new_client->token.size = field.size; - } + field = discord_config_get_field(new_client, + (char *[2]){ "discord", "token" }, 2); + if (field.size && 0 != strncmp("YOUR-BOT-TOKEN", field.start, field.size)) + cog_strndup(field.start, field.size, &new_client->token); + _discord_init(new_client); /* check for default prefix in config file */ - path[1] = "default_prefix"; - field = logconf_get_field(&new_client->conf, path, - sizeof(path) / sizeof *path); + field = discord_config_get_field( + new_client, (char *[2]){ "discord", "default_prefix" }, 2); if (field.size) { jsmn_parser parser; jsmntok_t tokens[16]; @@ -184,6 +173,7 @@ discord_cleanup(struct discord *client) discord_timers_cleanup(client, &client->timers.user); discord_timers_cleanup(client, &client->timers.internal); logconf_cleanup(&client->conf); + if (client->token) free(client->token); } free(client); } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 823572a82..add41e37c 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -74,6 +74,7 @@ on_hello(struct discord_gateway *gw) if (gw->session->status & DISCORD_SESSION_RESUMABLE) discord_gateway_send_resume(gw, &(struct discord_resume){ + .token = gw->id.token, .session_id = gw->session->id, .seq = gw->payload.seq, }); @@ -485,21 +486,16 @@ _discord_on_gateway_perform(struct io_poller *io, CURLM *mhandle, void *p_gw) void discord_gateway_init(struct discord_gateway *gw, struct logconf *conf, - struct ccord_szbuf_readonly *token) + const char token[]) { struct discord *client = CLIENT(gw, gw); - /* Web-Sockets callbacks */ - struct ws_callbacks cbs = { 0 }; + struct ws_callbacks cbs = { .data = gw, + .on_connect = &on_connect_cb, + .on_text = &on_text_cb, + .on_close = &on_close_cb }; /* Web-Sockets custom attributes */ - struct ws_attr attr = { 0 }; - - cbs.data = gw; - cbs.on_connect = &on_connect_cb; - cbs.on_text = &on_text_cb; - cbs.on_close = &on_close_cb; - - attr.conf = conf; + struct ws_attr attr = { .conf = conf }; /* Web-Sockets handler */ gw->mhandle = curl_multi_init(); @@ -515,17 +511,15 @@ discord_gateway_init(struct discord_gateway *gw, /* client connection status */ gw->session = calloc(1, sizeof *gw->session); gw->session->retry.enable = true; - gw->session->retry.limit = 5; /* TODO: shouldn't be a hard limit */ + gw->session->retry.limit = 5; /* FIXME: shouldn't be a hard limit */ /* connection identify token */ - cog_strndup(token->start, token->size, &gw->id.token); - + gw->id.token = (char *)token; /* connection identify properties */ gw->id.properties = calloc(1, sizeof *gw->id.properties); gw->id.properties->os = OSNAME; gw->id.properties->browser = "concord"; gw->id.properties->device = "concord"; - /* the bot initial presence */ gw->id.presence = calloc(1, sizeof *gw->id.presence); gw->id.presence->status = "online"; @@ -553,7 +547,6 @@ discord_gateway_cleanup(struct discord_gateway *gw) pthread_rwlock_destroy(&gw->timer->rwlock); free(gw->timer); /* cleanup bot identification */ - if (gw->id.token) free(gw->id.token); free(gw->id.properties); free(gw->id.presence); /* cleanup client session */ diff --git a/src/discord-rest.c b/src/discord-rest.c index e401ae22d..5ab05c4c7 100644 --- a/src/discord-rest.c +++ b/src/discord-rest.c @@ -60,9 +60,9 @@ _discord_on_rest_perform(struct io_poller *io, CURLM *mhandle, void *p_rest) void discord_rest_init(struct discord_rest *rest, struct logconf *conf, - struct ccord_szbuf_readonly *token) + const char token[]) { - if (!token->size) + if (!token || !*token) logconf_branch(&rest->conf, conf, "DISCORD_WEBHOOK"); else logconf_branch(&rest->conf, conf, "DISCORD_HTTP"); diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 97f8d745d..88823ce2e 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -22,16 +22,11 @@ _discord_request_cleanup(struct discord_request *req) static void _discord_on_curl_setopt(struct ua_conn *conn, void *p_token) { - struct ccord_szbuf *token = p_token; char auth[128]; - int len; - - len = snprintf(auth, sizeof(auth), "Bot %.*s", (int)token->size, - token->start); + int len = snprintf(auth, sizeof(auth), "Bot %s", (char *)p_token); ASSERT_NOT_OOB(len, sizeof(auth)); ua_conn_add_header(conn, "Authorization", auth); - #ifdef CCORD_DEBUG_HTTP curl_easy_setopt(ua_conn_get_easy_handle(conn), CURLOPT_VERBOSE, 1L); #endif @@ -40,13 +35,13 @@ _discord_on_curl_setopt(struct ua_conn *conn, void *p_token) void discord_requestor_init(struct discord_requestor *rqtor, struct logconf *conf, - struct ccord_szbuf_readonly *token) + const char token[]) { logconf_branch(&rqtor->conf, conf, "DISCORD_REQUEST"); rqtor->ua = ua_init(&(struct ua_attr){ .conf = conf }); ua_set_url(rqtor->ua, DISCORD_API_BASE_URL); - ua_set_opt(rqtor->ua, token, &_discord_on_curl_setopt); + ua_set_opt(rqtor->ua, (char *)token, &_discord_on_curl_setopt); /* queues are malloc'd to guarantee a client cloned by * discord_clone() will share the same queue with the original */ From 44d228fdc370e04b00f319199e3afb701842fac3 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 11:48:15 -0300 Subject: [PATCH 098/118] fix: deal with request retries from the REST thread --- include/discord-internal.h | 2 - src/discord-rest_ratelimit.c | 2 +- src/discord-rest_request.c | 181 ++++++++++++++++------------------- 3 files changed, 83 insertions(+), 102 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index bc0aeaf55..0a1aa7ac8 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -433,8 +433,6 @@ struct discord_request { CCORDcode code; /** how long to wait for in case of request being ratelimited */ int64_t wait_ms; - /** whether this request should be retried */ - bool retry; /** current retry attempt (stop at rest->retry_limit) */ int retry_attempt; diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index 1f6d20dbe..e60cdcee2 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -441,7 +441,7 @@ discord_bucket_request_unselect(struct discord_ratelimiter *rl, ASSERT_S(req == b->busy_req, "Attempt to unlock a bucket with a non-busy request"); - if (!req->retry && QUEUE_EMPTY(&b->queues.next)) { + if (QUEUE_EMPTY(&b->queues.next)) { QUEUE_REMOVE(&b->entry); QUEUE_INIT(&b->entry); } diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 88823ce2e..97ae4a08c 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -161,7 +161,7 @@ _discord_request_to_multipart(curl_mime *mime, void *p_req) } } -static void +static bool _discord_request_info_extract(struct discord_requestor *rqtor, struct discord_request *req, struct ua_info *info) @@ -169,79 +169,73 @@ _discord_request_info_extract(struct discord_requestor *rqtor, ua_info_extract(req->conn, info); if (info->code != CCORD_HTTP_CODE) { /* CCORD_OK or internal error */ - req->retry = false; req->code = info->code; + return false; } - else { - switch (info->httpcode) { - case HTTP_FORBIDDEN: - case HTTP_NOT_FOUND: - case HTTP_BAD_REQUEST: - req->retry = false; - req->code = CCORD_DISCORD_JSON_CODE; - break; - case HTTP_UNAUTHORIZED: - logconf_fatal( - &rqtor->conf, - "UNAUTHORIZED: Please provide a valid authentication token"); - req->retry = false; - req->code = CCORD_DISCORD_BAD_AUTH; - break; - case HTTP_METHOD_NOT_ALLOWED: - logconf_fatal( - &rqtor->conf, - "METHOD_NOT_ALLOWED: The server couldn't recognize the " - "received HTTP method"); - req->retry = false; - req->code = info->code; - break; - case HTTP_TOO_MANY_REQUESTS: { - struct ua_szbuf_readonly body = ua_info_get_body(info); - struct jsmnftok message = { 0 }; - double retry_after = 1.0; - bool is_global = false; - jsmn_parser parser; - jsmntok_t tokens[16]; - - jsmn_init(&parser); - if (0 < jsmn_parse(&parser, body.start, body.size, tokens, - sizeof(tokens) / sizeof *tokens)) + + switch (info->httpcode) { + case HTTP_FORBIDDEN: + case HTTP_NOT_FOUND: + case HTTP_BAD_REQUEST: + req->code = CCORD_DISCORD_JSON_CODE; + return false; + case HTTP_UNAUTHORIZED: + logconf_fatal( + &rqtor->conf, + "UNAUTHORIZED: Please provide a valid authentication token"); + req->code = CCORD_DISCORD_BAD_AUTH; + return false; + case HTTP_METHOD_NOT_ALLOWED: + logconf_fatal(&rqtor->conf, + "METHOD_NOT_ALLOWED: The server couldn't recognize the " + "received HTTP method"); + + req->code = info->code; + return false; + case HTTP_TOO_MANY_REQUESTS: { + struct ua_szbuf_readonly body = ua_info_get_body(info); + struct jsmnftok message = { 0 }; + double retry_after = 1.0; + bool is_global = false; + jsmn_parser parser; + jsmntok_t tokens[16]; + + jsmn_init(&parser); + if (0 < jsmn_parse(&parser, body.start, body.size, tokens, + sizeof(tokens) / sizeof *tokens)) + { + jsmnf_loader loader; + jsmnf_pair pairs[16]; + + jsmnf_init(&loader); + if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, + pairs, sizeof(pairs) / sizeof *pairs)) { - jsmnf_loader loader; - jsmnf_pair pairs[16]; - - jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, body.start, tokens, parser.toknext, - pairs, sizeof(pairs) / sizeof *pairs)) - { - jsmnf_pair *f; - - if ((f = jsmnf_find(pairs, body.start, "global", 6))) - is_global = ('t' == body.start[f->v.pos]); - if ((f = jsmnf_find(pairs, body.start, "message", 7))) - message = f->v; - if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) - retry_after = strtod(body.start + f->v.pos, NULL); - } + jsmnf_pair *f; + + if ((f = jsmnf_find(pairs, body.start, "global", 6))) + is_global = ('t' == body.start[f->v.pos]); + if ((f = jsmnf_find(pairs, body.start, "message", 7))) + message = f->v; + if ((f = jsmnf_find(pairs, body.start, "retry_after", 11))) + retry_after = strtod(body.start + f->v.pos, NULL); } + } - req->wait_ms = (int64_t)(1000 * retry_after); - if (req->wait_ms < 0) req->wait_ms = 0; + req->wait_ms = (int64_t)(1000 * retry_after); + if (req->wait_ms < 0) req->wait_ms = 0; - logconf_warn(&rqtor->conf, - "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", - is_global ? "GLOBAL " : "", req->wait_ms, message.len, - body.start + message.pos); + logconf_warn(&rqtor->conf, + "429 %sRATELIMITING (wait: %" PRId64 " ms) : %.*s", + is_global ? "GLOBAL " : "", req->wait_ms, message.len, + body.start + message.pos); - req->retry = true; - req->code = info->code; - break; - } - default: - req->retry = (info->httpcode >= 500); /* retry if Server Error */ - req->code = info->code; - break; - } + req->code = info->code; + return true; + } + default: + req->code = info->code; + return (info->httpcode >= 500); /* retry if Server Error */ } } @@ -258,8 +252,7 @@ static bool _discord_request_retry(struct discord_requestor *rqtor, struct discord_request *req) { - if (!req->retry || req->retry_attempt++ >= rqtor->retry_limit) - return false; + if (req->retry_attempt++ >= rqtor->retry_limit) return false; ua_conn_reset(req->conn); @@ -322,12 +315,8 @@ _discord_request_dispatch_response(struct discord_requestor *rqtor, discord_refcounter_decr(&client->refcounter, req->response.data); } } - - /* enqueue request for retry or recycle */ - if (!_discord_request_retry(rqtor, req)) - discord_request_cancel(rqtor, req); - - discord_bucket_request_unselect(&rqtor->ratelimiter, req->b, req); + /* enqueue request for recycle */ + discord_request_cancel(rqtor, req); return resp.code; } @@ -361,7 +350,6 @@ discord_requestor_dispatch_responses(struct discord_requestor *rqtor) CCORDcode discord_requestor_info_read(struct discord_requestor *rqtor) { - CCORDcode code; int alive = 0; if (CURLM_OK != curl_multi_socket_all(rqtor->mhandle, &alive)) @@ -377,6 +365,7 @@ discord_requestor_info_read(struct discord_requestor *rqtor) if (CURLMSG_DONE == msg->msg) { const CURLcode ecode = msg->data.result; struct discord_request *req; + bool retry = false; curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &req); curl_multi_remove_handle(rqtor->mhandle, msg->easy_handle); @@ -386,7 +375,7 @@ discord_requestor_info_read(struct discord_requestor *rqtor) struct ua_szbuf_readonly body; struct ua_info info; - _discord_request_info_extract(rqtor, req, &info); + retry = _discord_request_info_extract(rqtor, req, &info); body = ua_info_get_body(&info); if (info.code != CCORD_OK) { @@ -421,40 +410,34 @@ discord_requestor_info_read(struct discord_requestor *rqtor) ua_info_cleanup(&info); } break; - case CURLE_READ_ERROR: + default: logconf_warn(&rqtor->conf, "%s (CURLE code: %d)", curl_easy_strerror(ecode), ecode); - req->retry = true; - req->code = CCORD_CURLE_INTERNAL; - - break; - default: - logconf_error(&rqtor->conf, "%s (CURLE code: %d)", - curl_easy_strerror(ecode), ecode); - - req->retry = false; + retry = (ecode == CURLE_READ_ERROR); req->code = CCORD_CURLE_INTERNAL; - break; } - code = req->code; + if (!retry || !_discord_request_retry(rqtor, req)) { + discord_bucket_request_unselect(&rqtor->ratelimiter, req->b, + req); - if (req->dispatch.sync) { - pthread_mutex_lock(&rqtor->qlocks->pending); - pthread_cond_signal(req->cond); - pthread_mutex_unlock(&rqtor->qlocks->pending); - } - else { - pthread_mutex_lock(&rqtor->qlocks->finished); - QUEUE_INSERT_TAIL(&rqtor->queues->finished, &req->entry); - pthread_mutex_unlock(&rqtor->qlocks->finished); + if (req->dispatch.sync) { + pthread_mutex_lock(&rqtor->qlocks->pending); + pthread_cond_signal(req->cond); + pthread_mutex_unlock(&rqtor->qlocks->pending); + } + else { + pthread_mutex_lock(&rqtor->qlocks->finished); + QUEUE_INSERT_TAIL(&rqtor->queues->finished, &req->entry); + pthread_mutex_unlock(&rqtor->qlocks->finished); + } } } } - return code; + return CCORD_OK; } static void From 3d5ebebbe9599f8cf3270f853e996508fd42bdd2 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 12:35:30 -0300 Subject: [PATCH 099/118] fix(discord-rest_request.c): OpenBSD compliant pthread_cond_t init solution --- src/discord-rest_request.c | 52 ++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 97ae4a08c..6ac1bd5df 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -239,30 +239,6 @@ _discord_request_info_extract(struct discord_requestor *rqtor, } } -/** - * @brief If request can be retried then it will be moved back to its - * bucket's queue - * @note this **MUST** be called only after discord_request_info_extract() - * - * @param rqtor the requestor handle initialized with discord_requestor_init() - * @param req the request to be checked for retry - * @return `true` if request has been enqueued for retry - */ -static bool -_discord_request_retry(struct discord_requestor *rqtor, - struct discord_request *req) -{ - if (req->retry_attempt++ >= rqtor->retry_limit) return false; - - ua_conn_reset(req->conn); - - /* FIXME: wait_ms > 0 should be dealt with aswell */ - if (req->wait_ms <= 0) - discord_bucket_insert(&rqtor->ratelimiter, req->b, req, true); - - return true; -} - void discord_request_cancel(struct discord_requestor *rqtor, struct discord_request *req) @@ -345,6 +321,30 @@ discord_requestor_dispatch_responses(struct discord_requestor *rqtor) } } +/** + * @brief If request can be retried then it will be moved back to its + * bucket's queue + * @note this **MUST** be called only after discord_request_info_extract() + * + * @param rqtor the requestor handle initialized with discord_requestor_init() + * @param req the request to be checked for retry + * @return `true` if request has been enqueued for retry + */ +static bool +_discord_request_retry(struct discord_requestor *rqtor, + struct discord_request *req) +{ + if (req->retry_attempt++ >= rqtor->retry_limit) return false; + + ua_conn_reset(req->conn); + + /* FIXME: wait_ms > 0 should be dealt with aswell */ + if (req->wait_ms <= 0) + discord_bucket_insert(&rqtor->ratelimiter, req->b, req, true); + + return true; +} + /* parse request response and prepare callback that should be triggered * at _discord_rest_run_request_callback() */ CCORDcode @@ -611,8 +611,10 @@ discord_request_begin(struct discord_requestor *rqtor, pthread_mutex_unlock(&rqtor->qlocks->pending); } else { /* wait for request's completion if sync mode is active */ - req->cond = &(pthread_cond_t)PTHREAD_COND_INITIALIZER; + req->cond = &(pthread_cond_t){ 0 }; + pthread_cond_init(req->cond, NULL); pthread_cond_wait(req->cond, &rqtor->qlocks->pending); + pthread_cond_destroy(req->cond); req->cond = NULL; pthread_mutex_unlock(&rqtor->qlocks->pending); From 28d3dab61067a4caab49f21bb47d77f23a2c9163 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 13:58:04 -0300 Subject: [PATCH 100/118] refactor(discord-rest_request.c): replace 3d5ebe with a stackful solution --- src/discord-rest_request.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index 6ac1bd5df..e12d7794c 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -611,10 +611,9 @@ discord_request_begin(struct discord_requestor *rqtor, pthread_mutex_unlock(&rqtor->qlocks->pending); } else { /* wait for request's completion if sync mode is active */ - req->cond = &(pthread_cond_t){ 0 }; - pthread_cond_init(req->cond, NULL); + pthread_cond_t temp_cond = PTHREAD_COND_INITIALIZER; + req->cond = &temp_cond; pthread_cond_wait(req->cond, &rqtor->qlocks->pending); - pthread_cond_destroy(req->cond); req->cond = NULL; pthread_mutex_unlock(&rqtor->qlocks->pending); From 40db37c8ce797d423bafc5f8da9aee1de3821e79 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 15:18:37 -0300 Subject: [PATCH 101/118] wip: rename and move core/work* -> src/discord-worker.c and include/discord-worker.h --- Makefile | 4 +-- core/work.h | 37 ------------------------ include/discord-internal.h | 2 -- include/discord-worker.h | 44 +++++++++++++++++++++++++++++ src/concord-once.c | 11 ++++---- src/discord-gateway.c | 14 +++++---- src/discord-rest_request.c | 6 ++-- core/work.c => src/discord-worker.c | 11 +++++--- 8 files changed, 69 insertions(+), 60 deletions(-) delete mode 100644 core/work.h create mode 100644 include/discord-worker.h rename core/work.c => src/discord-worker.c (83%) diff --git a/Makefile b/Makefile index c0e4594e2..fe1ff5f98 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,7 @@ COGUTILS_OBJS = $(COGUTILS_DIR)/cog-utils.o \ $(COGUTILS_DIR)/logconf.o \ $(COGUTILS_DIR)/json-build.o \ $(COGUTILS_DIR)/jsmn-find.o -CORE_OBJS = $(CORE_DIR)/work.o \ - $(CORE_DIR)/user-agent.o \ +CORE_OBJS = $(CORE_DIR)/user-agent.o \ $(CORE_DIR)/websockets.o \ $(CORE_DIR)/io_poller.o THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ @@ -42,6 +41,7 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/discord-messagecommands.o \ $(SRC_DIR)/discord-timer.o \ $(SRC_DIR)/discord-misc.o \ + $(SRC_DIR)/discord-worker.o \ $(SRC_DIR)/application_command.o \ $(SRC_DIR)/interaction.o \ $(SRC_DIR)/audit_log.o \ diff --git a/core/work.h b/core/work.h deleted file mode 100644 index b06645c9f..000000000 --- a/core/work.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file work.h - */ - -#ifndef WORK_H -#define WORK_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/** - * @brief Initialize global threadpool and priority queue - * @return `0` on success, `1` if it has already been initialized - */ -int work_global_init(void); - -/** - * @brief Cleanup global threadpool and priority queue - */ -void work_global_cleanup(void); - -/** - * @brief Run a callback from a worker thread - * - * @param callback user callback to be executed - * @param data user data to be passed to callback - * @return 0 if all goes well, negative values in case of error (see - * threadpool.h for codes) - */ -int work_run(void (*callback)(void *data), void *data); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* WORK_H */ diff --git a/include/discord-internal.h b/include/discord-internal.h index 0a1aa7ac8..9b7c01065 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -20,10 +20,8 @@ #include "logconf.h" #include "user-agent.h" #include "websockets.h" -#include "work.h" #include "cog-utils.h" #include "io_poller.h" - #include "queue.h" #include "priority_queue.h" diff --git a/include/discord-worker.h b/include/discord-worker.h new file mode 100644 index 000000000..7d68cf303 --- /dev/null +++ b/include/discord-worker.h @@ -0,0 +1,44 @@ +/** + * @file discord-worker.h + * @author Cogmasters + * @brief Global threadpool + */ + +#ifndef DISCORD_WORKER_H +#define DISCORD_WORKER_H + +#include "error.h" + +/* forward declaration */ +struct discord; +/**/ + +/** @defgroup DiscordInternalWorker Global threadpool + * @ingroup DiscordInternal + * @brief A global threadpool for worker-threads handling + * @{ */ + +/** + * @brief Initialize global threadpool and priority queue + * @return `0` on success, `1` if it has already been initialized + */ +int discord_worker_global_init(void); + +/** @brief Cleanup global threadpool and priority queue */ +void discord_worker_global_cleanup(void); + +/** + * @brief Run a callback from a worker thread + * + * @param client the client that will be using the worker thread + * @param callback user callback to be executed + * @param data user data to be passed to callback + * @CCORD_return + */ +CCORDcode discord_worker_add(struct discord *client, + void (*callback)(void *data), + void *data); + +/** @} DiscordInternalWorker */ + +#endif /* DISCORD_WORKER_H */ diff --git a/src/concord-once.c b/src/concord-once.c index 09aba9c08..7714ac8e3 100644 --- a/src/concord-once.c +++ b/src/concord-once.c @@ -2,7 +2,7 @@ #include #include "error.h" -#include "work.h" +#include "discord-worker.h" /* if set to 1 then client(s) will be disconnected */ int ccord_has_sigint = 0; @@ -12,7 +12,7 @@ static int once; #ifdef CCORD_SIGINTCATCH /* shutdown gracefully on SIGINT received */ static void -sigint_handler(int signum) +_ccord_sigint_handler(int signum) { (void)signum; fputs("\nSIGINT: Disconnecting running concord client(s) ...\n", stderr); @@ -28,13 +28,13 @@ ccord_global_init() } else { #ifdef CCORD_SIGINTCATCH - signal(SIGINT, &sigint_handler); + signal(SIGINT, &_ccord_sigint_handler); #endif if (0 != curl_global_init(CURL_GLOBAL_DEFAULT)) { fputs("Couldn't start libcurl's globals\n", stderr); return CCORD_GLOBAL_INIT; } - if (work_global_init()) { + if (discord_worker_global_init()) { fputs("Attempt duplicate global initialization\n", stderr); return CCORD_GLOBAL_INIT; } @@ -47,6 +47,7 @@ void ccord_global_cleanup() { curl_global_cleanup(); - work_global_cleanup(); + discord_worker_global_cleanup(); once = 0; + ccord_has_sigint = 0; } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index add41e37c..8c9a7359e 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -4,6 +4,7 @@ #include "discord.h" #include "discord-internal.h" +#include "discord-worker.h" #include "osname.h" /* return enumerator as string in case of a match */ @@ -238,10 +239,11 @@ on_dispatch(struct discord_gateway *gw) break; case DISCORD_EVENT_WORKER_THREAD: { struct discord_gateway *clone = _discord_gateway_clone(gw); - int ret = work_run(&_discord_gateway_dispatch_thread, clone); + CCORDcode code = discord_worker_add( + CLIENT(clone, gw), &_discord_gateway_dispatch_thread, clone); - if (ret != 0) { - log_error("Couldn't execute worker-thread (code %d)", ret); + if (code != CCORD_OK) { + log_error("Couldn't schedule worker-thread (code %d)", code); _discord_gateway_clone_cleanup(clone); } } break; @@ -759,9 +761,9 @@ CCORDcode discord_gateway_perform(struct discord_gateway *gw) { /* check for pending transfer, exit if not running */ - if (!ws_multi_socket_run(gw->ws, &gw->timer->now)) - return CCORD_DISCORD_CONNECTION; - return CCORD_OK; + return !ws_multi_socket_run(gw->ws, &gw->timer->now) + ? CCORD_DISCORD_CONNECTION + : CCORD_OK; } void diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index e12d7794c..fb8058c7b 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -569,10 +569,8 @@ discord_request_begin(struct discord_requestor *rqtor, if (attr->attachments.size) _discord_attachments_dup(&req->attachments, &attr->attachments); - if (body) { - /* copy request body */ - if (body->size > req->body.realsize) { - /* needs to increase buffer size */ + if (body) { /* copy request body */ + if (body->size > req->body.realsize) { /* buffer needs a resize */ void *tmp = realloc(req->body.start, body->size); ASSERT_S(tmp != NULL, "Out of memory"); diff --git a/core/work.c b/src/discord-worker.c similarity index 83% rename from core/work.c rename to src/discord-worker.c index 24a155fd2..6771ed284 100644 --- a/core/work.c +++ b/src/discord-worker.c @@ -3,7 +3,7 @@ #include #include -#include "work.h" +#include "discord-worker.h" #include "threadpool.h" /** true after threadpool initialization */ @@ -13,7 +13,7 @@ static _Bool once; static threadpool_t *tpool; int -work_global_init(void) +discord_worker_global_init(void) { static int nthreads = 0; static int queue_size = 0; @@ -48,13 +48,16 @@ work_global_init(void) } int -work_run(void (*callback)(void *data), void *data) +discord_worker_add(struct discord *client, + void (*callback)(void *data), + void *data) { + (void)client; return threadpool_add(tpool, callback, data, 0); } void -work_global_cleanup(void) +discord_worker_global_cleanup(void) { /* cleanup thread-pool manager */ threadpool_destroy(tpool, threadpool_graceful); From e8b35dc9d90cc15558c44ffc262f079784ee9c4d Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 20:51:05 -0300 Subject: [PATCH 102/118] feat(discord-worker): add discord_worker_join() --- core/error.h | 4 +- include/discord-worker.h | 8 ++++ src/discord-worker.c | 83 +++++++++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 24 deletions(-) diff --git a/core/error.h b/core/error.h index c5a240bd8..d6e16e9ff 100644 --- a/core/error.h +++ b/core/error.h @@ -32,8 +32,8 @@ typedef int CCORDcode; #define CCORD_OWNERSHIP -9 /** couldn't perform action because resource is unavailable */ #define CCORD_UNAVAILABLE -10 -/** couldn't enqueue request (queue is full) */ -#define CCORD_FULL_QUEUE -11 +/** couldn't enqueue worker thread (queue is full) */ +#define CCORD_FULL_WORKER -11 /** @} ConcordError */ diff --git a/include/discord-worker.h b/include/discord-worker.h index 7d68cf303..e8b341616 100644 --- a/include/discord-worker.h +++ b/include/discord-worker.h @@ -39,6 +39,14 @@ CCORDcode discord_worker_add(struct discord *client, void (*callback)(void *data), void *data); +/** + * @brief Wait until worker-threads being used by `client` have been joined + * + * @param client the client currently using a worker thread + * @CCORD_return + */ +CCORDcode discord_worker_join(struct discord *client); + /** @} DiscordInternalWorker */ #endif /* DISCORD_WORKER_H */ diff --git a/src/discord-worker.c b/src/discord-worker.c index 6771ed284..d4381a016 100644 --- a/src/discord-worker.c +++ b/src/discord-worker.c @@ -3,63 +3,102 @@ #include #include -#include "discord-worker.h" #include "threadpool.h" +#include "discord.h" +#include "discord-internal.h" +#include "discord-worker.h" + /** true after threadpool initialization */ static _Bool once; -/** request thread and optional callback execution thread */ -static threadpool_t *tpool; +/** global threadpool manager */ +threadpool_t *g_tpool; int discord_worker_global_init(void) { - static int nthreads = 0; - static int queue_size = 0; + static int nthreads; + static int queue_size; const char *val; char *p_end; if (once) return 1; /* get threadpool thread amount */ - val = getenv("CCORD_THREADPOOL_SIZE"); - if (val != NULL) { - nthreads = (int)strtol(val, &p_end, 10); - } - if (nthreads < 2 || ERANGE == errno || p_end == val) { - nthreads = 2; + if (!nthreads) { + if ((val = getenv("CCORD_THREADPOOL_SIZE"))) + nthreads = (int)strtol(val, &p_end, 10); + if (nthreads < 2 || ERANGE == errno || p_end == val) nthreads = 2; } /* get threadpool queue size */ - val = getenv("CCORD_THREADPOOL_QUEUE_SIZE"); - if (val != NULL) { - queue_size = (int)strtol(val, &p_end, 10); - } - if (queue_size < 8 || ERANGE == errno || p_end == val) { - queue_size = 8; + if (!queue_size) { + if ((val = getenv("CCORD_THREADPOOL_QUEUE_SIZE"))) + queue_size = (int)strtol(val, &p_end, 10); + if (queue_size < 8 || ERANGE == errno || p_end == val) queue_size = 8; } /* initialize threadpool */ - tpool = threadpool_create(nthreads, queue_size, 0); + g_tpool = threadpool_create(nthreads, queue_size, 0); once = 1; return 0; } -int +struct discord_worker_context { + struct discord *client; + void *data; + void (*callback)(void *data); +}; + +static void +_discord_worker_cb(void *p_cxt) +{ + struct discord_worker_context *cxt = p_cxt; + + pthread_mutex_lock(&cxt->client->workers->lock); + ++cxt->client->workers->count; + pthread_mutex_unlock(&cxt->client->workers->lock); + + cxt->callback(cxt->data); + + pthread_mutex_lock(&cxt->client->workers->lock); + --cxt->client->workers->count; + pthread_cond_signal(&cxt->client->workers->cond); + pthread_mutex_unlock(&cxt->client->workers->lock); + + free(cxt); +} + +CCORDcode discord_worker_add(struct discord *client, void (*callback)(void *data), void *data) { - (void)client; - return threadpool_add(tpool, callback, data, 0); + struct discord_worker_context *cxt = malloc(sizeof *cxt); + *cxt = (struct discord_worker_context){ client, data, callback }; + + return 0 == threadpool_add(g_tpool, _discord_worker_cb, cxt, 0) + ? CCORD_OK + : CCORD_FULL_WORKER; +} + +CCORDcode +discord_worker_join(struct discord *client) +{ + pthread_mutex_lock(&client->workers->lock); + while (client->workers->count != 0) { + pthread_cond_wait(&client->workers->cond, &client->workers->lock); + } + pthread_mutex_unlock(&client->workers->lock); + return CCORD_OK; } void discord_worker_global_cleanup(void) { /* cleanup thread-pool manager */ - threadpool_destroy(tpool, threadpool_graceful); + threadpool_destroy(g_tpool, threadpool_graceful); once = 0; } From 5d31ab10ab2214ae3c89ebcecd770f7a34ee5eae Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 20:53:06 -0300 Subject: [PATCH 103/118] refactor!(discord-response.h): rename field high_p -> high_priority and document struct discord_response --- include/discord-response.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/discord-response.h b/include/discord-response.h index bdacd9356..e32f9611c 100644 --- a/include/discord-response.h +++ b/include/discord-response.h @@ -8,9 +8,13 @@ #ifndef DISCORD_RESPONSE_H #define DISCORD_RESPONSE_H +/** @brief The response for the completed request */ struct discord_response { + /** user arbitrary data provided at @ref discord_ret */ void *data; + /** kept concord's parameter provided at @ref discord_ret */ const void *keep; + /** request completion status @see @ref ConcordError */ CCORDcode code; }; @@ -22,7 +26,7 @@ struct discord_response { * @brief Macro containing common fields for `struct discord_ret*` datatypes * @note this exists for alignment purposes */ -#define DISCORD_RET_DEFAULT_FIELDS \ +#define DISCORD_RET_DEFAULT_FIELDS \ /** user arbitrary data to be passed to `done` or `fail` callbacks */ \ void *data; \ /** cleanup method to be called for `data`, once its no longer \ @@ -32,14 +36,14 @@ struct discord_response { const void *keep; \ /** if `true` then request will be prioritized over already enqueued \ requests */ \ - bool high_p; \ + bool high_priority; \ /** optional callback to be executed on a failed request */ \ void (*fail)(struct discord * client, struct discord_response * resp) -#define DISCORD_RETURN(_type) \ +#define DISCORD_RETURN(_type) \ /** @brief Request's return context */ \ struct discord_ret_##_type { \ - DISCORD_RET_DEFAULT_FIELDS; \ + DISCORD_RET_DEFAULT_FIELDS; \ /** optional callback to be executed on a successful request */ \ void (*done)(struct discord * client, \ struct discord_response *resp, \ From bf5b82db9b5b1fd2d81935829b4593ab5f71a2a1 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 21:00:11 -0300 Subject: [PATCH 104/118] fix: wait for client's worker threads completion before cleaning up its resources --- include/discord-internal.h | 16 +++++++++++++--- src/discord-client.c | 13 +++++++++++-- src/discord-gateway.c | 8 ++++---- src/discord-rest_ratelimit.c | 37 +++++++++++++++++------------------- src/discord-rest_request.c | 10 +++++----- 5 files changed, 50 insertions(+), 34 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 9b7c01065..3c08b0bdc 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -436,7 +436,7 @@ struct discord_request { int retry_attempt; /** synchronize synchronous requests */ pthread_cond_t *cond; - /** entry for @ref discord_ratelimitor and @ref discord_bucket queues */ + /** entry for @ref discord_ratelimiter and @ref discord_bucket queues */ QUEUE entry; }; @@ -1117,7 +1117,7 @@ bool discord_message_commands_try_perform( * @see discord_init(), discord_config_init(), discord_cleanup() */ struct discord { - /** DISCORD logging module */ + /** `DISCORD` logging module */ struct logconf conf; /** whether this is the original client or a clone */ bool is_original; @@ -1156,9 +1156,19 @@ struct discord { /** triggers once per loop cycle */ discord_ev_idle on_cycle; - /** space for user arbitrary data */ + /** user arbitrary data @see discord_set_data() */ void *data; + /** keep tab of amount of worker threads being used by client */ + struct { + /** amount of worker-threads currently being used by client */ + int count; + /** synchronize `count` between workers */ + pthread_mutex_t lock; + /** notify of `count` decrement */ + pthread_cond_t cond; + } * workers; + #ifdef CCORD_VOICE struct discord_voice vcs[DISCORD_MAX_VCS]; struct discord_voice_evcallbacks voice_cbs; diff --git a/src/discord-client.c b/src/discord-client.c index 85e4d00a9..4df646a09 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -1,11 +1,11 @@ #include #include #include -#include /* isgraph() */ #include #include "discord.h" #include "discord-internal.h" +#include "discord-worker.h" #include "cog-utils.h" static void @@ -16,9 +16,14 @@ _discord_init(struct discord *new_client) discord_timers_init(&new_client->timers.user); new_client->io_poller = io_poller_create(); + new_client->workers = calloc(1, sizeof *new_client->workers); + ASSERT_S(!pthread_mutex_init(&new_client->workers->lock, NULL), + "Couldn't initialize Client's mutex"); + ASSERT_S(!pthread_cond_init(&new_client->workers->cond, NULL), + "Couldn't initialize Client's cond"); + discord_refcounter_init(&new_client->refcounter, &new_client->conf); discord_message_commands_init(&new_client->commands, &new_client->conf); - discord_rest_init(&new_client->rest, &new_client->conf, new_client->token); discord_gateway_init(&new_client->gw, &new_client->conf, new_client->token); @@ -161,6 +166,7 @@ discord_cleanup(struct discord *client) _discord_clone_cleanup(client); } else { + discord_worker_join(client); discord_rest_cleanup(&client->rest); discord_gateway_cleanup(&client->gw); discord_message_commands_cleanup(&client->commands); @@ -174,6 +180,9 @@ discord_cleanup(struct discord *client) discord_timers_cleanup(client, &client->timers.internal); logconf_cleanup(&client->conf); if (client->token) free(client->token); + pthread_mutex_destroy(&client->workers->lock); + pthread_cond_destroy(&client->workers->cond); + free(client->workers); } free(client); } diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 8c9a7359e..cc95cd5c0 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -240,10 +240,10 @@ on_dispatch(struct discord_gateway *gw) case DISCORD_EVENT_WORKER_THREAD: { struct discord_gateway *clone = _discord_gateway_clone(gw); CCORDcode code = discord_worker_add( - CLIENT(clone, gw), &_discord_gateway_dispatch_thread, clone); + CLIENT(gw, gw), &_discord_gateway_dispatch_thread, clone); if (code != CCORD_OK) { - log_error("Couldn't schedule worker-thread (code %d)", code); + log_error("Couldn't start worker-thread (code %d)", code); _discord_gateway_clone_cleanup(clone); } } break; @@ -507,8 +507,8 @@ discord_gateway_init(struct discord_gateway *gw, logconf_branch(&gw->conf, conf, "DISCORD_GATEWAY"); gw->timer = calloc(1, sizeof *gw->timer); - if (pthread_rwlock_init(&gw->timer->rwlock, NULL)) - ERR("Couldn't initialize pthread rwlock"); + ASSERT_S(!pthread_rwlock_init(&gw->timer->rwlock, NULL), + "Couldn't initialize Gateway's rwlock"); /* client connection status */ gw->session = calloc(1, sizeof *gw->session); diff --git a/src/discord-rest_ratelimit.c b/src/discord-rest_ratelimit.c index e60cdcee2..be487b241 100644 --- a/src/discord-rest_ratelimit.c +++ b/src/discord-rest_ratelimit.c @@ -66,24 +66,22 @@ discord_ratelimiter_build_key(enum http_method method, /* consume variadic arguments */ for (size_t i = 0; i < currlen; ++i) { - if ('%' == curr[i]) { - const char *type = &curr[i + 1]; - - switch (*type) { - default: - VASSERT_S(0 == strncmp(type, PRIu64, sizeof(PRIu64) - 1), - "Internal error: Missing check for '%%%s'", - type); - - id_arg = va_arg(args, u64snowflake); - break; - case 's': - (void)va_arg(args, char *); - break; - case 'd': - (void)va_arg(args, int); - break; - } + if (curr[i] != '%') continue; + + const char *type = &curr[i + 1]; + switch (*type) { + default: + VASSERT_S(0 == strncmp(type, PRIu64, sizeof(PRIu64) - 1), + "Internal error: Missing check for '%%%s'", type); + + id_arg = va_arg(args, u64snowflake); + break; + case 's': + (void)va_arg(args, char *); + break; + case 'd': + (void)va_arg(args, int); + break; } } @@ -170,9 +168,8 @@ discord_ratelimiter_cleanup(struct discord_ratelimiter *rl) /* iterate and cleanup known buckets */ for (int i = 0; i < rl->capacity; ++i) { struct _discord_route *r = rl->routes + i; - if (CHASH_FILLED == r->state) { + if (CHASH_FILLED == r->state) _discord_bucket_cancel_all(rl, r->bucket); - } } free(rl->global_wait_ms); __chash_free(rl, RATELIMITER_TABLE); diff --git a/src/discord-rest_request.c b/src/discord-rest_request.c index fb8058c7b..379cdce3b 100644 --- a/src/discord-rest_request.c +++ b/src/discord-rest_request.c @@ -140,16 +140,16 @@ _discord_request_to_multipart(curl_mime *mime, void *p_req) curl_mime_name(part, name); } else if (req->attachments.array[i].filename) { - CURLcode code; + CURLcode ecode; /* fetch local file by the filename */ part = curl_mime_addpart(mime); - code = + ecode = curl_mime_filedata(part, req->attachments.array[i].filename); - if (code != CURLE_OK) { + if (ecode != CURLE_OK) { char errbuf[256]; snprintf(errbuf, sizeof(errbuf), "%s (file: %s)", - curl_easy_strerror(code), + curl_easy_strerror(ecode), req->attachments.array[i].filename); perror(errbuf); } @@ -491,7 +491,7 @@ discord_requestor_start_pending(struct discord_requestor *rqtor) req = QUEUE_DATA(qelem, struct discord_request, entry); b = discord_bucket_get(&rqtor->ratelimiter, req->key); discord_bucket_insert(&rqtor->ratelimiter, b, req, - req->dispatch.high_p); + req->dispatch.high_priority); } discord_bucket_request_selector(&rqtor->ratelimiter, rqtor, From 5a0b9d8100872f438f09e3d28e7e974a1c1d003b Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Tue, 31 May 2022 21:07:22 -0300 Subject: [PATCH 105/118] chore(racecond.c): match latest --- test/racecond.c | 63 +++++++++++-------------------------------------- 1 file changed, 14 insertions(+), 49 deletions(-) diff --git a/test/racecond.c b/test/racecond.c index 2be17181a..5629eda5b 100644 --- a/test/racecond.c +++ b/test/racecond.c @@ -13,10 +13,6 @@ #define THREADPOOL_SIZE "4" #define PREFIX "!" -pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; -bool g_keep_spamming = true; -unsigned g_thread_count; - void on_ready(struct discord *client, const struct discord_ready *event) { @@ -55,7 +51,7 @@ on_disconnect(struct discord *client, const struct discord_message *event) }, &(struct discord_ret_message){ .done = &disconnect, - .high_p = true, + .high_priority = true, }); } @@ -70,7 +66,7 @@ on_reconnect(struct discord *client, const struct discord_message *event) }, &(struct discord_ret_message){ .done = &reconnect, - .high_p = true, + .high_priority = true, }); } @@ -86,61 +82,31 @@ on_single(struct discord *client, const struct discord_message *event) NULL); } -void -on_stop_spam_sync(struct discord *client, const struct discord_message *event) -{ - pthread_mutex_lock(&g_lock); - g_keep_spamming = false; - g_thread_count = 0; - pthread_mutex_unlock(&g_lock); -} - void on_spam_sync(struct discord *client, const struct discord_message *event) { - const unsigned threadpool_size = strtol(THREADPOOL_SIZE, NULL, 10); - if (event->author->bot) return; - // prevent blocking all threads - pthread_mutex_lock(&g_lock); - if (g_thread_count >= threadpool_size - 1) { - discord_create_message(client, event->channel_id, - &(struct discord_create_message){ - .content = - "Too many threads (" THREADPOOL_SIZE - ") will block the threadpool!", - }, - &(struct discord_ret_message){ - .sync = DISCORD_SYNC_FLAG, - }); - - pthread_mutex_unlock(&g_lock); - return; - } - - ++g_thread_count; - g_keep_spamming = true; - pthread_mutex_unlock(&g_lock); - - char number[256]; - bool keep_alive = true; - for (int i = 0;; ++i) { - pthread_mutex_lock(&g_lock); - keep_alive = g_keep_spamming; - pthread_mutex_unlock(&g_lock); - - if (!keep_alive) break; + char text[32]; - snprintf(number, sizeof(number), "%d", i); + for (int i = 0; i < 128; ++i) { + snprintf(text, sizeof(text), "%d", i); discord_create_message(client, event->channel_id, &(struct discord_create_message){ - .content = number, + .content = text, }, &(struct discord_ret_message){ .sync = DISCORD_SYNC_FLAG, }); } + + discord_create_message(client, event->channel_id, + &(struct discord_create_message){ + .content = "CHECKPOINT", + }, + &(struct discord_ret_message){ + .sync = DISCORD_SYNC_FLAG, + }); } void @@ -264,7 +230,6 @@ main(int argc, char *argv[]) discord_set_on_command(client, "disconnect", &on_disconnect); discord_set_on_command(client, "reconnect", &on_reconnect); discord_set_on_command(client, "single", &on_single); - discord_set_on_command(client, "stop_spam_sync", &on_stop_spam_sync); discord_set_on_command(client, "spam_sync", &on_spam_sync); discord_set_on_command(client, "spam_async", &on_spam_async); discord_set_on_command(client, "force_error", &on_force_error); From 18aebcbfbb472f560c571a0cad6342612f2c85b0 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 1 Jun 2022 15:17:08 -0300 Subject: [PATCH 106/118] fix(gencodecs): replace 'this' C++ reserved keyword with 'self' --- gencodecs/api/application.pre.h | 6 +- gencodecs/api/application_commands.pre.h | 40 +++--- gencodecs/api/audit_log.pre.h | 20 +-- gencodecs/api/channel.pre.h | 150 +++++++++++----------- gencodecs/api/emoji.pre.h | 8 +- gencodecs/api/gateway.pre.h | 20 +-- gencodecs/api/guild.pre.h | 92 ++++++------- gencodecs/api/guild_scheduled_event.pre.h | 50 ++++---- gencodecs/api/guild_template.pre.h | 8 +- gencodecs/api/interactions.pre.h | 40 +++--- gencodecs/api/invite.pre.h | 22 ++-- gencodecs/api/message_components.pre.h | 16 +-- gencodecs/api/permissions.pre.h | 10 +- gencodecs/api/stage_instance.pre.h | 6 +- gencodecs/api/sticker.pre.h | 18 +-- gencodecs/api/teams.pre.h | 6 +- gencodecs/api/user.pre.h | 18 +-- gencodecs/api/voice.pre.h | 2 +- gencodecs/api/webhook.pre.h | 34 ++--- gencodecs/recipes/json-decoder.h | 32 ++--- gencodecs/recipes/json-encoder.h | 34 ++--- gencodecs/recipes/struct.h | 32 ++--- 22 files changed, 332 insertions(+), 332 deletions(-) diff --git a/gencodecs/api/application.pre.h b/gencodecs/api/application.pre.h index 7e6156c86..cfd644d47 100644 --- a/gencodecs/api/application.pre.h +++ b/gencodecs/api/application.pre.h @@ -25,7 +25,7 @@ PUB_STRUCT(discord_application) /** the description of the app */ FIELD_PTR(description, char, *) /** an array of rpc origin urls, if rpc is enabled */ - COND_WRITE(this->rpc_origins != NULL) + COND_WRITE(self->rpc_origins != NULL) FIELD_STRUCT_PTR(rpc_origins, strings, *) COND_END /** when false only app owner can join the app's bot to guilds */ @@ -38,7 +38,7 @@ PUB_STRUCT(discord_application) /** the url of the app's privacy policy */ FIELD_PTR(privacy_policy_url, char, *) /** partial user object containing info on the owner of the application */ - COND_WRITE(this->owner != NULL) + COND_WRITE(self->owner != NULL) FIELD_STRUCT_PTR(owner, discord_user, *) COND_END /** if this application is a game sold on Discord, this field will be the @@ -49,7 +49,7 @@ PUB_STRUCT(discord_application) FIELD_PTR(verify_key, char, *) /** if the application belongs to a team, this will be a list of the members of that team */ - COND_WRITE(this->team != NULL) + COND_WRITE(self->team != NULL) FIELD_STRUCT_PTR(team, discord_team, *) COND_END /** if this application is a game sold on Discord, this field will be the diff --git a/gencodecs/api/application_commands.pre.h b/gencodecs/api/application_commands.pre.h index 6d60b4bff..428d81815 100644 --- a/gencodecs/api/application_commands.pre.h +++ b/gencodecs/api/application_commands.pre.h @@ -42,12 +42,12 @@ PUB_STRUCT(discord_application_command) /** unique ID of the command */ FIELD_SNOWFLAKE(id) /** one of application command types */ - COND_WRITE(this->type != 0) + COND_WRITE(self->type != 0) FIELD_ENUM(type, discord_application_command_types) COND_END /** unique ID of the parent application */ FIELD_SNOWFLAKE(application_id) - COND_WRITE(this->guild_id != 0) + COND_WRITE(self->guild_id != 0) /** guild ID of the command, if not global */ FIELD_SNOWFLAKE(guild_id) COND_END @@ -57,12 +57,12 @@ PUB_STRUCT(discord_application_command) for `USER` and `MESSAGE` commands */ FIELD_PTR(description, char, *) /** the parameters for the command, max 25 */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_options, *) COND_END /** whether the command is enabled by default when the app is added to a guild */ - COND_WRITE(this->default_permission != true) + COND_WRITE(self->default_permission != true) FIELD(default_permission, bool, true) COND_END /** autoincrementing version identifier updated during substantial @@ -82,33 +82,33 @@ STRUCT(discord_application_command_option) /** 1-100 character description */ FIELD_PTR(description, char, *) /** if the parameter is required or optional -- default `false` */ - COND_WRITE(this->required != false) + COND_WRITE(self->required != false) FIELD(required, bool, false) COND_END /** choices for string and int types for the user to pick from */ - COND_WRITE(this->choices != NULL) + COND_WRITE(self->choices != NULL) FIELD_STRUCT_PTR(choices, discord_application_command_option_choices, *) COND_END /** if the option is a subcommand or subcommand group type, this nested options will be the parameters */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_options, *) COND_END /** if the option is a channel type, the channels shown will be restricted to these types */ - COND_WRITE(this->channel_types != NULL) + COND_WRITE(self->channel_types != NULL) FIELD_STRUCT_PTR(channel_types, integers, *) COND_END /** if the option is an INTEGER or NUMBER type, the minimum value permitted */ - COND_WRITE(this->min_value != NULL) + COND_WRITE(self->min_value != NULL) FIELD_PTR(min_value, char, *) COND_END /** if the option is an INTEGER or NUMBER type, the maximum value permitted */ - COND_WRITE(this->max_value != NULL) + COND_WRITE(self->max_value != NULL) FIELD_PTR(max_value, char, *) COND_END /** enable autocomplete interactions for this option */ - COND_WRITE(this->choices == NULL) + COND_WRITE(self->choices == NULL) FIELD(autocomplete, bool, false) COND_END STRUCT_END @@ -136,11 +136,11 @@ STRUCT(discord_application_command_interaction_data_option) FIELD_ENUM(type, discord_application_command_option_types) /** the value of the option resulting from user input @note in case of a string the value must be enclosed with escaped commands, ex: `\"hi\"` */ - COND_WRITE(this->value != NULL && *this->value != '\0') + COND_WRITE(self->value != NULL && *self->value != '\0') FIELD_PTR(value, json_char, *) COND_END /** present if this option is a group or subcommand */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_interaction_data_options, *) COND_END /** true if this option is the currently focused option for autocomplete */ @@ -189,14 +189,14 @@ PUB_STRUCT(discord_create_global_application_command) /** 1-100 character description */ FIELD_PTR(description, char, *) /** the parameters for the command */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_options, *) COND_END /** whether the command is enabled by default when the app is added to a guild */ FIELD(default_permission, bool, true) /** the type of command, default `1` if not set */ - COND_WRITE(this->type != 0) + COND_WRITE(self->type != 0) FIELD_ENUM(type, discord_application_command_types) COND_END STRUCT_END @@ -207,7 +207,7 @@ PUB_STRUCT(discord_edit_global_application_command) /** 1-100 character description */ FIELD_PTR(description, char, *) /** the parameters for the command */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_options, *) COND_END /** whether the command is enabled by default when the app is added to a @@ -221,14 +221,14 @@ PUB_STRUCT(discord_create_guild_application_command) /** 1-100 character description */ FIELD_PTR(description, char, *) /** the parameters for the command */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_options, *) COND_END /** whether the command is enabled by default when the app is added to a guild */ FIELD(default_permission, bool, true) /** the type of command, default `1` if not set */ - COND_WRITE(this->type != 0) + COND_WRITE(self->type != 0) FIELD_ENUM(type, discord_application_command_types) COND_END STRUCT_END @@ -239,7 +239,7 @@ PUB_STRUCT(discord_edit_guild_application_command) /** 1-100 character description */ FIELD_PTR(description, char, *) /** the parameters for the command */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_application_command_options, *) COND_END /** whether the command is enabled by default when the app is added to a @@ -249,7 +249,7 @@ STRUCT_END PUB_STRUCT(discord_edit_application_command_permissions) /** the permissions for the command in the guild */ - COND_WRITE(this->permissions != NULL) + COND_WRITE(self->permissions != NULL) FIELD_STRUCT_PTR(permissions, discord_application_command_permissions, *) COND_END STRUCT_END diff --git a/gencodecs/api/audit_log.pre.h b/gencodecs/api/audit_log.pre.h index 616f0ec34..a2fa9ea6c 100644 --- a/gencodecs/api/audit_log.pre.h +++ b/gencodecs/api/audit_log.pre.h @@ -55,27 +55,27 @@ ENUM_END /** @CCORD_pub_struct{discord_audit_log} */ PUB_STRUCT(discord_audit_log) /** list of audit log entries */ - COND_WRITE(this->audit_log_entries != NULL) + COND_WRITE(self->audit_log_entries != NULL) FIELD_STRUCT_PTR(audit_log_entries, discord_audit_log_entries, *) COND_END /** list of guild scheduled events found in the audit log */ - COND_WRITE(this->guild_scheduled_events != NULL) + COND_WRITE(self->guild_scheduled_events != NULL) FIELD_STRUCT_PTR(guild_scheduled_events, discord_guild_scheduled_events, *) COND_END /** list of partial integration objects */ - COND_WRITE(this->integrations != NULL) + COND_WRITE(self->integrations != NULL) FIELD_STRUCT_PTR(integrations, discord_integrations, *) COND_END /** list of threads found in the audit log */ - COND_WRITE(this->threads != NULL) + COND_WRITE(self->threads != NULL) FIELD_STRUCT_PTR(threads, discord_channels, *) COND_END /** list of users found in the audit log */ - COND_WRITE(this->users != NULL) + COND_WRITE(self->users != NULL) FIELD_STRUCT_PTR(users, discord_users, *) COND_END /** list of webhooks found in the audit log */ - COND_WRITE(this->webhooks != NULL) + COND_WRITE(self->webhooks != NULL) FIELD_STRUCT_PTR(webhooks, discord_webhooks, *) COND_END STRUCT_END @@ -84,7 +84,7 @@ STRUCT(discord_audit_log_entry) /** ID of the affected entity (webhook, user, role, etc.) */ FIELD_SNOWFLAKE(target_id) /** changes made to the target_id */ - COND_WRITE(this->changes != NULL) + COND_WRITE(self->changes != NULL) FIELD_STRUCT_PTR(changes, discord_audit_log_changes, *) COND_END /** the user who made the changes */ @@ -92,11 +92,11 @@ STRUCT(discord_audit_log_entry) /** id of the entry */ FIELD_SNOWFLAKE(id) /** type of action that occurred */ - COND_WRITE(this->action_type != 0) + COND_WRITE(self->action_type != 0) FIELD_ENUM(action_type, discord_audit_log_events) COND_END /** additional info for certain action types */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_optional_audit_entry_infos, *) COND_END /** the reason for the change (0-512) characters */ @@ -156,7 +156,7 @@ STRUCT(discord_get_guild_audit_log) /** filter the log before a certain entry ID */ FIELD_SNOWFLAKE(before) /** how many entries are returned (default 50, minimum 1, maximum 100) */ - COND_WRITE(this->limit >= 1 && this->limit <= 100) + COND_WRITE(self->limit >= 1 && self->limit <= 100) FIELD(limit, int, 50) COND_END STRUCT_END diff --git a/gencodecs/api/channel.pre.h b/gencodecs/api/channel.pre.h index 8fe53be99..1ebdd487e 100644 --- a/gencodecs/api/channel.pre.h +++ b/gencodecs/api/channel.pre.h @@ -108,7 +108,7 @@ PUB_STRUCT(discord_channel) FIELD_ENUM(type, discord_channel_types) /** the ID of the guild (may be missing for some channel objects received over gateway guild dispatches) */ - COND_WRITE(this->guild_id != 0) + COND_WRITE(self->guild_id != 0) FIELD_SNOWFLAKE(guild_id) COND_END /** sorting position of the channel */ @@ -192,7 +192,7 @@ PUB_STRUCT(discord_message) /** when this message was sent */ FIELD_TIMESTAMP(timestamp) /** when this message was edited (or null if never) */ - COND_WRITE(this->edited_timestamp != 0) + COND_WRITE(self->edited_timestamp != 0) FIELD_TIMESTAMP(edited_timestamp) COND_END /** whether this was a TTS message */ @@ -353,15 +353,15 @@ STRUCT(discord_attachment) /** attachment ID */ FIELD_SNOWFLAKE(id) /** name of file attached */ - COND_WRITE(this->filename != NULL) + COND_WRITE(self->filename != NULL) FIELD_PTR(filename, char, *) COND_END /** description for the file */ - COND_WRITE(this->description != NULL) + COND_WRITE(self->description != NULL) FIELD_PTR(description, char, *) COND_END /** the attachment media type */ - COND_WRITE(this->content_type != NULL) + COND_WRITE(self->content_type != NULL) FIELD_PTR(content_type, char, *) COND_END /** size of file in bytes */ @@ -371,11 +371,11 @@ STRUCT(discord_attachment) /** proxied url of file */ FIELD_PTR(proxy_url, char, *) /** height of file (if image) */ - COND_WRITE(this->height != 0) + COND_WRITE(self->height != 0) FIELD(height, int, 0) COND_END /** width of file (if image) */ - COND_WRITE(this->width != 0) + COND_WRITE(self->width != 0) FIELD(width, int, 0) COND_END /** whether this attachment is ephemeral */ @@ -398,36 +398,36 @@ PUB_STRUCT(discord_embed) /** url of embed */ FIELD_PTR(url, char, *) /** timestamp of embed content */ - COND_WRITE(this->timestamp != 0) + COND_WRITE(self->timestamp != 0) FIELD_TIMESTAMP(timestamp) COND_END /** color code of the embed */ - COND_WRITE(this->color != 0) + COND_WRITE(self->color != 0) FIELD(color, int, 0) COND_END /** footer information */ - COND_WRITE(this->footer != NULL) + COND_WRITE(self->footer != NULL) FIELD_STRUCT_PTR(footer, discord_embed_footer, *) COND_END /** image information */ - COND_WRITE(this->image != NULL) + COND_WRITE(self->image != NULL) FIELD_STRUCT_PTR(image, discord_embed_image, *) COND_END /** thumbnail information */ - COND_WRITE(this->thumbnail != NULL) + COND_WRITE(self->thumbnail != NULL) FIELD_STRUCT_PTR(thumbnail, discord_embed_thumbnail, *) COND_END /** video information */ - COND_WRITE(this->video != NULL) + COND_WRITE(self->video != NULL) FIELD_STRUCT_PTR(video, discord_embed_video, *) COND_END - COND_WRITE(this->provider != NULL) + COND_WRITE(self->provider != NULL) FIELD_STRUCT_PTR(provider, discord_embed_provider, *) COND_END - COND_WRITE(this->author != NULL) + COND_WRITE(self->author != NULL) FIELD_STRUCT_PTR(author, discord_embed_author, *) COND_END - COND_WRITE(this->fields != NULL) + COND_WRITE(self->fields != NULL) FIELD_STRUCT_PTR(fields, discord_embed_fields, *) COND_END STRUCT_END @@ -442,15 +442,15 @@ PUB_STRUCT(discord_embed_thumbnail) /** source url of thumbnail (only supports http(s) and attachments) */ FIELD_PTR(url, char, *) /** a proxied url of the thumbnail */ - COND_WRITE(this->proxy_url != NULL) + COND_WRITE(self->proxy_url != NULL) FIELD_PTR(proxy_url, char, *) COND_END /** height of thumbnail */ - COND_WRITE(this->height != 0) + COND_WRITE(self->height != 0) FIELD(height, int, 0) COND_END /** width of thumbnail */ - COND_WRITE(this->width != 0) + COND_WRITE(self->width != 0) FIELD(width, int, 0) COND_END STRUCT_END @@ -458,19 +458,19 @@ STRUCT_END /** @CCORD_pub_struct{discord_embed_video} */ PUB_STRUCT(discord_embed_video) /** source url of video */ - COND_WRITE(this->url != NULL) + COND_WRITE(self->url != NULL) FIELD_PTR(url, char, *) COND_END /** a proxied url of the video */ - COND_WRITE(this->proxy_url != NULL) + COND_WRITE(self->proxy_url != NULL) FIELD_PTR(proxy_url, char, *) COND_END /** height of video */ - COND_WRITE(this->height != 0) + COND_WRITE(self->height != 0) FIELD(height, int, 0) COND_END /** width of video */ - COND_WRITE(this->width != 0) + COND_WRITE(self->width != 0) FIELD(width, int, 0) COND_END STRUCT_END @@ -480,15 +480,15 @@ PUB_STRUCT(discord_embed_image) /** source url of image (only supports http(s) and attachments) */ FIELD_PTR(url, char, *) /** a proxied url of the image */ - COND_WRITE(this->proxy_url != NULL) + COND_WRITE(self->proxy_url != NULL) FIELD_PTR(proxy_url, char, *) COND_END /** height of image */ - COND_WRITE(this->height != 0) + COND_WRITE(self->height != 0) FIELD(height, int, 0) COND_END /** width of image */ - COND_WRITE(this->width != 0) + COND_WRITE(self->width != 0) FIELD(width, int, 0) COND_END STRUCT_END @@ -496,11 +496,11 @@ STRUCT_END /** @CCORD_pub_struct{discord_embed_provider} */ PUB_STRUCT(discord_embed_provider) /** name of provider */ - COND_WRITE(this->name != NULL) + COND_WRITE(self->name != NULL) FIELD_PTR(name, char, *) COND_END /** url of provider */ - COND_WRITE(this->url != NULL) + COND_WRITE(self->url != NULL) FIELD_PTR(url, char, *) COND_END STRUCT_END @@ -510,15 +510,15 @@ PUB_STRUCT(discord_embed_author) /** name of author */ FIELD_PTR(name, char, *) /** url of author */ - COND_WRITE(this->url != NULL) + COND_WRITE(self->url != NULL) FIELD_PTR(url, char, *) COND_END /** url of author icon (only supports http(s) and attachments) */ - COND_WRITE(this->icon_url != NULL) + COND_WRITE(self->icon_url != NULL) FIELD_PTR(icon_url, char, *) COND_END /** a proxied url of author icon */ - COND_WRITE(this->proxy_icon_url != NULL) + COND_WRITE(self->proxy_icon_url != NULL) FIELD_PTR(proxy_icon_url, char, *) COND_END STRUCT_END @@ -528,11 +528,11 @@ PUB_STRUCT(discord_embed_footer) /** footer text */ FIELD_PTR(text, char, *) /** url of footer icon (only supports http(s) and attachments) */ - COND_WRITE(this->icon_url != NULL) + COND_WRITE(self->icon_url != NULL) FIELD_PTR(icon_url, char, *) COND_END /** a proxied url of footer icon */ - COND_WRITE(this->proxy_icon_url != NULL) + COND_WRITE(self->proxy_icon_url != NULL) FIELD_PTR(proxy_icon_url, char, *) COND_END STRUCT_END @@ -608,7 +608,7 @@ PUB_STRUCT(discord_modify_channel) supported and only in guilds with the `NEWS` feature */ FIELD_ENUM(type, discord_channel_types) /** the position of the channel in the left-hand listing */ - COND_WRITE(this->position != 0) + COND_WRITE(self->position != 0) FIELD(position, int, 0) COND_END /** 0-1024 character channel topic */ @@ -618,32 +618,32 @@ PUB_STRUCT(discord_modify_channel) /** amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `MANAGE_MESSAGES` or `MANAGE_CHANNEL`, are unaffected */ - COND_WRITE(this->rate_limit_per_user != 0) + COND_WRITE(self->rate_limit_per_user != 0) FIELD(rate_limit_per_user, int, 0) COND_END /** the user limit of the voice channel; 0 refers to no limit, 1 to 99 refers to a user limit */ - COND_WRITE(this->user_limit != 0) + COND_WRITE(self->user_limit != 0) FIELD(user_limit, int, 0) COND_END /** channel or category-specific permissions */ - COND_WRITE(this->permission_overwrites != NULL) + COND_WRITE(self->permission_overwrites != NULL) FIELD_STRUCT_PTR(permission_overwrites, discord_overwrites, *) COND_END /** ID of the new parent category for a channel */ - COND_WRITE(this->parent_id != 0) + COND_WRITE(self->parent_id != 0) FIELD_SNOWFLAKE(parent_id) COND_END /** channel voice region id, automatic when set to NULL */ FIELD_PTR(rtc_region, char, *) /** the camera video quality mode of the voice channel */ - COND_WRITE(this->video_quality_mode != 0) + COND_WRITE(self->video_quality_mode != 0) FIELD(video_quality_mode, int, 0) COND_END /** the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity */ - COND_WRITE(this->default_auto_archive_duration != 0) + COND_WRITE(self->default_auto_archive_duration != 0) FIELD(default_auto_archive_duration, int, 0) COND_END /* THREAD */ @@ -651,7 +651,7 @@ PUB_STRUCT(discord_modify_channel) FIELD(archived, bool, false) /** duration in minutes to automatically arhived the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ - COND_WRITE(this->auto_archive_duration != 0) + COND_WRITE(self->auto_archive_duration != 0) FIELD(auto_archive_duration, int, 0) COND_END /** whether the thread is locked; when a thread is locked, only users with @@ -665,19 +665,19 @@ STRUCT_END #if defined(GENCODECS_ON_STRUCT) PUB_STRUCT(discord_get_channel_messages) /** get messages around this message ID */ - COND_WRITE(this->around != 0) + COND_WRITE(self->around != 0) FIELD_SNOWFLAKE(around) COND_END /** get messages before this message ID */ - COND_WRITE(this->before != 0) + COND_WRITE(self->before != 0) FIELD_SNOWFLAKE(before) COND_END /** get messages after this message ID */ - COND_WRITE(this->after != 0) + COND_WRITE(self->after != 0) FIELD_SNOWFLAKE(after) COND_END /** max number of messages to return (1-100) */ - COND_WRITE(this->limit != 0) + COND_WRITE(self->limit != 0) FIELD(limit, int, 50) COND_END STRUCT_END @@ -692,28 +692,28 @@ PUB_STRUCT(discord_create_message) /** embedded `rich` content (up to 6000 characters) */ FIELD_STRUCT_PTR(embeds, discord_embeds, *) /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** include to make your message a reply */ - COND_WRITE(this->message_reference != NULL) + COND_WRITE(self->message_reference != NULL) FIELD_STRUCT_PTR(message_reference, discord_message_reference, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** IDs of up to 3 stickers in the server to send in the message */ - COND_WRITE(this->sticker_ids != NULL) + COND_WRITE(self->sticker_ids != NULL) FIELD_STRUCT_PTR(sticker_ids, snowflakes, *) COND_END /** attachment objects with filename and description */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END /** @ref DiscordAPIChannelMessageFlags combined as a bitfield (only `SUPPRESS_EMBEDS` can be set) */ - COND_WRITE(this->flags != 0) + COND_WRITE(self->flags != 0) FIELD_BITMASK(flags) COND_END STRUCT_END @@ -721,11 +721,11 @@ STRUCT_END #if defined(GENCODECS_ON_STRUCT) PUB_STRUCT(discord_get_reactions) /** get users after this user ID */ - COND_WRITE(this->after != 0) + COND_WRITE(self->after != 0) FIELD_SNOWFLAKE(after) COND_END /** max number of users to return (1-100) */ - COND_WRITE(this->limit != 0) + COND_WRITE(self->limit != 0) FIELD(limit, int, 0) COND_END STRUCT_END @@ -739,19 +739,19 @@ PUB_STRUCT(discord_edit_message) FIELD_STRUCT_PTR(embeds, discord_embeds, *) /** @ref DiscordAPIChannelMessageFlags combined as a bitfield (only `SUPPRESS_EMBEDS` can be set) */ - COND_WRITE(this->flags != 0) + COND_WRITE(self->flags != 0) FIELD_BITMASK(flags) COND_END /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** attachment objects with filename and description */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END STRUCT_END @@ -766,12 +766,12 @@ STRUCT_END PUB_STRUCT(discord_edit_channel_permissions) /** the bitwise value of all allowed permissions (default \"0\") @see @ref DiscordPermissions */ - COND_WRITE(this->allow != 0) + COND_WRITE(self->allow != 0) FIELD_BITMASK(allow) COND_END /** the bitwise value of all disallowed permissions (default \"0\") @see @ref DiscordPermissions */ - COND_WRITE(this->deny != 0) + COND_WRITE(self->deny != 0) FIELD_BITMASK(deny) COND_END /** 0 for a role or 1 for a member */ @@ -782,34 +782,34 @@ STRUCT_END PUB_STRUCT(discord_create_channel_invite) /** duration of invite in seconds before expiry, or 0 for never. between 0 and 604800 (7 days) */ - COND_WRITE(this->max_age != 0) + COND_WRITE(self->max_age != 0) FIELD(max_age, int, 86400) COND_END /** max number of uses or 0 for unlimited. betwee 0 and 100 */ - COND_WRITE(this->max_uses != 0) + COND_WRITE(self->max_uses != 0) FIELD(max_uses, int, 0) COND_END /** whether this invite only grants temporary membership */ - COND_WRITE(this->temporary != 0) + COND_WRITE(self->temporary != 0) FIELD(temporary, bool, false) COND_END /** if true, don't true to reuse a similar invite (useful for creating many unique one time use invites) */ - COND_WRITE(this->unique != 0) + COND_WRITE(self->unique != 0) FIELD(unique, bool, false) COND_END /** the type of target for this voice channel invite */ - COND_WRITE(this->target_type != 0) + COND_WRITE(self->target_type != 0) FIELD_ENUM(target_type, discord_invite_target_types) COND_END /** the id of the user whose stream to display for this invite, required if `target_type` is 1, the user must be streaming in the channel */ - COND_WRITE(this->target_user_id != 0) + COND_WRITE(self->target_user_id != 0) FIELD_SNOWFLAKE(target_user_id) COND_END /** the id of the embedded application to open for this invite, required if `target_type` is 2, the application must have the `EMBEDDED` flag */ - COND_WRITE(this->target_application_id != 0) + COND_WRITE(self->target_application_id != 0) FIELD_SNOWFLAKE(target_application_id) COND_END STRUCT_END @@ -817,7 +817,7 @@ STRUCT_END /** @CCORD_pub_struct{discord_follow_news_channel} */ PUB_STRUCT(discord_follow_news_channel) /** id of target channel */ - COND_WRITE(this->webhook_channel_id != 0) + COND_WRITE(self->webhook_channel_id != 0) FIELD_SNOWFLAKE(webhook_channel_id) COND_END STRUCT_END @@ -836,13 +836,13 @@ PUB_STRUCT(discord_start_thread_with_message) FIELD_PTR(name, char, *) /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ - COND_WRITE(this->auto_archive_duration != 0) + COND_WRITE(self->auto_archive_duration != 0) FIELD(auto_archive_duration, int, 0) COND_END /** amount of seconds a user has to wait before sending another message (0-21600) */ - COND_WRITE(this->rate_limit_per_user >= 0 - && this->rate_limit_per_user <= 21600) + COND_WRITE(self->rate_limit_per_user >= 0 + && self->rate_limit_per_user <= 21600) FIELD(rate_limit_per_user, int, 0) COND_END STRUCT_END @@ -853,7 +853,7 @@ PUB_STRUCT(discord_start_thread_without_message) FIELD_PTR(name, char, *) /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ - COND_WRITE(this->auto_archive_duration != 0) + COND_WRITE(self->auto_archive_duration != 0) FIELD(auto_archive_duration, int, 0) COND_END /** the type of thread to create */ @@ -863,8 +863,8 @@ PUB_STRUCT(discord_start_thread_without_message) FIELD(invitable, bool, false) /** amount of seconds a user has to wait before sending another message (0-21600) */ - COND_WRITE(this->rate_limit_per_user >= 0 - && this->rate_limit_per_user <= 21600) + COND_WRITE(self->rate_limit_per_user >= 0 + && self->rate_limit_per_user <= 21600) FIELD(rate_limit_per_user, int, 0) COND_END STRUCT_END @@ -872,12 +872,12 @@ STRUCT_END /** @CCORD_pub_struct{discord_list_active_threads} */ PUB_STRUCT(discord_list_active_threads) /** the active threads */ - COND_WRITE(this->threads != NULL) + COND_WRITE(self->threads != NULL) FIELD_STRUCT_PTR(threads, discord_channels, *) COND_END /** a thread member object for each returned thread the current user has joined */ - COND_WRITE(this->members != NULL) + COND_WRITE(self->members != NULL) FIELD_STRUCT_PTR(members, discord_thread_members, *) COND_END /** whether there are potentially additional threads that could be returned diff --git a/gencodecs/api/emoji.pre.h b/gencodecs/api/emoji.pre.h index 8dd156729..c5190fb00 100644 --- a/gencodecs/api/emoji.pre.h +++ b/gencodecs/api/emoji.pre.h @@ -9,11 +9,11 @@ PUB_STRUCT(discord_emoji) /** emoji name */ FIELD_PTR(name, char, *) /** roles allowed to use this emoji */ - COND_WRITE(this->roles != NULL) + COND_WRITE(self->roles != NULL) FIELD_STRUCT_PTR(roles, discord_roles, *) COND_END /** user that created this emoji */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END /** whether this emoji must be wrapped in colons */ @@ -44,7 +44,7 @@ PUB_STRUCT(discord_create_guild_emoji) /** the 128x128 emoji image */ FIELD_PTR(image, char, *) /** roles allowed to use this emoji */ - COND_WRITE(this->roles != NULL) + COND_WRITE(self->roles != NULL) FIELD_STRUCT_PTR(roles, snowflakes, *) COND_END STRUCT_END @@ -57,7 +57,7 @@ PUB_STRUCT(discord_modify_guild_emoji) /** the 128x128 emoji image */ FIELD_PTR(image, char, *) /** roles allowed to use this emoji */ - COND_WRITE(this->roles != NULL) + COND_WRITE(self->roles != NULL) FIELD_STRUCT_PTR(roles, snowflakes, *) COND_END STRUCT_END diff --git a/gencodecs/api/gateway.pre.h b/gencodecs/api/gateway.pre.h index d99a8ee20..9904a9a2c 100644 --- a/gencodecs/api/gateway.pre.h +++ b/gencodecs/api/gateway.pre.h @@ -241,28 +241,28 @@ LIST_END /** @CCORD_pub_struct{discord_presence_update} */ PUB_STRUCT(discord_presence_update) /** the user presence is being updated for */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END /** id of the guild */ - COND_WRITE(this->guild_id != 0) + COND_WRITE(self->guild_id != 0) FIELD_SNOWFLAKE(guild_id) COND_END /** either "idle", "dnd", "online", or "offline" */ - COND_WRITE(this->status != NULL) + COND_WRITE(self->status != NULL) FIELD_PTR(status, char, *) COND_END /** user's platform-dependent status */ - COND_WRITE(this->client_status != NULL) + COND_WRITE(self->client_status != NULL) FIELD_STRUCT_PTR(client_status, discord_client_status, *) COND_END /** user's current activities */ - COND_WRITE(this->activities != NULL) + COND_WRITE(self->activities != NULL) FIELD_STRUCT_PTR(activities, discord_activities, *) COND_END /** unix time (in milliseconds) of when the client went idle, or null if the client is not idle */ - COND_WRITE(this->since != 0) + COND_WRITE(self->since != 0) FIELD_TIMESTAMP(since) COND_END /** whether or not the client is afk */ @@ -300,11 +300,11 @@ PUB_STRUCT(discord_identify) will stop sending offline members in the guild member list */ FIELD(large_threshold, int, 50) /** array of two integers (shard_id, num_shards) */ - COND_WRITE(this->shard != NULL) + COND_WRITE(self->shard != NULL) FIELD_STRUCT_PTR(shard, integers, *) COND_END /** presence structure for initial presence information */ - COND_WRITE(this->presence != NULL) + COND_WRITE(self->presence != NULL) FIELD_STRUCT_PTR(presence, discord_presence_update, *) COND_END /** the gateway intents you wish to receive @@ -346,7 +346,7 @@ PUB_STRUCT(discord_request_guild_members) FIELD_PTR(query, char, *) /** maximum numberof members to send matching the `query`; a limit of `0` * can be used with an empty string `query` to return all members */ - COND_WRITE(this->query != NULL) + COND_WRITE(self->query != NULL) FIELD(limit, int, 0) COND_END /** used to specify if we want the presences of the matched members */ @@ -354,7 +354,7 @@ PUB_STRUCT(discord_request_guild_members) /** used to specify which users you wish to fetch */ FIELD_STRUCT_PTR(user_ids, snowflakes, *) /** nonce to identify the `Guild Members Chunk` response */ - COND_WRITE(this->nonce != NULL) + COND_WRITE(self->nonce != NULL) FIELD_PTR(nonce, char, *) COND_END STRUCT_END diff --git a/gencodecs/api/guild.pre.h b/gencodecs/api/guild.pre.h index 382eb0ced..b38e48b23 100644 --- a/gencodecs/api/guild.pre.h +++ b/gencodecs/api/guild.pre.h @@ -87,7 +87,7 @@ PUB_STRUCT(discord_guild) /** icon hash */ FIELD_PTR(icon, char, *) /** icon hash, returned when in the template object */ - COND_WRITE(this->icon_hash != NULL) + COND_WRITE(self->icon_hash != NULL) FIELD_PTR(icon_hash, char, *) COND_END /** splash hash */ @@ -99,7 +99,7 @@ PUB_STRUCT(discord_guild) /** id of owner */ FIELD_SNOWFLAKE(owner_id) /** total permissions for the user in the guild (excludes overwrites) */ - COND_WRITE(this->permissions != NULL) + COND_WRITE(self->permissions != NULL) FIELD_PTR(permissions, char, *) COND_END /** id of afk channel */ @@ -136,7 +136,7 @@ PUB_STRUCT(discord_guild) guidelines */ FIELD_SNOWFLAKE(rules_channel_id) /** when this guild was joined at */ - COND_WRITE(this->joined_at != 0) + COND_WRITE(self->joined_at != 0) FIELD_TIMESTAMP(joined_at) COND_END /** true if this is considered a large guild */ @@ -146,34 +146,34 @@ PUB_STRUCT(discord_guild) /** total number of members in this guild */ FIELD(member_count, int, 0) /** states of members currently in voice channels; lacks `guild_id` */ - COND_WRITE(this->voice_states != NULL) + COND_WRITE(self->voice_states != NULL) FIELD_STRUCT_PTR(voice_states, discord_voice_states, *) COND_END /** users in the guild */ - COND_WRITE(this->members != NULL) + COND_WRITE(self->members != NULL) FIELD_STRUCT_PTR(members, discord_guild_members, *) COND_END /** channels in the guild */ - COND_WRITE(this->channels != NULL) + COND_WRITE(self->channels != NULL) FIELD_STRUCT_PTR(channels, discord_channels, *) COND_END /** all active threads in the guild that current user has permission to view */ - COND_WRITE(this->threads != NULL) + COND_WRITE(self->threads != NULL) FIELD_STRUCT_PTR(threads, discord_channels, *) COND_END /** presences of the members in the guild, will only include non-offline members if the size is greater than `large threshold` */ - COND_WRITE(this->presences != NULL) + COND_WRITE(self->presences != NULL) FIELD_STRUCT_PTR(presences, discord_presence_updates, *) COND_END /** the maximum number of presences for the guild (null is always returned, apart from the largest of guilds) */ - COND_WRITE(this->max_presences != 0) + COND_WRITE(self->max_presences != 0) FIELD(max_presences, int, 0) COND_END /** the maximum number of members for the guild */ - COND_WRITE(this->max_members != 0) + COND_WRITE(self->max_members != 0) FIELD(max_members, int, 0) COND_END /** the vanity url code for the guild */ @@ -193,34 +193,34 @@ PUB_STRUCT(discord_guild) receive notices from Discord */ FIELD_SNOWFLAKE(public_updates_channel_id) /** the maximum amount of users in a video channel */ - COND_WRITE(this->max_video_channel_users != 0) + COND_WRITE(self->max_video_channel_users != 0) FIELD(max_video_channel_users, int, 0) COND_END /** approximate number of members in this guild */ - COND_WRITE(this->approximate_member_count != 0) + COND_WRITE(self->approximate_member_count != 0) FIELD(approximate_member_count, int, 0) COND_END /** approximate number of non-offline members in this guild */ - COND_WRITE(this->approximate_presence_count != 0) + COND_WRITE(self->approximate_presence_count != 0) FIELD(approximate_presence_count, int, 0) COND_END /** the welcome screen of a Community guild, shown to new members, returned in an invite's guild object */ - COND_WRITE(this->welcome_screen != NULL) + COND_WRITE(self->welcome_screen != NULL) FIELD_STRUCT_PTR(welcome_screen, discord_welcome_screen, *) COND_END /** guild NSFW level */ FIELD_ENUM(nsfw_level, discord_guild_nsfw_level) /** stage instances in the guild */ - COND_WRITE(this->stage_instances != NULL) + COND_WRITE(self->stage_instances != NULL) FIELD_STRUCT_PTR(stage_instances, discord_stage_instances, *) COND_END /** custom guild stickers */ - COND_WRITE(this->stickers != NULL) + COND_WRITE(self->stickers != NULL) FIELD_STRUCT_PTR(stickers, discord_stickers, *) COND_END /** the scheduled events in the guilds */ - COND_WRITE(this->guild_scheduled_events != NULL) + COND_WRITE(self->guild_scheduled_events != NULL) FIELD_STRUCT_PTR(guild_scheduled_events, discord_guild_scheduled_events, *) COND_END /** whether the guild has the boost progress bar enabled */ @@ -283,15 +283,15 @@ STRUCT_END /** @CCORD_pub_struct{discord_guild_member} */ PUB_STRUCT(discord_guild_member) /** the user this guild member represents */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END /** this user's guild nickname */ - COND_WRITE(this->nick != NULL) + COND_WRITE(self->nick != NULL) FIELD_PTR(nick, char, *) COND_END /** the member's guild avatar hash */ - COND_WRITE(this->avatar != NULL) + COND_WRITE(self->avatar != NULL) FIELD_PTR(avatar, char, *) COND_END /** array of role object IDs */ @@ -299,7 +299,7 @@ PUB_STRUCT(discord_guild_member) /** when the user joined the guild */ FIELD_TIMESTAMP(joined_at) /** when the user started boosting the guild */ - COND_WRITE(this->premium_since != 0) + COND_WRITE(self->premium_since != 0) FIELD_TIMESTAMP(premium_since) COND_END /** whether the user is deafened in voice channels */ @@ -311,7 +311,7 @@ PUB_STRUCT(discord_guild_member) FIELD(pending, bool, false) /** total permission of the member in the channel, including overwrites, returned when in the interaction object */ - COND_WRITE(this->permissions != NULL) + COND_WRITE(self->permissions != NULL) FIELD_PTR(permissions, char, *) COND_END /** when the user's timeout will expire and the user will be able to @@ -388,7 +388,7 @@ STRUCT(discord_integration_application) /** the summary of the app */ FIELD_PTR(summary, char, *) /** the bot associated with this application */ - COND_WRITE(this->bot != NULL) + COND_WRITE(self->bot != NULL) FIELD_STRUCT_PTR(bot, discord_user, *) COND_END STRUCT_END @@ -411,7 +411,7 @@ PUB_STRUCT(discord_welcome_screen) /** the server description shown in the welcome screen */ FIELD_PTR(description, char, *) /** the channels shown in the welcome screen, up to 5 */ - COND_WRITE(this->welcome_channels != NULL) + COND_WRITE(self->welcome_channels != NULL) FIELD_STRUCT_PTR(welcome_channels, discord_welcome_screen_channels, *) COND_END STRUCT_END @@ -441,12 +441,12 @@ PUB_STRUCT(discord_create_guild) /** name of the guild (2-100 charaters) */ FIELD_PTR(name, char, *) /** voice region ID @deprecated deprecated field */ - COND_WRITE(this->region != NULL) + COND_WRITE(self->region != NULL) FIELD_PTR(region, char, *) COND_END /** base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has the `ANIMATED_ICON` feature) */ - COND_WRITE(this->icon != NULL) + COND_WRITE(self->icon != NULL) FIELD_PTR(icon, char, *) COND_END /** verification level */ @@ -456,15 +456,15 @@ PUB_STRUCT(discord_create_guild) /** explicit content filter level */ FIELD_ENUM(explicit_content_filter, discord_explicit_content_filter_level) /** new guild roles */ - COND_WRITE(this->roles != NULL) + COND_WRITE(self->roles != NULL) FIELD_STRUCT_PTR(roles, discord_roles, *) COND_END /** new guild's channels */ - COND_WRITE(this->channels != NULL) + COND_WRITE(self->channels != NULL) FIELD_STRUCT_PTR(channels, discord_channels, *) COND_END /** ID for afk channel */ - COND_WRITE(this->afk_channel_id != 0) + COND_WRITE(self->afk_channel_id != 0) FIELD_SNOWFLAKE(afk_channel_id) COND_END /** afk timeout in seconds */ @@ -535,11 +535,11 @@ PUB_STRUCT(discord_create_guild_channel) /** channel topic (0-1024 characters) */ FIELD_PTR(topic, char, *) /** the bitrate (in bits) of the voice channel (voice only) */ - COND_WRITE(this->bitrate != 0) + COND_WRITE(self->bitrate != 0) FIELD(bitrate, int, 0) COND_END /** the user limit of the voice channel (voice only) */ - COND_WRITE(this->user_limit != 0) + COND_WRITE(self->user_limit != 0) FIELD(user_limit, int, 0) COND_END /** amount of seconds a user has to wait before sending another message @@ -551,7 +551,7 @@ PUB_STRUCT(discord_create_guild_channel) /** the channel's permission overwrites */ FIELD_STRUCT_PTR(permission_overwrites, discord_overwrites, *) /** ID of the parent category for a channel */ - COND_WRITE(this->parent_id != 0) + COND_WRITE(self->parent_id != 0) FIELD_SNOWFLAKE(parent_id) COND_END /** whether the channel is nsfw */ @@ -562,14 +562,14 @@ STRUCT(discord_modify_guild_channel_position) /** channel ID */ FIELD_SNOWFLAKE(id) /** sorting position of the channel */ - COND_WRITE(this->position != 0) + COND_WRITE(self->position != 0) FIELD(position, int, 0) COND_END /** syncs the permission overwrites with the new parent, if moving to a new category */ FIELD(lock_category, bool, false) /** the new parent ID for the channel that is moved */ - COND_WRITE(this->parent_id != 0) + COND_WRITE(self->parent_id != 0) FIELD_SNOWFLAKE(parent_id) COND_END STRUCT_END @@ -641,7 +641,7 @@ PUB_STRUCT(discord_modify_guild_member) to NULL to remove timeout. WIll throw a @ref CCORD_HTTP_ERROR (403) error if the user has the `ADMINISTRATOR` permission or is the owner of the guild */ - COND_WRITE(this->communication_disabled_until != 0) + COND_WRITE(self->communication_disabled_until != 0) FIELD_TIMESTAMP(communication_disabled_until) COND_END STRUCT_END @@ -649,7 +649,7 @@ STRUCT_END /** @CCORD_pub_struct{discord_modify_current_member} */ PUB_STRUCT(discord_modify_current_member) /** value to set user's nickname to */ - COND_WRITE(this->nick != NULL) + COND_WRITE(self->nick != NULL) FIELD_PTR(nick, char, *) COND_END STRUCT_END @@ -657,7 +657,7 @@ STRUCT_END /** @CCORD_pub_struct{discord_modify_current_user_nick} */ PUB_STRUCT(discord_modify_current_user_nick) /** value to set user's nickname to */ - COND_WRITE(this->nick != NULL) + COND_WRITE(self->nick != NULL) FIELD_PTR(nick, char, *) COND_END STRUCT_END @@ -665,11 +665,11 @@ STRUCT_END /** @CCORD_pub_struct{discord_create_guild_ban} */ PUB_STRUCT(discord_create_guild_ban) /** number of days to delete messages for (0-7) */ - COND_WRITE(this->delete_message_days >= 0 && this->delete_message_days <= 7) + COND_WRITE(self->delete_message_days >= 0 && self->delete_message_days <= 7) FIELD(delete_message_days, int, 0) COND_END /** reason for the ban @deprecated deprecated field */ - COND_WRITE(this->reason != NULL) + COND_WRITE(self->reason != NULL) FIELD_PTR(reason, char, *) COND_END STRUCT_END @@ -697,7 +697,7 @@ STRUCT(discord_modify_guild_role_position) /** role */ FIELD_SNOWFLAKE(id) /** sorting position of the role */ - COND_WRITE(this->position != 0) + COND_WRITE(self->position != 0) FIELD(position, int, 0) COND_END STRUCT_END @@ -729,7 +729,7 @@ STRUCT_END #if defined(GENCODECS_ON_STRUCT) STRUCT(discord_get_guild_prune_count) /** number of days to count prune for (1-30) */ - COND_WRITE(this->count != 0) + COND_WRITE(self->count != 0) FIELD(count, int, 7) COND_END /** role(s) to include */ @@ -740,7 +740,7 @@ STRUCT_END /** @CCORD_pub_struct{discord_begin_guild_prune} */ PUB_STRUCT(discord_begin_guild_prune) /** number of days to prune */ - COND_WRITE(this->days != 0) + COND_WRITE(self->days != 0) FIELD(days, int, 7) COND_END /** whether 'pruned' is returned, discouraged for large guilds */ @@ -748,7 +748,7 @@ PUB_STRUCT(discord_begin_guild_prune) /** role(s) to include */ FIELD_STRUCT_PTR(include_roles, snowflakes, *) /** reason for the prune @deprecated deprecated field */ - COND_WRITE(this->reason != NULL) + COND_WRITE(self->reason != NULL) FIELD_PTR(reason, char, *) COND_END STRUCT_END @@ -757,7 +757,7 @@ STRUCT_END STRUCT(discord_get_guild_widget_image) /** style of the widget image returned @see https://discord.com/developers/docs/resources/guild#membership-screening-object-widget-style-options */ - COND_WRITE(this->style != NULL) + COND_WRITE(self->style != NULL) FIELD_PTR(style, char, *) COND_END STRUCT_END @@ -770,7 +770,7 @@ PUB_STRUCT(discord_modify_guild_welcome_screen) /** channels linked in the welcome screen and their display options */ FIELD_STRUCT_PTR(welcome_channels, discord_welcome_screen_channels, *) /** the server description to show in the welcome screen */ - COND_WRITE(this->description != NULL) + COND_WRITE(self->description != NULL) FIELD_PTR(description, char, *) COND_END STRUCT_END @@ -783,7 +783,7 @@ PUB_STRUCT(discord_modify_current_user_voice_state) FIELD(suppress, bool, false) /* TODO: should be able to write `null` */ /** set the user's request to speak */ - COND_WRITE(this->request_to_speak_timestamp != 0) + COND_WRITE(self->request_to_speak_timestamp != 0) FIELD_TIMESTAMP(request_to_speak_timestamp) COND_END STRUCT_END diff --git a/gencodecs/api/guild_scheduled_event.pre.h b/gencodecs/api/guild_scheduled_event.pre.h index 93f667990..923726403 100644 --- a/gencodecs/api/guild_scheduled_event.pre.h +++ b/gencodecs/api/guild_scheduled_event.pre.h @@ -41,25 +41,25 @@ PUB_STRUCT(discord_guild_scheduled_event) @ref DISCORD_SCHEDULED_ENTITY_EXTERNAL */ FIELD_TIMESTAMP(scheduled_end_time) /** the privacy level of the scheduled event */ - COND_WRITE(this->privacy_level != 0) + COND_WRITE(self->privacy_level != 0) FIELD_ENUM(privacy_level, discord_guild_scheduled_event_privacy_level) COND_END /** the status of the scheduled event */ - COND_WRITE(this->status != 0) + COND_WRITE(self->status != 0) FIELD_ENUM(status, discord_guild_scheduled_event_status) COND_END /** the type of scheduled event */ - COND_WRITE(this->entity_type != 0) + COND_WRITE(self->entity_type != 0) FIELD_ENUM(entity_type, discord_guild_scheduled_event_entity_types) COND_END /** the ID of an entity associated with a guild scheduled event */ FIELD_SNOWFLAKE(entity_id) /** additional metadata for the guild scheduled event */ - COND_WRITE(this->entity_metadata != NULL) + COND_WRITE(self->entity_metadata != NULL) FIELD_STRUCT_PTR(entity_metadata, discord_guild_scheduled_event_entity_metadata, *) COND_END /** the user that created the scheduled event */ - COND_WRITE(this->creator != NULL) + COND_WRITE(self->creator != NULL) FIELD_STRUCT_PTR(creator, discord_user, *) COND_END /** the number of users subscribed to the scheduled event */ @@ -75,7 +75,7 @@ LIST_END STRUCT(discord_guild_scheduled_event_entity_metadata) /** location of the event (1-100 characters) */ - COND_WRITE(this->location != NULL) + COND_WRITE(self->location != NULL) FIELD_PTR(location, char, *) COND_END STRUCT_END @@ -84,12 +84,12 @@ STRUCT(discord_guild_scheduled_event_user) /** the scheduled event ID which the user subscribed to */ FIELD_SNOWFLAKE(guild_scheduled_event_id) /** user which subscribed to an event */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END /** guild member data for this user for the guild which this event belongs to, if any */ - COND_WRITE(this->member != NULL) + COND_WRITE(self->member != NULL) FIELD_STRUCT_PTR(member, discord_guild_member, *) COND_END STRUCT_END @@ -107,33 +107,33 @@ STRUCT_END /** @CCORD_pub_struct{discord_create_guild_scheduled_event} */ PUB_STRUCT(discord_create_guild_scheduled_event) /** the channel ID of the scheduled event */ - COND_WRITE(this->channel_id != 0) + COND_WRITE(self->channel_id != 0) FIELD_SNOWFLAKE(channel_id) COND_END /** the entity metadata of the scheduled event */ - COND_WRITE(this->entity_metadata != NULL) + COND_WRITE(self->entity_metadata != NULL) FIELD_STRUCT_PTR(entity_metadata, discord_guild_scheduled_event_entity_metadata, *) COND_END /** the name of the scheduled event */ FIELD_PTR(name, char, *) /** the time the scheduled event will start */ - COND_WRITE(this->scheduled_start_time != 0) + COND_WRITE(self->scheduled_start_time != 0) FIELD_TIMESTAMP(scheduled_start_time) COND_END /** the time the scheduled event will end */ - COND_WRITE(this->scheduled_end_time != 0) + COND_WRITE(self->scheduled_end_time != 0) FIELD_TIMESTAMP(scheduled_end_time) COND_END /** the description of the scheduled event */ - COND_WRITE(this->description != NULL) + COND_WRITE(self->description != NULL) FIELD_PTR(description, char, *) COND_END /** the entity type of the scheduled event */ - COND_WRITE(this->entity_type != 0) + COND_WRITE(self->entity_type != 0) FIELD_ENUM(entity_type, discord_guild_scheduled_event_entity_types) COND_END /** the cover image of the scheduled event */ - COND_WRITE(this->image != NULL) + COND_WRITE(self->image != NULL) FIELD_PTR(image, char, *) COND_END STRUCT_END @@ -147,37 +147,37 @@ STRUCT_END /** @CCORD_pub_struct{discord_modify_guild_scheduled_event} */ PUB_STRUCT(discord_modify_guild_scheduled_event) /** the channel ID of the scheduled event */ - COND_WRITE(this->channel_id != 0) + COND_WRITE(self->channel_id != 0) FIELD_SNOWFLAKE(channel_id) COND_END /** the entity metadata of the scheduled event */ - COND_WRITE(this->entity_metadata != NULL) + COND_WRITE(self->entity_metadata != NULL) FIELD_STRUCT_PTR(entity_metadata, discord_guild_scheduled_event_entity_metadata, *) COND_END /** the name of the scheduled event */ FIELD_PTR(name, char, *) /** the time the scheduled event will start */ - COND_WRITE(this->scheduled_start_time != 0) + COND_WRITE(self->scheduled_start_time != 0) FIELD_TIMESTAMP(scheduled_start_time) COND_END /** the time the scheduled event will end */ - COND_WRITE(this->scheduled_end_time != 0) + COND_WRITE(self->scheduled_end_time != 0) FIELD_TIMESTAMP(scheduled_end_time) COND_END /** the description of the scheduled event */ - COND_WRITE(this->description != NULL) + COND_WRITE(self->description != NULL) FIELD_PTR(description, char, *) COND_END /** the entity type of the scheduled event */ - COND_WRITE(this->entity_type != 0) + COND_WRITE(self->entity_type != 0) FIELD_ENUM(entity_type, discord_guild_scheduled_event_entity_types) COND_END /** the status of the scheduled event */ - COND_WRITE(this->status != 0) + COND_WRITE(self->status != 0) FIELD_ENUM(status, discord_guild_scheduled_event_status) COND_END /** the cover image of the scheduled event */ - COND_WRITE(this->image != NULL) + COND_WRITE(self->image != NULL) FIELD_PTR(image, char, *) COND_END STRUCT_END @@ -189,11 +189,11 @@ PUB_STRUCT(discord_get_guild_scheduled_event_users) /** include guild member data if exists */ FIELD(with_member, bool, false) /** consider only users before given user ID */ - COND_WRITE(this->before != 0) + COND_WRITE(self->before != 0) FIELD_SNOWFLAKE(before) COND_END /** consider only users after given user ID */ - COND_WRITE(this->after != 0) + COND_WRITE(self->after != 0) FIELD_SNOWFLAKE(after) COND_END STRUCT_END diff --git a/gencodecs/api/guild_template.pre.h b/gencodecs/api/guild_template.pre.h index 149286b0e..36730fb97 100644 --- a/gencodecs/api/guild_template.pre.h +++ b/gencodecs/api/guild_template.pre.h @@ -37,7 +37,7 @@ PUB_STRUCT(discord_create_guild_from_guild_template) /** name of the guild (2-100 characters) */ FIELD_PTR(name, char, *) /** base64 128x128 image for the guild icon */ - COND_WRITE(this->icon != NULL) + COND_WRITE(self->icon != NULL) FIELD_PTR(icon, char, *) COND_END STRUCT_END @@ -47,7 +47,7 @@ PUB_STRUCT(discord_create_guild_template) /** name of the template (1-100 characters) */ FIELD_PTR(name, char, *) /** description for the template (0-120 characters) */ - COND_WRITE(this->description != NULL) + COND_WRITE(self->description != NULL) FIELD_PTR(description, char, *) COND_END STRUCT_END @@ -55,11 +55,11 @@ STRUCT_END /** @CCORD_pub_struct{discord_modify_guild_template} */ PUB_STRUCT(discord_modify_guild_template) /** name of the template (1-100 characters) */ - COND_WRITE(this->name != NULL) + COND_WRITE(self->name != NULL) FIELD_PTR(name, char, *) COND_END /** description for the template (0-120 characters) */ - COND_WRITE(this->description != NULL) + COND_WRITE(self->description != NULL) FIELD_PTR(description, char, *) COND_END STRUCT_END diff --git a/gencodecs/api/interactions.pre.h b/gencodecs/api/interactions.pre.h index 5359a6fd4..c13214957 100644 --- a/gencodecs/api/interactions.pre.h +++ b/gencodecs/api/interactions.pre.h @@ -115,37 +115,37 @@ PUB_STRUCT(discord_interaction_response) /** interaction callback type */ FIELD_ENUM(type, discord_interaction_callback_types) /** an optional response message */ - COND_WRITE(this->data != NULL) + COND_WRITE(self->data != NULL) FIELD_STRUCT_PTR(data, discord_interaction_callback_data, *) COND_END STRUCT_END STRUCT(discord_interaction_callback_data) /** message components */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /* MESSAGES */ /** is the response TTS */ - COND_WRITE(this->tts != false) + COND_WRITE(self->tts != false) FIELD(tts, bool, false) COND_END /** message content */ - COND_WRITE(this->content != NULL) + COND_WRITE(self->content != NULL) FIELD_PTR(content, char, *) COND_END /** supports up to 10 embeds */ - COND_WRITE(this->embeds != NULL) + COND_WRITE(self->embeds != NULL) FIELD_STRUCT_PTR(embeds, discord_embeds, *) COND_END /** @ref DiscordAPIChannelMessageFlags combined as a bitfield (only @ref DISCORD_MESSAGE_SUPRESS_EMBEDS and @ref DISCORD_MESSAGE_EPHEMERAL can be set) */ - COND_WRITE(this->flags != 0) + COND_WRITE(self->flags != 0) FIELD_BITMASK(flags) COND_END /** attachment objects with filename and description */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END /* AUTOCOMPLETE */ @@ -174,19 +174,19 @@ PUB_STRUCT(discord_edit_original_interaction_response) /** the message contents (up to 2000 characters) */ FIELD_PTR(content, char, *) /** embedded `rich` content */ - COND_WRITE(this->embeds != NULL) + COND_WRITE(self->embeds != NULL) FIELD_STRUCT_PTR(embeds, discord_embeds, *) COND_END /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** attached files to keep and possible descriptions for new files */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END STRUCT_END @@ -210,24 +210,24 @@ PUB_STRUCT(discord_create_followup_message) /** true if this is a TTS message */ FIELD(tts, bool, false) /** embedded `rich` content */ - COND_WRITE(this->embeds != NULL) + COND_WRITE(self->embeds != NULL) FIELD_STRUCT_PTR(embeds, discord_embeds, *) COND_END /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** attachment objects with filename and description */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END /** @ref DiscordAPIChannelMessageFlags combined as a bitfield (only `SUPPRESS_EMBEDS` can be set) */ - COND_WRITE(this->flags != 0) + COND_WRITE(self->flags != 0) FIELD_BITMASK(flags) COND_END STRUCT_END @@ -244,19 +244,19 @@ PUB_STRUCT(discord_edit_followup_message) /** the message contents (up to 2000 characters) */ FIELD_PTR(content, char, *) /** embedded `rich` content */ - COND_WRITE(this->embeds != NULL) + COND_WRITE(self->embeds != NULL) FIELD_STRUCT_PTR(embeds, discord_embeds, *) COND_END /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** attached files to keep and possible descriptions for new files */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END STRUCT_END diff --git a/gencodecs/api/invite.pre.h b/gencodecs/api/invite.pre.h index ce9966f35..7a741dae3 100644 --- a/gencodecs/api/invite.pre.h +++ b/gencodecs/api/invite.pre.h @@ -12,26 +12,26 @@ PUB_STRUCT(discord_invite) /** the invite code (unique ID) */ FIELD_PTR(code, char, *) /** the guild this invite is for */ - COND_WRITE(this->guild != NULL) + COND_WRITE(self->guild != NULL) FIELD_STRUCT_PTR(guild, discord_guild, *) COND_END /** the channel this invite is for */ FIELD_STRUCT_PTR(channel, discord_channel, *) /** the user who created the invite */ - COND_WRITE(this->inviter != NULL) + COND_WRITE(self->inviter != NULL) FIELD_STRUCT_PTR(inviter, discord_user, *) COND_END /** the type of target for this voice channel invite */ - COND_WRITE(this->target_type != 0) + COND_WRITE(self->target_type != 0) FIELD_ENUM(target_type, discord_invite_target_types) COND_END /** the user whose stream to display for this voice channel stream invite */ - COND_WRITE(this->target_user != NULL) + COND_WRITE(self->target_user != NULL) FIELD_STRUCT_PTR(target_user, discord_user, *) COND_END /** the embedded application to open for this voice channel embedded application invite */ - COND_WRITE(this->target_application != NULL) + COND_WRITE(self->target_application != NULL) FIELD_STRUCT_PTR(target_application, discord_application, *) COND_END /** approximate count of online members */ @@ -40,17 +40,17 @@ PUB_STRUCT(discord_invite) FIELD(approximate_member_count, int, 0) /* TODO: nullable */ /** the expiration date of this invite */ - COND_WRITE(this->expires_at != 0) + COND_WRITE(self->expires_at != 0) FIELD_TIMESTAMP(expires_at) COND_END /** stage instance data if there is a public stage instance in the stage channel this invite is for */ - COND_WRITE(this->stage_instance != NULL) + COND_WRITE(self->stage_instance != NULL) FIELD_STRUCT_PTR(stage_instance, discord_invite_stage_instance, *) COND_END /** guild scheduled event data, only included if `guild_scheduled_event_id` contains a valid guild scheduled event ID */ - COND_WRITE(this->guild_scheduled_event != NULL) + COND_WRITE(self->guild_scheduled_event != NULL) FIELD_STRUCT_PTR(guild_scheduled_event, discord_guild_scheduled_event, *) COND_END STRUCT_END @@ -70,14 +70,14 @@ STRUCT(discord_invite_metadata) /** whether this invite only grants temporary membership */ FIELD(temporary, bool, false) /** when this invite was created */ - COND_WRITE(this->created_at != 0) + COND_WRITE(self->created_at != 0) FIELD_TIMESTAMP(created_at) COND_END STRUCT_END STRUCT(discord_invite_stage_instance) /** the members speaking in the Stage */ - COND_WRITE(this->members != NULL) + COND_WRITE(self->members != NULL) FIELD_STRUCT_PTR(members, discord_guild_members, *) COND_END /** the number of users in the Stage */ @@ -99,7 +99,7 @@ PUB_STRUCT(discord_get_invite) /** whether the invite should contain the expiration date */ FIELD(with_expiration, bool, false) /** the guild scheduled event to include with the invite */ - COND_WRITE(this->guild_scheduled_event_id != 0) + COND_WRITE(self->guild_scheduled_event_id != 0) FIELD_SNOWFLAKE(guild_scheduled_event_id) COND_END STRUCT_END diff --git a/gencodecs/api/message_components.pre.h b/gencodecs/api/message_components.pre.h index 0c7382339..7e1867924 100644 --- a/gencodecs/api/message_components.pre.h +++ b/gencodecs/api/message_components.pre.h @@ -35,7 +35,7 @@ ENUM_END /** @CCORD_pub_struct{discord_component} */ PUB_STRUCT(discord_component) /** component type */ - COND_WRITE(this->type != 0) + COND_WRITE(self->type != 0) FIELD_ENUM(type, discord_component_types) COND_END /** a developer-defined identifier for the component, max 100 characters */ @@ -43,34 +43,34 @@ PUB_STRUCT(discord_component) /** whether the component is disabled, default `false` */ FIELD(disabled, bool, false) /** one of button or text styles */ - COND_WRITE(this->style != 0) + COND_WRITE(self->style != 0) FIELD_ENUM(style, discord_component_styles) COND_END /** text that appears on the button, max 80 characters */ FIELD_PTR(label, char, *) /** `name`, `id`, and `animated` */ - COND_WRITE(this->emoji != NULL) + COND_WRITE(self->emoji != NULL) FIELD_STRUCT_PTR(emoji, discord_emoji, *) COND_END /** a url for link-style buttons */ FIELD_PTR(url, char, *) /** the choices in the select, max 25 */ - COND_WRITE(this->options != NULL) + COND_WRITE(self->options != NULL) FIELD_STRUCT_PTR(options, discord_select_options, *) COND_END /** custom placeholder text if nothing is selected, max 100 characters */ FIELD_PTR(placeholder, char, *) /** the minimum number of items that must be chosen: default 1, min 0, max 25 */ - COND_WRITE(this->min_values >= 0 && this->max_values <= 25) + COND_WRITE(self->min_values >= 0 && self->max_values <= 25) FIELD(min_values, int, 1) COND_END /** the maximum number of items that must be chosen: default 1, max 25 */ - COND_WRITE(this->max_values <= 25) + COND_WRITE(self->max_values <= 25) FIELD(max_values, int, 1) COND_END /** a list of child components */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** the minimum input length for a text input */ @@ -96,7 +96,7 @@ STRUCT(discord_select_option) /** an additional description of the option, max 100 characters */ FIELD_PTR(description, char, *) /** `id`, `name`, and `animated` */ - COND_WRITE(this->emoji != NULL) + COND_WRITE(self->emoji != NULL) FIELD_STRUCT_PTR(emoji, discord_emoji, *) COND_END /** will render this option as selected by default */ diff --git a/gencodecs/api/permissions.pre.h b/gencodecs/api/permissions.pre.h index 8520ba2f9..4176486fc 100644 --- a/gencodecs/api/permissions.pre.h +++ b/gencodecs/api/permissions.pre.h @@ -111,11 +111,11 @@ PUB_STRUCT(discord_role) /** if this role is pinned in the user listing */ FIELD(hoist, bool, false) /** role icon hash */ - COND_WRITE(this->icon != NULL) + COND_WRITE(self->icon != NULL) FIELD_PTR(icon, char, *) COND_END /** role unicode emoji */ - COND_WRITE(this->unicode_emoji != NULL) + COND_WRITE(self->unicode_emoji != NULL) FIELD_PTR(unicode_emoji, char, *) COND_END /** position of this role */ @@ -127,7 +127,7 @@ PUB_STRUCT(discord_role) /** whether this roleis mentionable */ FIELD(mentionable, bool, false) /** the tags this role has */ - COND_WRITE(this->tags != NULL) + COND_WRITE(self->tags != NULL) FIELD_STRUCT_PTR(tags, discord_role_tag, *) COND_END STRUCT_END @@ -139,11 +139,11 @@ LIST_END STRUCT(discord_role_tag) /** the id of the bot this role belongs to */ - COND_WRITE(this->bot_id != 0) + COND_WRITE(self->bot_id != 0) FIELD_SNOWFLAKE(bot_id) COND_END /** the id of the integration this role belongs to */ - COND_WRITE(this->integration_id != 0) + COND_WRITE(self->integration_id != 0) FIELD_SNOWFLAKE(integration_id) COND_END /** whether this is the guild's premium subscribe role */ diff --git a/gencodecs/api/stage_instance.pre.h b/gencodecs/api/stage_instance.pre.h index db7d609bc..7f08316f6 100644 --- a/gencodecs/api/stage_instance.pre.h +++ b/gencodecs/api/stage_instance.pre.h @@ -20,7 +20,7 @@ PUB_STRUCT(discord_stage_instance) /** the topic of the Stage instance (1-120 characters) */ FIELD_PTR(topic, char, *) /** the privacy level of the stage instance */ - COND_WRITE(this->privacy_level != 0) + COND_WRITE(self->privacy_level != 0) FIELD_ENUM(privacy_level, discord_privacy_level) COND_END /** whether or not stage discovery is disabled @deprecated deprecated field */ @@ -42,7 +42,7 @@ PUB_STRUCT(discord_create_stage_instance) /** the topic of the Stage instance (1-120 characters) */ FIELD_PTR(topic, char, *) /** the privacy level of the stage instance */ - COND_WRITE(this->privacy_level != 0) + COND_WRITE(self->privacy_level != 0) FIELD_ENUM(privacy_level, discord_privacy_level) COND_END STRUCT_END @@ -52,7 +52,7 @@ PUB_STRUCT(discord_modify_stage_instance) /** the topic of the Stage instance (1-120 characters) */ FIELD_PTR(topic, char, *) /** the privacy level of the stage instance */ - COND_WRITE(this->privacy_level != 0) + COND_WRITE(self->privacy_level != 0) FIELD_ENUM(privacy_level, discord_privacy_level) COND_END STRUCT_END diff --git a/gencodecs/api/sticker.pre.h b/gencodecs/api/sticker.pre.h index 0278f9082..31b4c37ee 100644 --- a/gencodecs/api/sticker.pre.h +++ b/gencodecs/api/sticker.pre.h @@ -21,7 +21,7 @@ PUB_STRUCT(discord_sticker) /** ID of the sticker */ FIELD_SNOWFLAKE(id) /** for standard stickers, ID of the pack the sticker is from */ - COND_WRITE(this->pack_id != 0) + COND_WRITE(self->pack_id != 0) FIELD_SNOWFLAKE(pack_id) COND_END /** name of the sticker */ @@ -31,22 +31,22 @@ PUB_STRUCT(discord_sticker) /** autocomplete/suggestion tags for the sticker (max 200 characters) */ FIELD_PTR(tags, char, *) /** type of sticker */ - COND_WRITE(this->type != 0) + COND_WRITE(self->type != 0) FIELD_ENUM(type, discord_sticker_types) COND_END /** type of sticker format */ - COND_WRITE(this->format_type != 0) + COND_WRITE(self->format_type != 0) FIELD_ENUM(format_type, discord_sticker_format_types) COND_END /** whether this guild sticker can be used, may be false due to loss of Server Boosts */ FIELD(available, bool, false) /** ID of the guild that owns this sticker */ - COND_WRITE(this->guild_id != 0) + COND_WRITE(self->guild_id != 0) FIELD_SNOWFLAKE(guild_id) COND_END /** the user that uploaded the guild sticker */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END /** the standard sticker's sort order within its pack */ @@ -63,7 +63,7 @@ STRUCT(discord_sticker_item) /** name of the sticker */ FIELD_PTR(name, char, *) /** type of sticker format */ - COND_WRITE(this->format_type != 0) + COND_WRITE(self->format_type != 0) FIELD_ENUM(format_type, discord_sticker_format_types) COND_END STRUCT_END @@ -76,7 +76,7 @@ STRUCT(discord_sticker_pack) /** ID of the sticker */ FIELD_SNOWFLAKE(id) /** the stickers in the pack */ - COND_WRITE(this->stickers != NULL) + COND_WRITE(self->stickers != NULL) FIELD_STRUCT_PTR(stickers, discord_stickers, *) COND_END /** name of the sticker pack */ @@ -84,13 +84,13 @@ STRUCT(discord_sticker_pack) /** ID of the pack's SKU */ FIELD_SNOWFLAKE(sku_id) /** ID of a sticker in the pack which is shown as the pack's icon */ - COND_WRITE(this->cover_sticker_id != 0) + COND_WRITE(self->cover_sticker_id != 0) FIELD_SNOWFLAKE(cover_sticker_id) COND_END /** description of the sticker pack */ FIELD_PTR(description, char, *) /** ID of the sticker pack's banner image */ - COND_WRITE(this->banner_asset_id != 0) + COND_WRITE(self->banner_asset_id != 0) FIELD_SNOWFLAKE(banner_asset_id) COND_END STRUCT_END diff --git a/gencodecs/api/teams.pre.h b/gencodecs/api/teams.pre.h index ffe137055..ca63efd28 100644 --- a/gencodecs/api/teams.pre.h +++ b/gencodecs/api/teams.pre.h @@ -14,7 +14,7 @@ PUB_STRUCT(discord_team) /** the unique ID of the team */ FIELD_SNOWFLAKE(id) /** the members of the team */ - COND_WRITE(this->members != NULL) + COND_WRITE(self->members != NULL) FIELD_STRUCT_PTR(members, discord_team_members, *) COND_END /** the name of the team */ @@ -27,13 +27,13 @@ STRUCT(discord_team_member) /** the user's membership state on the team */ FIELD_ENUM(membership_state, discord_membership_state) /** will always be \"[\"*\"]\" */ - COND_WRITE(this->permissions != NULL) + COND_WRITE(self->permissions != NULL) FIELD_STRUCT_PTR(permissions, strings, *) COND_END /** the ID of the parent team of which they are a member */ FIELD_SNOWFLAKE(team_id) /** the avatar, discriminator, id,and username of the user */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END STRUCT_END diff --git a/gencodecs/api/user.pre.h b/gencodecs/api/user.pre.h index 497fd22fb..4ef4e8ee8 100644 --- a/gencodecs/api/user.pre.h +++ b/gencodecs/api/user.pre.h @@ -106,7 +106,7 @@ STRUCT(discord_connection) /** whether the connection is revoked */ FIELD(revoked, bool, false) /** an array of partial server integrations */ - COND_WRITE(this->integrations != NULL) + COND_WRITE(self->integrations != NULL) FIELD_STRUCT_PTR(integrations, discord_integrations, *) COND_END /** whether the connection is verified */ @@ -133,11 +133,11 @@ LIST_END PUB_STRUCT(discord_modify_current_user) /** user's username, if changed may cause the user's discriminator to be randomized */ - COND_WRITE(this->username != NULL) + COND_WRITE(self->username != NULL) FIELD_PTR(username, char, *) COND_END /** if passed, modified the user's avatar */ - COND_WRITE(this->avatar != NULL) + COND_WRITE(self->avatar != NULL) FIELD_PTR(avatar, char, *) COND_END STRUCT_END @@ -145,15 +145,15 @@ STRUCT_END #if defined(GENCODECS_ON_STRUCT) STRUCT(discord_get_current_user_guilds) /** get guilds before this guild ID */ - COND_WRITE(this->before != 0) + COND_WRITE(self->before != 0) FIELD_SNOWFLAKE(before) COND_END /** get guilds after this guild ID */ - COND_WRITE(this->after != 0) + COND_WRITE(self->after != 0) FIELD_SNOWFLAKE(after) COND_END /** max number of guilds to return (1-200) */ - COND_WRITE(this->limit >= 1 && this->limit <= 200) + COND_WRITE(self->limit >= 1 && self->limit <= 200) FIELD(limit, int, 200) COND_END STRUCT_END @@ -162,7 +162,7 @@ STRUCT_END /** @CCORD_pub_struct{discord_create_dm} */ PUB_STRUCT(discord_create_dm) /** the recipient to open a DM channel with */ - COND_WRITE(this->recipient_id != 0) + COND_WRITE(self->recipient_id != 0) FIELD_SNOWFLAKE(recipient_id) COND_END STRUCT_END @@ -170,11 +170,11 @@ STRUCT_END /** @CCORD_pub_struct{discord_create_group_dm} */ PUB_STRUCT(discord_create_group_dm) /** access tokens of users that have grantes your app `gdm.join` scope */ - COND_WRITE(this->access_tokens != NULL) + COND_WRITE(self->access_tokens != NULL) FIELD_STRUCT_PTR(access_tokens, snowflakes, *) COND_END /** a dictionary of user IDs to their respective nicknames */ - COND_WRITE(this->nicks != NULL) + COND_WRITE(self->nicks != NULL) FIELD_STRUCT_PTR(nicks, strings, *) COND_END STRUCT_END diff --git a/gencodecs/api/voice.pre.h b/gencodecs/api/voice.pre.h index c61a0a597..fbef27ad5 100644 --- a/gencodecs/api/voice.pre.h +++ b/gencodecs/api/voice.pre.h @@ -30,7 +30,7 @@ PUB_STRUCT(discord_voice_state) FIELD(suppress, bool, false) /* TODO: nullable */ /** the time at which the user requested to speak */ - COND_WRITE(this->request_to_speak_timestamp) + COND_WRITE(self->request_to_speak_timestamp) FIELD_TIMESTAMP(request_to_speak_timestamp) COND_END STRUCT_END diff --git a/gencodecs/api/webhook.pre.h b/gencodecs/api/webhook.pre.h index 5f38f176f..d57102542 100644 --- a/gencodecs/api/webhook.pre.h +++ b/gencodecs/api/webhook.pre.h @@ -17,7 +17,7 @@ PUB_STRUCT(discord_webhook) /** the ID of the webhook */ FIELD_SNOWFLAKE(id) /** the type of the webhook */ - COND_WRITE(this->type != 0) + COND_WRITE(self->type != 0) FIELD_ENUM(type, discord_webhook_types) COND_END /** the guild ID this webhook is for, if any */ @@ -26,7 +26,7 @@ PUB_STRUCT(discord_webhook) FIELD_SNOWFLAKE(channel_id) /** the user this webhook was created by (not returned when getting a webhook with its token) */ - COND_WRITE(this->user != NULL) + COND_WRITE(self->user != NULL) FIELD_STRUCT_PTR(user, discord_user, *) COND_END /** the default name of the webhook */ @@ -37,7 +37,7 @@ PUB_STRUCT(discord_webhook) FIELD_SNOWFLAKE(application_id) /** the guild of the channel that this webhook is following (returned for Channel Follower Webhooks) */ - COND_WRITE(this->source_channel != NULL) + COND_WRITE(self->source_channel != NULL) FIELD_STRUCT_PTR(source_channel, discord_channel, *) COND_END /** the url used for executing the webhook (returned by the webhooks @@ -60,7 +60,7 @@ PUB_STRUCT(discord_create_webhook) FIELD_PTR(name, char, *) /* TODO: base64 conv */ /** image for the default webhook avatar */ - COND_WRITE(this->avatar != NULL) + COND_WRITE(self->avatar != NULL) FIELD_PTR(avatar, char, *) COND_END STRUCT_END @@ -71,7 +71,7 @@ PUB_STRUCT(discord_modify_webhook) FIELD_PTR(name, char, *) /* TODO: base64 conv */ /** image for the default webhook avatar */ - COND_WRITE(this->avatar != NULL) + COND_WRITE(self->avatar != NULL) FIELD_PTR(avatar, char, *) COND_END /** the new channel ID for this webhook should be moved to */ @@ -84,7 +84,7 @@ PUB_STRUCT(discord_modify_webhook_with_token) FIELD_PTR(name, char, *) /* TODO: base64 conv */ /** image for the default webhook avatar */ - COND_WRITE(this->avatar != NULL) + COND_WRITE(self->avatar != NULL) FIELD_PTR(avatar, char, *) COND_END STRUCT_END @@ -112,24 +112,24 @@ PUB_STRUCT(discord_execute_webhook) /** true if this is a TTS message */ FIELD(tts, bool, false) /** embedded `rich` content */ - COND_WRITE(this->embeds != NULL) + COND_WRITE(self->embeds != NULL) FIELD_STRUCT_PTR(embeds, discord_embeds, *) COND_END /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** attachment objects with filename and description */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END /** @ref DiscordAPIChannelMessageFlags combined as a bitfield (only `SUPPRESS_EMBEDS` can be set) */ - COND_WRITE(this->flags != 0) + COND_WRITE(self->flags != 0) FIELD_BITMASK(flags) COND_END STRUCT_END @@ -137,7 +137,7 @@ STRUCT_END #if defined(GENCODECS_ON_STRUCT) STRUCT(discord_get_webhook_message) /** ID of the thread the message is in */ - COND_WRITE(this->thread_id != 0) + COND_WRITE(self->thread_id != 0) FIELD_SNOWFLAKE(thread_id) COND_END STRUCT_END @@ -155,19 +155,19 @@ PUB_STRUCT(discord_edit_webhook_message) /** the message contents (up to 2000 characters) */ FIELD_PTR(content, char, *) /** embedded `rich` content */ - COND_WRITE(this->embeds != NULL) + COND_WRITE(self->embeds != NULL) FIELD_STRUCT_PTR(embeds, discord_embeds, *) COND_END /** allowed mentions for the message */ - COND_WRITE(this->allowed_mentions != NULL) + COND_WRITE(self->allowed_mentions != NULL) FIELD_STRUCT_PTR(allowed_mentions, discord_allowed_mention, *) COND_END /** the components to include with the message */ - COND_WRITE(this->components != NULL) + COND_WRITE(self->components != NULL) FIELD_STRUCT_PTR(components, discord_components, *) COND_END /** attached files to keep and possible descriptions for new files */ - COND_WRITE(this->attachments != NULL) + COND_WRITE(self->attachments != NULL) FIELD_STRUCT_PTR(attachments, discord_attachments, *) COND_END STRUCT_END @@ -175,7 +175,7 @@ STRUCT_END #if defined(GENCODECS_ON_STRUCT) STRUCT(discord_delete_webhook_message) /** ID of the thread the message is in */ - COND_WRITE(this->thread_id != 0) + COND_WRITE(self->thread_id != 0) FIELD_SNOWFLAKE(thread_id) COND_END STRUCT_END diff --git a/gencodecs/recipes/json-decoder.h b/gencodecs/recipes/json-decoder.h index 3dbadcd91..86e220b5b 100644 --- a/gencodecs/recipes/json-decoder.h +++ b/gencodecs/recipes/json-decoder.h @@ -28,9 +28,9 @@ #define GENCODECS_PUB_STRUCT(_type) \ long _type##_from_jsmnf(jsmnf_pair *root, const char *js, \ - struct _type *this); \ + struct _type *self); \ size_t _type##_from_json(const char buf[], size_t size, \ - struct _type *this); + struct _type *self); #define GENCODECS_PUB_LIST(_type) GENCODECS_PUB_STRUCT(_type) #include "gencodecs-gen.pre.h" @@ -39,7 +39,7 @@ #define GENCODECS_STRUCT(_type) \ static long _type##_from_jsmnf(jsmnf_pair *root, const char *js, \ - struct _type *this); + struct _type *self); #define GENCODECS_LIST(_type) GENCODECS_STRUCT(_type) #include "gencodecs-gen.pre.h" @@ -48,7 +48,7 @@ #define GENCODECS_PUB_STRUCT(_type) \ long _type##_from_jsmnf(jsmnf_pair *root, const char *js, \ - struct _type *this) \ + struct _type *self) \ { \ jsmnf_pair *f; \ long ret = 0; @@ -57,49 +57,49 @@ #define GENCODECS_FIELD_CUSTOM(_name, _key, _type, _decor, _init, _cleanup, \ _encoder, _decoder, _default_value) \ f = jsmnf_find(root, js, _key, sizeof(_key) - 1); \ - _decoder(f, js, this->_name, _type); + _decoder(f, js, self->_name, _type); #define GENCODECS_FIELD_PRINTF(_name, _type, _printf_type, _scanf_type) \ f = jsmnf_find(root, js, #_name, sizeof(#_name) - 1); \ - if (f) sscanf(js + f->v.pos, _scanf_type, &this->_name); + if (f) sscanf(js + f->v.pos, _scanf_type, &self->_name); #define GENCODECS_STRUCT_END \ return ret; \ } #define GENCODECS_PUB_LIST(_type) \ long _type##_from_jsmnf(jsmnf_pair *root, const char *js, \ - struct _type *this) \ + struct _type *self) \ { \ - long ret = sizeof *this * root->size; \ + long ret = sizeof *self * root->size; \ int i; \ if (!ret) return 0; #define GENCODECS_LIST(_type) \ static GENCODECS_PUB_LIST(_type) #define GENCODECS_LISTTYPE(_type) \ - __carray_init(this, root->size, _type, , ); \ + __carray_init(self, root->size, _type, , ); \ for (i = 0; i < root->size; ++i) { \ jsmnf_pair *f = root->fields + i; \ _type o; \ GENCODECS_JSON_DECODER_##_type(f, js, o, _type); \ - carray_insert(this, i, o); \ + carray_insert(self, i, o); \ } #define GENCODECS_LISTTYPE_STRUCT(_type) \ - __carray_init(this, root->size, struct _type, , ); \ + __carray_init(self, root->size, struct _type, , ); \ for (i = 0; i < root->size; ++i) { \ jsmnf_pair *f = root->fields + i; \ struct _type o = { 0 }; \ long _ret = _type##_from_jsmnf(f, js, &o); \ if (_ret < 0) return _ret; \ ret += _ret; \ - carray_insert(this, i, o); \ + carray_insert(self, i, o); \ } #define GENCODECS_LISTTYPE_PTR(_type, _decor) \ - __carray_init(this, root->size, _type _decor, , ); \ + __carray_init(self, root->size, _type _decor, , ); \ for (i = 0; i < root->size; ++i) { \ jsmnf_pair *f = root->fields + i; \ _type *o; \ GENCODECS_JSON_DECODER_PTR_##_type(f, js, o, _type); \ - carray_insert(this, i, o); \ + carray_insert(self, i, o); \ } #define GENCODECS_LIST_END \ return ret; \ @@ -109,7 +109,7 @@ #define GENCODECS_PUB_STRUCT(_type) \ size_t _type##_from_json(const char buf[], size_t size, \ - struct _type *this) \ + struct _type *self) \ { \ size_t nbytes = 0; \ jsmn_parser parser; \ @@ -124,7 +124,7 @@ if (0 < jsmnf_load_auto(&loader, buf, tokens, parser.toknext, \ &pairs, &tmp)) { \ long ret; \ - if (0 < (ret = _type##_from_jsmnf(pairs, buf, this))) \ + if (0 < (ret = _type##_from_jsmnf(pairs, buf, self))) \ nbytes = ret; \ free(pairs); \ } \ diff --git a/gencodecs/recipes/json-encoder.h b/gencodecs/recipes/json-encoder.h index e7a326e26..ef4c04eee 100644 --- a/gencodecs/recipes/json-encoder.h +++ b/gencodecs/recipes/json-encoder.h @@ -14,8 +14,8 @@ #define GENCODECS_PUB_STRUCT(_type) \ jsonbcode _type##_to_jsonb(jsonb *b, char buf[], size_t size, \ - const struct _type *this); \ - size_t _type##_to_json(char buf[], size_t size, const struct _type *this); + const struct _type *self); \ + size_t _type##_to_json(char buf[], size_t size, const struct _type *self); #define GENCODECS_PUB_LIST(_type) GENCODECS_PUB_STRUCT(_type) #include "gencodecs-gen.pre.h" @@ -24,7 +24,7 @@ #define GENCODECS_STRUCT(_type) \ static jsonbcode _type##_to_jsonb(jsonb *b, char buf[], size_t size, \ - const struct _type *this); + const struct _type *self); #define GENCODECS_LIST(_type) GENCODECS_STRUCT(_type) #include "gencodecs-gen.pre.h" @@ -38,25 +38,25 @@ #define GENCODECS_PUB_STRUCT(_type) \ jsonbcode _type##_to_jsonb(jsonb *b, char buf[], size_t size, \ - const struct _type *this) \ + const struct _type *self) \ { \ jsonbcode code; \ if (0 > (code = jsonb_object(b, buf, size))) return code; \ - if (this != NULL) { + if (self != NULL) { #define GENCODECS_STRUCT(_type) \ static GENCODECS_PUB_STRUCT(_type) #define GENCODECS_FIELD_CUSTOM(_name, _key, _type, _decor, _init, _cleanup, \ _encoder, _decoder, _default_value) \ if (0 > (code = jsonb_key(b, buf, size, _key, sizeof(_key) - 1))) \ return code; \ - _encoder(b, buf, size, this->_name, _type); + _encoder(b, buf, size, self->_name, _type); #define GENCODECS_FIELD_PRINTF(_name, _type, _printf_type, _scanf_type) \ if (0 > (code = jsonb_key(b, buf, size, #_name, sizeof(#_name) - 1))) \ return code; \ else { \ char tok[64]; \ int toklen; \ - toklen = sprintf(tok, _printf_type, this->_name); \ + toklen = sprintf(tok, _printf_type, self->_name); \ if (0 > (code = jsonb_token(b, buf, size, tok, toklen))) \ return code; \ } @@ -68,26 +68,26 @@ #define GENCODECS_PUB_LIST(_type) \ jsonbcode _type##_to_jsonb(jsonb *b, char buf[], size_t size, \ - const struct _type *this) \ + const struct _type *self) \ { \ jsonbcode code; \ if (0 > (code = jsonb_array(b, buf, size))) return code; \ - if (this != NULL) { \ + if (self != NULL) { \ int i; #define GENCODECS_LIST(_type) \ static GENCODECS_PUB_LIST(_type) #define GENCODECS_LISTTYPE(_type) \ - for (i = 0; i < this->size; ++i) \ - GENCODECS_JSON_ENCODER_##_type(b, buf, size, this->array[i], \ + for (i = 0; i < self->size; ++i) \ + GENCODECS_JSON_ENCODER_##_type(b, buf, size, self->array[i], \ _type); #define GENCODECS_LISTTYPE_STRUCT(_type) \ - for (i = 0; i < this->size; ++i) \ + for (i = 0; i < self->size; ++i) \ if (0 > (code = _type##_to_jsonb(b, buf, size, \ - &this->array[i]))) \ + &self->array[i]))) \ return code; #define GENCODECS_LISTTYPE_PTR(_type, _decor) \ - for (i = 0; i < this->size; ++i) \ - GENCODECS_JSON_ENCODER_PTR_##_type(b, buf, size, this->array[i], \ + for (i = 0; i < self->size; ++i) \ + GENCODECS_JSON_ENCODER_PTR_##_type(b, buf, size, self->array[i], \ _type); #define GENCODECS_LIST_END \ } \ @@ -99,12 +99,12 @@ #define GENCODECS_PUB_STRUCT(_type) \ size_t _type##_to_json(char buf[], size_t size, \ - const struct _type *this) \ + const struct _type *self) \ { \ jsonb b; \ jsonbcode code; \ jsonb_init(&b); \ - code = _type##_to_jsonb(&b, buf, size, this); \ + code = _type##_to_jsonb(&b, buf, size, self); \ return code < 0 ? 0 : b.pos; \ } #define GENCODECS_PUB_LIST(_type) GENCODECS_PUB_STRUCT(_type) diff --git a/gencodecs/recipes/struct.h b/gencodecs/recipes/struct.h index 859c42350..785ef00ff 100644 --- a/gencodecs/recipes/struct.h +++ b/gencodecs/recipes/struct.h @@ -69,65 +69,65 @@ #ifdef GENCODECS_HEADER #define GENCODECS_PUB_STRUCT(_type) \ - void _type##_init(struct _type *this); \ - void _type##_cleanup(struct _type *this); + void _type##_init(struct _type *self); \ + void _type##_cleanup(struct _type *self); #define GENCODECS_PUB_LIST(_type) \ - void _type##_cleanup(struct _type *this); + void _type##_cleanup(struct _type *self); #include "gencodecs-gen.pre.h" #elif defined(GENCODECS_FORWARD) #define GENCODECS_STRUCT(_type) \ - static void _type##_init(struct _type *this); \ - static void _type##_cleanup(struct _type *this); + static void _type##_init(struct _type *self); \ + static void _type##_cleanup(struct _type *self); #define GENCODECS_LIST(_type) \ - static void _type##_cleanup(struct _type *this); + static void _type##_cleanup(struct _type *self); #include "gencodecs-gen.pre.h" #else #define GENCODECS_PUB_STRUCT(_type) \ - void _type##_init(struct _type *this) \ + void _type##_init(struct _type *self) \ { #define GENCODECS_STRUCT(_type) \ static GENCODECS_PUB_STRUCT(_type) #define GENCODECS_FIELD_CUSTOM(_name, _key, _type, _decor, _init, _cleanup, \ _encoder, _decoder, _default_value) \ - this->_name = _default_value; + self->_name = _default_value; #define GENCODECS_FIELD_PRINTF(_name, _type, printf_type, _scanf_type) \ - this->_name = (_type)0; + self->_name = (_type)0; #define GENCODECS_STRUCT_END \ } #include "gencodecs-gen.pre.h" #define GENCODECS_PUB_STRUCT(_type) \ - void _type##_cleanup(struct _type *this) \ + void _type##_cleanup(struct _type *self) \ { #define GENCODECS_STRUCT(_type) \ static GENCODECS_PUB_STRUCT(_type) #define GENCODECS_FIELD(_name, _type, _default_value) \ - (void)this->_name; + (void)self->_name; #define GENCODECS_FIELD_CUSTOM(_name, _key, _type, _decor, _init, _cleanup, \ _encoder, _decoder, _default_value) \ - _cleanup(this->_name, _type); + _cleanup(self->_name, _type); #define GENCODECS_STRUCT_END \ } #define GENCODECS_PUB_LIST(_type) \ - void _type##_cleanup(struct _type *this) \ + void _type##_cleanup(struct _type *self) \ { #define GENCODECS_LIST(_type) \ static GENCODECS_PUB_LIST(_type) #define GENCODECS_LISTTYPE(_type) \ - __carray_free(this, _type, NULL, NULL); + __carray_free(self, _type, NULL, NULL); #define GENCODECS_LISTTYPE_STRUCT(_type) \ - __carray_free(this, struct _type, NULL, \ + __carray_free(self, struct _type, NULL, \ _type##_cleanup(&__CARRAY_OPERAND_A)); #define GENCODECS_LISTTYPE_PTR(_type, _decor) \ - __carray_free(this, _type _decor, NULL, free(__CARRAY_OPERAND_A)); + __carray_free(self, _type _decor, NULL, free(__CARRAY_OPERAND_A)); #define GENCODECS_LIST_END \ } From 2ff4fbc5436f8a9e215014c448fb52f3982f0069 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 1 Jun 2022 15:21:10 -0300 Subject: [PATCH 107/118] chore: add C extern guards for C++ --- include/discord-internal.h | 8 ++++++++ include/discord.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/include/discord-internal.h b/include/discord-internal.h index 3c08b0bdc..29d1fb07a 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -8,6 +8,10 @@ #ifndef DISCORD_INTERNAL_H #define DISCORD_INTERNAL_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + #include #define JSONB_HEADER @@ -1177,4 +1181,8 @@ struct discord { /** @} DiscordInternal */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* DISCORD_INTERNAL_H */ diff --git a/include/discord.h b/include/discord.h index d86888391..6c0ed8007 100644 --- a/include/discord.h +++ b/include/discord.h @@ -11,6 +11,10 @@ #ifndef DISCORD_H #define DISCORD_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + #include #include @@ -504,4 +508,8 @@ bool discord_timer_cancel_and_delete(struct discord *client, unsigned id); /** @} DiscordTimer */ /** @} Discord */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* DISCORD_H */ From 2d2327c5255e3d5ed94825b1c7355cb9ceac5602 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 1 Jun 2022 20:49:59 -0300 Subject: [PATCH 108/118] refactor(discord-gateway): better modularization --- include/discord-internal.h | 84 +++++----- src/discord-client.c | 14 +- src/discord-gateway.c | 292 +++++++++++++++++---------------- src/discord-gateway_dispatch.c | 4 +- src/discord-messagecommands.c | 7 +- 5 files changed, 206 insertions(+), 195 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index 29d1fb07a..dd3012d17 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -644,12 +644,51 @@ typedef void (*discord_ev)(struct discord *client, void *event); #define DISCORD_SESSION_SHUTDOWN 1u << 1 /** @} DiscordInternalGatewaySessionStatus */ +/** @brief The handle for storing the Discord Gateway session */ +struct discord_gateway_session { + /** whether client is ready to start sending/receiving events */ + bool is_ready; + /** session id for resuming lost connections */ + char id[64]; + /** amount of shards being used by this session */ + int shards; + /** the session base url */ + char base_url[256]; + /** session limits */ + struct discord_session_start_limit start_limit; + /** active concurrent sessions */ + int concurrent; + /** event counter to avoid reaching limit of 120 events per 60 sec */ + int event_count; + /** @ref DiscordInternalGatewaySessionStatus */ + unsigned status; + + /** retry connection structure */ + struct { + /** will attempt reconnecting if true */ + bool enable; + /** current retry attempt (resets to 0 when succesful) */ + int attempt; + /** max amount of retries before giving up */ + int limit; + } retry; +}; + /** @brief The handle for storing the Discord response payload */ struct discord_gateway_payload { /** current iteration JSON string data */ - char *json; - /** current iteration JSON string data length */ - size_t length; + struct ccord_szbuf json; + /** parse JSON tokens into a `jsmnf_pairs` key/value pairs hashtable */ + struct { + /** current iteration JSON key/value pairs */ + jsmnf_pair *pairs; + /** current iteration number of JSON key/value pairs */ + unsigned npairs; + /** current iteration JSON tokens (fed to `jsmnf_pair`) */ + jsmntok_t *tokens; + /** current iteration number of JSON tokens */ + unsigned ntokens; + } parse; /** field 'op' */ enum discord_gateway_opcodes opcode; @@ -716,44 +755,7 @@ struct discord_gateway { struct discord_identify id; /** on-going session structure */ - struct { - /** whether client is ready to start sending/receiving events */ - bool is_ready; - /** session id for resuming lost connections */ - char id[64]; - /** amount of shards being used by this session */ - int shards; - /** session limits */ - struct discord_session_start_limit start_limit; - /** active concurrent sessions */ - int concurrent; - /** event counter to avoid reaching limit of 120 events per 60 sec */ - int event_count; - /** @ref DiscordInternalGatewaySessionStatus */ - unsigned status; - - /** retry connection structure */ - struct { - /** will attempt reconnecting if true */ - bool enable; - /** current retry attempt (resets to 0 when succesful) */ - int attempt; - /** max amount of retries before giving up */ - int limit; - } retry; - } * session; - - /** parse JSON tokens into a `jsmnf_pairs` key/value pairs hashtable */ - struct { - /** current iteration JSON key/value pairs */ - jsmnf_pair *pairs; - /** current iteration number of JSON key/value pairs */ - unsigned npairs; - /** current iteration JSON tokens (fed to `jsmnf_pair`) */ - jsmntok_t *tokens; - /** current iteration number of JSON tokens */ - unsigned ntokens; - } parse; + struct discord_gateway_session *session; /** response-payload structure */ struct discord_gateway_payload payload; diff --git a/src/discord-client.c b/src/discord-client.c index 4df646a09..ae8304474 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -123,14 +123,16 @@ _discord_clone_gateway(struct discord_gateway *clone, const struct discord_gateway *orig) { const size_t n = - orig->parse.npairs - (size_t)(orig->payload.data - orig->parse.pairs); + orig->payload.parse.npairs + - (size_t)(orig->payload.data - orig->payload.parse.pairs); - clone->payload.data = malloc(n * sizeof *orig->parse.pairs); + clone->payload.data = malloc(n * sizeof *orig->payload.parse.pairs); memcpy(clone->payload.data, orig->payload.data, - n * sizeof *orig->parse.pairs); + n * sizeof *orig->payload.parse.pairs); - clone->payload.length = cog_strndup( - orig->payload.json, orig->payload.length, &clone->payload.json); + clone->payload.json.size = + cog_strndup(orig->payload.json.start, orig->payload.json.size, + &clone->payload.json.start); } struct discord * @@ -150,7 +152,7 @@ static void _discord_clone_gateway_cleanup(struct discord_gateway *clone) { free(clone->payload.data); - free(clone->payload.json); + free(clone->payload.json.start); } static void diff --git a/src/discord-gateway.c b/src/discord-gateway.c index cc95cd5c0..3c1cf283a 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -52,9 +52,7 @@ _discord_gateway_close_opcode_print(enum discord_gateway_close_opcodes opcode) CASE_RETURN_STR(DISCORD_GATEWAY_CLOSE_REASON_DISALLOWED_INTENTS); CASE_RETURN_STR(DISCORD_GATEWAY_CLOSE_REASON_RECONNECT); default: { - const char *str; - - str = ws_close_opcode_print((enum ws_close_reason)opcode); + const char *str = ws_close_opcode_print((enum ws_close_reason)opcode); if (str) return str; log_warn("Unknown WebSockets close opcode (code: %d)", opcode); @@ -64,14 +62,14 @@ _discord_gateway_close_opcode_print(enum discord_gateway_close_opcodes opcode) } static void -on_hello(struct discord_gateway *gw) +_discord_on_hello(struct discord_gateway *gw) { jsmnf_pair *f; - if ((f = jsmnf_find(gw->payload.data, gw->payload.json, + if ((f = jsmnf_find(gw->payload.data, gw->payload.json.start, "heartbeat_interval", 18))) gw->timer->hbeat_interval = - strtoll(gw->payload.json + f->v.pos, NULL, 10); + strtoll(gw->payload.json.start + f->v.pos, NULL, 10); if (gw->session->status & DISCORD_SESSION_RESUMABLE) discord_gateway_send_resume(gw, &(struct discord_resume){ @@ -178,12 +176,11 @@ _discord_gateway_dispatch_thread(void *p_gw) } static void -on_dispatch(struct discord_gateway *gw) +_discord_on_dispatch(struct discord_gateway *gw) { - /* get dispatch event opcode */ - enum discord_event_scheduler mode; + struct discord *client = CLIENT(gw, gw); - /* XXX: this should only apply for user dispatched payloads? */ + /* TODO: this should only apply for user dispatched payloads? */ #if 0 /* Ratelimit check */ if (gw->timer->now - gw->timer->event_last < 60000) { @@ -203,10 +200,10 @@ on_dispatch(struct discord_gateway *gw) logconf_info(&gw->conf, "Succesfully started a Discord session!"); - if ((f = jsmnf_find(gw->payload.data, gw->payload.json, "session_id", - 10))) + if ((f = jsmnf_find(gw->payload.data, gw->payload.json.start, + "session_id", 10))) snprintf(gw->session->id, sizeof(gw->session->id), "%.*s", - (int)f->v.len, gw->payload.json + f->v.pos); + (int)f->v.len, gw->payload.json.start + f->v.pos); ASSERT_S(*gw->session->id, "Missing session_id from READY event"); gw->session->is_ready = true; @@ -226,11 +223,11 @@ on_dispatch(struct discord_gateway *gw) break; } - mode = gw->scheduler(CLIENT(gw, gw), - gw->payload.json + gw->payload.data->v.pos, - gw->payload.data->v.len, gw->payload.event); + /* get dispatch event opcode */ + enum discord_event_scheduler mode = + gw->scheduler(client, gw->payload.json.start + gw->payload.data->v.pos, + gw->payload.data->v.len, gw->payload.event); - /* user subscribed to event */ switch (mode) { case DISCORD_EVENT_IGNORE: break; @@ -240,7 +237,7 @@ on_dispatch(struct discord_gateway *gw) case DISCORD_EVENT_WORKER_THREAD: { struct discord_gateway *clone = _discord_gateway_clone(gw); CCORDcode code = discord_worker_add( - CLIENT(gw, gw), &_discord_gateway_dispatch_thread, clone); + client, &_discord_gateway_dispatch_thread, clone); if (code != CCORD_OK) { log_error("Couldn't start worker-thread (code %d)", code); @@ -253,16 +250,17 @@ on_dispatch(struct discord_gateway *gw) } static void -on_invalid_session(struct discord_gateway *gw) +_discord_on_invalid_session(struct discord_gateway *gw) { enum ws_close_reason opcode; const char *reason; - gw->session->status = DISCORD_SESSION_SHUTDOWN; - /* attempt to resume if session isn't invalid */ + gw->session->retry.enable = true; + gw->session->status = DISCORD_SESSION_SHUTDOWN; if (gw->payload.data->v.len != 5 - || strncmp("false", gw->payload.json + gw->payload.data->v.pos, 5)) + || strncmp("false", gw->payload.json.start + gw->payload.data->v.pos, + 5)) { gw->session->status |= DISCORD_SESSION_RESUMABLE; reason = "Invalid session, will attempt to resume"; @@ -272,14 +270,13 @@ on_invalid_session(struct discord_gateway *gw) reason = "Invalid session, can't resume"; opcode = WS_CLOSE_REASON_NORMAL; } - gw->session->retry.enable = true; ws_close(gw->ws, opcode, reason, SIZE_MAX); io_poller_curlm_enable_perform(CLIENT(gw, gw)->io_poller, gw->mhandle); } static void -on_reconnect(struct discord_gateway *gw) +_discord_on_reconnect(struct discord_gateway *gw) { const char reason[] = "Discord expects client to reconnect"; @@ -293,7 +290,7 @@ on_reconnect(struct discord_gateway *gw) } static void -on_heartbeat_ack(struct discord_gateway *gw) +_discord_on_heartbeat_ack(struct discord_gateway *gw) { /* get request / response interval in milliseconds */ pthread_rwlock_wrlock(&gw->timer->rwlock); @@ -304,10 +301,10 @@ on_heartbeat_ack(struct discord_gateway *gw) } static void -on_connect_cb(void *p_gw, - struct websockets *ws, - struct ws_info *info, - const char *ws_protocols) +_ws_on_connect(void *p_gw, + struct websockets *ws, + struct ws_info *info, + const char *ws_protocols) { (void)ws; (void)info; @@ -317,12 +314,12 @@ on_connect_cb(void *p_gw, } static void -on_close_cb(void *p_gw, - struct websockets *ws, - struct ws_info *info, - enum ws_close_reason wscode, - const char *reason, - size_t len) +_ws_on_close(void *p_gw, + struct websockets *ws, + struct ws_info *info, + enum ws_close_reason wscode, + const char *reason, + size_t len) { (void)ws; (void)info; @@ -383,53 +380,62 @@ on_close_cb(void *p_gw, } } +static bool +_discord_gateway_payload_from_json(struct discord_gateway_payload *payload, + const char text[], + size_t len) +{ + payload->json = (struct ccord_szbuf){ (char *)text, len }; + + jsmn_parser parser; + jsmn_init(&parser); + if (jsmn_parse_auto(&parser, text, len, &payload->parse.tokens, + &payload->parse.ntokens) + <= 0) + return false; + + jsmnf_loader loader; + jsmnf_init(&loader); + if (jsmnf_load_auto(&loader, text, payload->parse.tokens, parser.toknext, + &payload->parse.pairs, &payload->parse.npairs) + <= 0) + return false; + + jsmnf_pair *f; + if ((f = jsmnf_find(payload->parse.pairs, text, "t", 1))) { + if (JSMN_STRING == f->type) + snprintf(payload->name, sizeof(payload->name), "%.*s", + (int)f->v.len, text + f->v.pos); + else + *payload->name = '\0'; + + payload->event = _discord_gateway_event_eval(payload->name); + } + if ((f = jsmnf_find(payload->parse.pairs, text, "s", 1))) { + int seq = (int)strtol(text + f->v.pos, NULL, 10); + if (seq) payload->seq = seq; + } + if ((f = jsmnf_find(payload->parse.pairs, text, "op", 2))) + payload->opcode = + (enum discord_gateway_opcodes)strtol(text + f->v.pos, NULL, 10); + payload->data = jsmnf_find(payload->parse.pairs, text, "d", 1); + + return true; +} + static void -on_text_cb(void *p_gw, - struct websockets *ws, - struct ws_info *info, - const char *text, - size_t len) +_ws_on_text(void *p_gw, + struct websockets *ws, + struct ws_info *info, + const char *text, + size_t len) { (void)ws; struct discord_gateway *gw = p_gw; - jsmn_parser parser; - - gw->payload.json = (char *)text; - gw->payload.length = len; - jsmn_init(&parser); - if (0 < jsmn_parse_auto(&parser, text, len, &gw->parse.tokens, - &gw->parse.ntokens)) - { - jsmnf_loader loader; - - jsmnf_init(&loader); - if (0 < jsmnf_load_auto(&loader, text, gw->parse.tokens, - parser.toknext, &gw->parse.pairs, - &gw->parse.npairs)) - { - jsmnf_pair *f; - - if ((f = jsmnf_find(gw->parse.pairs, text, "t", 1))) { - if (JSMN_STRING == f->type) - snprintf(gw->payload.name, sizeof(gw->payload.name), - "%.*s", (int)f->v.len, - gw->payload.json + f->v.pos); - else - *gw->payload.name = '\0'; - - gw->payload.event = - _discord_gateway_event_eval(gw->payload.name); - } - if ((f = jsmnf_find(gw->parse.pairs, text, "s", 1))) { - int seq = (int)strtol(gw->payload.json + f->v.pos, NULL, 10); - if (seq) gw->payload.seq = seq; - } - if ((f = jsmnf_find(gw->parse.pairs, text, "op", 2))) - gw->payload.opcode = (enum discord_gateway_opcodes)strtol( - gw->payload.json + f->v.pos, NULL, 10); - gw->payload.data = jsmnf_find(gw->parse.pairs, text, "d", 1); - } + if (!_discord_gateway_payload_from_json(&gw->payload, text, len)) { + logconf_fatal(&gw->conf, "Couldn't parse Gateway Payload"); + return; } logconf_trace( @@ -442,19 +448,19 @@ on_text_cb(void *p_gw, switch (gw->payload.opcode) { case DISCORD_GATEWAY_DISPATCH: - on_dispatch(gw); + _discord_on_dispatch(gw); break; case DISCORD_GATEWAY_INVALID_SESSION: - on_invalid_session(gw); + _discord_on_invalid_session(gw); break; case DISCORD_GATEWAY_RECONNECT: - on_reconnect(gw); + _discord_on_reconnect(gw); break; case DISCORD_GATEWAY_HELLO: - on_hello(gw); + _discord_on_hello(gw); break; case DISCORD_GATEWAY_HEARTBEAT_ACK: - on_heartbeat_ack(gw); + _discord_on_heartbeat_ack(gw); break; default: logconf_error(&gw->conf, @@ -465,10 +471,10 @@ on_text_cb(void *p_gw, } static discord_event_scheduler_t -default_scheduler_cb(struct discord *a, - const char b[], - size_t c, - enum discord_gateway_events d) +_discord_on_scheduler_default(struct discord *a, + const char b[], + size_t c, + enum discord_gateway_events d) { (void)a; (void)b; @@ -493,9 +499,9 @@ discord_gateway_init(struct discord_gateway *gw, struct discord *client = CLIENT(gw, gw); /* Web-Sockets callbacks */ struct ws_callbacks cbs = { .data = gw, - .on_connect = &on_connect_cb, - .on_text = &on_text_cb, - .on_close = &on_close_cb }; + .on_connect = &_ws_on_connect, + .on_text = &_ws_on_text, + .on_close = &_ws_on_close }; /* Web-Sockets custom attributes */ struct ws_attr attr = { .conf = conf }; @@ -515,6 +521,9 @@ discord_gateway_init(struct discord_gateway *gw, gw->session->retry.enable = true; gw->session->retry.limit = 5; /* FIXME: shouldn't be a hard limit */ + /* default callbacks */ + gw->scheduler = _discord_on_scheduler_default; + /* connection identify token */ gw->id.token = (char *)token; /* connection identify properties */ @@ -526,10 +535,8 @@ discord_gateway_init(struct discord_gateway *gw, gw->id.presence = calloc(1, sizeof *gw->id.presence); gw->id.presence->status = "online"; gw->id.presence->since = cog_timestamp_ms(); - discord_gateway_send_presence_update(gw, gw->id.presence); - /* default callbacks */ - gw->scheduler = default_scheduler_cb; + discord_gateway_send_presence_update(gw, gw->id.presence); } void @@ -553,8 +560,8 @@ discord_gateway_cleanup(struct discord_gateway *gw) free(gw->id.presence); /* cleanup client session */ free(gw->session); - if (gw->parse.pairs) free(gw->parse.pairs); - if (gw->parse.tokens) free(gw->parse.tokens); + if (gw->payload.parse.pairs) free(gw->payload.parse.pairs); + if (gw->payload.parse.tokens) free(gw->payload.parse.tokens); } #ifdef CCORD_DEBUG_WEBSOCKETS @@ -644,13 +651,47 @@ _ws_curl_debug_trace( } #endif /* CCORD_DEBUG_WEBSOCKETS */ +static bool +_discord_gateway_session_from_json(struct discord_gateway_session *session, + const char text[], + size_t len) +{ + jsmn_parser parser; + jsmntok_t tokens[32]; + jsmn_init(&parser); + if (jsmn_parse(&parser, text, len, tokens, sizeof(tokens) / sizeof *tokens) + <= 0) + return false; + + jsmnf_loader loader; + jsmnf_pair pairs[32]; + jsmnf_init(&loader); + if (jsmnf_load(&loader, text, tokens, parser.toknext, pairs, + sizeof(pairs) / sizeof *pairs) + <= 0) + return false; + + jsmnf_pair *f; + if ((f = jsmnf_find(pairs, text, "url", 3))) { + const char *url = text + f->v.pos; + int url_len = (int)f->v.len; + + url_len = snprintf(session->base_url, sizeof(session->base_url), + "%.*s%c" DISCORD_GATEWAY_URL_SUFFIX, url_len, url, + ('/' == url[url_len - 1]) ? '\0' : '/'); + ASSERT_NOT_OOB(url_len, sizeof(session->base_url)); + } + if ((f = jsmnf_find(pairs, text, "shards", 6))) + session->shards = (int)strtol(text + f->v.pos, NULL, 10); + if ((f = jsmnf_find(pairs, text, "session_start_limit", 19))) + discord_session_start_limit_from_jsmnf(f, text, &session->start_limit); + return true; +} + CCORDcode discord_gateway_start(struct discord_gateway *gw) { - struct discord *client = CLIENT(gw, gw); struct ccord_szbuf json = { 0 }; - char url[1024]; - CURL *ehandle; if (gw->session->retry.attempt >= gw->session->retry.limit) { logconf_fatal(&gw->conf, @@ -659,50 +700,16 @@ discord_gateway_start(struct discord_gateway *gw) return CCORD_DISCORD_CONNECTION; } - else if (CCORD_OK != discord_get_gateway_bot(client, &json)) { + + if (discord_get_gateway_bot(CLIENT(gw, gw), &json) != CCORD_OK + || !_discord_gateway_session_from_json(gw->session, json.start, + json.size)) + { logconf_fatal(&gw->conf, "Couldn't retrieve Gateway Bot information"); + free(json.start); return CCORD_DISCORD_BAD_AUTH; } - else { - jsmn_parser parser; - jsmntok_t tokens[32]; - - jsmn_init(&parser); - if (0 < jsmn_parse(&parser, json.start, json.size, tokens, - sizeof(tokens) / sizeof *tokens)) - { - jsmnf_loader loader; - jsmnf_pair pairs[32]; - - jsmnf_init(&loader); - if (0 < jsmnf_load(&loader, json.start, tokens, parser.toknext, - pairs, sizeof(pairs) / sizeof *pairs)) - { - jsmnf_pair *f; - - if ((f = jsmnf_find(pairs, json.start, "url", 3))) { - const char *base_url = json.start + f->v.pos; - const int base_url_len = (int)f->v.len; - int len; - - len = snprintf( - url, sizeof(url), "%.*s%s" DISCORD_GATEWAY_URL_SUFFIX, - base_url_len, base_url, - ('/' == base_url[base_url_len - 1]) ? "" : "/"); - ASSERT_NOT_OOB(len, sizeof(url)); - } - if ((f = jsmnf_find(pairs, json.start, "shards", 6))) - gw->session->shards = - (int)strtol(json.start + f->v.pos, NULL, 10); - if ((f = jsmnf_find(pairs, json.start, "session_start_limit", - 19))) - discord_session_start_limit_from_jsmnf( - f, json.start, &gw->session->start_limit); - } - } - } - free(json.start); if (!gw->session->start_limit.remaining) { @@ -715,14 +722,13 @@ discord_gateway_start(struct discord_gateway *gw) return CCORD_DISCORD_RATELIMIT; } - ws_set_url(gw->ws, url, NULL); - ehandle = ws_start(gw->ws); - -#ifdef CCORD_DEBUG_WEBSOCKETS + ws_set_url(gw->ws, gw->session->base_url, NULL); +#ifndef CCORD_DEBUG_WEBSOCKETS + ws_start(gw->ws); +#else + CURL *ehandle = ws_start(gw->ws); curl_easy_setopt(ehandle, CURLOPT_DEBUGFUNCTION, _ws_curl_debug_trace); curl_easy_setopt(ehandle, CURLOPT_VERBOSE, 1L); -#else - (void)ehandle; #endif /* CCORD_DEBUG_WEBSOCKETS */ return CCORD_OK; diff --git a/src/discord-gateway_dispatch.c b/src/discord-gateway_dispatch.c index a2bf8a6ed..9b0bbd1a0 100644 --- a/src/discord-gateway_dispatch.c +++ b/src/discord-gateway_dispatch.c @@ -124,8 +124,8 @@ discord_gateway_dispatch(struct discord_gateway *gw) if (gw->cbs[event]) { void *event_data = calloc(1, dispatch[event].size); - dispatch[event].from_jsmnf(gw->payload.data, gw->payload.json, - event_data); + dispatch[event].from_jsmnf(gw->payload.data, + gw->payload.json.start, event_data); if (CCORD_UNAVAILABLE == discord_refcounter_incr(&client->refcounter, event_data)) diff --git a/src/discord-messagecommands.c b/src/discord-messagecommands.c index c5eeff1c9..d3730c17e 100644 --- a/src/discord-messagecommands.c +++ b/src/discord-messagecommands.c @@ -124,11 +124,11 @@ discord_message_commands_try_perform(struct discord_message_commands *cmds, { jsmnf_pair *f; - if (!(f = jsmnf_find(payload->data, payload->json, "content", 7))) + if (!(f = jsmnf_find(payload->data, payload->json.start, "content", 7))) return false; if (cmds->length - && !strncmp(cmds->prefix.start, payload->json + f->v.pos, + && !strncmp(cmds->prefix.start, payload->json.start + f->v.pos, cmds->prefix.size)) { struct discord *client = CLIENT(cmds, commands); @@ -137,7 +137,8 @@ discord_message_commands_try_perform(struct discord_message_commands *cmds, struct ccord_szbuf command; char *tmp; - discord_message_from_jsmnf(payload->data, payload->json, event_data); + discord_message_from_jsmnf(payload->data, payload->json.start, + event_data); command.start = event_data->content + cmds->prefix.size; command.size = strcspn(command.start, " \n\t\r"); From 1d58ad4d280038b81ba2e41501880d03ff96cf35 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 1 Jun 2022 21:30:52 -0300 Subject: [PATCH 109/118] refactor(discord-gateway): merge 'struct discord_gateway_payload' fields ('parse' and 'json') --- include/discord-internal.h | 22 ++++++++++++---------- src/discord-client.c | 9 ++++----- src/discord-gateway.c | 23 ++++++++++++----------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/include/discord-internal.h b/include/discord-internal.h index dd3012d17..2c87d4cde 100644 --- a/include/discord-internal.h +++ b/include/discord-internal.h @@ -676,19 +676,21 @@ struct discord_gateway_session { /** @brief The handle for storing the Discord response payload */ struct discord_gateway_payload { - /** current iteration JSON string data */ - struct ccord_szbuf json; - /** parse JSON tokens into a `jsmnf_pairs` key/value pairs hashtable */ + /** current iteration JSON */ struct { - /** current iteration JSON key/value pairs */ - jsmnf_pair *pairs; - /** current iteration number of JSON key/value pairs */ - unsigned npairs; - /** current iteration JSON tokens (fed to `jsmnf_pair`) */ + /** the JSON text */ + char *start; + /** the text length */ + size_t size; + /** jsmn tokens */ jsmntok_t *tokens; - /** current iteration number of JSON tokens */ + /** amount of jsmn tokens */ unsigned ntokens; - } parse; + /** jsmn-find key/value pairs */ + jsmnf_pair *pairs; + /** amount of jsmn-find key/value pairs */ + unsigned npairs; + } json; /** field 'op' */ enum discord_gateway_opcodes opcode; diff --git a/src/discord-client.c b/src/discord-client.c index ae8304474..60020dd20 100644 --- a/src/discord-client.c +++ b/src/discord-client.c @@ -122,13 +122,12 @@ static void _discord_clone_gateway(struct discord_gateway *clone, const struct discord_gateway *orig) { - const size_t n = - orig->payload.parse.npairs - - (size_t)(orig->payload.data - orig->payload.parse.pairs); + const size_t n = orig->payload.json.npairs + - (size_t)(orig->payload.data - orig->payload.json.pairs); - clone->payload.data = malloc(n * sizeof *orig->payload.parse.pairs); + clone->payload.data = malloc(n * sizeof *orig->payload.json.pairs); memcpy(clone->payload.data, orig->payload.data, - n * sizeof *orig->payload.parse.pairs); + n * sizeof *orig->payload.json.pairs); clone->payload.json.size = cog_strndup(orig->payload.json.start, orig->payload.json.size, diff --git a/src/discord-gateway.c b/src/discord-gateway.c index 3c1cf283a..071c83410 100644 --- a/src/discord-gateway.c +++ b/src/discord-gateway.c @@ -385,24 +385,25 @@ _discord_gateway_payload_from_json(struct discord_gateway_payload *payload, const char text[], size_t len) { - payload->json = (struct ccord_szbuf){ (char *)text, len }; + payload->json.start = (char *)text; + payload->json.size = len; jsmn_parser parser; jsmn_init(&parser); - if (jsmn_parse_auto(&parser, text, len, &payload->parse.tokens, - &payload->parse.ntokens) + if (jsmn_parse_auto(&parser, text, len, &payload->json.tokens, + &payload->json.ntokens) <= 0) return false; jsmnf_loader loader; jsmnf_init(&loader); - if (jsmnf_load_auto(&loader, text, payload->parse.tokens, parser.toknext, - &payload->parse.pairs, &payload->parse.npairs) + if (jsmnf_load_auto(&loader, text, payload->json.tokens, parser.toknext, + &payload->json.pairs, &payload->json.npairs) <= 0) return false; jsmnf_pair *f; - if ((f = jsmnf_find(payload->parse.pairs, text, "t", 1))) { + if ((f = jsmnf_find(payload->json.pairs, text, "t", 1))) { if (JSMN_STRING == f->type) snprintf(payload->name, sizeof(payload->name), "%.*s", (int)f->v.len, text + f->v.pos); @@ -411,14 +412,14 @@ _discord_gateway_payload_from_json(struct discord_gateway_payload *payload, payload->event = _discord_gateway_event_eval(payload->name); } - if ((f = jsmnf_find(payload->parse.pairs, text, "s", 1))) { + if ((f = jsmnf_find(payload->json.pairs, text, "s", 1))) { int seq = (int)strtol(text + f->v.pos, NULL, 10); if (seq) payload->seq = seq; } - if ((f = jsmnf_find(payload->parse.pairs, text, "op", 2))) + if ((f = jsmnf_find(payload->json.pairs, text, "op", 2))) payload->opcode = (enum discord_gateway_opcodes)strtol(text + f->v.pos, NULL, 10); - payload->data = jsmnf_find(payload->parse.pairs, text, "d", 1); + payload->data = jsmnf_find(payload->json.pairs, text, "d", 1); return true; } @@ -560,8 +561,8 @@ discord_gateway_cleanup(struct discord_gateway *gw) free(gw->id.presence); /* cleanup client session */ free(gw->session); - if (gw->payload.parse.pairs) free(gw->payload.parse.pairs); - if (gw->payload.parse.tokens) free(gw->payload.parse.tokens); + if (gw->payload.json.pairs) free(gw->payload.json.pairs); + if (gw->payload.json.tokens) free(gw->payload.json.tokens); } #ifdef CCORD_DEBUG_WEBSOCKETS From 8c0aaf6e80387536cc400808d62291f3bbd5d982 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 5 Jun 2022 12:49:08 -0300 Subject: [PATCH 110/118] feat(Makefile): turn Makefile.dynamic into a special target 'shared' for Makefile --- Makefile | 23 ++++++-- Makefile.dynamic | 135 ----------------------------------------------- README.md | 9 ++-- 3 files changed, 21 insertions(+), 146 deletions(-) delete mode 100644 Makefile.dynamic diff --git a/Makefile b/Makefile index fe1ff5f98..d977ed81b 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,12 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ OBJS := $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) \ $(GENCODECS_OBJ) -LIB := $(LIBDIR)/libdiscord.a +ARLIB = $(LIBDIR)/libdiscord.a +ARFLAGS = -cqsv + +SOLIB = $(LIBDIR)/libdiscord.so +SOFLAGS = -fPIC +LDFLAGS = -lcurl CFLAGS += -std=c99 -O0 -g -pthread -D_XOPEN_SOURCE=600 \ -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) -I$(THIRDP_DIR) \ @@ -71,7 +76,11 @@ $(SRC_DIR)/%.o: $(SRC_DIR)/%.c %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -all: $(LIB) +all: $(ARLIB) + +shared: + @ $(MAKE) clean + $(MAKE) CFLAGS="$(SOFLAGS) $(CFLAGS)" $(SOLIB) voice: @ $(MAKE) XFLAGS=-DCCORD_VOICE XOBJ=$(SRC_DIR)/discord-voice.o all @@ -88,8 +97,10 @@ examples: all gencodecs: @ $(MAKE) -C $(GENCODECS_DIR) -$(LIB): $(OBJS) | $(LIBDIR) - $(AR) -cqsv $@ $? +$(ARLIB): $(OBJS) | $(LIBDIR) + $(AR) $(ARFLAGS) $@ $? +$(SOLIB): $(OBJS) | $(LIBDIR) + $(CC) -shared $(LDFLAGS) -o $@ $< $(LIBDIR): @ mkdir -p $@ @@ -104,11 +115,13 @@ $(OBJDIR): $@/$(SRC_DIR) \ $@/$(GENCODECS_DIR) +.IGNORE: install: @ mkdir -p $(PREFIX)/lib/ @ mkdir -p $(PREFIX)/include/concord install -d $(PREFIX)/lib/ - install -m 644 $(LIB) $(PREFIX)/lib/ + install -m 644 $(ARLIB) $(PREFIX)/lib/ + install -m 644 $(SOLIB) $(PREFIX)/lib/ install -d $(PREFIX)/include/concord/ install -m 644 $(INCLUDE_DIR)/*.h $(COGUTILS_DIR)/*.h $(CORE_DIR)/*.h \ $(THIRDP_DIR)/*.h $(GENCODECS_DIR)/*.h $(PREFIX)/include/concord/ diff --git a/Makefile.dynamic b/Makefile.dynamic deleted file mode 100644 index 7b9601d70..000000000 --- a/Makefile.dynamic +++ /dev/null @@ -1,135 +0,0 @@ -PREFIX = /usr/local -CC = gcc - -SRC_DIR = src -INCLUDE_DIR = include -OBJDIR = obj -LIBDIR = lib -DOCS_DIR = docs -COGUTILS_DIR = cog-utils -GENCODECS_DIR = gencodecs -CORE_DIR = core -THIRDP_DIR = $(CORE_DIR)/third-party -EXAMPLES_DIR = examples -TEST_DIR = test -CCORDDOCS_DIR = concord-docs - -GENCODECS_HDR = $(GENCODECS_DIR)/discord_codecs.h -GENCODECS_OBJ = $(GENCODECS_DIR)/discord_codecs.o - -COGUTILS_OBJS = $(COGUTILS_DIR)/cog-utils.o \ - $(COGUTILS_DIR)/log.o \ - $(COGUTILS_DIR)/logconf.o \ - $(COGUTILS_DIR)/json-build.o \ - $(COGUTILS_DIR)/jsmn-find.o -CORE_OBJS = $(CORE_DIR)/work.o \ - $(CORE_DIR)/user-agent.o \ - $(CORE_DIR)/websockets.o \ - $(CORE_DIR)/io_poller.o -THIRDP_OBJS = $(THIRDP_DIR)/sha1.o \ - $(THIRDP_DIR)/curl-websocket.o \ - $(THIRDP_DIR)/threadpool.o \ - $(THIRDP_DIR)/priority_queue.o -DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ - $(SRC_DIR)/discord-adapter.o \ - $(SRC_DIR)/discord-adapter_ratelimit.o \ - $(SRC_DIR)/discord-adapter_refcount.o \ - $(SRC_DIR)/discord-client.o \ - $(SRC_DIR)/discord-loop.o \ - $(SRC_DIR)/discord-gateway.o \ - $(SRC_DIR)/discord-timer.o \ - $(SRC_DIR)/discord-misc.o \ - $(SRC_DIR)/application_command.o \ - $(SRC_DIR)/interaction.o \ - $(SRC_DIR)/audit_log.o \ - $(SRC_DIR)/channel.o \ - $(SRC_DIR)/emoji.o \ - $(SRC_DIR)/gateway.o \ - $(SRC_DIR)/guild.o \ - $(SRC_DIR)/guild_template.o \ - $(SRC_DIR)/invite.o \ - $(SRC_DIR)/user.o \ - $(SRC_DIR)/voice.o \ - $(SRC_DIR)/webhook.o \ - $(XOBJ) - -OBJS = $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) \ - $(GENCODECS_OBJ) - -LIB = $(LIBDIR)/libdiscord.so -DYNLIB_DEPS = -lcurl - -CFLAGS += -std=c99 -O0 -g -pthread -D_XOPEN_SOURCE=600 \ - -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) -I$(THIRDP_DIR) \ - -I$(GENCODECS_DIR) -I$(PREFIX)/include -DLOG_USE_COLOR -WFLAGS += -Wall -Wextra -Wshadow -Wdouble-promotion -Wconversion -Wpedantic - -$(SRC_DIR)/%.o: $(SRC_DIR)/%.c - $(CC) $(CFLAGS) $(WFLAGS) $(XFLAGS) -c -o $@ $< -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -all: $(LIB) - -voice: - @ $(MAKE) XFLAGS=-DCCORD_VOICE XOBJ=$(SRC_DIR)/discord-voice.o all - -debug: - @ $(MAKE) XFLAGS="-DCCORD_DEBUG_WEBSOCKETS -DCCORD_DEBUG_ADAPTER" all - -test: all - @ $(MAKE) -C $(TEST_DIR) - -examples: all - @ $(MAKE) -C $(EXAMPLES_DIR) - -gencodecs: - @ $(MAKE) -C $(GENCODECS_DIR) - -$(LIB): $(OBJS) - $(CC) -shared $(DYNLIB_DEPS) -o $@ $(OBJS) - -$(LIBDIR): - @ mkdir -p $@ - -$(OBJS): $(GENCODECS_HDR) | $(OBJDIR) - -$(GENCODECS_HDR): gencodecs - -$(OBJDIR): - @ mkdir -p $@/$(THIRDP_DIR) \ - $@/$(COGUTILS_DIR) \ - $@/$(SRC_DIR) \ - $@/$(GENCODECS_DIR) - -install: - @ mkdir -p $(PREFIX)/lib/ - @ mkdir -p $(PREFIX)/include/concord - install -d $(PREFIX)/lib/ - install -m 644 $(LIB) $(PREFIX)/lib/ - install -d $(PREFIX)/include/concord/ - install -m 644 $(INCLUDE_DIR)/*.h $(COGUTILS_DIR)/*.h $(CORE_DIR)/*.h \ - $(THIRDP_DIR)/*.h $(GENCODECS_DIR)/*.h $(PREFIX)/include/concord/ - -docs: - @ $(MAKE) -C $(GENCODECS_DIR) docs - -echo: - @ echo -e 'CC: $(CC)\n' - @ echo -e 'PREFIX: $(PREFIX)\n' - @ echo -e 'CFLAGS: $(CFLAGS)\n' - @ echo -e 'COGUTILS_OBJS: $(COGUTILS_OBJS)\n' - @ echo -e 'CORE_OBJS: $(CORE_OBJS)\n' - @ echo -e 'DISCORD_OBJS: $(DISCORD_OBJS)\n' - @ echo -e 'OBJS: $(OBJS)\n' - -clean: - @ $(RM) $(GENCODECS_OBJS) $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) - @ $(MAKE) -C $(TEST_DIR) clean - @ $(MAKE) -C $(EXAMPLES_DIR) clean - -purge: clean - @ $(RM) -r $(LIBDIR) - @ $(MAKE) -C $(GENCODECS_DIR) clean - -.PHONY: test examples install echo clean purge docs gencodecs diff --git a/README.md b/README.md index c5f8afa80..cb5f98cea 100644 --- a/README.md +++ b/README.md @@ -178,12 +178,6 @@ On Windows with Cygwin, you might need to pass both arguments to use POSIX threa $ CFLAGS="-pthread -lpthread" make ``` -#### Dynamic Linking Support - -If you wish to produce a dynamically-linked version of Concord, use -`make -f Makefile.dynamic`. Note that this Makefile is intended only for -GNU-style compilers, like `gcc` or `clang`. - ### Configuring Concord The following outlines the default fields of `config.json` @@ -255,6 +249,9 @@ $ CFLAGS="-DCCORD_SIGINTCATCH -DCCORD_DEBUG_HTTP" make #### Special targets +* `make shared` + * Produce a dynamically-linked version of Concord. This Makefile is intented for GNU-style compilers, such as `gcc` or `clang`. + * `make voice` * Enable experimental Voice Connection handling - not production ready. * `make debug` From fd7a6ab1c935423940bb8969e25612d3e788d634 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 5 Jun 2022 13:27:15 -0300 Subject: [PATCH 111/118] chore(Makefile): simpler logic for adding custom variables --- Makefile | 29 +++++++++++------------- examples/Makefile | 56 +++++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/Makefile b/Makefile index d977ed81b..d429c8de2 100644 --- a/Makefile +++ b/Makefile @@ -53,11 +53,10 @@ DISCORD_OBJS = $(SRC_DIR)/concord-once.o \ $(SRC_DIR)/invite.o \ $(SRC_DIR)/user.o \ $(SRC_DIR)/voice.o \ - $(SRC_DIR)/webhook.o \ - $(XOBJ) + $(SRC_DIR)/webhook.o -OBJS := $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) \ - $(GENCODECS_OBJ) +OBJS = $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) \ + $(GENCODECS_OBJ) ARLIB = $(LIBDIR)/libdiscord.a ARFLAGS = -cqsv @@ -66,27 +65,27 @@ SOLIB = $(LIBDIR)/libdiscord.so SOFLAGS = -fPIC LDFLAGS = -lcurl -CFLAGS += -std=c99 -O0 -g -pthread -D_XOPEN_SOURCE=600 \ +WFLAGS += -Wall -Wextra -Wshadow -Wdouble-promotion -Wconversion -Wpedantic +CFLAGS = -std=c99 -O0 -g -pthread -D_XOPEN_SOURCE=600 \ -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) -I$(THIRDP_DIR) \ -I$(GENCODECS_DIR) -I$(PREFIX)/include -DLOG_USE_COLOR -WFLAGS += -Wall -Wextra -Wshadow -Wdouble-promotion -Wconversion -Wpedantic $(SRC_DIR)/%.o: $(SRC_DIR)/%.c - $(CC) $(CFLAGS) $(WFLAGS) $(XFLAGS) -c -o $@ $< -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(WFLAGS) -c -o $@ $< all: $(ARLIB) shared: @ $(MAKE) clean - $(MAKE) CFLAGS="$(SOFLAGS) $(CFLAGS)" $(SOLIB) + @ $(MAKE) CFLAGS="$(SOFLAGS) $(CFLAGS)" $(SOLIB) voice: - @ $(MAKE) XFLAGS=-DCCORD_VOICE XOBJ=$(SRC_DIR)/discord-voice.o all + @ $(MAKE) CFLAGS="$(CFLAGS) -DCCORD_VOICE" \ + OBJS="$(OBJS) $(SRC_DIR)/discord-voice.o" all debug: - @ $(MAKE) XFLAGS="-DCCORD_DEBUG_WEBSOCKETS -DCCORD_DEBUG_HTTP" all + @ $(MAKE) CFLAGS="$(CFLAGS) -DCCORD_DEBUG_WEBSOCKETS -DCCORD_DEBUG_HTTP" \ + all test: all @ $(MAKE) -C $(TEST_DIR) @@ -110,9 +109,7 @@ $(OBJS): $(GENCODECS_HDR) | $(OBJDIR) $(GENCODECS_HDR): gencodecs $(OBJDIR): - @ mkdir -p $@/$(THIRDP_DIR) \ - $@/$(COGUTILS_DIR) \ - $@/$(SRC_DIR) \ + @ mkdir -p $@/$(THIRDP_DIR) $@/$(COGUTILS_DIR) $@/$(SRC_DIR) \ $@/$(GENCODECS_DIR) .IGNORE: @@ -140,11 +137,11 @@ echo: clean: @ $(RM) $(GENCODECS_OBJS) $(COGUTILS_OBJS) $(CORE_OBJS) $(THIRDP_OBJS) $(DISCORD_OBJS) + @ $(RM) -r $(LIBDIR) @ $(MAKE) -C $(TEST_DIR) clean @ $(MAKE) -C $(EXAMPLES_DIR) clean purge: clean - @ $(RM) -r $(LIBDIR) @ $(MAKE) -C $(GENCODECS_DIR) clean .PHONY: test examples install echo clean purge docs gencodecs diff --git a/examples/Makefile b/examples/Makefile index df5405ab2..ae2f5ed8b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -7,49 +7,47 @@ CORE_DIR = $(TOP)/core INCLUDE_DIR = $(TOP)/include GENCODECS_DIR = $(TOP)/gencodecs -BOTS = 8ball \ - audit-log \ - ban \ - channel \ - components \ - copycat \ - embed \ - emoji \ - fetch-messages \ - guild-template \ - guild \ - invite \ - manual-dm \ - pin \ - ping-pong \ - presence \ - reaction \ - shell \ - slash-commands \ - slash-commands2 \ - spam \ - webhook \ - timers \ - $(XSRC) - -VOICE_BOT = voice-join +VOICE_BOTS = voice-join +BOTS = 8ball \ + audit-log \ + ban \ + channel \ + components \ + copycat \ + embed \ + emoji \ + fetch-messages \ + guild-template \ + guild \ + invite \ + manual-dm \ + pin \ + ping-pong \ + presence \ + reaction \ + shell \ + slash-commands \ + slash-commands2 \ + spam \ + webhook \ + timers CFLAGS = -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) \ -I$(CORE_DIR)/third-party -I$(GENCODECS_DIR) \ - -O0 -g -pthread -Wall $(XFLAGS) + -O0 -g -pthread -Wall LDFLAGS = -L$(TOP)/lib LDLIBS = -ldiscord -lcurl all: $(BOTS) voice: - $(MAKE) XFLAGS=-DCCORD_VOICE XSRC=$(VOICE_BOT) all + @ $(MAKE) CFLAGS="$(CFLAGS) -DCCORD_VOICE" BOTS="$(BOTS) $(VOICE_BOTS)" all echo: @ echo -e 'CC: $(CC)\n' @ echo -e 'BOTS: $(BOTS)\n' clean: - @ $(RM) $(BOTS) $(VOICE_BOT) + @ $(RM) $(BOTS) $(VOICE_BOTS) .PHONY: all echo clean From 5c2e46842ab5df861e08b87c4a51fbd7a6cd83f4 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 5 Jun 2022 13:28:49 -0300 Subject: [PATCH 112/118] fix(examples/discord-voice.c): missing const and passing to wrong pointer --- examples/voice-join.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/voice-join.c b/examples/voice-join.c index 06d2f7c8e..884484544 100644 --- a/examples/voice-join.c +++ b/examples/voice-join.c @@ -41,7 +41,7 @@ done_list_voice_regions(struct discord *client, struct discord_response *resp, const struct discord_voice_regions *regions) { - struct discord_message *event = resp->keep; + const struct discord_message *event = resp->keep; for (int i = 0; i < regions->size; ++i) { struct discord_create_message params = { .content = @@ -53,7 +53,7 @@ done_list_voice_regions(struct discord *client, void fail_list_voice_regions(struct discord *client, struct discord_response *resp) { - struct discord_message *event = resp->keep; + const struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Could not fetch voice regions" @@ -70,7 +70,7 @@ on_list_voice_regions(struct discord *client, struct discord_ret_voice_regions ret = { .done = &done_list_voice_regions, .fail = &fail_list_voice_regions, - .data = event, + .keep = event, }; discord_list_voice_regions(client, &ret); @@ -143,7 +143,7 @@ void fail_disconnect_guild_member(struct discord *client, struct discord_response *resp) { - struct discord_message *event = resp->keep; + const struct discord_message *event = resp->keep; struct discord_create_message params = { .content = "Couldn't disconnect user from voice channel" From 0a322a1b78031e7e32d0a1040ef5f666579496ff Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 5 Jun 2022 14:28:59 -0300 Subject: [PATCH 113/118] fix(channel.c): discord_get_channel_pos() must keep track of .keep field --- src/channel.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/channel.c b/src/channel.c index e2873178d..db6d01acc 100644 --- a/src/channel.c +++ b/src/channel.c @@ -10,7 +10,7 @@ * Custom functions ******************************************************************************/ -struct _discord_get_channel_at_pos_cxt { +struct _discord_get_channel_at_pos { enum discord_channel_types type; int position; struct discord_ret_channel ret; @@ -23,13 +23,10 @@ _done_get_channels(struct discord *client, struct discord_response *resp, const struct discord_channels *chs) { - struct _discord_get_channel_at_pos_cxt *cxt = resp->data; - + struct _discord_get_channel_at_pos *cxt = resp->data; const struct discord_channel *found_ch = NULL; - int pos; - int i; - for (i = 0, pos = 0; i < chs->size; ++i) { + for (int i = 0, pos = 0; i < chs->size; ++i) { if (cxt->type == chs->array[i].type && pos++ == cxt->position) { found_ch = &chs->array[i]; break; @@ -47,7 +44,10 @@ _done_get_channels(struct discord *client, cxt->ret.fail(client, resp); } - discord_refcounter_decr(&client->refcounter, cxt->ret.data); + if (cxt->ret.keep) + discord_refcounter_decr(&client->refcounter, (void *)cxt->ret.keep); + if (cxt->ret.data) + discord_refcounter_decr(&client->refcounter, cxt->ret.data); } CCORDcode @@ -57,22 +57,28 @@ discord_get_channel_at_pos(struct discord *client, int position, struct discord_ret_channel *ret) { - struct _discord_get_channel_at_pos_cxt *cxt; - struct discord_ret_channels current_ret = { 0 }; + struct _discord_get_channel_at_pos *cxt; + struct discord_ret_channels channels_ret = { 0 }; CCORD_EXPECT(client, guild_id != 0, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, ret != NULL, CCORD_BAD_PARAMETER, ""); CCORD_EXPECT(client, ret->done != NULL, CCORD_BAD_PARAMETER, ""); cxt = malloc(sizeof *cxt); - cxt->type = type; - cxt->position = position; - cxt->ret = *ret; - - current_ret.done = &_done_get_channels; - current_ret.fail = ret->fail; - current_ret.data = cxt; - + *cxt = (struct _discord_get_channel_at_pos){ .type = type, + .position = position, + .ret = *ret }; + + channels_ret.done = &_done_get_channels; + channels_ret.fail = ret->fail; + channels_ret.data = cxt; + + if (ret->keep) { + CCORDcode code = + discord_refcounter_incr(&client->refcounter, (void *)ret->keep); + ASSERT_S(code == CCORD_OK, + "'.keep' data must be a Concord callback parameter"); + } if (ret->data && CCORD_UNAVAILABLE == discord_refcounter_incr(&client->refcounter, ret->data)) @@ -83,7 +89,7 @@ discord_get_channel_at_pos(struct discord *client, /* TODO: fetch channel via caching, and return if results are non-existent */ - return discord_get_guild_channels(client, guild_id, ¤t_ret); + return discord_get_guild_channels(client, guild_id, &channels_ret); } /****************************************************************************** From 57a95f3d9958d8eeceae2e14de5a57df8c428de9 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Sun, 5 Jun 2022 14:30:27 -0300 Subject: [PATCH 114/118] fix(discord-voice.c): update to latest refactoring and add a build test at workflows/test_build.yml to ensure its not broken --- .github/workflows/test_build.yml | 4 ++++ Makefile | 2 +- include/discord-voice.h | 2 +- src/discord-voice.c | 32 ++++++++++++++------------------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml index fb5134ccf..96f1e1744 100644 --- a/.github/workflows/test_build.yml +++ b/.github/workflows/test_build.yml @@ -28,6 +28,8 @@ jobs: make examples echo "Building tests" make test + echo "Building voice" + make voice - name: Run Makefile with parallelism run: | @@ -39,3 +41,5 @@ jobs: make examples -j$(nproc) echo "Building tests with parallelism" make test -j$(nproc) + echo "Building voice with parallelism" + make voice -j$(nproc) diff --git a/Makefile b/Makefile index d429c8de2..f3ba8f845 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ SOFLAGS = -fPIC LDFLAGS = -lcurl WFLAGS += -Wall -Wextra -Wshadow -Wdouble-promotion -Wconversion -Wpedantic -CFLAGS = -std=c99 -O0 -g -pthread -D_XOPEN_SOURCE=600 \ +CFLAGS += -std=c99 -O0 -g -pthread -D_XOPEN_SOURCE=600 \ -I$(INCLUDE_DIR) -I$(COGUTILS_DIR) -I$(CORE_DIR) -I$(THIRDP_DIR) \ -I$(GENCODECS_DIR) -I$(PREFIX)/include -DLOG_USE_COLOR diff --git a/include/discord-voice.h b/include/discord-voice.h index da22ee7af..dc3c0b498 100644 --- a/include/discord-voice.h +++ b/include/discord-voice.h @@ -86,7 +86,7 @@ struct discord_voice_evcallbacks { * @see discord_voice_get_vc() */ struct discord_voice { - /** DISCORD_VOICE logging module */ + /** `DISCORD_VOICE` logging module */ struct logconf conf; /** the session guild id @note obtained from discord_voice_join() */ u64snowflake guild_id; diff --git a/src/discord-voice.c b/src/discord-voice.c index 076a6c63a..fa3a1ddd1 100644 --- a/src/discord-voice.c +++ b/src/discord-voice.c @@ -529,14 +529,17 @@ discord_voice_join(struct discord *client, bool self_mute, bool self_deaf) { + struct discord_update_voice_state state = { .guild_id = guild_id, + .channel_id = vchannel_id, + .self_mute = self_mute, + .self_deaf = self_deaf }; bool found_a_running_vcs = false; struct discord_voice *vc = NULL; - int i; if (!ws_is_functional(client->gw.ws)) return DISCORD_VOICE_ERROR; pthread_mutex_lock(&client_lock); - for (i = 0; i < DISCORD_MAX_VCS; ++i) { + for (int i = 0; i < DISCORD_MAX_VCS; ++i) { if (0 == client->vcs[i].guild_id) { vc = client->vcs + i; _discord_voice_init(vc, client, guild_id, vchannel_id); @@ -563,8 +566,8 @@ discord_voice_join(struct discord *client, } recycle_active_vc(vc, guild_id, vchannel_id); - discord_send_voice_state_update(vc, guild_id, vchannel_id, self_mute, - self_deaf); + discord_gateway_send_update_voice_state(&client->gw, &state); + return DISCORD_VOICE_JOINED; } @@ -579,10 +582,9 @@ _discord_on_voice_state_update(struct discord *client, struct discord_voice_state *event) { struct discord_voice *vc = NULL; - int i; pthread_mutex_lock(&client_lock); - for (i = 0; i < DISCORD_MAX_VCS; ++i) { + for (int i = 0; i < DISCORD_MAX_VCS; ++i) { if (event->guild_id == client->vcs[i].guild_id) { vc = client->vcs + i; if (event->channel_id) { @@ -704,10 +706,9 @@ _discord_on_voice_server_update(struct discord *client, { struct discord_voice *vc = NULL; int len; - int i; pthread_mutex_lock(&client_lock); - for (i = 0; i < DISCORD_MAX_VCS; ++i) { + for (int i = 0; i < DISCORD_MAX_VCS; ++i) { if (event->guild_id == client->vcs[i].guild_id) { vc = client->vcs + i; break; @@ -751,9 +752,7 @@ _discord_on_voice_server_update(struct discord *client, void discord_voice_connections_init(struct discord *client) { - int i; - - for (i = 0; i < DISCORD_MAX_VCS; ++i) { + for (int i = 0; i < DISCORD_MAX_VCS; ++i) { client->vcs[i].p_voice_cbs = &client->voice_cbs; } } @@ -770,9 +769,7 @@ _discord_voice_cleanup(struct discord_voice *vc) void discord_voice_connections_cleanup(struct discord *client) { - int i; - - for (i = 0; i < DISCORD_MAX_VCS; ++i) { + for (int i = 0; i < DISCORD_MAX_VCS; ++i) { _discord_voice_cleanup(&client->vcs[i]); } } @@ -780,15 +777,14 @@ discord_voice_connections_cleanup(struct discord *client) void discord_voice_shutdown(struct discord_voice *vc) { + struct discord_update_voice_state state = { .guild_id = vc->guild_id }; const char reason[] = "Client triggered voice shutdown"; vc->reconnect.enable = false; vc->shutdown = true; vc->is_resumable = false; - /* TODO: check if discord_send_voice_state_update() is not being ignored - * because of ws_close() */ - discord_send_voice_state_update(vc, vc->guild_id, 0, false, false); + discord_gateway_send_update_voice_state(&vc->p_client->gw, &state); ws_close(vc->ws, WS_CLOSE_REASON_NORMAL, reason, sizeof(reason)); } @@ -815,7 +811,7 @@ discord_voice_is_alive(struct discord_voice *vc) void discord_set_voice_cbs(struct discord *client, - struct discord_voice_cbs *callbacks) + struct discord_voice_evcallbacks *callbacks) { if (callbacks->on_speaking) client->voice_cbs.on_speaking = callbacks->on_speaking; From cdd5d92f20f44ceee102b77c80f700c6996537d5 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 8 Jun 2022 14:52:33 -0300 Subject: [PATCH 115/118] docs(README.md): update 'Migrating from Orca' link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb5f98cea..2f0645854 100644 --- a/README.md +++ b/README.md @@ -351,4 +351,4 @@ All kinds of contributions are welcome, all we ask is to abide to our [guideline ## Useful links -- [Migrating from Orca](https://gist.github.com/lcsmuller/a5f2b205c3871888656b86825db90187) +- [Migrating from Orca](https://gist.github.com/lcsmuller/b5137e66d534a57e0075f9d838c9170e) From 98174512d5a12f33939db45f128758a68f8f21bd Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 8 Jun 2022 17:12:53 -0300 Subject: [PATCH 116/118] docs: fix Modules ordering --- Doxyfile | 5 +++-- include/discord.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Doxyfile b/Doxyfile index db3021dc4..f934a91d5 100644 --- a/Doxyfile +++ b/Doxyfile @@ -833,8 +833,9 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = include/ core/ docs/api \ - README.md docs/DISCORD_ROADMAP.md docs/WINDOWS.md +INPUT = core/error.h core/types.h include/discord.h include/ \ + core/ docs/api README.md docs/DISCORD_ROADMAP.md \ + docs/WINDOWS.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/include/discord.h b/include/discord.h index 6c0ed8007..bb25cd802 100644 --- a/include/discord.h +++ b/include/discord.h @@ -126,7 +126,7 @@ const char *discord_strerror(CCORDcode code, struct discord *client); #include "voice.h" #include "webhook.h" #include "gateway.h" -/** @defgroup DiscordAPIInteractions Interactions API +/** @defgroup DiscordAPIInteractions Interactions * @brief Interactions public API supported by Concord * @{ */ #include "application_command.h" From 75cd14705728d534aed9bf89bcfee05c2ebff821 Mon Sep 17 00:00:00 2001 From: lcsmuller Date: Wed, 8 Jun 2022 21:41:35 -0300 Subject: [PATCH 117/118] docs(README.md): add 'Migrating from V1' link, tidy up README --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2f0645854..fe194dcc3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -
-
-

- Concord -

-
-

-
Discord server
-

-
+[migrating-shield]: https://img.shields.io/badge/Gist-Migrating%20from%20v1-yellow +[migrating-link]: https://gist.github.com/lcsmuller/d6aee306bac229a7873f5c243bf7858b +[migrating-orca-link]: https://gist.github.com/lcsmuller/b5137e66d534a57e0075f9d838c9170e +[discord-shield]: https://img.shields.io/discord/928763123362578552?color=5865F2&logo=discord&logoColor=white +[discord-invite]: https://discord.gg/Y7Xa6MA82v + +Concord Logo + +[ ![discord-shield][] ][discord-invite] +[ ![migrating-shield][] ][migrating-link] # Concord - C Discord API library @@ -338,7 +338,7 @@ For a more comprehensive guide check [Beej's Quick Guide to GDB](https://beej.us ## Support -Problems? Check out our [Discord Server](https://discord.gg/Y7Xa6MA82v). +Problems? Check out our [Discord Server][discord-invite] ## Contributing @@ -351,4 +351,5 @@ All kinds of contributions are welcome, all we ask is to abide to our [guideline ## Useful links -- [Migrating from Orca](https://gist.github.com/lcsmuller/b5137e66d534a57e0075f9d838c9170e) +- [Migrating from V1][migrating-link] +- [Migrating from Orca][migrating-orca-link] From 002c6628541d18d98cc26a2463ff561ff27b4674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=BCller?= Date: Wed, 8 Jun 2022 21:53:20 -0300 Subject: [PATCH 118/118] docs(README.md): realign logo and move shields under title --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe194dcc3..34b4704a4 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,15 @@ [discord-shield]: https://img.shields.io/discord/928763123362578552?color=5865F2&logo=discord&logoColor=white [discord-invite]: https://discord.gg/Y7Xa6MA82v -Concord Logo +
+Concord Logo +
+ +# Concord - C Discord API library [ ![discord-shield][] ][discord-invite] [ ![migrating-shield][] ][migrating-link] -# Concord - C Discord API library - ## About Concord is an asynchronous C99 Discord API library. It has minimal external dependencies, and a low-level translation of the Discord official documentation to C code.