From 1f3f82026e81221aa37b965bce37092a2178da3e Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 22 May 2022 16:19:08 +0200 Subject: [PATCH 001/136] added three attributes (tags, install_params and custom_install_url) to application.h --- include/dpp/application.h | 15 +++++++++++++-- src/dpp/application.cpp | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/dpp/application.h b/include/dpp/application.h index b3d8492005..282f86ec38 100644 --- a/include/dpp/application.h +++ b/include/dpp/application.h @@ -62,6 +62,14 @@ enum application_flags : uint32_t { apf_gateway_message_content_limited = (1 << 19) }; +/** + * @brief Represents the settings for the bot/application's in-app authorization link + */ +struct DPP_EXPORT application_install_params { + uint64_t permissions; //!< The dpp::role_permissions to request for the bot role + std::vector scopes; //!< The [scopes](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) as strings to add the application to the server with +}; + /** * @brief Represents a team member on a team who maintain a bot/application */ @@ -99,7 +107,7 @@ class DPP_EXPORT application : public managed, public json_interface tags; //!< Up to 5 tags describing the content and functionality of the application + application_install_params install_params; //!< Settings for the application's default in-app authorization link, if enabled + std::string custom_install_url; //!< The application's default custom authorization link, if enabled + /** Constructor */ application(); diff --git a/src/dpp/application.cpp b/src/dpp/application.cpp index af0c0c6c59..b5eea41394 100644 --- a/src/dpp/application.cpp +++ b/src/dpp/application.cpp @@ -59,6 +59,19 @@ application& application::fill_from_json(nlohmann::json* j) { cover_image = ci; } set_int32_not_null(j, "flags", flags); + if (j->contains("tags")) { + for (const auto& tag : (*j)["tags"]) { + this->tags.push_back(to_string(tag)); + } + } + if (j->contains("install_params")) { + json& p = (*j)["install_params"]; + set_snowflake_not_null(&p, "permissions", this->install_params.permissions); + for (const auto& scope : p["scopes"]) { + this->install_params.scopes.push_back(to_string(scope)); + } + } + set_string_not_null(j, "custom_install_url", custom_install_url); if (j->contains("team")) { json& t = (*j)["team"]; std::string i = string_not_null(&t, "icon"); From 0667cf803eeded046bbfec3438e83d6e9a54b5cb Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 22 May 2022 16:43:23 +0200 Subject: [PATCH 002/136] deprecated guild_bulk_command_edit_permissions --- include/dpp/cluster.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index a1f099eebd..40c0957f29 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -1736,6 +1736,7 @@ class DPP_EXPORT cluster { * @param guild_id Guild ID to edit permissions of the slash commands in * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::guild_command_permissions_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead */ void guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id, command_completion_event_t callback = utility::log_error()); From 9f1dc6b5d70a15993f3ea037004504d6314909c0 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 22 May 2022 16:44:06 +0200 Subject: [PATCH 003/136] . --- include/dpp/auditlog.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/auditlog.h b/include/dpp/auditlog.h index 31c609905a..346963071a 100644 --- a/include/dpp/auditlog.h +++ b/include/dpp/auditlog.h @@ -126,7 +126,7 @@ enum audit_type { ae_thread_update = 111, /// Thread delete ae_thread_delete = 112, - // Application command permissions update + /// Application command permissions update ae_appcommand_permission_update = 121, /// Auto moderation rule creation ae_automod_rule_create = 140, From 13aa6f07f92befa0de0506fb861d0ddfabd47dc3 Mon Sep 17 00:00:00 2001 From: Hou In Si Tou Date: Sun, 22 May 2022 21:28:03 -0700 Subject: [PATCH 004/136] Depend discord_voice_client::voice_payload priority only on timestamps. Previously, the condition depended also on the sequence number. This was supposed to handle the wrap around case. In reality, it depends on what LHS and RHS are, and flipping the LHS and RHS breaks the ordering. Recall that LHS has lower priority, i.e. should appear behind, RHS if LHS.seq > RHS.seq || LHS.timestamp > RHS.timestamp The timestamp is only taken into account if LHS.seq <= RHS.seq. For example, in case of LHS.seq = 1 < 65530 = RHS.seq, the timestamp will be taken into account, which could place LHS (1) after RHS (65530). However, if we flip the sides, so that LHS.seq = 65530 and RHS.seq = 1, then LHS (65530) is immediately placed after RHS (1), without considering the timestamp, which is incorrect behavior. The consequence is that the ordering in the payload queue is incorrect. For instance, seq=65530 can appear after seq=65531, and the loop has to run through all sequence numbers from 65531, to 65535, to 1, back to 65530. As we approach the wrap-around case, we tend to hang long in the voice courier thread. --- src/dpp/discordvoiceclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 8c6618710c..422e33688d 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -68,7 +68,7 @@ struct rtp_header { bool discord_voice_client::sodium_initialised = false; bool discord_voice_client::voice_payload::operator<(const voice_payload& other) const { - return seq > other.seq || timestamp > other.timestamp; + return timestamp > other.timestamp; } #ifdef HAVE_VOICE From cb04fbe49e8e2363608e62f757cab471ffb5ea84 Mon Sep 17 00:00:00 2001 From: Phil B Date: Tue, 24 May 2022 18:25:43 +0200 Subject: [PATCH 005/136] updated docs --- docpages/03_example_programs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docpages/03_example_programs.md b/docpages/03_example_programs.md index c669923081..b0ea77d28a 100644 --- a/docpages/03_example_programs.md +++ b/docpages/03_example_programs.md @@ -161,7 +161,7 @@ int main() { ### 4. Attach to another event to receive slash commands -If you want to handle a slash command, you should also attach your program to the `on_slashcommand` event (dpp::cluster::on_slashcommand) which is the same as the Discord.js `interactionCreate` event. Lets add this to the program before the `on_ready` event: +If you want to handle a slash command, you should also attach your program to the `on_slashcommand` event (dpp::cluster::on_slashcommand) which is basically the same as the Discord.js `interactionCreate` event. Lets add this to the program before the `on_ready` event: ~~~~~~~~~~~~~~{.cpp} #include @@ -221,7 +221,7 @@ Let's break down the code in the `on_slashcommand` event so that we can discuss This code is simply comparing the command name `event.command.get_command_name()` (dpp::interaction::get_command_name) against the value in a constant string value `"ping"`. If they match, then the `event.reply` method is called. -The `event.reply` function (dpp::interaction_crete_t::reply) replies to a slash command with a message. There are many ways to call this function to send embed messages, upload files, and more, but for this simple demonstration we will just send some message text. +The `event.reply` function (dpp::slashcommand_t::reply) replies to a slash command with a message. There are many ways to call this function to send embed messages, upload files, and more, but for this simple demonstration we will just send some message text. ### 6. Add code to start the bot! From d5906777b9e3f4ce8c6c3b312946c896fe7010e2 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Tue, 24 May 2022 17:48:55 +0100 Subject: [PATCH 006/136] update title for better SEO --- Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index 5b9f17be84..46f8d78ea7 100644 --- a/Doxyfile +++ b/Doxyfile @@ -47,7 +47,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "A Lightweight C++ library for Discord" +PROJECT_BRIEF = "C++ Discord API Bot Library" # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels From 9cbb2ddf2fd534504a54c822baa6fae560675c6f Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Tue, 24 May 2022 17:53:47 +0100 Subject: [PATCH 007/136] adjust main page title --- docpages/INDEX.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docpages/INDEX.md b/docpages/INDEX.md index fa902477af..a7586fd921 100644 --- a/docpages/INDEX.md +++ b/docpages/INDEX.md @@ -1,4 +1,4 @@ -# D++ Developer Documentation +# D++: A C++ Discord API Library for Bots ## What is D++ (DPP)? From 4bac9d9bf9d2075df9221a707cacdb8d8611cd56 Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 25 May 2022 17:13:39 +0200 Subject: [PATCH 008/136] removed deprecated exclamation mark from guild_member::get_mention --- src/dpp/guild.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 21119c452b..7b69fd6f1e 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -98,7 +98,7 @@ guild_member::guild_member() : } std::string guild_member::get_mention() const { - return "<@!" + std::to_string(user_id) + ">"; + return "<@" + std::to_string(user_id) + ">"; } guild_member& guild_member::set_nickname(const std::string& nick) { From 1f595c39603eb30c832f87c18e7243c62ff05fa0 Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 25 May 2022 23:32:54 +0200 Subject: [PATCH 009/136] small doc improvements of slashcommand.h and some intent fixes --- include/dpp/appcommand.h | 12 ++++++++---- src/dpp/slashcommand.cpp | 16 ++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index 5a9006126a..c042746241 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -814,8 +814,7 @@ class DPP_EXPORT slashcommand : public managed, public json_interface permissions; @@ -864,6 +864,7 @@ class DPP_EXPORT slashcommand : public managed, public json_interfacecontains("name_localizations")) { - for(auto loc = (*j)["name_localizations"].begin(); loc != (*j)["name_localizations"].end(); ++loc) { - o.name_localizations[loc.key()] = loc.value(); + if (j->contains("name_localizations")) { + for(auto loc = (*j)["name_localizations"].begin(); loc != (*j)["name_localizations"].end(); ++loc) { + o.name_localizations[loc.key()] = loc.value(); + } } - } - if (j->contains("description_localizations")) { - for(auto loc = (*j)["description_localizations"].begin(); loc != (*j)["description_localizations"].end(); ++loc) { - o.description_localizations[loc.key()] = loc.value(); + if (j->contains("description_localizations")) { + for(auto loc = (*j)["description_localizations"].begin(); loc != (*j)["description_localizations"].end(); ++loc) { + o.description_localizations[loc.key()] = loc.value(); + } } - } if (j->contains("options") && i > 0) { i--; // prevent infinite recursion call with a counter From eb45706dacf31978db8b90d66b1128ff29564af6 Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 25 May 2022 23:56:01 +0200 Subject: [PATCH 010/136] implemented channel::get_icon_url --- src/dpp/channel.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 57791789f8..feab8d3d7f 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using json = nlohmann::json; @@ -422,8 +423,12 @@ std::string channel::get_icon_url(uint16_t size) const { * At some point in the future this URL *will* change! */ if (!this->icon.to_string().empty()) { - // TODO implement this, endpoint for that isn't finished yet https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints - return std::string(); + return fmt::format("{}/channel-icons/{}/{}.png{}", + utility::cdn_host, + this->id, + this->icon.to_string(), + utility::avatar_size(size) + ); } else { return std::string(); } From b4c2b0d1f23cd33829a28560ad699456aed8180f Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 11:58:01 +0200 Subject: [PATCH 011/136] renamed role_permissions to permissions --- include/dpp/application.h | 2 +- include/dpp/channel.h | 4 ++-- include/dpp/role.h | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/dpp/application.h b/include/dpp/application.h index 282f86ec38..47e4d22b41 100644 --- a/include/dpp/application.h +++ b/include/dpp/application.h @@ -66,7 +66,7 @@ enum application_flags : uint32_t { * @brief Represents the settings for the bot/application's in-app authorization link */ struct DPP_EXPORT application_install_params { - uint64_t permissions; //!< The dpp::role_permissions to request for the bot role + uint64_t permissions; //!< The dpp::permissions to request for the bot role std::vector scopes; //!< The [scopes](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) as strings to add the application to the server with }; diff --git a/include/dpp/channel.h b/include/dpp/channel.h index f64eb9d251..99cbed4c16 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -343,8 +343,8 @@ class DPP_EXPORT channel : public managed, public json_interface { * * @param id ID of the role or the member you want to add overwrite for * @param type type of overwrite (0 for role, 1 for member) - * @param allowed_permissions bitmask of allowed permissions (refer to enum role_permissions) for this user/role in this channel - * @param denied_permissions bitmask of denied permissions (refer to enum role_permissions) for this user/role in this channel + * @param allowed_permissions bitmask of allowed permissions (refer to enum dpp::permissions) for this user/role in this channel + * @param denied_permissions bitmask of denied permissions (refer to enum dpp::permissions) for this user/role in this channel * * @return Reference to self, so these method calls may be chained */ diff --git a/include/dpp/role.h b/include/dpp/role.h index 212d8eb07f..9d49b8a9b4 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -38,7 +38,7 @@ enum role_flags : uint8_t { /** * @brief Represents the various discord permissions */ -enum role_permissions : uint64_t { +enum permissions : uint64_t { p_create_instant_invite = 0x00000000001, //!< allows creation of instant invites p_kick_members = 0x00000000002, //!< allows kicking members p_ban_members = 0x00000000004, //!< allows banning members @@ -82,6 +82,12 @@ enum role_permissions : uint64_t { p_moderate_members = 0x10000000000, //!< allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels }; +/** + * @brief Represents the various discord permissions + * @deprecated Use dpp::permissions instead. + */ +using role_permissions = permissions; + /** * @brief Represents a role within a dpp::guild. * Roles are combined via logical OR of the permission bitmasks, then channel-specific overrides @@ -109,7 +115,7 @@ class DPP_EXPORT role : public managed, public json_interface { uint32_t colour; /** Role position */ uint8_t position; - /** Role permissions bitmask values from dpp::role_permissions */ + /** Role permissions bitmask values from dpp::permissions */ uint64_t permissions; /** Role flags from dpp::role_flags */ uint8_t flags; From abc3c4210fe990fa9a7c2642a5ffce86ef67b52b Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 12:00:02 +0200 Subject: [PATCH 012/136] updated doc of application_install_params --- include/dpp/application.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/application.h b/include/dpp/application.h index 47e4d22b41..9183f86e45 100644 --- a/include/dpp/application.h +++ b/include/dpp/application.h @@ -66,7 +66,7 @@ enum application_flags : uint32_t { * @brief Represents the settings for the bot/application's in-app authorization link */ struct DPP_EXPORT application_install_params { - uint64_t permissions; //!< The dpp::permissions to request for the bot role + uint64_t permissions; //!< A bitmask of dpp::permissions to request for the bot role std::vector scopes; //!< The [scopes](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) as strings to add the application to the server with }; From 430ab9643c76b85f1be931608f3d0f3480bb5a7c Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 13:25:17 +0200 Subject: [PATCH 013/136] moved the permissions from role.h to the new permissions.h & added permission class --- include/dpp/dpp.h | 1 + include/dpp/permissions.h | 120 ++++++++++++++++++++++++++++++++++++++ include/dpp/role.h | 48 +-------------- src/dpp/permissions.cpp | 54 +++++++++++++++++ 4 files changed, 176 insertions(+), 47 deletions(-) create mode 100644 include/dpp/permissions.h create mode 100644 src/dpp/permissions.cpp diff --git a/include/dpp/dpp.h b/include/dpp/dpp.h index 856f855c9f..0e8c0aa598 100644 --- a/include/dpp/dpp.h +++ b/include/dpp/dpp.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h new file mode 100644 index 0000000000..1d87a303ad --- /dev/null +++ b/include/dpp/permissions.h @@ -0,0 +1,120 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include + +namespace dpp { + +/** + * @brief Represents the various discord permissions + */ +enum permissions : uint64_t { + p_create_instant_invite = 0x00000000001, //!< allows creation of instant invites + p_kick_members = 0x00000000002, //!< allows kicking members + p_ban_members = 0x00000000004, //!< allows banning members + p_administrator = 0x00000000008, //!< allows all permissions and bypasses channel permission overwrites + p_manage_channels = 0x00000000010, //!< allows management and editing of channels + p_manage_guild = 0x00000000020, //!< allows management and editing of the guild + p_add_reactions = 0x00000000040, //!< allows for the addition of reactions to messages + p_view_audit_log = 0x00000000080, //!< allows for viewing of audit logs + p_priority_speaker = 0x00000000100, //!< allows for using priority speaker in a voice channel + p_stream = 0x00000000200, //!< allows the user to go live + p_view_channel = 0x00000000400, //!< allows guild members to view a channel, which includes reading messages in text channels and joining voice channels + p_send_messages = 0x00000000800, //!< allows for sending messages in a channel + p_send_tts_messages = 0x00000001000, //!< allows for sending of /tts messages + p_manage_messages = 0x00000002000, //!< allows for deletion of other users messages + p_embed_links = 0x00000004000, //!< links sent by users with this permission will be auto-embedded + p_attach_files = 0x00000008000, //!< allows for uploading images and files + p_read_message_history = 0x00000010000, //!< allows for reading of message history + p_mention_everyone = 0x00000020000, //!< allows for using the everyone and the here tag to notify users in a channel + p_use_external_emojis = 0x00000040000, //!< allows the usage of custom emojis from other servers + p_view_guild_insights = 0x00000080000, //!< allows for viewing guild insights + p_connect = 0x00000100000, //!< allows for joining of a voice channel + p_speak = 0x00000200000, //!< allows for speaking in a voice channel + p_mute_members = 0x00000400000, //!< allows for muting members in a voice channel + p_deafen_members = 0x00000800000, //!< allows for deafening of members in a voice channel + p_move_members = 0x00001000000, //!< allows for moving of members between voice channels + p_use_vad = 0x00002000000, //!< allows for using voice-activity-detection in a voice channel + p_change_nickname = 0x00004000000, //!< allows for modification of own nickname + p_manage_nicknames = 0x00008000000, //!< allows for modification of other users nicknames + p_manage_roles = 0x00010000000, //!< allows management and editing of roles + p_manage_webhooks = 0x00020000000, //!< allows management and editing of webhooks + p_manage_emojis_and_stickers = 0x00040000000, //!< allows management and editing of emojis and stickers + p_use_application_commands = 0x00080000000, //!< allows members to use application commands, including slash commands and context menus + p_request_to_speak = 0x00100000000, //!< allows for requesting to speak in stage channels. (Discord: This permission is under active development and may be changed or removed.) + p_manage_events = 0x00200000000, //!< allows for management (creation, updating, deleting, starting) of scheduled events + p_manage_threads = 0x00400000000, //!< allows for deleting and archiving threads, and viewing all private threads + p_create_public_threads = 0x00800000000, //!< allows for creating public and announcement threads + p_create_private_threads = 0x01000000000, //!< allows for creating private threads + p_use_external_stickers = 0x02000000000, //!< allows the usage of custom stickers from other servers + p_send_messages_in_threads = 0x04000000000, //!< allows for sending messages in threads + p_use_embedded_activities = 0x08000000000, //!< allows for using activities (applications with the EMBEDDED flag) in a voice channel + p_moderate_members = 0x10000000000, //!< allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels +}; + +/** + * @brief Permission type which holds the permission bitmask in an uint64_t + */ +class permission { +private: + /** + * @brief The permission bitmask value + */ + uint64_t value; +public: + template permission(T v); + + /** + * @brief Construct a permission object + */ + permission(); + + operator uint64_t() const; + + operator uint64_t &(); + + /** + * @brief Check if it has a permission flag set + * @param p The permission flag from dpp::permissions + * @return True if it has the permission + */ + bool has(uint64_t p) const; + + /** + * @brief Add a permission with the Bitwise OR operation + * @param p The permission from dpp::permissions to add + */ + void add(uint64_t p); + + /** + * @brief Set a permission with the Bitwise AND operation + * @param p The permission from dpp::permissions to set + */ + void set(uint64_t p); + + /** + * @brief Remove a permission with the Bitwise NOT operation + * @param p The permission from dpp::permissions to remove + */ + void remove(uint64_t p); +}; + +} \ No newline at end of file diff --git a/include/dpp/role.h b/include/dpp/role.h index 9d49b8a9b4..9392c95456 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -35,53 +36,6 @@ enum role_flags : uint8_t { r_premium_subscriber = 0b00001000, //!< This is set for the role given to nitro }; -/** - * @brief Represents the various discord permissions - */ -enum permissions : uint64_t { - p_create_instant_invite = 0x00000000001, //!< allows creation of instant invites - p_kick_members = 0x00000000002, //!< allows kicking members - p_ban_members = 0x00000000004, //!< allows banning members - p_administrator = 0x00000000008, //!< allows all permissions and bypasses channel permission overwrites - p_manage_channels = 0x00000000010, //!< allows management and editing of channels - p_manage_guild = 0x00000000020, //!< allows management and editing of the guild - p_add_reactions = 0x00000000040, //!< allows for the addition of reactions to messages - p_view_audit_log = 0x00000000080, //!< allows for viewing of audit logs - p_priority_speaker = 0x00000000100, //!< allows for using priority speaker in a voice channel - p_stream = 0x00000000200, //!< allows the user to go live - p_view_channel = 0x00000000400, //!< allows guild members to view a channel, which includes reading messages in text channels and joining voice channels - p_send_messages = 0x00000000800, //!< allows for sending messages in a channel - p_send_tts_messages = 0x00000001000, //!< allows for sending of /tts messages - p_manage_messages = 0x00000002000, //!< allows for deletion of other users messages - p_embed_links = 0x00000004000, //!< links sent by users with this permission will be auto-embedded - p_attach_files = 0x00000008000, //!< allows for uploading images and files - p_read_message_history = 0x00000010000, //!< allows for reading of message history - p_mention_everyone = 0x00000020000, //!< allows for using the everyone and the here tag to notify users in a channel - p_use_external_emojis = 0x00000040000, //!< allows the usage of custom emojis from other servers - p_view_guild_insights = 0x00000080000, //!< allows for viewing guild insights - p_connect = 0x00000100000, //!< allows for joining of a voice channel - p_speak = 0x00000200000, //!< allows for speaking in a voice channel - p_mute_members = 0x00000400000, //!< allows for muting members in a voice channel - p_deafen_members = 0x00000800000, //!< allows for deafening of members in a voice channel - p_move_members = 0x00001000000, //!< allows for moving of members between voice channels - p_use_vad = 0x00002000000, //!< allows for using voice-activity-detection in a voice channel - p_change_nickname = 0x00004000000, //!< allows for modification of own nickname - p_manage_nicknames = 0x00008000000, //!< allows for modification of other users nicknames - p_manage_roles = 0x00010000000, //!< allows management and editing of roles - p_manage_webhooks = 0x00020000000, //!< allows management and editing of webhooks - p_manage_emojis_and_stickers = 0x00040000000, //!< allows management and editing of emojis and stickers - p_use_application_commands = 0x00080000000, //!< allows members to use application commands, including slash commands and context menus - p_request_to_speak = 0x00100000000, //!< allows for requesting to speak in stage channels. (Discord: This permission is under active development and may be changed or removed.) - p_manage_events = 0x00200000000, //!< allows for management (creation, updating, deleting, starting) of scheduled events - p_manage_threads = 0x00400000000, //!< allows for deleting and archiving threads, and viewing all private threads - p_create_public_threads = 0x00800000000, //!< allows for creating public and announcement threads - p_create_private_threads = 0x01000000000, //!< allows for creating private threads - p_use_external_stickers = 0x02000000000, //!< allows the usage of custom stickers from other servers - p_send_messages_in_threads = 0x04000000000, //!< allows for sending messages in threads - p_use_embedded_activities = 0x08000000000, //!< allows for using activities (applications with the EMBEDDED flag) in a voice channel - p_moderate_members = 0x10000000000, //!< allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels -}; - /** * @brief Represents the various discord permissions * @deprecated Use dpp::permissions instead. diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp new file mode 100644 index 0000000000..7f9e9a5d7d --- /dev/null +++ b/src/dpp/permissions.cpp @@ -0,0 +1,54 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include + +namespace dpp { + +template permission::permission(T v) : value(v) {}; + +permission::permission() : permission(0) {} + +permission::operator uint64_t() const { + return value; +} + +permission::operator uint64_t &() { + return value; +} + +bool permission::has(uint64_t p) const { + return (value & p); +} + +void permission::add(uint64_t p) { + value |= p; +} + +void permission::set(uint64_t p) { + value = p; +} + +void permission::remove(uint64_t p) { + value &= ~p; +} + +} \ No newline at end of file From 1905d04022ac709dc0b0af79c9ed1881ffddd18b Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 15:26:22 +0200 Subject: [PATCH 014/136] updated permission class --- include/dpp/permissions.h | 4 +++- src/dpp/permissions.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 1d87a303ad..36ce7d6934 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -80,7 +80,7 @@ class permission { */ uint64_t value; public: - template permission(T v); + permission(const uint64_t& value); /** * @brief Construct a permission object @@ -91,6 +91,8 @@ class permission { operator uint64_t &(); + operator nlohmann::json() const; + /** * @brief Check if it has a permission flag set * @param p The permission flag from dpp::permissions diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index 7f9e9a5d7d..e8a248dddf 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -18,12 +18,12 @@ * limitations under the License. * ************************************************************************************/ -#pragma once #include +#include namespace dpp { -template permission::permission(T v) : value(v) {}; +permission::permission(const uint64_t &value) : value(value) {} permission::permission() : permission(0) {} @@ -35,6 +35,10 @@ permission::operator uint64_t &() { return value; } +permission::operator nlohmann::json() const { + return value; +} + bool permission::has(uint64_t p) const { return (value & p); } From 58cef628c53c1db1e06af084a3a539091852b633 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 15:55:15 +0200 Subject: [PATCH 015/136] updated docs of the permission class --- include/dpp/permissions.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 36ce7d6934..5a5cdf40be 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -71,7 +71,7 @@ enum permissions : uint64_t { }; /** - * @brief Permission type which holds the permission bitmask in an uint64_t + * @brief Represents a permission bitmask which are hold in an uint64_t */ class permission { private: @@ -80,6 +80,10 @@ class permission { */ uint64_t value; public: + /** + * @brief Construct a permission object + * @param value A permission bitmask + */ permission(const uint64_t& value); /** From 67d0dbc1523bab997807d5852cd18b3f26a30b18 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 17:14:12 +0200 Subject: [PATCH 016/136] updated permission class --- include/dpp/permissions.h | 14 ++++++++++---- include/dpp/role.h | 6 ------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 5a5cdf40be..3b247ca5cf 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -71,7 +71,13 @@ enum permissions : uint64_t { }; /** - * @brief Represents a permission bitmask which are hold in an uint64_t + * @brief Represents the various discord permissions + * @deprecated Use dpp::permissions instead. + */ +using role_permissions = permissions; + +/** + * @brief Represents a permission bitmask (refer to enum dpp::permissions) which are hold in an uint64_t */ class permission { private: @@ -106,19 +112,19 @@ class permission { /** * @brief Add a permission with the Bitwise OR operation - * @param p The permission from dpp::permissions to add + * @param p The permissions (from dpp::permissions) to add */ void add(uint64_t p); /** * @brief Set a permission with the Bitwise AND operation - * @param p The permission from dpp::permissions to set + * @param p The permissions (from dpp::permissions) to set */ void set(uint64_t p); /** * @brief Remove a permission with the Bitwise NOT operation - * @param p The permission from dpp::permissions to remove + * @param p The permissions (from dpp::permissions) to remove */ void remove(uint64_t p); }; diff --git a/include/dpp/role.h b/include/dpp/role.h index 9392c95456..9fd3ed64cb 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -36,12 +36,6 @@ enum role_flags : uint8_t { r_premium_subscriber = 0b00001000, //!< This is set for the role given to nitro }; -/** - * @brief Represents the various discord permissions - * @deprecated Use dpp::permissions instead. - */ -using role_permissions = permissions; - /** * @brief Represents a role within a dpp::guild. * Roles are combined via logical OR of the permission bitmasks, then channel-specific overrides From 71d9224d7f53130b933a40334412b48c4b91a541 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 17:36:49 +0200 Subject: [PATCH 017/136] make return type permission methods for chaining --- include/dpp/permissions.h | 21 ++++++++++++++++++--- src/dpp/permissions.cpp | 9 ++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 3b247ca5cf..23af675c8d 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -97,10 +97,22 @@ class permission { */ permission(); + /** + * @brief For acting like an integer + * @return The permission bitmask value + */ operator uint64_t() const; + /** + * @brief For acting like an integer + * @return A reference to the permission bitmask value + */ operator uint64_t &(); + /** + * @brief For building json + * @return The permission bitmask value + */ operator nlohmann::json() const; /** @@ -113,20 +125,23 @@ class permission { /** * @brief Add a permission with the Bitwise OR operation * @param p The permissions (from dpp::permissions) to add + * @return permission& reference to self for chaining */ - void add(uint64_t p); + const permission& add(uint64_t p); /** * @brief Set a permission with the Bitwise AND operation * @param p The permissions (from dpp::permissions) to set + * @return permission& reference to self for chaining */ - void set(uint64_t p); + const permission& set(uint64_t p); /** * @brief Remove a permission with the Bitwise NOT operation * @param p The permissions (from dpp::permissions) to remove + * @return permission& reference to self for chaining */ - void remove(uint64_t p); + const permission& remove(uint64_t p); }; } \ No newline at end of file diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index e8a248dddf..c8ef552a0f 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -43,16 +43,19 @@ bool permission::has(uint64_t p) const { return (value & p); } -void permission::add(uint64_t p) { +const permission& permission::add(uint64_t p) { value |= p; + return *this; } -void permission::set(uint64_t p) { +const permission& permission::set(uint64_t p) { value = p; + return *this; } -void permission::remove(uint64_t p) { +const permission& permission::remove(uint64_t p) { value &= ~p; + return *this; } } \ No newline at end of file From e8d703c3c3a2d89c09994fdee67c9dd97857c8d0 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 17:53:44 +0200 Subject: [PATCH 018/136] big rework: replace uint64_t on various places with dpp::permission --- include/dpp/appcommand.h | 11 ++++- include/dpp/application.h | 3 +- include/dpp/channel.h | 39 +++++++++++------ include/dpp/cluster.h | 4 +- include/dpp/guild.h | 9 ++-- include/dpp/role.h | 2 +- include/dpp/utility.h | 2 +- src/dpp/channel.cpp | 8 +++- src/dpp/cluster/channel.cpp | 4 +- src/dpp/guild.cpp | 8 ++-- src/dpp/role.cpp | 83 +++++++++++++++++++------------------ 11 files changed, 101 insertions(+), 72 deletions(-) diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index c042746241..293c8c41f1 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -468,7 +468,7 @@ struct DPP_EXPORT command_resolved { /** * @brief Resolved total guild member permissions in the channel, including overwrites */ - std::map member_permissions; + std::map member_permissions; /** * @brief Resolved roles */ @@ -866,7 +866,7 @@ class DPP_EXPORT slashcommand : public managed, public json_interface #include #include +#include #include #include @@ -66,7 +67,7 @@ enum application_flags : uint32_t { * @brief Represents the settings for the bot/application's in-app authorization link */ struct DPP_EXPORT application_install_params { - uint64_t permissions; //!< A bitmask of dpp::permissions to request for the bot role + permission permissions; //!< A bitmask of dpp::permissions to request for the bot role std::vector scopes; //!< The [scopes](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) as strings to add the application to the server with }; diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 99cbed4c16..2711adeb5d 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -81,17 +82,31 @@ enum overwrite_type : uint8_t { }; /** - * @brief channel permission overwrites + * @brief Channel permission overwrites */ struct DPP_EXPORT permission_overwrite { - /// Overwrite id + /// ID of the role or the member snowflake id; - /// Allow mask - uint64_t allow; - /// Deny mask - uint64_t deny; - /// Overwrite type + /// Bitmask of allowed permissions + permission allow; + /// Bitmask of denied permissions + permission deny; + /// Type of overwrite. See dpp::overwrite_type uint8_t type; + + /** + * @brief Construct a new permission_overwrite object + */ + permission_overwrite(); + + /** + * @brief Construct a new permission_overwrite object + * @param id ID of the role or the member to create the overwrite for + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) for this user/role in this channel + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) for this user/role in this channel + * @param type Type of overwrite + */ + permission_overwrite(snowflake id, uint64_t allow, uint64_t deny, overwrite_type type); }; @@ -191,7 +206,7 @@ class DPP_EXPORT channel : public managed, public json_interface { * it contains the calculated permission bitmask of the user issuing the command * within this channel. */ - uint64_t permissions; + permission permissions; /** Sorting position, lower number means higher up the list */ uint16_t position; @@ -342,13 +357,13 @@ class DPP_EXPORT channel : public managed, public json_interface { * @brief Add a permission_overwrite to this channel object * * @param id ID of the role or the member you want to add overwrite for - * @param type type of overwrite (0 for role, 1 for member) + * @param type type of overwrite * @param allowed_permissions bitmask of allowed permissions (refer to enum dpp::permissions) for this user/role in this channel * @param denied_permissions bitmask of denied permissions (refer to enum dpp::permissions) for this user/role in this channel * * @return Reference to self, so these method calls may be chained */ - channel& add_permission_overwrite(const snowflake id, const uint8_t type, const uint64_t allowed_permissions, const uint64_t denied_permissions); + channel& add_permission_overwrite(const snowflake id, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); /** * @brief Get the mention ping for the channel @@ -361,11 +376,11 @@ class DPP_EXPORT channel : public managed, public json_interface { * @brief Get the user permissions for a user on this channel * * @param member The user to return permissions for - * @return uint64_t Permissions bitmask made of bits in role_permissions. + * @return permission Permissions bitmask made of bits in dpp::permissions. * Note that if the user is not on the channel or the guild is * not in the cache, the function will always return 0. */ - uint64_t get_user_permissions(const class user* member) const; + permission get_user_permissions(const class user* member) const; /** * @brief Return a map of members on the channel, built from the guild's diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 40c0957f29..255e98394d 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2106,7 +2106,7 @@ class DPP_EXPORT cluster { * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint32_t allow, const uint32_t deny, const bool member, command_completion_event_t callback = utility::log_error()); + void channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback = utility::log_error()); /** * @brief Edit a channel's permissions @@ -2121,7 +2121,7 @@ class DPP_EXPORT cluster { * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint32_t allow, const uint32_t deny, const bool member, command_completion_event_t callback = utility::log_error()); + void channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback = utility::log_error()); /** * @brief Delete a channel diff --git a/include/dpp/guild.h b/include/dpp/guild.h index a81904abc0..46fbde558e 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -540,9 +541,9 @@ class DPP_EXPORT guild : public managed, public json_interface { * before permission overwrites are applied. * * @param member member to get permissions for - * @return uint64_t permissions bitmask + * @return permission permissions bitmask */ - uint64_t base_permissions(const class user* member) const; + permission base_permissions(const class user* member) const; /** * @brief Get the permission overwrites for a member @@ -552,9 +553,9 @@ class DPP_EXPORT guild : public managed, public json_interface { * from channel::base_permissions * @param member Member to fetch permissions for * @param channel Channel to fetch permissions against - * @return uint64_t Merged permissions bitmask of overwrites. + * @return permission Merged permissions bitmask of overwrites. */ - uint64_t permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const; + permission permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const; /** * @brief Rehash members map diff --git a/include/dpp/role.h b/include/dpp/role.h index 9fd3ed64cb..00d380a6cb 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -64,7 +64,7 @@ class DPP_EXPORT role : public managed, public json_interface { /** Role position */ uint8_t position; /** Role permissions bitmask values from dpp::permissions */ - uint64_t permissions; + permission permissions; /** Role flags from dpp::role_flags */ uint8_t flags; /** Integration id if any (e.g. role is a bot's role created when it was invited) */ diff --git a/include/dpp/utility.h b/include/dpp/utility.h index a3a21b0b12..1b5484f54e 100644 --- a/include/dpp/utility.h +++ b/include/dpp/utility.h @@ -355,7 +355,7 @@ namespace dpp { * @brief Create a bot invite * * @param bot_id Bot ID - * @param permissions Permissions of the bot to invite + * @param permissions Permission bitmask of the bot to invite * @param scopes Scopes to use * @return Invite URL */ diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index feab8d3d7f..8bd429fa46 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -32,6 +32,10 @@ using json = nlohmann::json; namespace dpp { +permission_overwrite::permission_overwrite() : id(0), allow(0), deny(0), type(0) {} + +permission_overwrite::permission_overwrite(snowflake id, uint64_t allow, uint64_t deny, overwrite_type type) : id(id), allow(allow), deny(deny), type(type) {} + const uint8_t CHANNEL_TYPE_MASK = 0b00001111; thread_member& thread_member::fill_from_json(nlohmann::json* j) { @@ -145,7 +149,7 @@ channel& channel::set_user_limit(const uint8_t user_limit) { return *this; } -channel& channel::add_permission_overwrite(const snowflake id, const uint8_t type, const uint64_t allowed_permissions, const uint64_t denied_permissions) { +channel& channel::add_permission_overwrite(const snowflake id, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions) { permission_overwrite po {id, allowed_permissions, denied_permissions, type}; this->permission_overwrites.push_back(po); return *this; @@ -365,7 +369,7 @@ std::string channel::build_json(bool with_id) const { return j.dump(); } -uint64_t channel::get_user_permissions(const user* member) const +permission channel::get_user_permissions(const user* member) const { if (member == nullptr) return 0; diff --git a/src/dpp/cluster/channel.cpp b/src/dpp/cluster/channel.cpp index 2b5dfe4f61..9db9995294 100644 --- a/src/dpp/cluster/channel.cpp +++ b/src/dpp/cluster/channel.cpp @@ -35,11 +35,11 @@ void cluster::channel_delete(snowflake channel_id, command_completion_event_t ca rest_request(this, API_PATH "/channels", std::to_string(channel_id), "", m_delete, "", callback); } -void cluster::channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint32_t allow, const uint32_t deny, const bool member, command_completion_event_t callback) { +void cluster::channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback) { channel_edit_permissions(c.id, overwrite_id, allow, deny, member, callback); } -void cluster::channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint32_t allow, const uint32_t deny, const bool member, command_completion_event_t callback) { +void cluster::channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback) { json j({ {"allow", std::to_string(allow)}, {"deny", std::to_string(deny)}, {"type", member ? 1 : 0} }); rest_request(this, API_PATH "/channels", std::to_string(channel_id), "permissions/" + std::to_string(overwrite_id), m_put, j.dump(), callback); } diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 7b69fd6f1e..1d47e7f19b 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -532,7 +532,7 @@ std::string guild_widget::build_json(bool with_id) const { } -uint64_t guild::base_permissions(const user* member) const +permission guild::base_permissions(const user* member) const { if (owner_id == member->id) return ~0; @@ -543,7 +543,7 @@ uint64_t guild::base_permissions(const user* member) const return 0; guild_member gm = mi->second; - uint64_t permissions = everyone->permissions; + permission permissions = everyone->permissions; for (auto& rid : gm.roles) { role* r = dpp::find_role(rid); @@ -558,12 +558,12 @@ uint64_t guild::base_permissions(const user* member) const return permissions; } -uint64_t guild::permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const +permission guild::permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const { if (base_permissions & p_administrator) return ~0; - int64_t permissions = base_permissions; + permission permissions = base_permissions; for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { if (it->id == id && it->type == ot_role) { permissions &= ~it->deny; diff --git a/src/dpp/role.cpp b/src/dpp/role.cpp index 187f23a097..618e16d216 100644 --- a/src/dpp/role.cpp +++ b/src/dpp/role.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -143,167 +144,167 @@ bool role::is_managed() const { } bool role::has_create_instant_invite() const { - return ((this->permissions & p_administrator) | (this->permissions & p_create_instant_invite)); + return has_administrator() || permissions.has(p_create_instant_invite); } bool role::has_kick_members() const { - return ((this->permissions & p_administrator) | (this->permissions & p_kick_members)); + return has_administrator() || permissions.has(p_kick_members); } bool role::has_ban_members() const { - return ((this->permissions & p_administrator) | (this->permissions & p_ban_members)); + return has_administrator() || permissions.has(p_ban_members); } bool role::has_administrator() const { - return (this->permissions & p_administrator); + return permissions.has(p_administrator); } bool role::has_manage_channels() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_channels)); + return has_administrator() || permissions.has(p_manage_channels); } bool role::has_manage_guild() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_guild)); + return has_administrator() || permissions.has(p_manage_guild); } bool role::has_add_reactions() const { - return ((this->permissions & p_administrator) | (this->permissions & p_add_reactions)); + return has_administrator() || permissions.has(p_add_reactions); } bool role::has_view_audit_log() const { - return ((this->permissions & p_administrator) | (this->permissions & p_view_audit_log)); + return has_administrator() || permissions.has(p_view_audit_log); } bool role::has_priority_speaker() const { - return ((this->permissions & p_administrator) | (this->permissions & p_priority_speaker)); + return has_administrator() || permissions.has(p_priority_speaker); } bool role::has_stream() const { - return ((this->permissions & p_administrator) | (this->permissions & p_stream)); + return has_administrator() || permissions.has(p_stream); } bool role::has_view_channel() const { - return ((this->permissions & p_administrator) | (this->permissions & p_view_channel)); + return has_administrator() || permissions.has(p_view_channel); } bool role::has_send_messages() const { - return ((this->permissions & p_administrator) | (this->permissions & p_send_messages)); + return has_administrator() || permissions.has(p_send_messages); } bool role::has_send_tts_messages() const { - return ((this->permissions & p_administrator) | (this->permissions & p_send_tts_messages)); + return has_administrator() || permissions.has(p_send_tts_messages); } bool role::has_manage_messages() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_messages)); + return has_administrator() || permissions.has(p_manage_messages); } bool role::has_embed_links() const { - return ((this->permissions & p_administrator) | (this->permissions & p_embed_links)); + return has_administrator() || permissions.has(p_embed_links); } bool role::has_attach_files() const { - return ((this->permissions & p_administrator) | (this->permissions & p_attach_files)); + return has_administrator() || permissions.has(p_attach_files); } bool role::has_read_message_history() const { - return ((this->permissions & p_administrator) | (this->permissions & p_read_message_history)); + return has_administrator() || permissions.has(p_read_message_history); } bool role::has_mention_everyone() const { - return ((this->permissions & p_administrator) | (this->permissions & p_mention_everyone)); + return has_administrator() || permissions.has(p_mention_everyone); } bool role::has_use_external_emojis() const { - return ((this->permissions & p_administrator) | (this->permissions & p_use_external_emojis)); + return has_administrator() || permissions.has(p_use_external_emojis); } bool role::has_view_guild_insights() const { - return ((this->permissions & p_administrator) | (this->permissions & p_view_guild_insights)); + return has_administrator() || permissions.has(p_view_guild_insights); } bool role::has_connect() const { - return ((this->permissions & p_administrator) | (this->permissions & p_connect)); + return has_administrator() || permissions.has(p_connect); } bool role::has_speak() const { - return ((this->permissions & p_administrator) | (this->permissions & p_speak)); + return has_administrator() || permissions.has(p_speak); } bool role::has_mute_members() const { - return ((this->permissions & p_administrator) | (this->permissions & p_mute_members)); + return has_administrator() || permissions.has(p_mute_members); } bool role::has_deafen_members() const { - return ((this->permissions & p_administrator) | (this->permissions & p_deafen_members)); + return has_administrator() || permissions.has(p_deafen_members); } bool role::has_move_members() const { - return ((this->permissions & p_administrator) | (this->permissions & p_move_members)); + return has_administrator() || permissions.has(p_move_members); } bool role::has_use_vad() const { - return ((this->permissions & p_administrator) | (this->permissions & p_use_vad)); + return has_administrator() || permissions.has(p_use_vad); } bool role::has_change_nickname() const { - return ((this->permissions & p_administrator) | (this->permissions & p_change_nickname)); + return has_administrator() || permissions.has(p_change_nickname); } bool role::has_manage_nicknames() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_nicknames)); + return has_administrator() || permissions.has(p_manage_nicknames); } bool role::has_manage_roles() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_roles)); + return has_administrator() || permissions.has(p_manage_roles); } bool role::has_manage_webhooks() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_webhooks)); + return has_administrator() || permissions.has(p_manage_webhooks); } bool role::has_manage_emojis_and_stickers() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_emojis_and_stickers)); + return has_administrator() || permissions.has(p_manage_emojis_and_stickers); } bool role::has_use_application_commands() const { - return ((this->permissions & p_administrator) | (this->permissions & p_use_application_commands)); + return has_administrator() || permissions.has(p_use_application_commands); } bool role::has_request_to_speak() const { - return ((this->permissions & p_administrator) | (this->permissions & p_request_to_speak)); + return has_administrator() || permissions.has(p_request_to_speak); } bool role::has_manage_threads() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_threads)); + return has_administrator() || permissions.has(p_manage_threads); } bool role::has_create_public_threads() const { - return ((this->permissions & p_administrator) | (this->permissions & p_create_public_threads)); + return has_administrator() || permissions.has(p_create_public_threads); } bool role::has_create_private_threads() const { - return ((this->permissions & p_administrator) | (this->permissions & p_create_private_threads)); + return has_administrator() || permissions.has(p_create_private_threads); } bool role::has_use_external_stickers() const { - return ((this->permissions & p_administrator) | (this->permissions & p_use_external_stickers)); + return has_administrator() || permissions.has(p_use_external_stickers); } bool role::has_send_messages_in_threads() const { - return ((this->permissions & p_administrator) | (this->permissions & p_send_messages_in_threads)); + return has_administrator() || permissions.has(p_send_messages_in_threads); } bool role::has_use_embedded_activities() const { - return ((this->permissions & p_administrator) | (this->permissions & p_use_embedded_activities)); + return has_administrator() || permissions.has(p_use_embedded_activities); } bool role::has_manage_events() const { - return ((this->permissions & p_administrator) | (this->permissions & p_manage_events)); + return has_administrator() || permissions.has(p_manage_events); } bool role::has_moderate_members() const { - return ((this->permissions & p_administrator) | (this->permissions & p_moderate_members)); + return has_administrator() || permissions.has(p_moderate_members); } role& role::set_name(const std::string& n) { From 9914ed703655dbcf5192ffc99b8dcafe550d0e5f Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 20:42:29 +0200 Subject: [PATCH 019/136] make permission::value protected --- include/dpp/permissions.h | 8 ++++---- src/dpp/permissions.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 23af675c8d..ebfd7b2a16 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -80,7 +80,7 @@ using role_permissions = permissions; * @brief Represents a permission bitmask (refer to enum dpp::permissions) which are hold in an uint64_t */ class permission { -private: +protected: /** * @brief The permission bitmask value */ @@ -111,12 +111,12 @@ class permission { /** * @brief For building json - * @return The permission bitmask value + * @return The permission bitmask value as a string */ operator nlohmann::json() const; /** - * @brief Check if it has a permission flag set + * @brief Check if it has a permission flag set. It uses the Bitwise AND operator * @param p The permission flag from dpp::permissions * @return True if it has the permission */ @@ -130,7 +130,7 @@ class permission { const permission& add(uint64_t p); /** - * @brief Set a permission with the Bitwise AND operation + * @brief Assign a permission. This will reset the bitmask to the new value. * @param p The permissions (from dpp::permissions) to set * @return permission& reference to self for chaining */ diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index c8ef552a0f..bc6ad8bb3f 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -36,7 +36,7 @@ permission::operator uint64_t &() { } permission::operator nlohmann::json() const { - return value; + return std::to_string(value); } bool permission::has(uint64_t p) const { From ba84c59c0da6d8d09a00f6dd733eccf5a4f8bf18 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 23:10:10 +0200 Subject: [PATCH 020/136] added unit test for dpp::permission --- include/dpp/cluster_sync_calls.h | 3 ++- src/test.cpp | 20 ++++++++++++++++++++ src/unittest.cpp | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 1a493905eb..90ea3cbbd4 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -33,7 +33,7 @@ * @see dpp::cluster::global_bulk_command_create * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands * @param commands Vector of slash commands to create/update. - * overwriting existing commands that are registered globally for this application. + * overwriting existing commands that are registered globally for this application. Updates will be available in all guilds after 1 hour. * Commands that do not already exist will count toward daily application command create limits. * @return slashcommand_map returned object on completion * \memberof dpp::cluster @@ -154,6 +154,7 @@ guild_command_permissions_map guild_commands_get_permissions_sync(snowflake guil * @param commands A vector of slash commands to edit/overwrite the permissions for * @param guild_id Guild ID to edit permissions of the slash commands in * @return guild_command_permissions_map returned object on completion + * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. diff --git a/src/test.cpp b/src/test.cpp index ca46f07913..d83bcc0612 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -174,6 +174,26 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test("COMMANDOPTIONCHOICEFILLFROMJSON", (success_double && success_int && success_int2 && success_bool && success_snowflake && success_string)); } + { + set_test("PERMISSION_CLASS", false); + bool success = false; + auto p = dpp::permission(); + p = 16; + success = p == 16 && success; + p |= 4; + success = p == 20 && success; + p <<= 8; + success = p == 4096 && success; + auto s = std::to_string(p); + success = s == "4096" && success; + json j; + j["value"] = p; + success = dpp::snowflake_not_null(&j, "value") == 4096 && success; + p.set(8); + success = p.has(8) && success; + set_test("PERMISSION_CLASS", success); + } + set_test("TIMESTRINGTOTIMESTAMP", false); json tj; tj["t1"] = "2022-01-19T17:18:14.506000+00:00"; diff --git a/src/unittest.cpp b/src/unittest.cpp index 5ed546ea11..df35ee6f45 100644 --- a/src/unittest.cpp +++ b/src/unittest.cpp @@ -78,6 +78,7 @@ std::map tests = { {"COMPARISON", {"dpp::manged object comparison", false, false}}, {"CHANNELCACHE", {"dpp::find_channel()", false, false}}, {"CHANNELTYPES", {"dpp::channel type flags", false, false}}, + {"PERMISSION_CLASS", {"testing dpp::permission functionality", false, false}}, }; double start = dpp::utility::time_f(); From be649d8a6c065566606202cd235d363686059c4c Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 23:22:40 +0200 Subject: [PATCH 021/136] updated docs on channel_edit_permissions --- include/dpp/cluster.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 255e98394d..89aa7bbcd5 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2100,8 +2100,8 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param c Channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions - * @param deny deny permissions + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask * @param member true if the overwrite_id is a user id, false if it is a channel id * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). @@ -2115,8 +2115,8 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param channel_id ID of the channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions - * @param deny deny permissions + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask * @param member true if the overwrite_id is a user id, false if it is a channel id * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). From 79ca2c944dbd91b3b454c05497e1872e2b0b503f Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 26 May 2022 23:39:43 +0200 Subject: [PATCH 022/136] updated dpp::permission::has --- src/dpp/permissions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index bc6ad8bb3f..fee8ada4aa 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -40,7 +40,7 @@ permission::operator nlohmann::json() const { } bool permission::has(uint64_t p) const { - return (value & p); + return (value & p) == p; } const permission& permission::add(uint64_t p) { From 045afb5f052922dcc00bf184b44fc989ebb3d7f9 Mon Sep 17 00:00:00 2001 From: Phil B Date: Fri, 27 May 2022 00:47:26 +0200 Subject: [PATCH 023/136] fixed dpp::permission unit test --- src/test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test.cpp b/src/test.cpp index d83bcc0612..ffdc3281ba 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -179,16 +179,16 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b bool success = false; auto p = dpp::permission(); p = 16; - success = p == 16 && success; + success = p == 16; p |= 4; success = p == 20 && success; - p <<= 8; - success = p == 4096 && success; + p <<= 8; // left shift + success = p == 5120 && success; auto s = std::to_string(p); - success = s == "4096" && success; + success = s == "5120" && success; json j; j["value"] = p; - success = dpp::snowflake_not_null(&j, "value") == 4096 && success; + success = dpp::snowflake_not_null(&j, "value") == 5120 && success; p.set(8); success = p.has(8) && success; set_test("PERMISSION_CLASS", success); From 0f7afcbfcc965110d9a7a3e7af96eba4ea45fdcf Mon Sep 17 00:00:00 2001 From: brain Date: Fri, 27 May 2022 02:04:05 +0100 Subject: [PATCH 024/136] ci: refactor unit tests to allow for offline tests and online tests. PRs will run offline tests. Offline means it doesnt need to talk to discord with a valid API token, but could still do network/internet based things. --- src/test.cpp | 77 +++++++++++++-------- src/test.h | 142 ++++++++++++++++++++++---------------- src/unittest.cpp | 173 ++++++++++++++++++++++++++--------------------- 3 files changed, 226 insertions(+), 166 deletions(-) diff --git a/src/test.cpp b/src/test.cpp index ffdc3281ba..e28362d1bc 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -25,7 +25,11 @@ int main() { std::string token(get_token()); - std::cout << "Running unit tests. Guild ID: " << TEST_GUILD_ID << " Text Channel ID: " << TEST_TEXT_CHANNEL_ID << " VC ID: " << TEST_VC_ID << " User ID: " << TEST_USER_ID << " Event ID: " << TEST_EVENT_ID << "\n"; + if (offline) { + std::cout << "Running offline unit tests only.\n"; + } else { + std::cout << "Running offline and online unit tests. Guild ID: " << TEST_GUILD_ID << " Text Channel ID: " << TEST_TEXT_CHANNEL_ID << " VC ID: " << TEST_VC_ID << " User ID: " << TEST_USER_ID << " Event ID: " << TEST_EVENT_ID << "\n"; + } std::string test_to_escape = "*** _This is a test_ ***\n```cpp\n\ int main() {\n\ @@ -95,23 +99,25 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test("HOSTINFO", hci_test); set_test("HTTPS", false); - dpp::multipart_content multipart = dpp::https_client::build_multipart( - "{\"content\":\"test\"}", {"test.txt", "blob.blob"}, {"ABCDEFGHI", "BLOB!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"} - ); - try { - dpp::https_client c("discord.com", 443, "/api/channels/" + std::to_string(TEST_TEXT_CHANNEL_ID) + "/messages", "POST", multipart.body, - { - {"Content-Type", multipart.mimetype}, - {"Authorization", "Bot " + token} - } + if (!offline) { + dpp::multipart_content multipart = dpp::https_client::build_multipart( + "{\"content\":\"test\"}", {"test.txt", "blob.blob"}, {"ABCDEFGHI", "BLOB!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"} ); - std::string hdr1 = c.get_header("server"); - std::string content1 = c.get_content(); - set_test("HTTPS", hdr1 == "cloudflare" && c.get_status() == 200); - } - catch (const dpp::exception& e) { - std::cout << e.what() << "\n"; - set_test("HTTPS", false); + try { + dpp::https_client c("discord.com", 443, "/api/channels/" + std::to_string(TEST_TEXT_CHANNEL_ID) + "/messages", "POST", multipart.body, + { + {"Content-Type", multipart.mimetype}, + {"Authorization", "Bot " + token} + } + ); + std::string hdr1 = c.get_header("server"); + std::string content1 = c.get_content(); + set_test("HTTPS", hdr1 == "cloudflare" && c.get_status() == 200); + } + catch (const dpp::exception& e) { + std::cout << e.what() << "\n"; + set_test("HTTPS", false); + } } set_test("HTTP", false); @@ -216,7 +222,11 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test("ICONHASH", false); set_test("MSGCOLLECT", false); - message_collector collect_messages(&bot, 25); + if (!offline) { + /* Intentional leak: freed on unit test end */ + [[maybe_unused]] + message_collector* collect_messages = new message_collector(&bot, 25); + } dpp::utility::iconhash i; std::string dummyval("fcffffffffffff55acaaaaaaaaaaaa66"); @@ -338,8 +348,10 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b }); set_test("SYNC", false); - dpp::message m = dpp::sync(&bot, &dpp::cluster::message_create, dpp::message(TEST_TEXT_CHANNEL_ID, "TEST")); - set_test("SYNC", m.content == "TEST"); + if (!offline) { + dpp::message m = dpp::sync(&bot, &dpp::cluster::message_create, dpp::message(TEST_TEXT_CHANNEL_ID, "TEST")); + set_test("SYNC", m.content == "TEST"); + } bot.on_guild_create([&](const dpp::guild_create_t & event) { if (event.created->id == TEST_GUILD_ID) { @@ -435,8 +447,10 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test("BOTSTART", false); try { - bot.start(true); - set_test("BOTSTART", true); + if (!offline) { + bot.start(true); + set_test("BOTSTART", true); + } } catch (const std::exception &) { set_test("BOTSTART", false); @@ -502,19 +516,24 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test("TIMERSTOP", bot.stop_timer(th)); set_test("USERCACHE", false); - dpp::user* u = dpp::find_user(TEST_USER_ID); - set_test("USERCACHE", u); + if (!offline) { + dpp::user* u = dpp::find_user(TEST_USER_ID); + set_test("USERCACHE", u); + } set_test("CHANNELCACHE", false); set_test("CHANNELTYPES", false); - dpp::channel* c = dpp::find_channel(TEST_TEXT_CHANNEL_ID); - dpp::channel* c2 = dpp::find_channel(TEST_VC_ID); - set_test("CHANNELCACHE", c && c2); - set_test("CHANNELTYPES", c && c->is_text_channel() && !c->is_voice_channel() && c2 && c2->is_voice_channel() && !c2->is_text_channel()); + if (!offline) { + dpp::channel* c = dpp::find_channel(TEST_TEXT_CHANNEL_ID); + dpp::channel* c2 = dpp::find_channel(TEST_VC_ID); + set_test("CHANNELCACHE", c && c2); + set_test("CHANNELTYPES", c && c->is_text_channel() && !c->is_voice_channel() && c2 && c2->is_voice_channel() && !c2->is_text_channel()); + } wait_for_tests(); } - catch (const std::exception &) { + catch (const std::exception &e) { + std::cout << e.what() << "\n"; set_test("CLUSTER", false); } diff --git a/src/test.h b/src/test.h index 68fc0fc1b4..0b714f9566 100644 --- a/src/test.h +++ b/src/test.h @@ -35,8 +35,17 @@ _Pragma("warning( disable : 5105 )"); // 4251 warns when we export classes or st using json = nlohmann::json; +enum test_type_t { + /* A test that does not require discord connectivity */ + tt_offline, + /* A test that requires discord connectivity */ + tt_online, +}; + /* Represents a test case */ struct test_t { + /* Test type */ + test_type_t type; /* Description of test */ std::string description; /* Has been executed */ @@ -64,105 +73,118 @@ extern dpp::snowflake TEST_VC_ID; extern dpp::snowflake TEST_USER_ID; extern dpp::snowflake TEST_EVENT_ID; +/* True if we skip tt_online tests */ +extern bool offline; + /** * @brief Perform a test of a REST base API call with one parameter */ #define singleparam_api_test(func_name, param, return_type, testname) \ set_test(testname, false); \ - bot.func_name (param, [&](const dpp::confirmation_callback_t &cc) { \ - if (!cc.is_error()) { \ - return_type g = std::get(cc.value); \ - if (g.id == param) { \ - set_test(testname, true); \ + if (!offline) { \ + bot.func_name (param, [&](const dpp::confirmation_callback_t &cc) { \ + if (!cc.is_error()) { \ + return_type g = std::get(cc.value); \ + if (g.id == param) { \ + set_test(testname, true); \ + } else { \ + bot.log(dpp::ll_debug, cc.http_info.body); \ + set_test(testname, false); \ + } \ } else { \ bot.log(dpp::ll_debug, cc.http_info.body); \ set_test(testname, false); \ } \ - } else { \ - bot.log(dpp::ll_debug, cc.http_info.body); \ - set_test(testname, false); \ - } \ - }); + }); \ + } /** * @brief Perform a test of a REST base API call with one parameter */ #define twoparam_api_test(func_name, param1, param2, return_type, testname) \ set_test(testname, false); \ - bot.func_name (param1, param2, [&](const dpp::confirmation_callback_t &cc) { \ - if (!cc.is_error()) { \ - return_type g = std::get(cc.value); \ - if (g.id > 0) { \ - set_test(testname, true); \ + if (!offline) { \ + bot.func_name (param1, param2, [&](const dpp::confirmation_callback_t &cc) { \ + if (!cc.is_error()) { \ + return_type g = std::get(cc.value); \ + if (g.id > 0) { \ + set_test(testname, true); \ + } else { \ + bot.log(dpp::ll_debug, cc.http_info.body); \ + set_test(testname, false); \ + } \ } else { \ bot.log(dpp::ll_debug, cc.http_info.body); \ set_test(testname, false); \ } \ - } else { \ - bot.log(dpp::ll_debug, cc.http_info.body); \ - set_test(testname, false); \ - } \ - }); + }); \ + } /** * @brief Perform a test of a REST base API call with one parameter that returns a list */ #define singleparam_api_test_list(func_name, param, return_type, testname) \ set_test(testname, false); \ - bot.func_name (param, [&](const dpp::confirmation_callback_t &cc) { \ - if (!cc.is_error()) { \ - return_type g = std::get(cc.value); \ - if (g.size() > 0) { \ - set_test(testname, true); \ + if (!offline) { \ + bot.func_name (param, [&](const dpp::confirmation_callback_t &cc) { \ + if (!cc.is_error()) { \ + return_type g = std::get(cc.value); \ + if (g.size() > 0) { \ + set_test(testname, true); \ + } else { \ + set_test(testname, false); \ + bot.log(dpp::ll_debug, cc.http_info.body); \ + } \ } else { \ set_test(testname, false); \ bot.log(dpp::ll_debug, cc.http_info.body); \ } \ - } else { \ - set_test(testname, false); \ - bot.log(dpp::ll_debug, cc.http_info.body); \ - } \ - }); + }); \ + } /** * @brief Perform a test of a REST base API call with one parameter that returns a list */ #define multiparam_api_test_list(func_name, param, return_type, testname) \ set_test(testname, false); \ - bot.func_name (param, 0, 0, 1000, [&](const dpp::confirmation_callback_t &cc) { \ - if (!cc.is_error()) { \ - return_type g = std::get(cc.value); \ - if (g.size() > 0) { \ - set_test(testname, true); \ + if (!offline) { \ + bot.func_name (param, 0, 0, 1000, [&](const dpp::confirmation_callback_t &cc) { \ + if (!cc.is_error()) { \ + return_type g = std::get(cc.value); \ + if (g.size() > 0) { \ + set_test(testname, true); \ + } else { \ + set_test(testname, false); \ + bot.log(dpp::ll_debug, cc.http_info.body); \ + } \ } else { \ set_test(testname, false); \ bot.log(dpp::ll_debug, cc.http_info.body); \ } \ - } else { \ - set_test(testname, false); \ - bot.log(dpp::ll_debug, cc.http_info.body); \ - } \ - }); + }); \ + } /** * @brief Perform a test of a REST base API call with two parameters */ #define twoparam_api_test_list(func_name, param1, param2, return_type, testname) \ set_test(testname, false); \ - bot.func_name (param1, param2, [&](const dpp::confirmation_callback_t &cc) { \ - if (!cc.is_error()) { \ - return_type g = std::get(cc.value); \ - if (g.size() > 0) { \ - set_test(testname, true); \ + if (!offline) { \ + bot.func_name (param1, param2, [&](const dpp::confirmation_callback_t &cc) { \ + if (!cc.is_error()) { \ + return_type g = std::get(cc.value); \ + if (g.size() > 0) { \ + set_test(testname, true); \ + } else { \ + bot.log(dpp::ll_debug, cc.http_info.body); \ + set_test(testname, false); \ + } \ } else { \ bot.log(dpp::ll_debug, cc.http_info.body); \ set_test(testname, false); \ } \ - } else { \ - bot.log(dpp::ll_debug, cc.http_info.body); \ - set_test(testname, false); \ - } \ - }); + }); \ + } /** @@ -170,15 +192,17 @@ extern dpp::snowflake TEST_EVENT_ID; */ #define noparam_api_test(func_name, return_type, testname) \ set_test(testname, false); \ - bot.func_name ([&](const dpp::confirmation_callback_t &cc) { \ - if (!cc.is_error()) { \ - return_type g = std::get(cc.value); \ - set_test(testname, true); \ - } else { \ - bot.log(dpp::ll_debug, cc.http_info.body); \ - set_test(testname, false); \ - } \ - }); + if (!offline) { \ + bot.func_name ([&](const dpp::confirmation_callback_t &cc) { \ + if (!cc.is_error()) { \ + return_type g = std::get(cc.value); \ + set_test(testname, true); \ + } else { \ + bot.log(dpp::ll_debug, cc.http_info.body); \ + set_test(testname, false); \ + } \ + }); \ + } /** * @brief Sets a test's status diff --git a/src/unittest.cpp b/src/unittest.cpp index df35ee6f45..20aa081621 100644 --- a/src/unittest.cpp +++ b/src/unittest.cpp @@ -22,66 +22,67 @@ /* Current list of unit tests */ std::map tests = { - {"CLUSTER", {"Instantiate DPP cluster", false, false}}, - {"BOTSTART", {"cluster::start method", false, false}}, - {"CONNECTION", {"Connection to client websocket", false, false}}, - {"APPCOMMAND", {"Creation of application command", false, false}}, - {"DELCOMMAND", {"Deletion of application command", false, false}}, - {"LOGGER", {"Log events", false, false}}, - {"MESSAGECREATE", {"Creation of a channel message", false, false}}, - {"MESSAGEDELETE", {"Deletion of a channel message", false, false}}, - {"MESSAGERECEIVE", {"Receipt of a created message", false, false}}, - {"CACHE", {"Test guild cache", false, false}}, - {"USERCACHE", {"Test user cache", false, false}}, - {"VOICECONN", {"Connect to voice channel", false, false}}, - {"VOICESEND", {"Send audio to voice channel", false, false}}, - {"REACT", {"React to a message", false, false}}, - {"REACTEVENT", {"Reaction event", false, false}}, - {"GUILDCREATE", {"Receive guild create event", false, false}}, - {"MESSAGESGET", {"Get messages", false, false}}, - {"TIMESTAMP", {"crossplatform_strptime()", false, false}}, - {"ICONHASH", {"utility::iconhash", false, false}}, - {"CURRENTUSER", {"cluster::current_user_get()", false, false}}, - {"GETGUILD", {"cluster::guild_get()", false, false}}, - {"GETCHAN", {"cluster::channel_get()", false, false}}, - {"GETCHANS", {"cluster::channels_get()", false, false}}, - {"GETROLES", {"cluster::roles_get()", false, false}}, - {"GETINVS", {"cluster::guild_get_invites()", false, false}}, - {"GETBANS", {"cluster::guild_get_bans()", false, false}}, - {"GETPINS", {"cluster::channel_pins_get()", false, false}}, - {"GETEVENTS", {"cluster::guild_events_get()", false, false}}, - {"GETEVENT", {"cluster::guild_event_get()", false, false}}, - {"MSGMENTIONUSER", {"message_create_t::reply() (mention)", false, false}}, - {"MSGCREATESEND", {"message_create_t::send()", false, false}}, - {"MSGCREATEREPLY", {"message_create_t::reply()", false, false}}, - {"GETEVENTUSERS", {"cluster::guild_event_users_get()", false, false}}, - {"TIMERSTART", {"start timer", false, false}}, - {"TIMERSTOP", {"stop timer", false, false}}, - {"ONESHOT", {"one-shot timer", false, false}}, - {"PRESENCE", {"Presence intent", false, false}}, - {"CUSTOMCACHE", {"Instantiate a cache", false, false}}, - {"MSGCOLLECT", {"message_collector", false, false}}, - {"TS", {"managed::get_creation_date()", false, false}}, - {"READFILE", {"utility::read_file()", false, false}}, - {"TIMESTAMPTOSTRING", {"dpp::ts_to_string()", false, false}}, - {"TIMESTRINGTOTIMESTAMP", {"dpp::ts_not_null()", false, false}}, - {"COMMANDOPTIONCHOICEFILLFROMJSON", {"dpp::command_option_choice::fill_from_json()", false, false}}, - {"HOSTINFO", {"dpp::https_client::get_host_info()", false, false}}, - {"HTTPS", {"dpp::https_client HTTPS request", false, false}}, - {"HTTP", {"dpp::https_client HTTP request", false, false}}, - {"RUNONCE", {"dpp::run_once", false, false}}, - {"WEBHOOK", {"dpp::webhook construct from URL", false, false}}, - {"MD_ESC_1", {"Markdown escaping (ignore code block contents)", false, false}}, - {"MD_ESC_2", {"Markdown escaping (escape code block contents)", false, false}}, - {"URLENC", {"URL encoding", false, false}}, - {"SYNC", {"dpp::sync()", false, false}}, - {"COMPARISON", {"dpp::manged object comparison", false, false}}, - {"CHANNELCACHE", {"dpp::find_channel()", false, false}}, - {"CHANNELTYPES", {"dpp::channel type flags", false, false}}, - {"PERMISSION_CLASS", {"testing dpp::permission functionality", false, false}}, + {"CLUSTER", {tt_offline, "Instantiate DPP cluster", false, false}}, + {"BOTSTART", {tt_online, "cluster::start method", false, false}}, + {"CONNECTION", {tt_online, "Connection to client websocket", false, false}}, + {"APPCOMMAND", {tt_online, "Creation of application command", false, false}}, + {"DELCOMMAND", {tt_online, "Deletion of application command", false, false}}, + {"LOGGER", {tt_online, "Log events", false, false}}, + {"MESSAGECREATE", {tt_online, "Creation of a channel message", false, false}}, + {"MESSAGEDELETE", {tt_online, "Deletion of a channel message", false, false}}, + {"MESSAGERECEIVE", {tt_online, "Receipt of a created message", false, false}}, + {"CACHE", {tt_online, "Test guild cache", false, false}}, + {"USERCACHE", {tt_online, "Test user cache", false, false}}, + {"VOICECONN", {tt_online, "Connect to voice channel", false, false}}, + {"VOICESEND", {tt_online, "Send audio to voice channel", false, false}}, + {"REACT", {tt_online, "React to a message", false, false}}, + {"REACTEVENT", {tt_online, "Reaction event", false, false}}, + {"GUILDCREATE", {tt_online, "Receive guild create event", false, false}}, + {"MESSAGESGET", {tt_online, "Get messages", false, false}}, + {"TIMESTAMP", {tt_online, "crossplatform_strptime()", false, false}}, + {"ICONHASH", {tt_offline, "utility::iconhash", false, false}}, + {"CURRENTUSER", {tt_online, "cluster::current_user_get()", false, false}}, + {"GETGUILD", {tt_online, "cluster::guild_get()", false, false}}, + {"GETCHAN", {tt_online, "cluster::channel_get()", false, false}}, + {"GETCHANS", {tt_online, "cluster::channels_get()", false, false}}, + {"GETROLES", {tt_online, "cluster::roles_get()", false, false}}, + {"GETINVS", {tt_online, "cluster::guild_get_invites()", false, false}}, + {"GETBANS", {tt_online, "cluster::guild_get_bans()", false, false}}, + {"GETPINS", {tt_online, "cluster::channel_pins_get()", false, false}}, + {"GETEVENTS", {tt_online, "cluster::guild_events_get()", false, false}}, + {"GETEVENT", {tt_online, "cluster::guild_event_get()", false, false}}, + {"MSGMENTIONUSER", {tt_online, "message_create_t::reply() (mention)", false, false}}, + {"MSGCREATESEND", {tt_online, "message_create_t::send()", false, false}}, + {"MSGCREATEREPLY", {tt_online, "message_create_t::reply()", false, false}}, + {"GETEVENTUSERS", {tt_online, "cluster::guild_event_users_get()", false, false}}, + {"TIMERSTART", {tt_online, "start timer", false, false}}, + {"TIMERSTOP", {tt_online, "stop timer", false, false}}, + {"ONESHOT", {tt_online, "one-shot timer", false, false}}, + {"PRESENCE", {tt_online, "Presence intent", false, false}}, + {"CUSTOMCACHE", {tt_offline, "Instantiate a cache", false, false}}, + {"MSGCOLLECT", {tt_online, "message_collector", false, false}}, + {"TS", {tt_offline, "managed::get_creation_date()", false, false}}, + {"READFILE", {tt_offline, "utility::read_file()", false, false}}, + {"TIMESTAMPTOSTRING", {tt_offline, "dpp::ts_to_string()", false, false}}, + {"TIMESTRINGTOTIMESTAMP", {tt_offline, "dpp::ts_not_null()", false, false}}, + {"COMMANDOPTIONCHOICEFILLFROMJSON", {tt_offline, "dpp::command_option_choice::fill_from_json()", false, false}}, + {"HOSTINFO", {tt_offline, "dpp::https_client::get_host_info()", false, false}}, + {"HTTPS", {tt_online, "dpp::https_client HTTPS request", false, false}}, + {"HTTP", {tt_offline, "dpp::https_client HTTP request", false, false}}, + {"RUNONCE", {tt_offline, "dpp::run_once", false, false}}, + {"WEBHOOK", {tt_offline, "dpp::webhook construct from URL", false, false}}, + {"MD_ESC_1", {tt_offline, "Markdown escaping (ignore code block contents)", false, false}}, + {"MD_ESC_2", {tt_offline, "Markdown escaping (escape code block contents)", false, false}}, + {"URLENC", {tt_offline, "URL encoding", false, false}}, + {"SYNC", {tt_online, "dpp::sync()", false, false}}, + {"COMPARISON", {tt_offline, "dpp::manged object comparison", false, false}}, + {"CHANNELCACHE", {tt_online, "dpp::find_channel()", false, false}}, + {"CHANNELTYPES", {tt_online, "dpp::channel type flags", false, false}}, + {"PERMISSION_CLASS", {tt_offline, "testing dpp::permission functionality", false, false}}, }; double start = dpp::utility::time_f(); +bool offline = false; dpp::snowflake TEST_GUILD_ID = std::stoull(SAFE_GETENV("TEST_GUILD_ID")); dpp::snowflake TEST_TEXT_CHANNEL_ID = std::stoull(SAFE_GETENV("TEST_TEXT_CHANNEL_ID")); @@ -92,15 +93,21 @@ dpp::snowflake TEST_EVENT_ID = std::stoull(SAFE_GETENV("TEST_EVENT_ID")); void set_test(const std::string &testname, bool success) { auto i = tests.find(testname); if (i != tests.end()) { - if (!i->second.executed) { - std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[33mTESTING\u001b[0m] " << i->second.description << "\n"; - } else if (!success) { - std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[31mFAILED\u001b[0m] " << i->second.description << "\n"; - } - i->second.executed = true; - if (success) { + if (offline && i->second.type == tt_online) { i->second.success = true; - std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[32mSUCCESS\u001b[0m] " << i->second.description << "\n"; + i->second.executed = true; + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[33mSKIPPED\u001b[0m] " << i->second.description << "\n"; + } else { + if (!i->second.executed) { + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[33mTESTING\u001b[0m] " << i->second.description << "\n"; + } else if (!success) { + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[31mFAILED\u001b[0m] " << i->second.description << "\n"; + } + i->second.executed = true; + if (success) { + i->second.success = true; + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[32mSUCCESS\u001b[0m] " << i->second.description << "\n"; + } } } } @@ -115,17 +122,23 @@ double get_time() { int test_summary() { /* Report on all test cases */ - int failed = 0, passed = 0; + int failed = 0, passed = 0, skipped = 0; std::cout << "\u001b[37;1m\n\nUNIT TEST SUMMARY\n==================\n\u001b[0m"; for (auto & t : tests) { - if (t.second.success == false || t.second.executed == false) { - failed++; + bool test_skipped = false; + if (t.second.type == tt_online && offline) { + skipped++; + test_skipped = true; } else { - passed++; + if (t.second.success == false || t.second.executed == false) { + failed++; + } else { + passed++; + } } - std::cout << std::left << std::setw(50) << t.second.description << " " << std::fixed << std::setw(6) << (t.second.executed && t.second.success ? "\u001b[32mPASS" : "\u001b[31mFAIL") << std::setw(0) << "\u001b[0m\n"; + std::cout << std::left << std::setw(50) << t.second.description << " " << std::fixed << std::setw(6) << (test_skipped ? "\u001b[33mSKIPPED" : (t.second.executed && t.second.success ? "\u001b[32mPASS" : "\u001b[31mFAIL")) << std::setw(0) << "\u001b[0m\n"; } - std::cout << "\u001b[37;1m\nExecution finished in " << std::fixed << std::setprecision(3) << get_time() << std::setprecision(0) << " seconds.\nFailed: " << failed << " Passed: " << passed << " Percentage: " << std::setprecision(2) << ((float)(passed) / (float)(tests.size()) * 100.0f) << "%\u001b[0m\n"; + std::cout << "\u001b[37;1m\nExecution finished in " << std::fixed << std::setprecision(3) << get_time() << std::setprecision(0) << " seconds.\nFailed: " << failed << " Passed: " << passed << (skipped ? " Skipped: " : "") << (skipped ? std::to_string(skipped) : "") << " Percentage: " << std::setprecision(2) << ((float)(passed) / (float)(passed + failed) * 100.0f) << "%\u001b[0m\n"; return failed; } @@ -148,14 +161,14 @@ std::vector load_test_audio() { std::string get_token() { char* t = getenv("DPP_UNIT_TEST_TOKEN"); + std::string tok; if (!t) { - std::cerr << "\u001b[31mDPP_UNIT_TEST_TOKEN not defined -- this is likely a fork.\n\nNot running unit tests.\u001b[0m\n"; - exit(0); - } - std::string tok = std::string(t); - if (tok.empty()) { - std::cerr << "\u001b[31mDPP_UNIT_TEST_TOKEN empty -- this is likely a PR.\n\nNot running unit tests.\u001b[0m\n"; - exit(0); + offline = true; + } else { + tok = std::string(t); + if (tok.empty()) { + offline = true; + } } return tok; } @@ -167,6 +180,10 @@ void wait_for_tests() { for (auto & t : tests) { if (t.second.executed == true) { executed++; + } else if (offline && t.second.type == tt_online && !t.second.executed) { + executed++; + t.second.success = true; + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[33mSKIPPED\u001b[0m] " << t.second.description << "\n"; } } if (executed == tests.size()) { From 51cbbfee1f3d25608597ef9eecd3a22d21d624a4 Mon Sep 17 00:00:00 2001 From: Phil B Date: Fri, 27 May 2022 11:08:26 +0200 Subject: [PATCH 025/136] removed example from slashcommand::set_default_permissions as you can also use dpp::permission for this --- include/dpp/appcommand.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index 293c8c41f1..424823c2c0 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -913,18 +913,11 @@ class DPP_EXPORT slashcommand : public managed, public json_interface Date: Fri, 27 May 2022 11:13:39 +0200 Subject: [PATCH 026/136] added guild_member_remove_role and deprecated guild_member_delete_role because it's just a confusing name --- include/dpp/cluster.h | 16 ++++++++++++++++ src/dpp/cluster/guild_member.cpp | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 89aa7bbcd5..a53f2426ca 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2419,9 +2419,25 @@ class DPP_EXPORT cluster { * @param role_id Role to remove * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @deprecated Use dpp::cluster::guild_member_remove_role instead */ void guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback = utility::log_error()); + /** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback = utility::log_error()); + /** * @brief Remove (kick) a guild member * diff --git a/src/dpp/cluster/guild_member.cpp b/src/dpp/cluster/guild_member.cpp index af9fbfbece..15c5c26a8b 100644 --- a/src/dpp/cluster/guild_member.cpp +++ b/src/dpp/cluster/guild_member.cpp @@ -95,6 +95,11 @@ void cluster::guild_member_timeout(snowflake guild_id, snowflake user_id, time_t void cluster::guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback) { + guild_member_remove_role(guild_id, user_id, role_id, callback); +} + + +void cluster::guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback) { rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id) + "/roles/" + std::to_string(role_id), m_delete, "", callback); } From dc35e592b2374fa5c4f96e73074dd2724acb4139 Mon Sep 17 00:00:00 2001 From: Phil B Date: Fri, 27 May 2022 11:20:03 +0200 Subject: [PATCH 027/136] updated sync call methods --- include/dpp/cluster_sync_calls.h | 20 ++++++++++++++++++++ src/dpp/cluster/guild_member.cpp | 2 +- src/dpp/cluster_sync_calls.cpp | 4 ++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 90ea3cbbd4..3a74083549 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -1191,6 +1191,7 @@ confirmation guild_member_timeout_sync(snowflake guild_id, snowflake user_id, ti * @param user_id User ID to remove role from * @param role_id Role to remove * @return confirmation returned object on completion + * @deprecated Use dpp::cluster::guild_member_remove_role instead * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. @@ -1198,6 +1199,25 @@ confirmation guild_member_timeout_sync(snowflake guild_id, snowflake user_id, ti */ confirmation guild_member_delete_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); +/** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_remove_role + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_remove_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); + /** * @brief Moves the guild member to a other voice channel, if member is connected to one * diff --git a/src/dpp/cluster/guild_member.cpp b/src/dpp/cluster/guild_member.cpp index 15c5c26a8b..b4cc7c07d2 100644 --- a/src/dpp/cluster/guild_member.cpp +++ b/src/dpp/cluster/guild_member.cpp @@ -95,7 +95,7 @@ void cluster::guild_member_timeout(snowflake guild_id, snowflake user_id, time_t void cluster::guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback) { - guild_member_remove_role(guild_id, user_id, role_id, callback); + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "members/" + std::to_string(user_id) + "/roles/" + std::to_string(role_id), m_delete, "", callback); } diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index d879f280e3..c1fad8baa7 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -328,6 +328,10 @@ confirmation cluster::guild_member_delete_role_sync(snowflake guild_id, snowflak return dpp::sync(this, &cluster::guild_member_delete_role, guild_id, user_id, role_id); } +confirmation cluster::guild_member_remove_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id) { + return dpp::sync(this, &cluster::guild_member_remove_role, guild_id, user_id, role_id); +} + guild_member cluster::guild_member_move_sync(const snowflake channel_id, const snowflake guild_id, const snowflake user_id) { return dpp::sync(this, &cluster::guild_member_move, channel_id, guild_id, user_id); } From 4ac4910530c08e9b0fd238d1ecd868f872876a32 Mon Sep 17 00:00:00 2001 From: brain Date: Fri, 27 May 2022 11:47:29 +0100 Subject: [PATCH 028/136] impro: support multiple parameters to permission::add(), remove(), set() --- include/dpp/permissions.h | 48 ++++++++++++++++++++++++++++++++++----- src/dpp/permissions.cpp | 9 +++----- src/test.cpp | 4 ++-- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index ebfd7b2a16..a0746611bb 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -20,6 +20,7 @@ ************************************************************************************/ #pragma once #include +#include namespace dpp { @@ -85,6 +86,25 @@ class permission { * @brief The permission bitmask value */ uint64_t value; + + /** + * @brief Internal function called by remove() to apply iterative changes + * @param p flags to add + */ + void add_one(uint64_t p); + + /** + * @brief Internal function called by remove() to apply iterative changes + * @param p flags to set + */ + void set_one(uint64_t p); + + /** + * @brief Internal function called by remove() to apply iterative changes + * @param p flags to remove + */ + void remove_one(uint64_t p); + public: /** * @brief Construct a permission object @@ -124,24 +144,40 @@ class permission { /** * @brief Add a permission with the Bitwise OR operation - * @param p The permissions (from dpp::permissions) to add + * @tparam Args one or more uint64_t permission bits + * @param args The permissions (from dpp::permissions) to set * @return permission& reference to self for chaining */ - const permission& add(uint64_t p); + template permission& add(Args&&... args) { + [[maybe_unused]] + int dummy[] = { 0, ((void) add_one(std::forward(args)),0)... }; + return *this; + } /** * @brief Assign a permission. This will reset the bitmask to the new value. - * @param p The permissions (from dpp::permissions) to set + * @tparam Args one or more uint64_t permission bits + * @param args The permissions (from dpp::permissions) to set * @return permission& reference to self for chaining */ - const permission& set(uint64_t p); + template permission& set(Args&&... args) { + this->set_one(0); + [[maybe_unused]] + int dummy[] = { 0, ((void) add_one(std::forward(args)),0)... }; + return *this; + } /** * @brief Remove a permission with the Bitwise NOT operation - * @param p The permissions (from dpp::permissions) to remove + * @tparam Args one or more uint64_t permission bits + * @param args The permissions (from dpp::permissions) to remove * @return permission& reference to self for chaining */ - const permission& remove(uint64_t p); + template permission& remove(Args&&... args) { + [[maybe_unused]] + int dummy[] = { 0, ((void) remove_one(std::forward(args)),0)... }; + return *this; + } }; } \ No newline at end of file diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index fee8ada4aa..dc7212fb32 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -43,19 +43,16 @@ bool permission::has(uint64_t p) const { return (value & p) == p; } -const permission& permission::add(uint64_t p) { +void permission::add_one(uint64_t p) { value |= p; - return *this; } -const permission& permission::set(uint64_t p) { +void permission::set_one(uint64_t p) { value = p; - return *this; } -const permission& permission::remove(uint64_t p) { +void permission::remove_one(uint64_t p) { value &= ~p; - return *this; } } \ No newline at end of file diff --git a/src/test.cpp b/src/test.cpp index e28362d1bc..8195fcdc94 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -195,8 +195,8 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b json j; j["value"] = p; success = dpp::snowflake_not_null(&j, "value") == 5120 && success; - p.set(8); - success = p.has(8) && success; + p.set(dpp::p_administrator, dpp::p_ban_members); + success = p.has(dpp::p_administrator) && p.has(dpp::p_ban_members) && success; set_test("PERMISSION_CLASS", success); } From c4cd11d5b256b8dacfa008beaf69807630406f23 Mon Sep 17 00:00:00 2001 From: brain Date: Fri, 27 May 2022 15:29:47 +0100 Subject: [PATCH 029/136] ci: unit test 'TS' is an online test --- src/unittest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unittest.cpp b/src/unittest.cpp index 20aa081621..52b84a62af 100644 --- a/src/unittest.cpp +++ b/src/unittest.cpp @@ -61,7 +61,7 @@ std::map tests = { {"PRESENCE", {tt_online, "Presence intent", false, false}}, {"CUSTOMCACHE", {tt_offline, "Instantiate a cache", false, false}}, {"MSGCOLLECT", {tt_online, "message_collector", false, false}}, - {"TS", {tt_offline, "managed::get_creation_date()", false, false}}, + {"TS", {tt_online, "managed::get_creation_date()", false, false}}, {"READFILE", {tt_offline, "utility::read_file()", false, false}}, {"TIMESTAMPTOSTRING", {tt_offline, "dpp::ts_to_string()", false, false}}, {"TIMESTRINGTOTIMESTAMP", {tt_offline, "dpp::ts_not_null()", false, false}}, From 539357e78e85b20543fbd4456ca0253110352f02 Mon Sep 17 00:00:00 2001 From: Phil B Date: Fri, 27 May 2022 20:20:42 +0200 Subject: [PATCH 030/136] added examples in the method docs of dpp::permission --- docpages/03_example_programs.md | 10 +++++---- include/dpp/cluster.h | 9 +++++--- include/dpp/permissions.h | 37 ++++++++++++++++++++++++++++++--- src/test.cpp | 4 ++++ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/docpages/03_example_programs.md b/docpages/03_example_programs.md index b0ea77d28a..f1958d644c 100644 --- a/docpages/03_example_programs.md +++ b/docpages/03_example_programs.md @@ -602,7 +602,7 @@ basic text-only messages (if your message is 'ephemeral' you must use this) and replies. Please note that at present, Discord only supports a small subset of message and embed features within an interaction response object. -\note You can also use the unified command handler, which lets you combine channel based message commands and slash commands under the same lambda with the same code like they were one and the same. Note that after April of 2022 Discord will be discouraging bots from using commands that are prefixed messages via means of a privileged message intent. It is advised that you exclusively use slash commands, or the unified handler with only a prefix of "/" going forward for any new bots you create and look to migrating existing bots to this setup. +\note You can also use the unified command handler, which lets you combine channel based message commands and slash commands under the same lambda with the same code like they were one and the same. Note that after August of 2022 Discord will be discouraging bots from using commands that are prefixed messages via means of a privileged message intent. It is advised that you exclusively use slash commands, or the unified handler with only a prefix of "/" going forward for any new bots you create and look to migrating existing bots to this setup. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} #include @@ -614,7 +614,7 @@ int main() bot.on_log(dpp::utility::cout_logger()); - /* The interaction create event is fired when someone issues your commands */ + /* The event is fired when someone issues your commands */ bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { /* Check which command they ran */ if (event.command.get_command_name() == "blep") { @@ -1200,14 +1200,14 @@ int main(int argc, char const *argv[]) You might have seen these special messages, often sent by bots. In this section, we will show how to create an embed. -To make an embed use this. +@note Because this example utilizes message content, it requires the message content privileged intent. ~~~~~~~~~~{.cpp} #include int main() { /* Setup the bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content + dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); /* Message handler to look for a command called !embed */ bot.on_message_create([&bot](const dpp::message_create_t & event) { @@ -1335,6 +1335,8 @@ D++ has this helper function to read a file: `dpp::utility::read_file`. An example program: +@note Because these examples utilizes message content, they require the message content privileged intent. + ~~~~~~~~~~{.cpp} #include diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index a53f2426ca..f3be6ddf4f 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -825,12 +825,15 @@ class DPP_EXPORT cluster { * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type interaction_create_t&, and returns void. + * + * @note There are dedicated events to handle slashcommands (See dpp::cluster::on_slashcommand), + * user context menus (See dpp::cluster::on_user_context_menu) and message context menus (See dpp::cluster::on_message_context_menu) */ event_router_t on_interaction_create; /** * @brief Called when a slash command is issued. - * Only ctxm_chat_input types of interaction are routed to this event. + * Only dpp::ctxm_chat_input types of interaction are routed to this event. * For an example of this in action please see \ref slashcommands * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. @@ -871,7 +874,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a user right-clicks or long-presses on a message, - * where a slash command is bound to the ctxm_message command type. + * where a slash command is bound to the dpp::ctxm_message command type. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type select_click_t&, and returns void. @@ -880,7 +883,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a user right-clicks or long-presses on a user, - * where a slash command is bound to the ctxm_user command type. + * where a slash command is bound to the dpp::ctxm_user command type. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type select_click_t&, and returns void. diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index a0746611bb..26fbde9d9f 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -88,13 +88,13 @@ class permission { uint64_t value; /** - * @brief Internal function called by remove() to apply iterative changes + * @brief Internal function called by add() to apply iterative changes * @param p flags to add */ void add_one(uint64_t p); /** - * @brief Internal function called by remove() to apply iterative changes + * @brief Internal function called by set() to apply iterative changes * @param p flags to set */ void set_one(uint64_t p); @@ -138,6 +138,14 @@ class permission { /** * @brief Check if it has a permission flag set. It uses the Bitwise AND operator * @param p The permission flag from dpp::permissions + * + * **Example:** + * + * ```cpp + * bool is_mod = permission.has(dpp::p_kick_members | dpp::p_ban_members); + * // Returns true if the permission bitmask contains p_kick_members and p_ban_members + * ``` + * * @return True if it has the permission */ bool has(uint64_t p) const; @@ -145,7 +153,15 @@ class permission { /** * @brief Add a permission with the Bitwise OR operation * @tparam Args one or more uint64_t permission bits - * @param args The permissions (from dpp::permissions) to set + * @param args The permissions (from dpp::permissions) to add + * + * **Example:** + * + * ```cpp + * permission.add(dpp::p_view_channel, dpp::p_send_messages); + * // Adds p_view_channel and p_send_messages to the permission bitmask + * ``` + * * @return permission& reference to self for chaining */ template permission& add(Args&&... args) { @@ -158,6 +174,13 @@ class permission { * @brief Assign a permission. This will reset the bitmask to the new value. * @tparam Args one or more uint64_t permission bits * @param args The permissions (from dpp::permissions) to set + * + * **Example:** + * + * ```cpp + * permission.set(dpp::p_view_channel, dpp::p_send_messages); + * ``` + * * @return permission& reference to self for chaining */ template permission& set(Args&&... args) { @@ -171,6 +194,14 @@ class permission { * @brief Remove a permission with the Bitwise NOT operation * @tparam Args one or more uint64_t permission bits * @param args The permissions (from dpp::permissions) to remove + * + * **Example:** + * + * ```cpp + * permission.remove(dpp::p_view_channel, dpp::p_send_messages); + * // Removes p_view_channel and p_send_messages permission + * ``` + * * @return permission& reference to self for chaining */ template permission& remove(Args&&... args) { diff --git a/src/test.cpp b/src/test.cpp index 8195fcdc94..bbcf36b3a3 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -197,6 +197,10 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b success = dpp::snowflake_not_null(&j, "value") == 5120 && success; p.set(dpp::p_administrator, dpp::p_ban_members); success = p.has(dpp::p_administrator) && p.has(dpp::p_ban_members) && success; + success = p.has(dpp::p_administrator | dpp::p_ban_members) && success; + + p.set(dpp::p_administrator); + success = ! p.has(dpp::p_administrator | dpp::p_ban_members) && success; // must return false because they're not both set set_test("PERMISSION_CLASS", success); } From f7748e545aced3a634aff603c3e41d3685933521 Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Fri, 27 May 2022 16:08:12 -0400 Subject: [PATCH 031/136] make permissions not look like dog shit --- include/dpp/permissions.h | 43 +++++++++------------------------------ src/dpp/permissions.cpp | 12 ----------- 2 files changed, 10 insertions(+), 45 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 26fbde9d9f..512fc42cea 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -20,7 +20,6 @@ ************************************************************************************/ #pragma once #include -#include namespace dpp { @@ -87,24 +86,6 @@ class permission { */ uint64_t value; - /** - * @brief Internal function called by add() to apply iterative changes - * @param p flags to add - */ - void add_one(uint64_t p); - - /** - * @brief Internal function called by set() to apply iterative changes - * @param p flags to set - */ - void set_one(uint64_t p); - - /** - * @brief Internal function called by remove() to apply iterative changes - * @param p flags to remove - */ - void remove_one(uint64_t p); - public: /** * @brief Construct a permission object @@ -164,10 +145,9 @@ class permission { * * @return permission& reference to self for chaining */ - template permission& add(Args&&... args) { - [[maybe_unused]] - int dummy[] = { 0, ((void) add_one(std::forward(args)),0)... }; - return *this; + template + void add(T... values) { + (value |= (0 | ... | values)); } /** @@ -183,11 +163,9 @@ class permission { * * @return permission& reference to self for chaining */ - template permission& set(Args&&... args) { - this->set_one(0); - [[maybe_unused]] - int dummy[] = { 0, ((void) add_one(std::forward(args)),0)... }; - return *this; + template + void set(T... values) { + (value = (0 | ... | values)); } /** @@ -204,11 +182,10 @@ class permission { * * @return permission& reference to self for chaining */ - template permission& remove(Args&&... args) { - [[maybe_unused]] - int dummy[] = { 0, ((void) remove_one(std::forward(args)),0)... }; - return *this; + template + void remove(T... values) { + (value &= ~(0 | ... | values)); } }; -} \ No newline at end of file +} diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index dc7212fb32..355fd118f1 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -43,16 +43,4 @@ bool permission::has(uint64_t p) const { return (value & p) == p; } -void permission::add_one(uint64_t p) { - value |= p; } - -void permission::set_one(uint64_t p) { - value = p; -} - -void permission::remove_one(uint64_t p) { - value &= ~p; -} - -} \ No newline at end of file From ae812991d2893f2f3635b440190f3ee626229cfd Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Fri, 27 May 2022 16:51:23 -0400 Subject: [PATCH 032/136] add back returning *this --- include/dpp/permissions.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 512fc42cea..5b7347fb52 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -146,8 +146,9 @@ class permission { * @return permission& reference to self for chaining */ template - void add(T... values) { + permission& add(T... values) { (value |= (0 | ... | values)); + return *this; } /** @@ -164,8 +165,9 @@ class permission { * @return permission& reference to self for chaining */ template - void set(T... values) { + permission& set(T... values) { (value = (0 | ... | values)); + return *this; } /** @@ -183,8 +185,9 @@ class permission { * @return permission& reference to self for chaining */ template - void remove(T... values) { + permission& remove(T... values) { (value &= ~(0 | ... | values)); + return *this; } }; From 45834e07cc119eb6b67ddfe64e040170ce3c46dd Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Fri, 27 May 2022 22:04:43 +0100 Subject: [PATCH 033/136] fix/docs: doxygen --- include/dpp/permissions.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 5b7347fb52..f937e7b080 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -133,8 +133,8 @@ class permission { /** * @brief Add a permission with the Bitwise OR operation - * @tparam Args one or more uint64_t permission bits - * @param args The permissions (from dpp::permissions) to add + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to add * * **Example:** * @@ -153,8 +153,8 @@ class permission { /** * @brief Assign a permission. This will reset the bitmask to the new value. - * @tparam Args one or more uint64_t permission bits - * @param args The permissions (from dpp::permissions) to set + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to set * * **Example:** * @@ -172,8 +172,8 @@ class permission { /** * @brief Remove a permission with the Bitwise NOT operation - * @tparam Args one or more uint64_t permission bits - * @param args The permissions (from dpp::permissions) to remove + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to remove * * **Example:** * From 1bd58019be5d87efe222fbbdd5fd412ae74308f7 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Fri, 27 May 2022 22:06:05 +0100 Subject: [PATCH 034/136] fix: add export --- include/dpp/permissions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index f937e7b080..b2e4e4598f 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -79,7 +79,7 @@ using role_permissions = permissions; /** * @brief Represents a permission bitmask (refer to enum dpp::permissions) which are hold in an uint64_t */ -class permission { +class DPP_EXPORT permission { protected: /** * @brief The permission bitmask value From b00b1cd403d8593a3d0910a394e338666c5b76a1 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sat, 28 May 2022 16:19:49 +0200 Subject: [PATCH 035/136] added missing include in permissions.h --- include/dpp/permissions.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index b2e4e4598f..130c37b336 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -20,6 +20,7 @@ ************************************************************************************/ #pragma once #include +#include namespace dpp { From 7841ab57aff30a349edcaabfe2d1f6b70d1743e8 Mon Sep 17 00:00:00 2001 From: brain Date: Sat, 28 May 2022 15:51:28 +0100 Subject: [PATCH 036/136] change to using ctest to run tests, as this is more standardized --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 14 +++++++++++--- docpages/03_example_programs.md | 2 +- docpages/04_advanced_reference.md | 6 ++++-- include/dpp/dispatcher.h | 3 +++ src/dpp/cluster/webhook.cpp | 6 +++++- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec7a3adfa3..6fb4bbf9fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: run: cd build && ninja - name: Run unit tests - run: cd build && ./test + run: cd build && ctest -VV env: DPP_UNIT_TEST_TOKEN: ${{secrets.DPP_UNIT_TEST_TOKEN}} TEST_GUILD_ID: ${{secrets.TEST_GUILD_ID}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 949374be90..bbcc4b3eb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,9 +286,16 @@ target_compile_features(dpp PRIVATE cxx_variadic_templates) target_compile_features(dpp PRIVATE cxx_attribute_deprecated) if (DPP_BUILD_TEST) - add_executable(test ${coresrc}) - target_compile_features(test PRIVATE cxx_std_17) - target_link_libraries(test PUBLIC ${modname}) + enable_testing(${PROJECT_SOURCE_DIR}) + add_executable(unittest ${coresrc}) + target_compile_features(unittest PRIVATE cxx_std_17) + target_link_libraries(unittest PUBLIC ${modname}) + add_test( + NAME unittests + COMMAND unittest + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) + endif() if(HAVE_PRCTL) @@ -333,3 +340,4 @@ include("cmake/CPackSetup.cmake") # Setup information for packaging and dis # CPack initialization for distribution include(CPack) + diff --git a/docpages/03_example_programs.md b/docpages/03_example_programs.md index f1958d644c..7785abc1f4 100644 --- a/docpages/03_example_programs.md +++ b/docpages/03_example_programs.md @@ -259,7 +259,7 @@ int main() { ### 7. Compile and run your bot -Compile your bot using `g++ -std=c++17 -o test test.cpp -ldpp` (if your .cpp file is called `test.cpp`) and run it with `./test`. +Compile your bot using `g++ -std=c++17 -o bot bot.cpp -ldpp` (if your .cpp file is called `bot.cpp`) and run it with `./bot`. ### 8. Inviting your bot to your server diff --git a/docpages/04_advanced_reference.md b/docpages/04_advanced_reference.md index 8d310af3bc..cc0a9c4295 100644 --- a/docpages/04_advanced_reference.md +++ b/docpages/04_advanced_reference.md @@ -162,7 +162,7 @@ Before running test cases, create a test server for your test bot. You should: * Create at least one voice channel * Create at least one text channel -Then, set the following variables to the appropriate values. (This uses a fake token, don't bother trying to use it.) +Then, set the following variables to the appropriate values. (Below is a fake token, don't bother trying to use it) export DPP_UNIT_TEST_TOKEN="ODI2ZSQ4CFYyMzgxUzkzzACy.HPL5PA.9qKR4uh8po63-pjYVrPAvQQO4ln" export TEST_GUILD_ID="907951970017480704" @@ -171,7 +171,9 @@ Then, set the following variables to the appropriate values. (This uses a fake t export TEST_USER_ID="826535422381391913" export TEST_EVENT_ID="909928577951203360" -Then, after cloning and building DPP, run `./build/test` for unit test cases. +Then, after cloning and building DPP, run `cd build && ctest -VV` for unit test cases. + +If you do not specify the `DPP_UNIT_TEST_TOKEN` environment variable, a subset of the tests will run which do not require discord connectivity. \page lambdas-and-locals Ownership of local variables and safely transferring into a lambda diff --git a/include/dpp/dispatcher.h b/include/dpp/dispatcher.h index 3174938307..b366c62d37 100644 --- a/include/dpp/dispatcher.h +++ b/include/dpp/dispatcher.h @@ -428,6 +428,9 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { virtual ~interaction_create_t() = default; }; +/** + * @brief User has issued a slash command + */ struct DPP_EXPORT slashcommand_t : public interaction_create_t { public: /** Constructor diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 24b227b5d1..954ce3ee46 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -67,7 +67,11 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"wait", wait}, {"thread_id", thread_id}, }); - rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(false), callback); + this->post_rest_multipart(API_PATH API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(), [this, callback](json &j, const http_request_completion_t& http) { + if (callback) { + callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); + } + }, m.filename, m.filecontent); } From f6687225ded93d02aab61535aeb11b3e4c5ff482 Mon Sep 17 00:00:00 2001 From: brain Date: Sat, 28 May 2022 15:52:42 +0100 Subject: [PATCH 037/136] add ctest to cspell --- .cspell.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index 73b3a08af6..02e7209f48 100644 --- a/.cspell.json +++ b/.cspell.json @@ -75,7 +75,8 @@ "clion", "followup", "gifv", - "ctls" + "ctls", + "ctest" ], "flagWords": [ "hte" From 30b32e0bf1cc348309f09686f9131298c5e07df9 Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Sat, 28 May 2022 14:12:22 -0400 Subject: [PATCH 038/136] add template to only enable for int convertible types --- include/dpp/permissions.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index b2e4e4598f..276a9ac214 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -145,7 +145,7 @@ class DPP_EXPORT permission { * * @return permission& reference to self for chaining */ - template + template ...>>> permission& add(T... values) { (value |= (0 | ... | values)); return *this; @@ -164,7 +164,7 @@ class DPP_EXPORT permission { * * @return permission& reference to self for chaining */ - template + template ...>>> permission& set(T... values) { (value = (0 | ... | values)); return *this; @@ -184,7 +184,7 @@ class DPP_EXPORT permission { * * @return permission& reference to self for chaining */ - template + template ...>>> permission& remove(T... values) { (value &= ~(0 | ... | values)); return *this; From 57f31073ff30acfec7814275c8328d344833cf23 Mon Sep 17 00:00:00 2001 From: Linker <66379820+Linker-123@users.noreply.github.com> Date: Sat, 28 May 2022 21:23:21 +0300 Subject: [PATCH 039/136] Fill member data from the whole json object --- src/dpp/events/guild_create.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/events/guild_create.cpp b/src/dpp/events/guild_create.cpp index efbfba9a0d..6f1e3692c5 100644 --- a/src/dpp/events/guild_create.cpp +++ b/src/dpp/events/guild_create.cpp @@ -103,7 +103,7 @@ void guild_create::handle(discord_client* client, json &j, const std::string &ra u->refcount++; } dpp::guild_member gm; - gm.fill_from_json(&(user["user"]), g->id, userid); + gm.fill_from_json(&user, g->id, userid); g->members[userid] = gm; } } From c4a34ff7f030f2fb5648c0d8d2a8af8e3cfee661 Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Sat, 28 May 2022 15:11:31 -0400 Subject: [PATCH 040/136] use folding instead of conjunction --- include/dpp/permissions.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 276a9ac214..68e5f2d379 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -145,9 +145,10 @@ class DPP_EXPORT permission { * * @return permission& reference to self for chaining */ - template ...>>> - permission& add(T... values) { - (value |= (0 | ... | values)); + template + typename std::enable_if<(std::is_convertible::value && ...), permission&>::type + add(T... values) { + value |= (0 | ... | values); return *this; } @@ -164,9 +165,10 @@ class DPP_EXPORT permission { * * @return permission& reference to self for chaining */ - template ...>>> - permission& set(T... values) { - (value = (0 | ... | values)); + template + typename std::enable_if<(std::is_convertible::value && ...), permission&>::type + set(T... values) { + value = (0 | ... | values); return *this; } @@ -184,9 +186,10 @@ class DPP_EXPORT permission { * * @return permission& reference to self for chaining */ - template ...>>> - permission& remove(T... values) { - (value &= ~(0 | ... | values)); + template + typename std::enable_if<(std::is_convertible::value && ...), permission&>::type + remove(T... values) { + value &= ~(0 | ... | values); return *this; } }; From 2429dc1f367a7a80b50e679c3c7074454f1ddbc1 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sat, 28 May 2022 23:20:03 +0200 Subject: [PATCH 041/136] make permission::has takes multiple params --- include/dpp/permissions.h | 15 +++++++++------ src/dpp/permissions.cpp | 4 ---- src/test.cpp | 5 ++++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index cc1ff1e382..1ce84a2284 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -20,7 +20,6 @@ ************************************************************************************/ #pragma once #include -#include namespace dpp { @@ -118,19 +117,23 @@ class DPP_EXPORT permission { operator nlohmann::json() const; /** - * @brief Check if it has a permission flag set. It uses the Bitwise AND operator - * @param p The permission flag from dpp::permissions + * @brief Check for permission flags set. It uses the Bitwise AND operator + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to check for * * **Example:** * * ```cpp - * bool is_mod = permission.has(dpp::p_kick_members | dpp::p_ban_members); + * bool is_mod = permission.has(dpp::p_kick_members, dpp::p_ban_members); * // Returns true if the permission bitmask contains p_kick_members and p_ban_members * ``` * - * @return True if it has the permission + * @return bool True if it has all the given permissions */ - bool has(uint64_t p) const; + template + bool has(T... values) const { + return (value & (0 | ... | values)) == (0 | ... | values); + } /** * @brief Add a permission with the Bitwise OR operation diff --git a/src/dpp/permissions.cpp b/src/dpp/permissions.cpp index 355fd118f1..5985e6e224 100644 --- a/src/dpp/permissions.cpp +++ b/src/dpp/permissions.cpp @@ -39,8 +39,4 @@ permission::operator nlohmann::json() const { return std::to_string(value); } -bool permission::has(uint64_t p) const { - return (value & p) == p; -} - } diff --git a/src/test.cpp b/src/test.cpp index bbcf36b3a3..ee6881b102 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -196,11 +196,14 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b j["value"] = p; success = dpp::snowflake_not_null(&j, "value") == 5120 && success; p.set(dpp::p_administrator, dpp::p_ban_members); + success = p.has(dpp::p_administrator) && success; success = p.has(dpp::p_administrator) && p.has(dpp::p_ban_members) && success; + success = p.has(dpp::p_administrator, dpp::p_ban_members) && success; success = p.has(dpp::p_administrator | dpp::p_ban_members) && success; p.set(dpp::p_administrator); - success = ! p.has(dpp::p_administrator | dpp::p_ban_members) && success; // must return false because they're not both set + success = ! p.has(dpp::p_administrator, dpp::p_ban_members) && success; // must return false because they're not both set + success = ! p.has(dpp::p_administrator | dpp::p_ban_members) && success; set_test("PERMISSION_CLASS", success); } From 7339bfc17e533359e744fc90bf8902ecbf1c7fd3 Mon Sep 17 00:00:00 2001 From: Hou In Si Tou Date: Sat, 28 May 2022 17:55:10 -0700 Subject: [PATCH 042/136] Fix discord_voice_client voice_payload priority when two payloads have the same timestamp. Seems to be prevalent in payloads from web clients; two payloads with different sequence numebrs can have the same timestamp. We can't be lazy and not look at the sequence number to order the payloads. --- src/dpp/discordvoiceclient.cpp | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 422e33688d..5faba37416 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -68,7 +68,40 @@ struct rtp_header { bool discord_voice_client::sodium_initialised = false; bool discord_voice_client::voice_payload::operator<(const voice_payload& other) const { - return timestamp > other.timestamp; + if (timestamp != other.timestamp) { + return timestamp > other.timestamp; + } + + constexpr rtp_seq_t wrap_around_test_boundary = 5000; + if (seq < wrap_around_test_boundary != other.seq < wrap_around_test_boundary) { + /* Match the cases where exactly one of the sequence numbers "may have" + * wrapped around. + * + * Examples: + * 1. this->seq = 65530, other.seq = 10 // Did wrap around + * 2. this->seq = 5002, other.seq = 4990 // Not wrapped around + * + * Add 5000 to both sequence numbers to force wrap around so they can be + * compared. This should be fine to do to case 2 as well, as long as the + * addend (5000) is not too large to cause one of them to wrap around. + * + * In practice, we should be unlikely to hit the case where + * + * this->seq = 65530, other.seq = 5001 + * + * because we shouldn't receive more than 5000 payloads in one batch, unless + * the voice courier thread is super slow. Also remember that the timestamp + * is compared first, and payloads this far apart shouldn't have the same + * timestamp. + */ + + /* Casts here ensure the sum wraps around and not implicitly converted to + * wider types. */ + return static_cast(seq + wrap_around_test_boundary) + > static_cast(other.seq + wrap_around_test_boundary); + } else { + return seq > other.seq; + } } #ifdef HAVE_VOICE From 42f19ab814b6b7d7adfc18f3bececc261fa819d2 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 29 May 2022 16:38:10 +0200 Subject: [PATCH 043/136] breaking change: updated webhook methods in cluster --- include/dpp/cluster.h | 10 +++++++--- src/dpp/cluster/webhook.cpp | 29 +++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index f3be6ddf4f..3895fa693c 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2976,10 +2976,11 @@ class DPP_EXPORT cluster { * * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message * @param wh Webhook to get the original message for + * @param message_id The message ID * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void get_webhook_message(const class webhook &wh, command_completion_event_t callback = utility::log_error()); + void get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); /** * @brief Edit webhook message @@ -2993,10 +2994,11 @@ class DPP_EXPORT cluster { * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. * @param wh Webhook to edit message for * @param m New message + * @param thread_id ID of the thread the message is in * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void edit_webhook_message(const class webhook &wh, const struct message &m, command_completion_event_t callback = utility::log_error()); + void edit_webhook_message(const class webhook &wh, const struct message &m, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); /** * @brief Delete webhook message @@ -3004,10 +3006,12 @@ class DPP_EXPORT cluster { * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message * @param wh Webhook to delete message for * @param message_id Message ID to delete + * @param thread_id ID of the thread the message is in * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void delete_webhook_message(const class webhook &wh, snowflake message_id, command_completion_event_t callback = utility::log_error()); + void delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); + /** * @brief Get a role for a guild diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 954ce3ee46..322ceefee4 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -33,8 +33,11 @@ void cluster::delete_webhook(snowflake webhook_id, command_completion_event_t ca } -void cluster::delete_webhook_message(const class webhook &wh, snowflake message_id, command_completion_event_t callback) { - rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(message_id), m_delete, "", callback); +void cluster::delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id, command_completion_event_t callback) { + std::string parameters = utility::make_url_parameters({ + {"thread_id", thread_id}, + }); + rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(message_id) + parameters, m_delete, "", callback); } @@ -44,12 +47,19 @@ void cluster::delete_webhook_with_token(snowflake webhook_id, const std::string void cluster::edit_webhook(const class webhook& wh, command_completion_event_t callback) { - rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), "", m_patch, wh.build_json(true), callback); + rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), "", m_patch, wh.build_json(false), callback); } -void cluster::edit_webhook_message(const class webhook &wh, const struct message& m, command_completion_event_t callback) { - rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(m.id), m_patch, m.build_json(false), callback); +void cluster::edit_webhook_message(const class webhook &wh, const struct message& m, snowflake thread_id, command_completion_event_t callback) { + std::string parameters = utility::make_url_parameters({ + {"thread_id", thread_id}, + }); + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(m.id) + parameters, m_patch, m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { + if (callback) { + callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); + } + }, m.filename, m.filecontent); } @@ -67,7 +77,7 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"wait", wait}, {"thread_id", thread_id}, }); - this->post_rest_multipart(API_PATH API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(), [this, callback](json &j, const http_request_completion_t& http) { + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } @@ -90,8 +100,11 @@ void cluster::get_webhook(snowflake webhook_id, command_completion_event_t callb } -void cluster::get_webhook_message(const class webhook &wh, command_completion_event_t callback) { - rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/@original", m_get, "", callback); +void cluster::get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id, command_completion_event_t callback) { + std::string parameters = utility::make_url_parameters({ + {"thread_id", thread_id}, + }); + rest_request(this, API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(message_id) + parameters, m_get, "", callback); } From 3fed629adb892b300b7686bcca094318a4892019 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 29 May 2022 16:43:25 +0200 Subject: [PATCH 044/136] updated doc of cluster::get_webhook_message --- include/dpp/cluster.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 3895fa693c..7ad5ade647 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2977,6 +2977,7 @@ class DPP_EXPORT cluster { * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message * @param wh Webhook to get the original message for * @param message_id The message ID + * @param thread_id ID of the thread the message is in * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ From 4d6e03023df89430622b1a48fee3ad7bc4cb4805 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 29 May 2022 17:01:20 +0200 Subject: [PATCH 045/136] make sync methods --- include/dpp/cluster_sync_calls.h | 121 +------------------------------ src/dpp/cluster_sync_calls.cpp | 36 +-------- 2 files changed, 4 insertions(+), 153 deletions(-) diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 3a74083549..2c6cc3d931 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -2222,13 +2222,14 @@ confirmation delete_webhook_sync(snowflake webhook_id); * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message * @param wh Webhook to delete message for * @param message_id Message ID to delete + * @param thread_id ID of the thread the message is in * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id); +confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Delete webhook with token @@ -2258,123 +2259,5 @@ confirmation delete_webhook_with_token_sync(snowflake webhook_id, const std::str */ webhook edit_webhook_sync(const class webhook& wh); -/** - * @brief Edit webhook message - * - * When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on - * the new content. The allowed_mentions field of the edit request controls how this happens. If there is no explicit - * allowed_mentions in the edit request, the content will be parsed with default allowances, that is, without regard to - * whether or not an allowed_mentions was present in the request that originally created the message. - * - * @see dpp::cluster::edit_webhook_message - * @see https://discord.com/developers/docs/resources/webhook#edit-webhook-message - * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. - * @param wh Webhook to edit message for - * @param m New message - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message edit_webhook_message_sync(const class webhook &wh, const struct message &m); - -/** - * @brief Edit webhook with token (token is encapsulated in the webhook object) - * @see dpp::cluster::edit_webhook_with_token - * @see https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token - * @param wh Webhook to edit (should include token) - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook edit_webhook_with_token_sync(const class webhook& wh); - -/** - * @brief Execute webhook - * - * @see dpp::cluster::execute_webhook - * @see https://discord.com/developers/docs/resources/webhook#execute-webhook - * @param wh Webhook to execute - * @param m Message to send - * @param wait waits for server confirmation of message send before response, and returns the created message body - * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0); - -/** - * @brief Get channel webhooks - * @see dpp::cluster::get_channel_webhooks - * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks - * @param channel_id Channel ID to get webhooks for - * @return webhook_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook_map get_channel_webhooks_sync(snowflake channel_id); - -/** - * @brief Get guild webhooks - * @see dpp::cluster::get_guild_webhooks - * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks - * @param guild_id Guild ID to get webhooks for - * @return webhook_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook_map get_guild_webhooks_sync(snowflake guild_id); - -/** - * @brief Get webhook - * @see dpp::cluster::get_webhook - * @see https://discord.com/developers/docs/resources/webhook#get-webhook - * @param webhook_id Webhook ID to get - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook get_webhook_sync(snowflake webhook_id); - -/** - * @brief Get webhook message - * - * @see dpp::cluster::get_webhook_message - * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message - * @param wh Webhook to get the original message for - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message get_webhook_message_sync(const class webhook &wh); - -/** - * @brief Get webhook using token - * @see dpp::cluster::get_webhook_with_token - * @see https://discord.com/developers/docs/resources/webhook#get-webhook-with-token - * @param webhook_id Webhook ID to retrieve - * @param token Token of webhook - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook get_webhook_with_token_sync(snowflake webhook_id, const std::string &token); - /* End of auto-generated definitions */ diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index c1fad8baa7..8779b4cd86 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -604,8 +604,8 @@ confirmation cluster::delete_webhook_sync(snowflake webhook_id) { return dpp::sync(this, &cluster::delete_webhook, webhook_id); } -confirmation cluster::delete_webhook_message_sync(const class webhook &wh, snowflake message_id) { - return dpp::sync(this, &cluster::delete_webhook_message, wh, message_id); +confirmation cluster::delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id) { + return dpp::sync(this, &cluster::delete_webhook_message, wh, message_id, thread_id); } confirmation cluster::delete_webhook_with_token_sync(snowflake webhook_id, const std::string &token) { @@ -616,38 +616,6 @@ webhook cluster::edit_webhook_sync(const class webhook& wh) { return dpp::sync(this, &cluster::edit_webhook, wh); } -message cluster::edit_webhook_message_sync(const class webhook &wh, const struct message& m) { - return dpp::sync(this, &cluster::edit_webhook_message, wh, m); -} - -webhook cluster::edit_webhook_with_token_sync(const class webhook& wh) { - return dpp::sync(this, &cluster::edit_webhook_with_token, wh); -} - -message cluster::execute_webhook_sync(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id) { - return dpp::sync(this, &cluster::execute_webhook, wh, m, wait, thread_id); -} - -webhook_map cluster::get_channel_webhooks_sync(snowflake channel_id) { - return dpp::sync(this, &cluster::get_channel_webhooks, channel_id); -} - -webhook_map cluster::get_guild_webhooks_sync(snowflake guild_id) { - return dpp::sync(this, &cluster::get_guild_webhooks, guild_id); -} - -webhook cluster::get_webhook_sync(snowflake webhook_id) { - return dpp::sync(this, &cluster::get_webhook, webhook_id); -} - -message cluster::get_webhook_message_sync(const class webhook &wh) { - return dpp::sync(this, &cluster::get_webhook_message, wh); -} - -webhook cluster::get_webhook_with_token_sync(snowflake webhook_id, const std::string &token) { - return dpp::sync(this, &cluster::get_webhook_with_token, webhook_id, token); -} - }; From 9923455486786b76d257f7bdbb6f61a76f181fe9 Mon Sep 17 00:00:00 2001 From: brain Date: Sun, 29 May 2022 17:29:40 +0100 Subject: [PATCH 046/136] fix double API_PATH and weirdness of operators --- src/dpp/cluster/webhook.cpp | 2 +- src/dpp/discordvoiceclient.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 954ce3ee46..32ce9d2f29 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -67,7 +67,7 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"wait", wait}, {"thread_id", thread_id}, }); - this->post_rest_multipart(API_PATH API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(), [this, callback](json &j, const http_request_completion_t& http) { + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 5faba37416..fe4125fd82 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -73,7 +73,7 @@ bool discord_voice_client::voice_payload::operator<(const voice_payload& other) } constexpr rtp_seq_t wrap_around_test_boundary = 5000; - if (seq < wrap_around_test_boundary != other.seq < wrap_around_test_boundary) { + if ((seq < wrap_around_test_boundary) != (other.seq < wrap_around_test_boundary)) { /* Match the cases where exactly one of the sequence numbers "may have" * wrapped around. * From ed0c614c73a8ae90ca5f7d1e797ad44c47fe8949 Mon Sep 17 00:00:00 2001 From: brain Date: Sun, 29 May 2022 19:20:06 +0100 Subject: [PATCH 047/136] fix: wrap-around for voice receive --- src/dpp/discordvoiceclient.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index fe4125fd82..2e1eff85a8 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -73,8 +73,9 @@ bool discord_voice_client::voice_payload::operator<(const voice_payload& other) } constexpr rtp_seq_t wrap_around_test_boundary = 5000; - if ((seq < wrap_around_test_boundary) != (other.seq < wrap_around_test_boundary)) { - /* Match the cases where exactly one of the sequence numbers "may have" + if ((seq < wrap_around_test_boundary && other.seq >= wrap_around_test_boundary) + || (seq >= wrap_around_test_boundary && other.seq < wrap_around_test_boundary)) { + /* Match the cases where exactly one of the sequence numbers "may have" * wrapped around. * * Examples: @@ -96,7 +97,8 @@ bool discord_voice_client::voice_payload::operator<(const voice_payload& other) */ /* Casts here ensure the sum wraps around and not implicitly converted to - * wider types. */ + * wider types. + */ return static_cast(seq + wrap_around_test_boundary) > static_cast(other.seq + wrap_around_test_boundary); } else { From c43413e7a41889ebbbbfcc8b571c727830760ce6 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 29 May 2022 22:12:39 +0200 Subject: [PATCH 048/136] small regex hotfix in make_sync_struct.php --- buildtools/make_sync_struct.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/buildtools/make_sync_struct.php b/buildtools/make_sync_struct.php index 3d6afaa0c1..a33e598f78 100644 --- a/buildtools/make_sync_struct.php +++ b/buildtools/make_sync_struct.php @@ -82,11 +82,12 @@ } elseif ($state == 1) { if (preg_match('/^\}\s*$/', $cpp)) { $state = 2; + /* look for the return type of the method */ } elseif (preg_match('/rest_request<([^>]+)>/', $cpp, $matches)) { $returnType = $matches[1]; } elseif (preg_match('/rest_request_list<([^>]+)>/', $cpp, $matches)) { $returnType = $matches[1] . '_map'; - } elseif (preg_match('/callback\(confirmation_callback_t\(\w+, ([^(]+)\(\).*, \w+\)\)/', $cpp, $matches)) { + } elseif (preg_match('/callback\(confirmation_callback_t\(\w+, ([^(]+)\(.*, \w+\)\)/', $cpp, $matches)) { $returnType = $matches[1]; } elseif (!empty($forcedReturn[$currentFunction])) { $returnType = $forcedReturn[$currentFunction]; From b3f67034c2513ebb88298461c12a4627cd22943b Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 29 May 2022 22:12:51 +0200 Subject: [PATCH 049/136] updated sync methods --- include/dpp/cluster_sync_calls.h | 10 +++++++--- src/dpp/cluster_sync_calls.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 3a74083549..b8cadba535 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -2222,13 +2222,14 @@ confirmation delete_webhook_sync(snowflake webhook_id); * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message * @param wh Webhook to delete message for * @param message_id Message ID to delete + * @param thread_id ID of the thread the message is in * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id); +confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Delete webhook with token @@ -2271,13 +2272,14 @@ webhook edit_webhook_sync(const class webhook& wh); * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. * @param wh Webhook to edit message for * @param m New message + * @param thread_id ID of the thread the message is in * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -message edit_webhook_message_sync(const class webhook &wh, const struct message &m); +message edit_webhook_message_sync(const class webhook &wh, const struct message &m, snowflake thread_id = 0); /** * @brief Edit webhook with token (token is encapsulated in the webhook object) @@ -2354,13 +2356,15 @@ webhook get_webhook_sync(snowflake webhook_id); * @see dpp::cluster::get_webhook_message * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message * @param wh Webhook to get the original message for + * @param message_id The message ID + * @param thread_id ID of the thread the message is in * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -message get_webhook_message_sync(const class webhook &wh); +message get_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Get webhook using token diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index c1fad8baa7..c8cc77e9d6 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -604,8 +604,8 @@ confirmation cluster::delete_webhook_sync(snowflake webhook_id) { return dpp::sync(this, &cluster::delete_webhook, webhook_id); } -confirmation cluster::delete_webhook_message_sync(const class webhook &wh, snowflake message_id) { - return dpp::sync(this, &cluster::delete_webhook_message, wh, message_id); +confirmation cluster::delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id) { + return dpp::sync(this, &cluster::delete_webhook_message, wh, message_id, thread_id); } confirmation cluster::delete_webhook_with_token_sync(snowflake webhook_id, const std::string &token) { @@ -616,8 +616,8 @@ webhook cluster::edit_webhook_sync(const class webhook& wh) { return dpp::sync(this, &cluster::edit_webhook, wh); } -message cluster::edit_webhook_message_sync(const class webhook &wh, const struct message& m) { - return dpp::sync(this, &cluster::edit_webhook_message, wh, m); +message cluster::edit_webhook_message_sync(const class webhook &wh, const struct message& m, snowflake thread_id) { + return dpp::sync(this, &cluster::edit_webhook_message, wh, m, thread_id); } webhook cluster::edit_webhook_with_token_sync(const class webhook& wh) { @@ -640,8 +640,8 @@ webhook cluster::get_webhook_sync(snowflake webhook_id) { return dpp::sync(this, &cluster::get_webhook, webhook_id); } -message cluster::get_webhook_message_sync(const class webhook &wh) { - return dpp::sync(this, &cluster::get_webhook_message, wh); +message cluster::get_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id) { + return dpp::sync(this, &cluster::get_webhook_message, wh, message_id, thread_id); } webhook cluster::get_webhook_with_token_sync(snowflake webhook_id, const std::string &token) { From 7f6ade1c2c1063fee2e3135c5d4d9babee0df867 Mon Sep 17 00:00:00 2001 From: brain Date: Sun, 29 May 2022 23:17:00 +0100 Subject: [PATCH 050/136] style: improve comments and PSR-12 style in this file --- buildtools/make_sync_struct.php | 65 +++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/buildtools/make_sync_struct.php b/buildtools/make_sync_struct.php index a33e598f78..4c2c5d8ca5 100644 --- a/buildtools/make_sync_struct.php +++ b/buildtools/make_sync_struct.php @@ -1,5 +1,9 @@ 'message', 'guild_get_members' => 'guild_member_map', @@ -17,8 +25,16 @@ 'message_create' => 'message', 'message_edit' => 'message', ]; + +/* Get the contents of cluster.h into an array */ $header = explode("\n", file_get_contents('include/dpp/cluster.h')); -$state = 0; + +/* Finite state machine state constants */ +define('STATE_SEARCH_FOR_FUNCTION', 0); +define('STATE_IN_FUNCTION', 1); +define('STATE_END_OF_FUNCTION', 2); + +$state = STATE_SEARCH_FOR_FUNCTION; $currentFunction = $parameters = $returnType = ''; $content = <<]+)>/', $cpp, $matches)) { + /* rest_request */ $returnType = $matches[1]; } elseif (preg_match('/rest_request_list<([^>]+)>/', $cpp, $matches)) { + /* rest_request_list */ $returnType = $matches[1] . '_map'; } elseif (preg_match('/callback\(confirmation_callback_t\(\w+, ([^(]+)\(.*, \w+\)\)/', $cpp, $matches)) { + /* confirmation_callback_t */ $returnType = $matches[1]; } elseif (!empty($forcedReturn[$currentFunction])) { + /* Forced return type */ $returnType = $forcedReturn[$currentFunction]; } } - if ($state == 2 && !empty($currentFunction) && !empty($returnType)) { + /* Completed parsing of function body */ + if ($state == STATE_END_OF_FUNCTION && !empty($currentFunction) && !empty($returnType)) { if (!in_array($currentFunction, $blacklist)) { $parameterList = explode(',', $parameters); $parameterNames = []; @@ -113,7 +140,7 @@ $cppcontent .= "$returnType cluster::{$currentFunction}_sync($noDefaults) {\n\treturn dpp::sync<$returnType>(this, &cluster::$currentFunction$parameterNames);\n}\n\n"; } $currentFunction = $parameters = $returnType = ''; - $state = 0; + $state = STATE_SEARCH_FOR_FUNCTION; } } $content .= << $line) { @@ -173,5 +215,6 @@ function getComments(string $currentFunction, string $returnType, array $paramet return ''; } +/* Finished parsing, output autogenerated files */ file_put_contents('include/dpp/cluster_sync_calls.h', $content); file_put_contents('src/dpp/cluster_sync_calls.cpp', $cppcontent); From 95ca8b869a71160be37cde6c09b54d14db6dfe69 Mon Sep 17 00:00:00 2001 From: brain Date: Sun, 29 May 2022 23:19:43 +0100 Subject: [PATCH 051/136] style: PSR-12 --- buildtools/make_sync_struct.php | 208 ++++++++++++++++---------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/buildtools/make_sync_struct.php b/buildtools/make_sync_struct.php index 4c2c5d8ca5..e1336ef843 100644 --- a/buildtools/make_sync_struct.php +++ b/buildtools/make_sync_struct.php @@ -5,25 +5,25 @@ /* These methods have signatures incompatible with this script */ $blacklist = [ - 'channel_edit_permissions', - 'message_add_reaction', - 'message_delete_reaction', - 'message_delete_reaction_emoji', - 'message_delete_all_reactions', - 'message_delete_own_reaction', - 'message_get_reactions', - 'channel_typing', + 'channel_edit_permissions', + 'message_add_reaction', + 'message_delete_reaction', + 'message_delete_reaction_emoji', + 'message_delete_all_reactions', + 'message_delete_own_reaction', + 'message_get_reactions', + 'channel_typing', ]; /* The script cannot determine the correct return type of these methods, * so we specify it by hand here. */ $forcedReturn = [ - 'direct_message_create' => 'message', - 'guild_get_members' => 'guild_member_map', - 'guild_search_members' => 'guild_member_map', - 'message_create' => 'message', - 'message_edit' => 'message', + 'direct_message_create' => 'message', + 'guild_get_members' => 'guild_member_map', + 'guild_search_members' => 'guild_member_map', + 'message_create' => 'message', + 'message_edit' => 'message', ]; /* Get the contents of cluster.h into an array */ @@ -64,7 +64,7 @@ * DO NOT EDIT BY HAND! * * To re-generate this header file re-run the script! - */ + */ EOT; $cppcontent = $content; @@ -82,8 +82,8 @@ $us = file_exists('include/dpp/cluster_sync_calls.h') ? filemtime('include/dpp/cluster_sync_calls.h') : 0; $them = filemtime('include/dpp/cluster.h'); if ($them <= $us) { - echo "-- No change required.\n"; - exit(0); + echo "-- No change required.\n"; + exit(0); } echo "-- Autogenerating include/dpp/cluster_sync_calls.h\n"; @@ -91,57 +91,57 @@ /* Scan every line of the C++ source */ foreach ($clustercpp as $cpp) { - /* Look for declaration of function body */ - if ($state == STATE_SEARCH_FOR_FUNCTION && - preg_match('/^\s*void\s+cluster::([^(]+)\s*\((.*)command_completion_event_t\s*callback\s*\)/', $cpp, $matches)) { - $currentFunction = $matches[1]; - $parameters = preg_replace('/,\s*$/', '', $matches[2]); - if (!in_array($currentFunction, $blacklist)) { - $state = STATE_IN_FUNCTION; - } - /* Scan function body */ - } elseif ($state == STATE_IN_FUNCTION) { - /* End of function */ - if (preg_match('/^\}\s*$/', $cpp)) { - $state = STATE_END_OF_FUNCTION; - /* look for the return type of the method */ - } elseif (preg_match('/rest_request<([^>]+)>/', $cpp, $matches)) { - /* rest_request */ - $returnType = $matches[1]; - } elseif (preg_match('/rest_request_list<([^>]+)>/', $cpp, $matches)) { - /* rest_request_list */ - $returnType = $matches[1] . '_map'; - } elseif (preg_match('/callback\(confirmation_callback_t\(\w+, ([^(]+)\(.*, \w+\)\)/', $cpp, $matches)) { - /* confirmation_callback_t */ - $returnType = $matches[1]; - } elseif (!empty($forcedReturn[$currentFunction])) { - /* Forced return type */ - $returnType = $forcedReturn[$currentFunction]; - } - } - /* Completed parsing of function body */ - if ($state == STATE_END_OF_FUNCTION && !empty($currentFunction) && !empty($returnType)) { - if (!in_array($currentFunction, $blacklist)) { - $parameterList = explode(',', $parameters); - $parameterNames = []; - foreach ($parameterList as $parameter) { - $parts = explode(' ', trim($parameter)); - $parameterNames[] = trim(preg_replace('/[\s\*\&]+/', '', $parts[count($parts) - 1])); - } - $content .= getComments($currentFunction, $returnType, $parameterNames) . "\n"; - $fullParameters = getFullParameters($currentFunction, $parameterNames); - $parameterNames = trim(join(', ', $parameterNames)); - if (!empty($parameterNames)) { - $parameterNames = ', ' . $parameterNames; - } - $noDefaults = $parameters; - $parameters = !empty($fullParameters) ? $fullParameters : $parameters; - $content .= "$returnType {$currentFunction}_sync($parameters);\n\n"; - $cppcontent .= "$returnType cluster::{$currentFunction}_sync($noDefaults) {\n\treturn dpp::sync<$returnType>(this, &cluster::$currentFunction$parameterNames);\n}\n\n"; - } - $currentFunction = $parameters = $returnType = ''; - $state = STATE_SEARCH_FOR_FUNCTION; - } + /* Look for declaration of function body */ + if ($state == STATE_SEARCH_FOR_FUNCTION && + preg_match('/^\s*void\s+cluster::([^(]+)\s*\((.*)command_completion_event_t\s*callback\s*\)/', $cpp, $matches)) { + $currentFunction = $matches[1]; + $parameters = preg_replace('/,\s*$/', '', $matches[2]); + if (!in_array($currentFunction, $blacklist)) { + $state = STATE_IN_FUNCTION; + } + /* Scan function body */ + } elseif ($state == STATE_IN_FUNCTION) { + /* End of function */ + if (preg_match('/^\}\s*$/', $cpp)) { + $state = STATE_END_OF_FUNCTION; + /* look for the return type of the method */ + } elseif (preg_match('/rest_request<([^>]+)>/', $cpp, $matches)) { + /* rest_request */ + $returnType = $matches[1]; + } elseif (preg_match('/rest_request_list<([^>]+)>/', $cpp, $matches)) { + /* rest_request_list */ + $returnType = $matches[1] . '_map'; + } elseif (preg_match('/callback\(confirmation_callback_t\(\w+, ([^(]+)\(.*, \w+\)\)/', $cpp, $matches)) { + /* confirmation_callback_t */ + $returnType = $matches[1]; + } elseif (!empty($forcedReturn[$currentFunction])) { + /* Forced return type */ + $returnType = $forcedReturn[$currentFunction]; + } + } + /* Completed parsing of function body */ + if ($state == STATE_END_OF_FUNCTION && !empty($currentFunction) && !empty($returnType)) { + if (!in_array($currentFunction, $blacklist)) { + $parameterList = explode(',', $parameters); + $parameterNames = []; + foreach ($parameterList as $parameter) { + $parts = explode(' ', trim($parameter)); + $parameterNames[] = trim(preg_replace('/[\s\*\&]+/', '', $parts[count($parts) - 1])); + } + $content .= getComments($currentFunction, $returnType, $parameterNames) . "\n"; + $fullParameters = getFullParameters($currentFunction, $parameterNames); + $parameterNames = trim(join(', ', $parameterNames)); + if (!empty($parameterNames)) { + $parameterNames = ', ' . $parameterNames; + } + $noDefaults = $parameters; + $parameters = !empty($fullParameters) ? $fullParameters : $parameters; + $content .= "$returnType {$currentFunction}_sync($parameters);\n\n"; + $cppcontent .= "$returnType cluster::{$currentFunction}_sync($noDefaults) {\n\treturn dpp::sync<$returnType>(this, &cluster::$currentFunction$parameterNames);\n}\n\n"; + } + $currentFunction = $parameters = $returnType = ''; + $state = STATE_SEARCH_FOR_FUNCTION; + } } $content .= << $line) { - if (preg_match('/^\s*void\s+' . $currentFunction . '\s*\(.*' . join('.*', $parameters) . '.*command_completion_event_t\s*callback\s*/', $line)) { - /* Backpeddle */ - $lineIndex = 1; - for ($n = $i; $n != 0; --$n, $lineIndex++) { - $header[$n] = preg_replace('/^\t+/', '', $header[$n]); - $header[$n] = preg_replace('/@see (.+?)$/', '@see dpp::cluster::' . $currentFunction. "\n * @see \\1", $header[$n]); - $header[$n] = preg_replace('/@param callback .*$/', '@return ' . $returnType . ' returned object on completion', $header[$n]); - if (preg_match('/\s*\* On success /i', $header[$n])) { - $header[$n] = ""; - } - if (preg_match('/\s*\/\*\*\s*$/', $header[$n])) { - $part = array_slice($header, $n, $lineIndex - 1); - array_splice($part, count($part) - 1, 0, - [ - " * \memberof dpp::cluster", - " * @throw dpp::rest_exception upon failure to execute REST function", - " * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.", - " * Avoid direct use of this function inside an event handler.", - ] - ); - return str_replace("\n\n", "\n", join("\n", $part)); - } - } - return ''; - } - } - return ''; + global $header; + /* First find the function */ + foreach ($header as $i => $line) { + if (preg_match('/^\s*void\s+' . $currentFunction . '\s*\(.*' . join('.*', $parameters) . '.*command_completion_event_t\s*callback\s*/', $line)) { + /* Backpeddle */ + $lineIndex = 1; + for ($n = $i; $n != 0; --$n, $lineIndex++) { + $header[$n] = preg_replace('/^\t+/', '', $header[$n]); + $header[$n] = preg_replace('/@see (.+?)$/', '@see dpp::cluster::' . $currentFunction . "\n * @see \\1", $header[$n]); + $header[$n] = preg_replace('/@param callback .*$/', '@return ' . $returnType . ' returned object on completion', $header[$n]); + if (preg_match('/\s*\* On success /i', $header[$n])) { + $header[$n] = ""; + } + if (preg_match('/\s*\/\*\*\s*$/', $header[$n])) { + $part = array_slice($header, $n, $lineIndex - 1); + array_splice($part, count($part) - 1, 0, + [ + " * \memberof dpp::cluster", + " * @throw dpp::rest_exception upon failure to execute REST function", + " * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.", + " * Avoid direct use of this function inside an event handler.", + ] + ); + return str_replace("\n\n", "\n", join("\n", $part)); + } + } + return ''; + } + } + return ''; } /* Finished parsing, output autogenerated files */ From eab369ffaedac9fc290d6ea03769c8a4b063cd6e Mon Sep 17 00:00:00 2001 From: brain Date: Sun, 29 May 2022 23:22:48 +0100 Subject: [PATCH 052/136] style: PSR-12 --- buildtools/changelog.php | 171 ++++++++++++++++----------------- buildtools/close-master-pr.php | 33 +++---- 2 files changed, 101 insertions(+), 103 deletions(-) diff --git a/buildtools/changelog.php b/buildtools/changelog.php index aafa8e5c55..b49e5bac87 100755 --- a/buildtools/changelog.php +++ b/buildtools/changelog.php @@ -4,47 +4,47 @@ // Pattern list $categories = [ - 'break' => '💣 Breaking Changes', - 'breaking' => '💣 Breaking Changes', - 'feat' => '✨ New Features', - 'feature' => '✨ New Features', - 'add' => '✨ New Features', - 'added' => '✨ New Features', - 'fix' => '🐞 Bug Fixes', - 'bug' => '🐞 Bug Fixes', - 'bugfix' => '🐞 Bug Fixes', - 'fixed' => '🐞 Bug Fixes', - 'fixes' => '🐞 Bug Fixes', - 'perf' => '🚀 Performance Improvements', - 'performance' => '🚀 Performance Improvements', - 'impro' => '♻️ Refactoring', - 'improved' => '♻️ Refactoring', - 'improvement' => '♻️ Refactoring', - 'refactor' => '♻️ Refactoring', - 'refactored' => '♻️ Refactoring', - 'deprecated' => '♻️ Refactoring', - 'deprecate' => '♻️ Refactoring', - 'remove' => '♻️ Refactoring', - 'change' => '♻️ Refactoring', - 'changed' => '♻️ Refactoring', - 'test' => '🚨 Testing', - 'testing' => '🚨 Testing', - 'ci' => '👷 Build/CI', - 'build' => '👷 Build/CI', - 'docs' => '📚 Documentation', - 'documentation' => '📚 Documentation', - 'style' => '💎 Style Changes', - 'chore' => '🔧 Chore', - 'misc' => '📜 Miscellaneous Changes', - 'update' => '📜 Miscellaneous Changes', - 'updated' => '📜 Miscellaneous Changes', + 'break' => '💣 Breaking Changes', + 'breaking' => '💣 Breaking Changes', + 'feat' => '✨ New Features', + 'feature' => '✨ New Features', + 'add' => '✨ New Features', + 'added' => '✨ New Features', + 'fix' => '🐞 Bug Fixes', + 'bug' => '🐞 Bug Fixes', + 'bugfix' => '🐞 Bug Fixes', + 'fixed' => '🐞 Bug Fixes', + 'fixes' => '🐞 Bug Fixes', + 'perf' => '🚀 Performance Improvements', + 'performance' => '🚀 Performance Improvements', + 'impro' => '♻️ Refactoring', + 'improved' => '♻️ Refactoring', + 'improvement' => '♻️ Refactoring', + 'refactor' => '♻️ Refactoring', + 'refactored' => '♻️ Refactoring', + 'deprecated' => '♻️ Refactoring', + 'deprecate' => '♻️ Refactoring', + 'remove' => '♻️ Refactoring', + 'change' => '♻️ Refactoring', + 'changed' => '♻️ Refactoring', + 'test' => '🚨 Testing', + 'testing' => '🚨 Testing', + 'ci' => '👷 Build/CI', + 'build' => '👷 Build/CI', + 'docs' => '📚 Documentation', + 'documentation' => '📚 Documentation', + 'style' => '💎 Style Changes', + 'chore' => '🔧 Chore', + 'misc' => '📜 Miscellaneous Changes', + 'update' => '📜 Miscellaneous Changes', + 'updated' => '📜 Miscellaneous Changes', ]; $catgroup = []; $changelog = []; $githubstyle = true; if (count($argv) > 2 && $argv[1] == '--discord') { - $githubstyle = false; + $githubstyle = false; } // Magic sauce @@ -52,71 +52,70 @@ // Leadin if ($githubstyle) { - echo "The changelog is listed below:\n\nRelease Changelog\n===========\n"; + echo "The changelog is listed below:\n\nRelease Changelog\n===========\n"; } else { - echo "The changelog is listed below:\n\n__**Release Changelog**__\n"; + echo "The changelog is listed below:\n\n__**Release Changelog**__\n"; } // Case insensitive removal of duplicates $changelog = array_intersect_key($changelog, array_unique(array_map("strtolower", $changelog))); foreach ($changelog as $change) { - - // Wrap anything that looks like a symbol name in backticks - $change = preg_replace('/([\w_\/]+\.\w+|\S+\(\)|\w+::\w+|dpp::\w+|utility::\w+|(\w+_\w+)+)/', '`$1`', $change); - $change = preg_replace("/vs(\d+)/", "Visual Studio $1", $change); - $change = preg_replace("/\bfaq\b/", "FAQ", $change); - $change = preg_replace("/\bdiscord\b/", "Discord", $change); - $change = preg_replace("/\bmicrosoft\b/", "Microsoft", $change); - $change = preg_replace("/\bwindows\b/", "Windows", $change); - $change = preg_replace("/\blinux\b/", "Linux", $change); - $change = preg_replace("/\sarm(\d+)\s/i", ' ARM$1 ', $change); - $change = preg_replace("/\b(was|is|wo)nt\b/i", '$1n\'t', $change); - $change = preg_replace("/\bfreebsd\b/", 'FreeBSD', $change); - - // Match keywords against categories - $matched = false; - foreach ($categories as $cat => $header) { - // Purposefully ignored - if (preg_match("/^Merge (branch|pull request|remote-tracking branch) /", $change) or preg_match("/version bump/i", $change)) { - $matched = true; - continue; - } - // Groupings - if ((preg_match("/^" . $cat . ":/i", $change)) or (preg_match("/^\[" . $cat . "\//i", $change)) or (preg_match("/^\[" . $cat . "\]/i", $change))or (preg_match("/^\[" . $cat . ":/i", $change)) or (preg_match("/^" . $cat . "\//i", $change)) or (preg_match("/^" . $cat . ":/i", $change))) { - if (!isset($catgroup[$header])) { - $catgroup[$header] = []; - } - $matched = true; - $catgroup[$header][] = preg_replace("/^\S+\s+/", "", $change); - break; - } else if (preg_match("/^" . $cat . " /i", $change)) { - if (!isset($catgroup[$header])) { - $catgroup[$header] = []; - } - $matched = true; - $catgroup[$header][] = $change; - break; - } - } + + // Wrap anything that looks like a symbol name in backticks + $change = preg_replace('/([\w_\/]+\.\w+|\S+\(\)|\w+::\w+|dpp::\w+|utility::\w+|(\w+_\w+)+)/', '`$1`', $change); + $change = preg_replace("/vs(\d+)/", "Visual Studio $1", $change); + $change = preg_replace("/\bfaq\b/", "FAQ", $change); + $change = preg_replace("/\bdiscord\b/", "Discord", $change); + $change = preg_replace("/\bmicrosoft\b/", "Microsoft", $change); + $change = preg_replace("/\bwindows\b/", "Windows", $change); + $change = preg_replace("/\blinux\b/", "Linux", $change); + $change = preg_replace("/\sarm(\d+)\s/i", ' ARM$1 ', $change); + $change = preg_replace("/\b(was|is|wo)nt\b/i", '$1n\'t', $change); + $change = preg_replace("/\bfreebsd\b/", 'FreeBSD', $change); + + // Match keywords against categories + $matched = false; + foreach ($categories as $cat => $header) { + // Purposefully ignored + if (preg_match("/^Merge (branch|pull request|remote-tracking branch) /", $change) or preg_match("/version bump/i", $change)) { + $matched = true; + continue; + } + // Groupings + if ((preg_match("/^" . $cat . ":/i", $change)) or (preg_match("/^\[" . $cat . "\//i", $change)) or (preg_match("/^\[" . $cat . "\]/i", $change)) or (preg_match("/^\[" . $cat . ":/i", $change)) or (preg_match("/^" . $cat . "\//i", $change)) or (preg_match("/^" . $cat . ":/i", $change))) { + if (!isset($catgroup[$header])) { + $catgroup[$header] = []; + } + $matched = true; + $catgroup[$header][] = preg_replace("/^\S+\s+/", "", $change); + break; + } else if (preg_match("/^" . $cat . " /i", $change)) { + if (!isset($catgroup[$header])) { + $catgroup[$header] = []; + } + $matched = true; + $catgroup[$header][] = $change; + break; + } + } } // Output tidy formatting foreach ($catgroup as $cat => $list) { - echo "\n" . ($githubstyle ? '## ' : '__**') . $cat . ($githubstyle ? '' : '**__') . "\n"; - foreach ($list as $item) { - // Exclude bad commit messages like 'typo fix', 'test push' etc by pattern - if (!preg_match("/^(typo|test|fix)\s\w+$/", $item)) { - echo ($githubstyle ? '-' : '•') . ' ' . ucfirst(str_replace('@','', $item)) . "\n"; - } - } + echo "\n" . ($githubstyle ? '## ' : '__**') . $cat . ($githubstyle ? '' : '**__') . "\n"; + foreach ($list as $item) { + // Exclude bad commit messages like 'typo fix', 'test push' etc by pattern + if (!preg_match("/^(typo|test|fix)\s\w+$/", $item)) { + echo ($githubstyle ? '-' : '•') . ' ' . ucfirst(str_replace('@', '', $item)) . "\n"; + } + } } // Leadout echo "\n\n**Thank you for using D++!**\n\n"; if (!$githubstyle) { - $version = $argv[2]; - echo 'The ' . $version . ' download can be found here: '; - echo "\n"; + $version = $argv[2]; + echo 'The ' . $version . ' download can be found here: '; + echo "\n"; } - diff --git a/buildtools/close-master-pr.php b/buildtools/close-master-pr.php index 469131acd2..c58ca01c8f 100755 --- a/buildtools/close-master-pr.php +++ b/buildtools/close-master-pr.php @@ -6,27 +6,26 @@ exec("gh pr list --base master | sed 's/\|/ /' |awk '{print $1}'", $master_prs); foreach ($master_prs as $pr) { - - $pr = (int)$pr; - if ($pr > 0) { - system("gh pr comment $pr -b \"You have opened a PR against the master branch. PRs must target the \`dev\` branch, as such this PR has been automatically closed. Please re-target your PR against the dev branch if you reopen it. Thank you for your contribution.\""); - system("gh pr close $pr"); - } + $pr = (int)$pr; + if ($pr > 0) { + system("gh pr comment $pr -b \"You have opened a PR against the master branch. PRs must target the \`dev\` branch, as such this PR has been automatically closed. Please re-target your PR against the dev branch if you reopen it. Thank you for your contribution.\""); + system("gh pr close $pr"); + } } // Tidy up the workflow run list so it isn't littered with these exec("gh run list -w \"Close master-targeted PRs\"", $runs); $runindex = 0; foreach ($runs as $run) { - $run = preg_replace('/ /', ' ', $run); - $data = preg_split('/\s+/', $run); - $id = $data[sizeof($data) - 3]; - $id = (int)$id; - if ($id > 0 && $runindex > 0) { - // Delete all but the first completed workflow run and this one - // (the first is the currently executing one!) - exec("gh api repos/brainboxdotcc/DPP/actions/runs/$id -X DELETE"); - sleep(1); - } - $runindex++; + $run = preg_replace('/ /', ' ', $run); + $data = preg_split('/\s+/', $run); + $id = $data[sizeof($data) - 3]; + $id = (int)$id; + if ($id > 0 && $runindex > 0) { + // Delete all but the first completed workflow run and this one + // (the first is the currently executing one!) + exec("gh api repos/brainboxdotcc/DPP/actions/runs/$id -X DELETE"); + sleep(1); + } + $runindex++; } From 2f00aec0734696cbefc83d4bec9188a112a60a44 Mon Sep 17 00:00:00 2001 From: Axyte <85938894+Axyte@users.noreply.github.com> Date: Mon, 30 May 2022 17:14:08 +0530 Subject: [PATCH 053/136] [misc] some common git ignores --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d4d6fdef15..103b07d5dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ src/build *.autosave .misspell-fixer.ignore core +compile_commands.json +tags +.cache From fffb74444f7e2b6dfa1ac0c1c90453a9b9a91e51 Mon Sep 17 00:00:00 2001 From: Axyte <85938894+Axyte@users.noreply.github.com> Date: Mon, 30 May 2022 17:15:24 +0530 Subject: [PATCH 054/136] [fix] don't document deps --- Doxyfile | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Doxyfile b/Doxyfile index 46f8d78ea7..abc1867807 100644 --- a/Doxyfile +++ b/Doxyfile @@ -805,18 +805,8 @@ EXCLUDE_SYMLINKS = YES # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */json_fwd.hpp \ - */json.hpp \ - */format.cpp \ - */format.h \ - */printf.h \ - */os.h \ - */os.cpp \ - */color.h \ - */compile.h \ - */ostream.h \ - */ranges.h \ - */format-inl.h +EXCLUDE_PATTERNS = */dpp/nlohmann/* \ + */dpp/fmt/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the From a83c754d5516c1daba43ce9acb88b38f833741b7 Mon Sep 17 00:00:00 2001 From: Axyte <85938894+Axyte@users.noreply.github.com> Date: Mon, 30 May 2022 17:15:50 +0530 Subject: [PATCH 055/136] [fix] docs typo --- include/dpp/message.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/message.h b/include/dpp/message.h index 46cb00e1dc..1c80553f4c 100644 --- a/include/dpp/message.h +++ b/include/dpp/message.h @@ -358,7 +358,7 @@ class DPP_EXPORT component : public json_interface { * For action rows, this field is ignored. Setting the * value will auto-set the type to dpp::cot_text. * - * @param value Value text to set. It will be truncated to the maximum length of 4000 UTF-8 characters. + * @param val Value text to set. It will be truncated to the maximum length of 4000 UTF-8 characters. * @return component& Reference to self */ component& set_default_value(const std::string &val); From 40a6f264dd97f2f6890eceb23d3b7cd7545c57c3 Mon Sep 17 00:00:00 2001 From: Axyte <85938894+Axyte@users.noreply.github.com> Date: Mon, 30 May 2022 17:56:55 +0530 Subject: [PATCH 056/136] [docs] ignore src directory --- Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index abc1867807..d65a0408f6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -751,7 +751,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = docpages src include +INPUT = docpages include # 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 From d8feb55d6db6c028348a7e121c8417ece43f736a Mon Sep 17 00:00:00 2001 From: Axyte <85938894+Axyte@users.noreply.github.com> Date: Mon, 30 May 2022 18:41:16 +0530 Subject: [PATCH 057/136] [minor] add new channel flags --- include/dpp/channel.h | 17 +++++++++++++++++ src/dpp/channel.cpp | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 2711adeb5d..7335b28ba8 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -69,6 +69,16 @@ enum channel_flags : uint8_t { c_video_quality_720p = 0b00100000, /// Lock permissions (only used when updating channel positions) c_lock_permissions = 0b01000000, + /// Thread pinned in a forum (type 15) channel + c_pinned_thread = 0b10000000, +}; + +/** + * @brief The flags in discord channel's raw "flags" field. We use these for serialisation only, right now. Might be better to create a new field than to make the existing channel::flags from uint8_t to uint16_t, if discord adds more flags in future. + */ +enum discord_channel_flags : uint8_t { + /// Thread pinned in a forum (type 15) channel + dc_pinned_thread = 0b00000001, }; /** @@ -509,6 +519,13 @@ class DPP_EXPORT channel : public managed, public json_interface { */ bool is_video_720p() const; + /** + * @brief Returns true if channel is a pinned thread in forum + * + * @return true, if channel is a pinned thread in forum + */ + bool is_pinned_thread() const; + }; /** @brief A definition of a discord thread. diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 8bd429fa46..405372c5bd 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -212,6 +212,9 @@ bool channel::is_video_720p() const { return flags & dpp::c_video_quality_720p; } +bool channel::is_pinned_thread() const { + return flags & dpp::c_pinned_thread; +} bool thread::is_news_thread() const { return (flags & CHANNEL_TYPE_MASK) == CHANNEL_NEWS_THREAD; @@ -270,6 +273,9 @@ channel& channel::fill_from_json(json* j) { uint8_t type = int8_not_null(j, "type"); this->flags |= (type & CHANNEL_TYPE_MASK); + uint8_t dflags = int8_not_null(j, "flags"); + this->flags |= (dflags & dpp::dc_pinned_thread) ? dpp::c_pinned_thread : 0; + uint8_t vqm = int8_not_null(j, "video_quality_mode"); if (vqm == 2) { /* If this is set to 2, this means full quality 720p video for voice channel. From 131018791136d6ae37188a1334d3f76a74bf16ba Mon Sep 17 00:00:00 2001 From: brain Date: Mon, 30 May 2022 19:30:44 +0100 Subject: [PATCH 058/136] refactor: move a bunch of socket headers inside the cpp files that use them rather than them being in the .h --- include/dpp/discordvoiceclient.h | 19 ------------------- src/dpp/discordclient.cpp | 16 +++++++++++++--- src/dpp/discordvoiceclient.cpp | 1 + src/dpp/queues.cpp | 2 +- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h index 5e604af3f5..9cd9354577 100644 --- a/include/dpp/discordvoiceclient.h +++ b/include/dpp/discordvoiceclient.h @@ -23,20 +23,6 @@ #include #include - -#ifdef _WIN32 -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#endif - #include #include #include @@ -295,11 +281,6 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ dpp::socket fd; - /** - * @brief Socket address of voice server - */ - sockaddr_in servaddr; - /** * @brief Secret key for encrypting voice. * If it has been sent, this is non-null and points to a diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index 70d743c163..d2d8d86261 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -21,9 +21,6 @@ #include #include #include -#ifndef _WIN32 -#include -#endif #include #include #include @@ -33,6 +30,19 @@ #include #include #include +#ifdef _WIN32 + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include +#endif #define PATH_UNCOMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json" #define PATH_COMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json&compress=zlib-stream" diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 2e1eff85a8..259199b764 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -362,6 +362,7 @@ void discord_voice_client::run() int discord_voice_client::udp_send(const char* data, size_t length) { + sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(this->port); diff --git a/src/dpp/queues.cpp b/src/dpp/queues.cpp index 4fa1d38d06..033c0b17f1 100644 --- a/src/dpp/queues.cpp +++ b/src/dpp/queues.cpp @@ -78,7 +78,7 @@ void populate_result(const std::string &url, cluster* owner, http_request_comple rv.ratelimit_reset_after = from_string(res.get_header("x-ratelimit-reset-after")); rv.ratelimit_bucket = res.get_header("x-ratelimit-bucket"); rv.ratelimit_global = (res.get_header("x-ratelimit-global") == "true"); - owner->rest_ping = rv.latency; + owner->rest_ping = rv.latency; if (res.get_header("x-ratelimit-retry-after") != "") { rv.ratelimit_retry_after = from_string(res.get_header("x-ratelimit-retry-after")); } From 23f32b9588910212866a5ce7be9b43c4ff6d1de5 Mon Sep 17 00:00:00 2001 From: brain Date: Mon, 30 May 2022 20:06:43 +0100 Subject: [PATCH 059/136] fix: win32 builds with moved headers --- src/dpp/discordvoiceclient.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 259199b764..1e3a334409 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -18,12 +18,22 @@ * limitations under the License. * ************************************************************************************/ -#ifndef WIN32 - #include - #include -#else + +#ifdef _WIN32 /* Windows #define's min() and max(), breaking std::max(). stupid stupid stupid... */ #define NOMINMAX + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include #endif #include #include From 924935bf644865bf4750bbfc95533699227382b4 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 31 May 2022 19:20:57 +0100 Subject: [PATCH 060/136] fix duplicate includes --- src/dpp/discordclient.cpp | 1 - src/dpp/discordvoiceclient.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index d2d8d86261..9444fcbff1 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -41,7 +41,6 @@ #include #include #include - #include #endif #define PATH_UNCOMPRESSED_JSON "/?v=" DISCORD_API_VERSION "&encoding=json" diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 1e3a334409..ee3ac8e77b 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -33,7 +33,6 @@ #include #include #include - #include #endif #include #include From cb1b999925002ad832e3ac35e58290c8e1d1563c Mon Sep 17 00:00:00 2001 From: brain Date: Thu, 2 Jun 2022 10:03:33 +0100 Subject: [PATCH 061/136] version bump --- include/dpp/version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dpp/version.h b/include/dpp/version.h index c5acebe823..b21d245db7 100644 --- a/include/dpp/version.h +++ b/include/dpp/version.h @@ -21,9 +21,9 @@ #pragma once #if !defined(DPP_VERSION_LONG) -#define DPP_VERSION_LONG 0x00100010 -#define DPP_VERSION_SHORT 100010 -#define DPP_VERSION_TEXT "D++ 10.0.10 (18-May-2022)" +#define DPP_VERSION_LONG 0x00100011 +#define DPP_VERSION_SHORT 100011 +#define DPP_VERSION_TEXT "D++ 10.0.11 (02-Jun-2022)" #define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) #define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) From 0e93f00e5e0b28eee03bd5be69728a57dfc6cb62 Mon Sep 17 00:00:00 2001 From: Axyte <85938894+Axyte@users.noreply.github.com> Date: Thu, 2 Jun 2022 20:30:48 +0530 Subject: [PATCH 062/136] [fix] UB (__next_handle) --- include/dpp/cluster.h | 4 ++-- src/dpp/cluster.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 7ad5ade647..defeee9ae5 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -293,7 +293,7 @@ typedef std::function command_completion_e */ typedef std::function json_encode_t; -extern DPP_EXPORT event_handle __next_handle; +extern DPP_EXPORT event_handle _next_handle; /** * @brief Handles routing of an event to multiple listeners. @@ -442,7 +442,7 @@ template class event_router_t { */ event_handle attach(std::function func) { std::unique_lock l(lock); - event_handle h = __next_handle++; + event_handle h = _next_handle++; dispatch_container.emplace(h, func); return h; } diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index aa1aad84c4..210753ffcb 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -46,7 +46,7 @@ namespace dpp { #endif #endif -event_handle __next_handle = 1; +event_handle _next_handle = 1; /** * @brief An audit reason for each thread. These are per-thread to make the cluster From 4c3fa96c77f2f0671e629221c930344da7834281 Mon Sep 17 00:00:00 2001 From: Hou In Si Tou Date: Sun, 5 Jun 2022 20:42:42 -0700 Subject: [PATCH 063/136] Added a voice client option to skip throttling for live audio data. So far, the primary use case for sending audio data has been sending recorded audio, which usually has large chunks of data available instantly. Built-in throttling simulates the VoIP behavior where data becomes available over time. For live audio, however, built-in throttling isn't necessary because the throttling is already inherent in capturing live audio. Extra throttling can cause delays in delivering the audio packets, which can further cause occasional silence and allegedly lost packets on the receiving end. Adding an option to discord_voice_client so client can identify the kind of audio data before sending, which effectively turns on/off the built-in throttling in discord_voice_client. --- include/dpp/discordvoiceclient.h | 35 ++++++++++++++++++++++++++++++++ src/dpp/discordvoiceclient.cpp | 23 ++++++++++++++++----- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h index 9cd9354577..e11d6539d7 100644 --- a/include/dpp/discordvoiceclient.h +++ b/include/dpp/discordvoiceclient.h @@ -492,6 +492,34 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ snowflake channel_id; + /** + * @brief The audio type to be sent. The default type is recorded audio. + * + * If the audio is recorded, the sending of audio packets is throttled. + * Otherwise, if the audio is live, the sending is not throttled. + * + * Discord voice engine is expecting audio data as if they were from + * some audio device, e.g. microphone, where the data become available + * as they get captured from the audio device. + * + * In case of recorded audio, unlike from a device, the audio data are + * usually instantly available in large chunks. Throttling is needed to + * simulate audio data coming from an audio device. In case of live audio, + * the throttling is by nature, so no extra throttling is needed. + * + * Using live audio mode for recorded audio can cause Discord to skip + * audio data because Discord does not expect to receive, say, 3 minutes' + * worth of audio data in 1 second. + * + * Use discord_voice_client::set_send_audio_type to change this value as + * it ensures thread safety. + */ + enum send_audio_type_t + { + satype_recorded_audio, + satype_live_audio, + } send_audio_type = satype_recorded_audio; + /** * @brief Sets the gain for the specified user. * @@ -679,6 +707,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ discord_voice_client& send_silence(const uint64_t duration); + /** + * @brief Sets the audio type that will be sent with send_audio_* methods. + * + * @see send_audio_type_t + */ + discord_voice_client& set_send_audio_type(send_audio_type_t type); + /** * @brief Set the timescale in nanoseconds. * diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index ee3ac8e77b..a1b6247175 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -762,10 +762,11 @@ void discord_voice_client::write_ready() uint64_t duration = 0; bool track_marker_found = false; uint64_t bufsize = 0; + send_audio_type_t type = satype_recorded_audio; { std::lock_guard lock(this->stream_mutex); if (!this->paused && outbuf.size()) { - + type = send_audio_type; if (outbuf[0].packet.size() == 2 && ((uint16_t)(*(outbuf[0].packet.data()))) == AUDIO_TRACK_MARKER) { outbuf.erase(outbuf.begin()); track_marker_found = true; @@ -782,11 +783,14 @@ void discord_voice_client::write_ready() } } if (duration) { - std::chrono::nanoseconds latency = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - last_timestamp); - std::chrono::nanoseconds sleep_time = std::chrono::nanoseconds(duration) - latency; - if (sleep_time.count() > 0) { - std::this_thread::sleep_for(sleep_time); + if (type == satype_recorded_audio) { + std::chrono::nanoseconds latency = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - last_timestamp); + std::chrono::nanoseconds sleep_time = std::chrono::nanoseconds(duration) - latency; + if (sleep_time.count() > 0) { + std::this_thread::sleep_for(sleep_time); + } } + last_timestamp = std::chrono::high_resolution_clock::now(); if (!creator->on_voice_buffer_send.empty()) { voice_buffer_send_t snd(nullptr, ""); @@ -1079,6 +1083,15 @@ discord_voice_client& discord_voice_client::send_silence(const uint64_t duration return *this; } +discord_voice_client& discord_voice_client::set_send_audio_type(send_audio_type_t type) +{ + { + std::lock_guard lock(this->stream_mutex); + send_audio_type = type; + } + return *this; +} + discord_voice_client& discord_voice_client::send_audio_raw(uint16_t* audio_data, const size_t length) { #if HAVE_VOICE const size_t max_frame_bytes = 11520; From eef34d91b9144515b3f0a3ff8ad1cdc51e39945f Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 8 Jun 2022 22:06:07 +0200 Subject: [PATCH 064/136] Remove audit log reference under Modify Guild Channel Positions --- include/dpp/cluster.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 7ad5ade647..5ad18b47cf 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2087,8 +2087,7 @@ class DPP_EXPORT cluster { * Modify the positions of a set of channel objects for the guild. * Requires `MANAGE_CHANNELS` permission. Fires multiple `Channel Update Gateway` events. * Only channels to be modified are required. - * - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * * @see https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions * @param c Channel to change the position for * @param callback Function to call when the API call completes. From 93884d26829802c1394cce7358ff2c39eb3a4b62 Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 8 Jun 2022 23:04:26 +0200 Subject: [PATCH 065/136] breaking: added thread_name parameter to dpp::cluster::execute_webhook --- include/dpp/cluster.h | 3 ++- src/dpp/cluster/webhook.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 5ad18b47cf..6a498a269c 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2965,10 +2965,11 @@ class DPP_EXPORT cluster { * @param m Message to send * @param wait waits for server confirmation of message send before response, and returns the created message body * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived + * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); + void execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, std::string thread_name = "", command_completion_event_t callback = utility::log_error()); /** * @brief Get webhook message diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 322ceefee4..5cdb960281 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -72,12 +72,16 @@ void cluster::edit_webhook_with_token(const class webhook& wh, command_completio } -void cluster::execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, command_completion_event_t callback) { +void cluster::execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, std::string thread_name, command_completion_event_t callback) { std::string parameters = utility::make_url_parameters({ {"wait", wait}, {"thread_id", thread_id}, }); - this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { + json j = json::parse(m.build_json(false)); + if (!thread_name.empty()) { + j["thread_name"] = thread_name; + } + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, j.dump(), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } From 3e6b09da7096a55307c67807fbb7e33860bd3a0b Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 8 Jun 2022 23:10:57 +0200 Subject: [PATCH 066/136] added note to execute_webhook --- include/dpp/cluster.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 6a498a269c..4aa1d70460 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2967,6 +2967,7 @@ class DPP_EXPORT cluster { * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) * @param callback Function to call when the API call completes. + * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ void execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, std::string thread_name = "", command_completion_event_t callback = utility::log_error()); From 628ac12c3a665a65d4dc59d539296642e02ecbf2 Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 8 Jun 2022 23:14:15 +0200 Subject: [PATCH 067/136] forget to make the sync methods --- include/dpp/cluster_sync_calls.h | 11 ++++++----- src/dpp/cluster_sync_calls.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index b8cadba535..8ab1b0de1e 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -20,12 +20,12 @@ ************************************************************************************/ -/* Auto-generated by buildtools/make_sync_struct.php. +/* Auto @generated by buildtools/make_sync_struct.php. * * DO NOT EDIT BY HAND! * * To re-generate this header file re-run the script! - */ + */ /** * @brief Create/overwrite global slash commands. * Any existing global slash commands will be deleted and replaced with these. @@ -423,8 +423,7 @@ confirmation channel_delete_sync(snowflake channel_id); * Modify the positions of a set of channel objects for the guild. * Requires `MANAGE_CHANNELS` permission. Fires multiple `Channel Update Gateway` events. * Only channels to be modified are required. - * - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * * @see dpp::cluster::channel_edit_positions * @see https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions * @param c Channel to change the position for @@ -2303,13 +2302,15 @@ webhook edit_webhook_with_token_sync(const class webhook& wh); * @param m Message to send * @param wait waits for server confirmation of message send before response, and returns the created message body * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived + * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) * @return message returned object on completion + * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0); +message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, std::string thread_name = ""); /** * @brief Get channel webhooks diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index c8cc77e9d6..f6cd34698f 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -20,12 +20,12 @@ ************************************************************************************/ -/* Auto-generated by buildtools/make_sync_struct.php. +/* Auto @generated by buildtools/make_sync_struct.php. * * DO NOT EDIT BY HAND! * * To re-generate this header file re-run the script! - */ + */ #include #include @@ -624,8 +624,8 @@ webhook cluster::edit_webhook_with_token_sync(const class webhook& wh) { return dpp::sync(this, &cluster::edit_webhook_with_token, wh); } -message cluster::execute_webhook_sync(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id) { - return dpp::sync(this, &cluster::execute_webhook, wh, m, wait, thread_id); +message cluster::execute_webhook_sync(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, std::string thread_name) { + return dpp::sync(this, &cluster::execute_webhook, wh, m, wait, thread_id, thread_name); } webhook_map cluster::get_channel_webhooks_sync(snowflake channel_id) { From 4ce47003a1cbdec65ebee3bb3448430e1d878904 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 9 Jun 2022 00:05:08 +0200 Subject: [PATCH 068/136] make thread_name param of execute_webhook const ref --- include/dpp/cluster.h | 2 +- include/dpp/cluster_sync_calls.h | 2 +- src/dpp/cluster/webhook.cpp | 2 +- src/dpp/cluster_sync_calls.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 4aa1d70460..af90b6a3ed 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2970,7 +2970,7 @@ class DPP_EXPORT cluster { * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, std::string thread_name = "", command_completion_event_t callback = utility::log_error()); + void execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = "", command_completion_event_t callback = utility::log_error()); /** * @brief Get webhook message diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 8ab1b0de1e..2f74bc45a9 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -2310,7 +2310,7 @@ webhook edit_webhook_with_token_sync(const class webhook& wh); * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, std::string thread_name = ""); +message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = ""); /** * @brief Get channel webhooks diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 5cdb960281..bb209cd97a 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -72,7 +72,7 @@ void cluster::edit_webhook_with_token(const class webhook& wh, command_completio } -void cluster::execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, std::string thread_name, command_completion_event_t callback) { +void cluster::execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, const std::string& thread_name, command_completion_event_t callback) { std::string parameters = utility::make_url_parameters({ {"wait", wait}, {"thread_id", thread_id}, diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index f6cd34698f..f557ca7dae 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -624,7 +624,7 @@ webhook cluster::edit_webhook_with_token_sync(const class webhook& wh) { return dpp::sync(this, &cluster::edit_webhook_with_token, wh); } -message cluster::execute_webhook_sync(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, std::string thread_name) { +message cluster::execute_webhook_sync(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, const std::string& thread_name) { return dpp::sync(this, &cluster::execute_webhook, wh, m, wait, thread_id, thread_name); } From c82ea233cb5ada78c5cc337efdb3869235f86a3c Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 9 Jun 2022 15:19:37 +0200 Subject: [PATCH 069/136] make execute_webhook nicer --- src/dpp/cluster/webhook.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index bb209cd97a..3a74d31106 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -77,11 +77,15 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"wait", wait}, {"thread_id", thread_id}, }); - json j = json::parse(m.build_json(false)); - if (!thread_name.empty()) { + std::string body; + if (!thread_name.empty()) { // only use json::parse if thread_name is set + json j = json::parse(m.build_json(false)); j["thread_name"] = thread_name; + body = j.dump(); + } else { + body = m.build_json(false); } - this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, j.dump(), [this, callback](json &j, const http_request_completion_t& http) { + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, body, [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } From 8e07aa2ed45ef1e61e5dd4012f9722a31fa796f1 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 9 Jun 2022 18:04:52 +0200 Subject: [PATCH 070/136] make execute_webhook nicer --- src/dpp/cluster/webhook.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 3a74d31106..917d421c04 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -82,10 +82,8 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, json j = json::parse(m.build_json(false)); j["thread_name"] = thread_name; body = j.dump(); - } else { - body = m.build_json(false); } - this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, body, [this, callback](json &j, const http_request_completion_t& http) { + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, !body.empty() ? body : m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } From 77a2e6dab0e4353f91b6dae501584bf31b7e4bc5 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 9 Jun 2022 23:30:36 +0200 Subject: [PATCH 071/136] deprecated cluster::guild_ban_add with reason param. Added a similar method without this reason param. --- include/dpp/cluster.h | 18 +++++++++++++++++- src/dpp/cluster/guild.cpp | 10 ++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index af90b6a3ed..8dc873d12b 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2495,10 +2495,26 @@ class DPP_EXPORT cluster { * @param delete_message_days How many days of their user's messages to also delete * @param reason Reason for ban * @param callback Function to call when the API call completes. - * On success the callback will contain a dpp::ban object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @deprecated Discord deprecated the reason parameter in the json body. Use the similar method to this without the reason param and use cluster::set_audit_log_reason() to set the reason. */ void guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, const std::string &reason, command_completion_event_t callback = utility::log_error()); + /** + * @brief Add guild ban + * + * Create a guild ban, and optionally delete previous messages sent by the banned user. + * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#create-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add ban to + * @param user_id User ID to ban + * @param delete_message_days How many days of their user's messages to also delete (0-7). Defaults to 0 + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days = 0, command_completion_event_t callback = utility::log_error()); + /** * @brief Delete guild ban * diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index 6399e049cf..83b0e1d997 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -44,6 +44,16 @@ void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t dele } +void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, command_completion_event_t callback) { + json j; + if (delete_message_days > 7) + delete_message_days = 7; + if (delete_message_days) + j["delete_message_days"] = delete_message_days; + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, j.dump(), callback); +} + + void cluster::guild_ban_delete(snowflake guild_id, snowflake user_id, command_completion_event_t callback) { rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_delete, "", callback); } From 005b42eb03927ce0542150cb36eb6e54f3489036 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 9 Jun 2022 23:30:59 +0200 Subject: [PATCH 072/136] fix cluster::guild_begin_prune --- src/dpp/cluster/guild.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index 83b0e1d997..bea3a7f91f 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -133,7 +133,7 @@ void cluster::guild_get_prune_counts(snowflake guild_id, const struct prune& pru } void cluster::guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo, command_completion_event_t callback) { - rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "prune", m_get, pruneinfo.build_json(true), callback); + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "prune", m_post, pruneinfo.build_json(true), callback); } From c7bec362ebcb36c02f3546d0c643942d19211e69 Mon Sep 17 00:00:00 2001 From: Phil B Date: Thu, 9 Jun 2022 23:37:06 +0200 Subject: [PATCH 073/136] breaking: removed reason parameter from cluster::guild_ban_add method --- include/dpp/cluster.h | 17 ----------------- include/dpp/cluster_sync_calls.h | 7 +++---- src/dpp/cluster/guild.cpp | 12 ------------ src/dpp/cluster_sync_calls.cpp | 4 ++-- 4 files changed, 5 insertions(+), 35 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 8dc873d12b..f6aede8102 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -2483,23 +2483,6 @@ class DPP_EXPORT cluster { */ void guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until, command_completion_event_t callback = utility::log_error()); - /** - * @brief Add guild ban - * - * Create a guild ban, and optionally delete previous messages sent by the banned user. - * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. - * @see https://discord.com/developers/docs/resources/guild#create-guild-ban - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to add ban to - * @param user_id User ID to ban - * @param delete_message_days How many days of their user's messages to also delete - * @param reason Reason for ban - * @param callback Function to call when the API call completes. - * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). - * @deprecated Discord deprecated the reason parameter in the json body. Use the similar method to this without the reason param and use cluster::set_audit_log_reason() to set the reason. - */ - void guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, const std::string &reason, command_completion_event_t callback = utility::log_error()); - /** * @brief Add guild ban * diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 2f74bc45a9..940afd48cf 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -717,7 +717,7 @@ auditlog guild_auditlog_get_sync(snowflake guild_id); /** * @brief Add guild ban - * + * * Create a guild ban, and optionally delete previous messages sent by the banned user. * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. * @see dpp::cluster::guild_ban_add @@ -725,15 +725,14 @@ auditlog guild_auditlog_get_sync(snowflake guild_id); * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param guild_id Guild ID to add ban to * @param user_id User ID to ban - * @param delete_message_days How many days of their user's messages to also delete - * @param reason Reason for ban + * @param delete_message_days How many days of their user's messages to also delete (0-7). Defaults to 0 * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. * Avoid direct use of this function inside an event handler. */ -confirmation guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, const std::string &reason); +confirmation guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_days = 0); /** * @brief Delete guild ban diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index bea3a7f91f..59c7a5719e 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -32,18 +32,6 @@ void cluster::guild_auditlog_get(snowflake guild_id, command_completion_event_t } -void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, const std::string &reason, command_completion_event_t callback) { - json j; - if (delete_message_days > 7) - delete_message_days = 7; - if (!reason.empty()) - j["reason"] = reason; - if (delete_message_days) - j["delete_message_days"] = delete_message_days; - rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, j.dump(), callback); -} - - void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, command_completion_event_t callback) { json j; if (delete_message_days > 7) diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index f557ca7dae..49a0df0eed 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -216,8 +216,8 @@ auditlog cluster::guild_auditlog_get_sync(snowflake guild_id) { return dpp::sync(this, &cluster::guild_auditlog_get, guild_id); } -confirmation cluster::guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, const std::string &reason) { - return dpp::sync(this, &cluster::guild_ban_add, guild_id, user_id, delete_message_days, reason); +confirmation cluster::guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_days) { + return dpp::sync(this, &cluster::guild_ban_add, guild_id, user_id, delete_message_days); } confirmation cluster::guild_ban_delete_sync(snowflake guild_id, snowflake user_id) { From 99d6ce9fb906189b8db1631e870f197afd60a2ea Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Mon, 13 Jun 2022 08:32:21 +0100 Subject: [PATCH 074/136] ci: add gitguardian --- .github/workflows/gitguardian.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/gitguardian.yml diff --git a/.github/workflows/gitguardian.yml b/.github/workflows/gitguardian.yml new file mode 100644 index 0000000000..1fedcc0150 --- /dev/null +++ b/.github/workflows/gitguardian.yml @@ -0,0 +1,21 @@ +name: GitGuardian scan + +on: [push, pull_request] + +jobs: + scanning: + name: GitGuardian scan + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 # fetch all history so multiple commits can be scanned + - name: GitGuardian scan + uses: GitGuardian/ggshield-action@master + env: + GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }} + GITHUB_PUSH_BASE_SHA: ${{ github.event.base }} + GITHUB_PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }} + GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }} From 5fef1a1247634642ac70fe91546452f5fea7b150 Mon Sep 17 00:00:00 2001 From: Jakub 'Eremiell' Marek Date: Tue, 14 Jun 2022 09:55:03 +0200 Subject: [PATCH 075/136] =?UTF-8?q?=F0=9F=93=9D=20Add=20AUR=20shield?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * First package out! 🎉 * Official AUR shield! * Such shiny! Much fancy! Wow! * Will update automatically with AUR --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 647b223e1a..463b2d158a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/39b054c38bba411d9b25b39524016c9e)](https://www.codacy.com/gh/brainboxdotcc/DPP/dashboard?utm_source=github.com&utm_medium=referral&utm_content=brainboxdotcc/DPP&utm_campaign=Badge_Grade) ![Lines of code](https://img.shields.io/tokei/lines/github/brainboxdotcc/DPP) [![D++ CI](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml/badge.svg)](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml) +![AUR version](https://img.shields.io/aur/version/dpp) D++ is a lightweight and efficient library for Discord written in modern C++. It is designed to cover as much of the API specification as possible and to have a incredibly small memory footprint, even when caching large amounts of data. From b48cc7f28af41ee09ad582779861b2e698c2d878 Mon Sep 17 00:00:00 2001 From: Jakub 'Eremiell' Marek Date: Tue, 14 Jun 2022 10:09:04 +0200 Subject: [PATCH 076/136] =?UTF-8?q?=F0=9F=93=9D=20Add=20link=20to=20the=20?= =?UTF-8?q?AUR=20shield?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Somehow expected shields to sort this * Well, sorted now * AUR shield now leads to the AUR package page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 463b2d158a..dfc922f123 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/39b054c38bba411d9b25b39524016c9e)](https://www.codacy.com/gh/brainboxdotcc/DPP/dashboard?utm_source=github.com&utm_medium=referral&utm_content=brainboxdotcc/DPP&utm_campaign=Badge_Grade) ![Lines of code](https://img.shields.io/tokei/lines/github/brainboxdotcc/DPP) [![D++ CI](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml/badge.svg)](https://github.com/brainboxdotcc/DPP/actions/workflows/ci.yml) -![AUR version](https://img.shields.io/aur/version/dpp) +[![AUR version](https://img.shields.io/aur/version/dpp)](https://aur.archlinux.org/packages/dpp) D++ is a lightweight and efficient library for Discord written in modern C++. It is designed to cover as much of the API specification as possible and to have a incredibly small memory footprint, even when caching large amounts of data. From 555734a14c45bcb62744b7ce7cad829775e5a9f6 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 14 Jun 2022 14:22:34 +0100 Subject: [PATCH 077/136] fix: dont run gg on pr --- .github/workflows/gitguardian.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gitguardian.yml b/.github/workflows/gitguardian.yml index 1fedcc0150..2866cbdde2 100644 --- a/.github/workflows/gitguardian.yml +++ b/.github/workflows/gitguardian.yml @@ -1,6 +1,6 @@ name: GitGuardian scan -on: [push, pull_request] +on: [push] jobs: scanning: From 006f152b5b89e0b00a1f7808d046baba9cec8ffc Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 15 Jun 2022 18:49:19 +0200 Subject: [PATCH 078/136] updated how the url parameters are created in guild_member.cpp --- src/dpp/cluster/guild_member.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/dpp/cluster/guild_member.cpp b/src/dpp/cluster/guild_member.cpp index b4cc7c07d2..f048dee2f5 100644 --- a/src/dpp/cluster/guild_member.cpp +++ b/src/dpp/cluster/guild_member.cpp @@ -49,7 +49,11 @@ void cluster::guild_get_member(snowflake guild_id, snowflake user_id, command_co void cluster::guild_get_members(snowflake guild_id, uint16_t limit, snowflake after, command_completion_event_t callback) { - this->post_rest(API_PATH "/guilds", std::to_string(guild_id), fmt::format("members?limit={}&after={}", limit, after), m_get, "", [this, callback, guild_id](json &j, const http_request_completion_t& http) { + std::string parameters = utility::make_url_parameters({ + {"limit", std::to_string(limit)}, + {"after", std::to_string(after)}, + }); + this->post_rest(API_PATH "/guilds", std::to_string(guild_id), "members" + parameters, m_get, "", [this, callback, guild_id](json &j, const http_request_completion_t& http) { guild_member_map guild_members; confirmation_callback_t e(this, confirmation(), http); if (!e.is_error()) { @@ -117,7 +121,11 @@ void cluster::guild_member_move(const snowflake channel_id, const snowflake guil void cluster::guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit, command_completion_event_t callback) { - this->post_rest(API_PATH "/guilds", std::to_string(guild_id), fmt::format("members/search?query={}&limit={}", utility::url_encode(query), limit), m_get, "", [this, callback, guild_id] (json &j, const http_request_completion_t& http) { + std::string parameters = utility::make_url_parameters({ + {"query", query}, + {"limit", std::to_string(limit)}, + }); + this->post_rest(API_PATH "/guilds", std::to_string(guild_id), "members/search" + parameters, m_get, "", [this, callback, guild_id] (json &j, const http_request_completion_t& http) { guild_member_map guild_members; confirmation_callback_t e(this, confirmation(), http); if (!e.is_error()) { From 7e61fdcb02b5f7a021cf3fcdd10e8c6134d61a96 Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 15 Jun 2022 19:05:54 +0200 Subject: [PATCH 079/136] updated docs of some events --- include/dpp/cluster.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index f6aede8102..dbbb2a37de 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -837,7 +837,7 @@ class DPP_EXPORT cluster { * For an example of this in action please see \ref slashcommands * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. - * The function signature for this event takes a single `const` reference of type interaction_create_t&, and returns void. + * The function signature for this event takes a single `const` reference of type slashcommand_t&, and returns void. */ event_router_t on_slashcommand; @@ -877,7 +877,7 @@ class DPP_EXPORT cluster { * where a slash command is bound to the dpp::ctxm_message command type. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. - * The function signature for this event takes a single `const` reference of type select_click_t&, and returns void. + * The function signature for this event takes a single `const` reference of type message_context_menu_t&, and returns void. */ event_router_t on_message_context_menu; @@ -886,7 +886,7 @@ class DPP_EXPORT cluster { * where a slash command is bound to the dpp::ctxm_user command type. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. - * The function signature for this event takes a single `const` reference of type select_click_t&, and returns void. + * The function signature for this event takes a single `const` reference of type user_context_menu_t&, and returns void. */ event_router_t on_user_context_menu; From 763534b8cd6e6c2a83a0a57fa1be6d417c4b79c2 Mon Sep 17 00:00:00 2001 From: Ruben Versavel Date: Fri, 17 Jun 2022 19:33:11 +0200 Subject: [PATCH 080/136] Added voice action bitflag for guild_member fixed #417 set_deaf and set_mute now set a bitflag, guild_member_update only sets deaf and mute parameters if the flag is on. --- include/dpp/guild.h | 5 ++++- src/dpp/guild.cpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 46fbde558e..07dbffd353 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -138,7 +138,8 @@ enum guild_flags_extra : uint8_t { }; /** - * @brief Various flags that can be used to indicate the status of a guild member + * @brief Various flags that can be used to indicate the status of a guild member. + * @note Use set_mute and set_deaf member functions and do not toggle the bits yourself. */ enum guild_member_flags : uint8_t { /** Member deafened in voice channels */ @@ -149,6 +150,8 @@ enum guild_member_flags : uint8_t { gm_pending = 0b00100, /** Member has animated guild-specific avatar */ gm_animated_avatar = 0b01000, + /** gm_deaf or gm_mute has been toggled */ + gm_voice_action = 0b10000, }; /** diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 1d47e7f19b..171e1c256c 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -108,11 +108,13 @@ guild_member& guild_member::set_nickname(const std::string& nick) { guild_member& guild_member::set_mute(const bool is_muted) { this->flags = (is_muted) ? flags | gm_mute : flags & ~gm_mute; + this->flags |= gm_voice_action; return *this; } guild_member& guild_member::set_deaf(const bool is_deafened) { this->flags = (is_deafened) ? flags | gm_deaf : flags & ~gm_deaf; + this->flags |= gm_voice_action; return *this; } @@ -125,6 +127,7 @@ guild_member& guild_member::fill_from_json(nlohmann::json* j, snowflake g_id, sn this->guild_id = g_id; this->user_id = u_id; j->get_to(*this); + return *this; } @@ -200,8 +203,12 @@ std::string guild_member::build_json(bool with_id) const { j["roles"].push_back(std::to_string(role)); } } - j["mute"] = is_muted(); - j["deaf"] = is_deaf(); + + if (flags & gm_voice_action) { + j["mute"] = is_muted(); + j["deaf"] = is_deaf(); + } + return j.dump(); } From 446478d4a62f20c154f8a7ceeed525326032c5d8 Mon Sep 17 00:00:00 2001 From: brain Date: Sat, 18 Jun 2022 00:49:28 +0100 Subject: [PATCH 081/136] update to new identify fields without $ prefix --- src/dpp/discordclient.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index 9444fcbff1..5e6bb572b4 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -307,9 +307,9 @@ bool discord_client::handle_frame(const std::string &buffer) { "token", this->token }, { "properties", { - { "$os", "Linux" }, - { "$browser", "D++" }, - { "$device", "D++" } + { "os", "Linux" }, + { "browser", "D++" }, + { "device", "D++" } } }, { "shard", json::array({ shard_id, max_shards }) }, From e7972d0be90d8bf5bc2f8247ee3cc088176051b7 Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Fri, 17 Jun 2022 21:58:36 -0400 Subject: [PATCH 082/136] add os detection --- CMakeLists.txt | 65 +++++++++++++++++++++++++++++++++++---- src/dpp/discordclient.cpp | 9 +++++- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbcc4b3eb7..a3554815f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,8 @@ project(libdpp VERSION ${DPP_VERSION} LANGUAGES CXX) check_cxx_symbol_exists(prctl "sys/prctl.h" HAVE_PRCTL) check_cxx_symbol_exists(pthread_setname_np "pthread.h" HAVE_PTHREAD_SETNAME_NP) +add_compile_definitions(DPP_OS=${CMAKE_SYSTEM_NAME}) + if(WIN32 AND NOT MINGW) if (NOT WINDOWS_32_BIT) message("-- Building for windows with precompiled packaged dependencies") @@ -217,6 +219,8 @@ set(modules_dir "src") file(GLOB subdirlist ${modules_dir}/dpp) + + foreach (fullmodname ${subdirlist}) get_filename_component(modname ${fullmodname} NAME) set (modsrc "") @@ -237,12 +241,6 @@ foreach (fullmodname ${subdirlist}) $ ) - target_precompile_headers( - ${modname} - PRIVATE - "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json.hpp" - "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json_fwd.hpp" - ) if (WIN32 AND NOT MINGW) if (NOT WINDOWS_32_BIT) @@ -271,8 +269,63 @@ foreach (fullmodname ${subdirlist}) include_directories(${OPUS_INCLUDE_DIRS} ${sodium_INCLUDE_DIR}) endif() + + +target_precompile_headers(${modname} +PRIVATE +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"include/dpp/stringops.h" +"include/dpp/snowflake.h" +"include/dpp/nlohmann/json_fwd.hpp" +"include/dpp/nlohmann/json.hpp" +"include/dpp/managed.h" +"include/dpp/json_interface.h" +"include/dpp/export.h" +"include/dpp/discordevents.h" +"include/dpp/cluster.h" +"" +"" +"" +) endforeach() +target_precompile_headers(dpp + PRIVATE + "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json.hpp" + "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json_fwd.hpp" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "include/dpp/stringops.h" + "include/dpp/snowflake.h" + "include/dpp/nlohmann/json_fwd.hpp" + "include/dpp/nlohmann/json.hpp" + "include/dpp/managed.h" + "include/dpp/json_interface.h" + "include/dpp/export.h" + "include/dpp/discordevents.h" + "include/dpp/cluster.h" + "" + "" + "" + ) + target_compile_features(dpp PRIVATE cxx_std_17) target_compile_features(dpp PRIVATE cxx_constexpr) target_compile_features(dpp PRIVATE cxx_auto_type) diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index 5e6bb572b4..ac6eee8c31 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -49,6 +49,13 @@ #define PATH_COMPRESSED_ETF "/?v=" DISCORD_API_VERSION "&encoding=etf&compress=zlib-stream" #define DECOMP_BUFFER_SIZE 512 * 1024 +#define STRINGIFY(a) STRINGIFY_(a) +#define STRINGIFY_(a) #a + +#ifndef DPP_OS +#define DPP_OS "unknown" +#endif + namespace dpp { /** @@ -307,7 +314,7 @@ bool discord_client::handle_frame(const std::string &buffer) { "token", this->token }, { "properties", { - { "os", "Linux" }, + { "os", STRINGIFY(DPP_OS) }, { "browser", "D++" }, { "device", "D++" } } From 266a49d3ca260fd456a3be55b2fa12fc33bd8ede Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Fri, 17 Jun 2022 22:13:46 -0400 Subject: [PATCH 083/136] include limits to fix buids --- src/dpp/discordvoiceclient.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index a1b6247175..6d4785d232 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -22,6 +22,7 @@ #ifdef _WIN32 /* Windows #define's min() and max(), breaking std::max(). stupid stupid stupid... */ #define NOMINMAX + #include #include #include @@ -34,6 +35,7 @@ #include #include #endif +#include #include #include #include From 375754e063b59dd85ca3c5f21f7936304f31db2d Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Fri, 17 Jun 2022 22:16:47 -0400 Subject: [PATCH 084/136] add parens --- src/dpp/discordvoiceclient.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 6d4785d232..8bc03e4db0 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -22,7 +22,6 @@ #ifdef _WIN32 /* Windows #define's min() and max(), breaking std::max(). stupid stupid stupid... */ #define NOMINMAX - #include #include #include @@ -35,7 +34,6 @@ #include #include #endif -#include #include #include #include @@ -127,7 +125,7 @@ size_t audio_mix(discord_voice_client& client, opus_int32* pcm_mix, const opus_i for (opus_int32 v = 0; v < samples * opus_channel_count; ++v) { pcm_mix[v] += pcm[v]; } - max_samples = std::max(samples, max_samples); + max_samples = (std::max)(samples, max_samples); return park_count + 1; } #endif From 6e1f3566c5da2892286787c916178dbbe5a396e5 Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Sat, 18 Jun 2022 16:50:21 -0400 Subject: [PATCH 085/136] remove other stuff --- CMakeLists.txt | 64 +++++--------------------------------------------- 1 file changed, 6 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3554815f5..f25ed26bf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,8 +219,6 @@ set(modules_dir "src") file(GLOB subdirlist ${modules_dir}/dpp) - - foreach (fullmodname ${subdirlist}) get_filename_component(modname ${fullmodname} NAME) set (modsrc "") @@ -241,6 +239,12 @@ foreach (fullmodname ${subdirlist}) $ ) + target_precompile_headers( + ${modname} + PRIVATE + "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json.hpp" + "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json_fwd.hpp" + ) if (WIN32 AND NOT MINGW) if (NOT WINDOWS_32_BIT) @@ -269,63 +273,8 @@ foreach (fullmodname ${subdirlist}) include_directories(${OPUS_INCLUDE_DIRS} ${sodium_INCLUDE_DIR}) endif() - - -target_precompile_headers(${modname} -PRIVATE -"" -"" -"" -"" -"" -"" -"" -"" -"" -"" -"include/dpp/stringops.h" -"include/dpp/snowflake.h" -"include/dpp/nlohmann/json_fwd.hpp" -"include/dpp/nlohmann/json.hpp" -"include/dpp/managed.h" -"include/dpp/json_interface.h" -"include/dpp/export.h" -"include/dpp/discordevents.h" -"include/dpp/cluster.h" -"" -"" -"" -) endforeach() -target_precompile_headers(dpp - PRIVATE - "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json.hpp" - "${PROJECT_SOURCE_DIR}/include/dpp/nlohmann/json_fwd.hpp" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "include/dpp/stringops.h" - "include/dpp/snowflake.h" - "include/dpp/nlohmann/json_fwd.hpp" - "include/dpp/nlohmann/json.hpp" - "include/dpp/managed.h" - "include/dpp/json_interface.h" - "include/dpp/export.h" - "include/dpp/discordevents.h" - "include/dpp/cluster.h" - "" - "" - "" - ) - target_compile_features(dpp PRIVATE cxx_std_17) target_compile_features(dpp PRIVATE cxx_constexpr) target_compile_features(dpp PRIVATE cxx_auto_type) @@ -393,4 +342,3 @@ include("cmake/CPackSetup.cmake") # Setup information for packaging and dis # CPack initialization for distribution include(CPack) - From c6d956489a6acf0b1c653333c4ee7a455eeff5a5 Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Sat, 18 Jun 2022 16:53:32 -0400 Subject: [PATCH 086/136] remove NOMINMAX definition --- src/dpp/discordvoiceclient.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dpp/discordvoiceclient.cpp b/src/dpp/discordvoiceclient.cpp index 8bc03e4db0..7b2b22ef4e 100644 --- a/src/dpp/discordvoiceclient.cpp +++ b/src/dpp/discordvoiceclient.cpp @@ -20,8 +20,6 @@ ************************************************************************************/ #ifdef _WIN32 - /* Windows #define's min() and max(), breaking std::max(). stupid stupid stupid... */ - #define NOMINMAX #include #include #include From 4328515b722599caa4a5cc9be97dbf6f284a96bd Mon Sep 17 00:00:00 2001 From: es183923 <72744903+es183923@users.noreply.github.com> Date: Sat, 18 Jun 2022 17:05:16 -0400 Subject: [PATCH 087/136] make stringify work when its unknown --- src/dpp/discordclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index ac6eee8c31..e82e115b1f 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -53,7 +53,7 @@ #define STRINGIFY_(a) #a #ifndef DPP_OS -#define DPP_OS "unknown" +#define DPP_OS unknown #endif namespace dpp { From fc820607fe4cedde0cc7ab6f818aeca3c2a17442 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sat, 18 Jun 2022 23:20:31 +0200 Subject: [PATCH 088/136] deprecated message::set_file_content and message::set_filename --- include/dpp/channel.h | 2 +- include/dpp/guild.h | 2 +- include/dpp/message.h | 10 ++++++---- src/dpp/cluster/guild.cpp | 4 +--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 7335b28ba8..d5999ee421 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -385,7 +385,7 @@ class DPP_EXPORT channel : public managed, public json_interface { /** * @brief Get the user permissions for a user on this channel * - * @param member The user to return permissions for + * @param member The user to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. * Note that if the user is not on the channel or the guild is * not in the cache, the function will always return 0. diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 07dbffd353..c116164480 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -554,7 +554,7 @@ class DPP_EXPORT guild : public managed, public json_interface { * * @param base_permissions base permissions before overwrites, * from channel::base_permissions - * @param member Member to fetch permissions for + * @param member Member to resolve the permissions for * @param channel Channel to fetch permissions against * @return permission Merged permissions bitmask of overwrites. */ diff --git a/include/dpp/message.h b/include/dpp/message.h index 1c80553f4c..96bb2b1d4a 100644 --- a/include/dpp/message.h +++ b/include/dpp/message.h @@ -1191,19 +1191,19 @@ struct DPP_EXPORT message : public managed { */ struct allowed_ref { /** - * @brief Set to true to parse user mentions in the text + * @brief Set to true to parse user mentions in the text. Default is false */ bool parse_users; /** - * @brief Set to true to at-everyone and at-here mentions in the text + * @brief Set to true to at-everyone and at-here mentions in the text. Default is false */ bool parse_everyone; /** - * @brief Set to true to parse role mentions in the text + * @brief Set to true to parse role mentions in the text. Default is false */ bool parse_roles; /** - * @brief Set to true to mention the user who sent the message this one is replying to + * @brief Set to true to mention the user who sent the message this one is replying to. Default is false */ bool replied_user; /** @@ -1396,6 +1396,7 @@ struct DPP_EXPORT message : public managed { * * @param fn filename * @return message& reference to self + * @deprecated Use message::add_file instead */ message& set_filename(const std::string &fn); @@ -1404,6 +1405,7 @@ struct DPP_EXPORT message : public managed { * * @param fc raw file content contained in std::string * @return message& reference to self + * @deprecated Use message::add_file instead */ message& set_file_content(const std::string &fc); diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index 59c7a5719e..e26777a104 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -34,10 +34,8 @@ void cluster::guild_auditlog_get(snowflake guild_id, command_completion_event_t void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_days, command_completion_event_t callback) { json j; - if (delete_message_days > 7) - delete_message_days = 7; if (delete_message_days) - j["delete_message_days"] = delete_message_days; + j["delete_message_days"] = delete_message_days > 7 ? 7 : delete_message_days; rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, j.dump(), callback); } From a2c45ac8aac8d80b9e5ee98d5361e8919ec4a008 Mon Sep 17 00:00:00 2001 From: harshfeudal <87577447+harshfeudal@users.noreply.github.com> Date: Sun, 19 Jun 2022 16:12:36 +0700 Subject: [PATCH 089/136] edit the `has_create_instant_invite()` definition --- include/dpp/role.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/role.h b/include/dpp/role.h index 00d380a6cb..4d6d13fdd6 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -220,7 +220,7 @@ class DPP_EXPORT role : public managed, public json_interface { */ bool has_create_instant_invite() const; /** - * @brief True if has create instant invite permission + * @brief True if has the kick members permission. * @note Having the administrator permission causes this method to always return true * Channel specific overrides may apply to permissions. * @return bool True if user has the kick members permission or is administrator. From 4d2c855d9e515cc4686ed05d6ffd1ef988e8da2d Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 19 Jun 2022 15:43:35 +0200 Subject: [PATCH 090/136] updated some docs in cluster --- include/dpp/cluster.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 7f7f94e63d..dfb27b0594 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -904,7 +904,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a guild is deleted. * A guild can be deleted via the bot being kicked, the bot leaving the guild - * explicitly with dpp::guild_delete, or via the guild being unavailable due to + * explicitly with dpp::cluster::guild_delete, or via the guild being unavailable due to * an outage. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. @@ -938,7 +938,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a shard is connected and ready. - * A set of on_guild_create events will follow this event. + * A set of cluster::on_guild_create events will follow this event. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type ready_t&, and returns void. @@ -1006,7 +1006,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a set of members is received for a guild. - * D++ will request these for all new guilds if needed, after the on_guild_create + * D++ will request these for all new guilds if needed, after the cluster::on_guild_create * events. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. @@ -1220,7 +1220,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a user is updated. - * This is separate to guild_member_update and includes things such as an avatar change, + * This is separate to cluster::on_guild_member_update and includes things such as an avatar change, * username change, discriminator change or change in subscription status for nitro. * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. @@ -1234,6 +1234,7 @@ class DPP_EXPORT cluster { * Note that D++ does not cache messages. If you want to cache these objects you * should create something yourself within your bot. Caching of messages is not on * the roadmap to be supported as it consumes excessive amounts of RAM. + * For an example for caching of messages, please see \ref caching-messages * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type message_create_t&, and returns void. @@ -1294,8 +1295,8 @@ class DPP_EXPORT cluster { /** - * @brief Called when a thread is created - * Note: Threads are not cached by D++, but a list of thread IDs is accessible in a guild object + * @brief Called when a thread is created. + * Note that threads are not cached by D++, but a list of thread IDs is accessible in a guild object * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type thread_create_t&, and returns void. @@ -1322,8 +1323,8 @@ class DPP_EXPORT cluster { /** - * @brief Called when thread list is synced (upon gaining access to a channel) - * Note: Threads are not cached by D++, but a list of thread IDs is accessible in a guild object + * @brief Called when thread list is synced (upon gaining access to a channel). + * Note that threads are not cached by D++, but a list of thread IDs is accessible in a guild object * * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. * The function signature for this event takes a single `const` reference of type thread_list_sync_t&, and returns void. @@ -1398,7 +1399,7 @@ class DPP_EXPORT cluster { * @brief Called when packets are sent from the voice buffer. * The voice buffer contains packets that are already encoded with Opus and encrypted * with Sodium, and merged into packets by the repacketizer, which is done in the - * dpp::discord_voice_client::send_audio method. You should use the buffer size properties + * dpp::discord_voice_client::send_audio_opus method. You should use the buffer size properties * of dpp::voice_buffer_send_t to determine if you should fill the buffer with more * content. * From 30a2084ae3f5d178eaaf3d98a605009b79a542a4 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 19 Jun 2022 20:03:19 +0200 Subject: [PATCH 091/136] update --- include/dpp/channel.h | 2 +- include/dpp/guild.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index d5999ee421..794a79faff 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -383,7 +383,7 @@ class DPP_EXPORT channel : public managed, public json_interface { std::string get_mention() const; /** - * @brief Get the user permissions for a user on this channel + * @brief Get the user permissions for a user on this channel including channel overwrites * * @param member The user to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. diff --git a/include/dpp/guild.h b/include/dpp/guild.h index c116164480..ccb6e663b4 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -541,7 +541,7 @@ class DPP_EXPORT guild : public managed, public json_interface { /** * @brief Get the base permissions for a member on this guild, - * before permission overwrites are applied. + * before channel overwrites are applied. * * @param member member to get permissions for * @return permission permissions bitmask From c26bf97b8eff9c3df6a7215e669b43350beecc70 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 19 Jun 2022 20:19:03 +0200 Subject: [PATCH 092/136] breaking: updated params of guild::permission_overwrites and guild::base_permissions --- include/dpp/guild.h | 21 +++++++++++++++ src/dpp/guild.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/include/dpp/guild.h b/include/dpp/guild.h index ccb6e663b4..b42fcf19d2 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -545,9 +545,19 @@ class DPP_EXPORT guild : public managed, public json_interface { * * @param member member to get permissions for * @return permission permissions bitmask + * @deprecated */ permission base_permissions(const class user* member) const; + /** + * @brief Get the base permissions for a member on this guild, + * before channel overwrites are applied. + * + * @param member member to get permissions for + * @return permission permissions bitmask + */ + permission base_permissions(const guild_member& member) const; + /** * @brief Get the permission overwrites for a member * merged into a bitmask. @@ -557,9 +567,20 @@ class DPP_EXPORT guild : public managed, public json_interface { * @param member Member to resolve the permissions for * @param channel Channel to fetch permissions against * @return permission Merged permissions bitmask of overwrites. + * @deprecated */ permission permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const; + /** + * @brief Get the permission overwrites for a member + * merged into a bitmask. + * + * @param member Member to resolve the permissions for + * @param channel Channel to get permission overwrites for + * @return permission Merged permissions bitmask of overwrites. + */ + permission permission_overwrites(const guild_member& member, const channel& channel) const; + /** * @brief Rehash members map */ diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 171e1c256c..3ad124b45c 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -565,6 +565,27 @@ permission guild::base_permissions(const user* member) const return permissions; } +permission guild::base_permissions(const guild_member &member) const { + if (owner_id == member.user_id) + return ~0; + + role* everyone = dpp::find_role(id); + + permission permissions = everyone->permissions; + + for (auto& rid : member.roles) { + role* r = dpp::find_role(rid); + if (r) { + permissions |= r->permissions; + } + } + + if (permissions & p_administrator) + return ~0; + + return permissions; +} + permission guild::permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const { if (base_permissions & p_administrator) @@ -607,6 +628,49 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u return permissions; } +permission guild::permission_overwrites(const guild_member &member, const channel& channel) const { + permission base_permissions = this->base_permissions(member); + + if (base_permissions & p_administrator) + return ~0; + + permission permissions = base_permissions; + for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { + if (it->id == id && it->type == ot_role) { + permissions &= ~it->deny; + permissions |= it->allow; + break; + } + } + + auto mi = members.find(member.user_id); + if (mi == members.end()) + return 0; + guild_member gm = mi->second; + uint64_t allow = 0; + uint64_t deny = 0; + + for (auto& rid : gm.roles) { + + /* Skip \@everyone, calculated above */ + if (rid == id) + continue; + + for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { + if ((rid == it->id && it->type == ot_role) || (member.user_id == it->id && it->type == ot_member)) { + allow |= it->allow; + deny |= it->deny; + break; + } + } + } + + permissions &= ~deny; + permissions |= allow; + + return permissions; +} + bool guild::connect_member_voice(snowflake user_id, bool self_mute, bool self_deaf) { for (auto & c : channels) { channel* ch = dpp::find_channel(c); From e324b0ca3cd19676ab9e152487e8ef3a17d63cc3 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 19 Jun 2022 20:36:20 +0200 Subject: [PATCH 093/136] make the params pointers --- include/dpp/channel.h | 11 +++++++++++ include/dpp/guild.h | 4 ++-- src/dpp/channel.cpp | 9 +++++++++ src/dpp/guild.cpp | 20 ++++++++------------ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 794a79faff..224345049a 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -389,9 +390,19 @@ class DPP_EXPORT channel : public managed, public json_interface { * @return permission Permissions bitmask made of bits in dpp::permissions. * Note that if the user is not on the channel or the guild is * not in the cache, the function will always return 0. + * @deprecated */ permission get_user_permissions(const class user* member) const; + /** + * @brief Get the user permissions for a user on this channel including channel overwrites + * + * @param member The user to resolve the permissions for + * @return permission Permissions bitmask made of bits in dpp::permissions. + * Note that if the user is not on the channel, the function will always return 0. + */ + permission get_user_permissions(const guild_member* member) const; + /** * @brief Return a map of members on the channel, built from the guild's * member list based on which members have the VIEW_CHANNEL permission. diff --git a/include/dpp/guild.h b/include/dpp/guild.h index b42fcf19d2..6c30397b51 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -556,7 +556,7 @@ class DPP_EXPORT guild : public managed, public json_interface { * @param member member to get permissions for * @return permission permissions bitmask */ - permission base_permissions(const guild_member& member) const; + permission base_permissions(const guild_member* member) const; /** * @brief Get the permission overwrites for a member @@ -579,7 +579,7 @@ class DPP_EXPORT guild : public managed, public json_interface { * @param channel Channel to get permission overwrites for * @return permission Merged permissions bitmask of overwrites. */ - permission permission_overwrites(const guild_member& member, const channel& channel) const; + permission permission_overwrites(const guild_member* member, const channel* channel) const; /** * @brief Rehash members map diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 405372c5bd..575f704d55 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -387,6 +387,15 @@ permission channel::get_user_permissions(const user* member) const return g->permission_overwrites(g->base_permissions(member), member, this); } +permission channel::get_user_permissions(const guild_member* member) const +{ + guild* g = dpp::find_guild(guild_id); + if (g == nullptr) + return 0; + + return g->permission_overwrites(member, this); +} + std::map channel::get_members() { std::map rv; guild* g = dpp::find_guild(guild_id); diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 3ad124b45c..c8b3a07a5b 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -565,15 +565,15 @@ permission guild::base_permissions(const user* member) const return permissions; } -permission guild::base_permissions(const guild_member &member) const { - if (owner_id == member.user_id) +permission guild::base_permissions(const guild_member *member) const { + if (owner_id == member->user_id) return ~0; role* everyone = dpp::find_role(id); permission permissions = everyone->permissions; - for (auto& rid : member.roles) { + for (auto& rid : member->roles) { role* r = dpp::find_role(rid); if (r) { permissions |= r->permissions; @@ -628,14 +628,14 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u return permissions; } -permission guild::permission_overwrites(const guild_member &member, const channel& channel) const { +permission guild::permission_overwrites(const guild_member *member, const channel* channel) const { permission base_permissions = this->base_permissions(member); if (base_permissions & p_administrator) return ~0; permission permissions = base_permissions; - for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { + for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { if (it->id == id && it->type == ot_role) { permissions &= ~it->deny; permissions |= it->allow; @@ -643,21 +643,17 @@ permission guild::permission_overwrites(const guild_member &member, const channe } } - auto mi = members.find(member.user_id); - if (mi == members.end()) - return 0; - guild_member gm = mi->second; uint64_t allow = 0; uint64_t deny = 0; - for (auto& rid : gm.roles) { + for (auto& rid : member->roles) { /* Skip \@everyone, calculated above */ if (rid == id) continue; - for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { - if ((rid == it->id && it->type == ot_role) || (member.user_id == it->id && it->type == ot_member)) { + for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { + if ((rid == it->id && it->type == ot_role) || (member->user_id == it->id && it->type == ot_member)) { allow |= it->allow; deny |= it->deny; break; From 064e2880c1bb255b39a4f8a92bd467c10919cf42 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 19 Jun 2022 21:02:54 +0200 Subject: [PATCH 094/136] updated channel permission getter --- include/dpp/channel.h | 6 +++--- src/dpp/channel.cpp | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 224345049a..324a8543ff 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -397,11 +397,11 @@ class DPP_EXPORT channel : public managed, public json_interface { /** * @brief Get the user permissions for a user on this channel including channel overwrites * - * @param member The user to resolve the permissions for + * @param member The member to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. - * Note that if the user is not on the channel, the function will always return 0. + * @note If the guild this channel belongs to is not in the cache, the function will always return 0. */ - permission get_user_permissions(const guild_member* member) const; + permission get_member_overwrites(const guild_member* member) const; /** * @brief Return a map of members on the channel, built from the guild's diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 575f704d55..5bf6cdece3 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -387,7 +387,7 @@ permission channel::get_user_permissions(const user* member) const return g->permission_overwrites(g->base_permissions(member), member, this); } -permission channel::get_user_permissions(const guild_member* member) const +permission channel::get_member_overwrites(const guild_member* member) const { guild* g = dpp::find_guild(guild_id); if (g == nullptr) @@ -401,11 +401,8 @@ std::map channel::get_members() { guild* g = dpp::find_guild(guild_id); if (g) { for (auto m = g->members.begin(); m != g->members.end(); ++m) { - user* u = dpp::find_user(m->second.user_id); - if (u) { - if (get_user_permissions(u) & p_view_channel) { - rv[m->second.user_id] = &(m->second); - } + if (get_member_overwrites(&m->second) & p_view_channel) { + rv[m->second.user_id] = &(m->second); } } } From a7fc514e89cba79ec82bbfbb678ce37b706e4bd8 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sun, 19 Jun 2022 22:04:49 +0200 Subject: [PATCH 095/136] check for nullptr --- src/dpp/channel.cpp | 3 +++ src/dpp/guild.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 5bf6cdece3..dc9161d1d5 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -389,6 +389,9 @@ permission channel::get_user_permissions(const user* member) const permission channel::get_member_overwrites(const guild_member* member) const { + if (member == nullptr) + return 0; + guild* g = dpp::find_guild(guild_id); if (g == nullptr) return 0; diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index c8b3a07a5b..e45f287d39 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -566,6 +566,9 @@ permission guild::base_permissions(const user* member) const } permission guild::base_permissions(const guild_member *member) const { + if (member == nullptr) + return 0; + if (owner_id == member->user_id) return ~0; @@ -629,6 +632,9 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u } permission guild::permission_overwrites(const guild_member *member, const channel* channel) const { + if (member == nullptr || channel == nullptr) + return 0; + permission base_permissions = this->base_permissions(member); if (base_permissions & p_administrator) From 61e0d45dde57ab8827ab15a4e90f0b69b4aec2a5 Mon Sep 17 00:00:00 2001 From: Phil B Date: Mon, 20 Jun 2022 17:44:35 +0200 Subject: [PATCH 096/136] update --- include/dpp/channel.h | 12 +++++------- include/dpp/cluster.h | 2 +- include/dpp/guild.h | 22 +++++++++++----------- src/dpp/channel.cpp | 6 +++--- src/dpp/guild.cpp | 22 ++++++++++++++-------- 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 324a8543ff..9f8b465d52 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -384,18 +384,16 @@ class DPP_EXPORT channel : public managed, public json_interface { std::string get_mention() const; /** - * @brief Get the user permissions for a user on this channel including channel overwrites + * @brief Get the user permissions for a member on this channel including channel overwrites * - * @param member The user to resolve the permissions for + * @param user The user to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. - * Note that if the user is not on the channel or the guild is - * not in the cache, the function will always return 0. - * @deprecated + * @note If the guild member or the guild this channel belongs to is not in cache, the method will always return 0. */ - permission get_user_permissions(const class user* member) const; + permission get_user_permissions(const class user* user) const; /** - * @brief Get the user permissions for a user on this channel including channel overwrites + * @brief Get the user permissions for a member on this channel including channel overwrites * * @param member The member to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index dfb27b0594..95f19c5361 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -1399,7 +1399,7 @@ class DPP_EXPORT cluster { * @brief Called when packets are sent from the voice buffer. * The voice buffer contains packets that are already encoded with Opus and encrypted * with Sodium, and merged into packets by the repacketizer, which is done in the - * dpp::discord_voice_client::send_audio_opus method. You should use the buffer size properties + * dpp::discord_voice_client::send_audio method. You should use the buffer size properties * of dpp::voice_buffer_send_t to determine if you should fill the buffer with more * content. * diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 6c30397b51..7fb56585a3 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -543,11 +543,12 @@ class DPP_EXPORT guild : public managed, public json_interface { * @brief Get the base permissions for a member on this guild, * before channel overwrites are applied. * - * @param member member to get permissions for + * @param user User to get permissions for * @return permission permissions bitmask - * @deprecated + * @note The method will search for the guild member in the cache by the user id. + * If the guild member is not in cache, the method will always return 0. */ - permission base_permissions(const class user* member) const; + permission base_permissions(const class user* user) const; /** * @brief Get the base permissions for a member on this guild, @@ -559,21 +560,20 @@ class DPP_EXPORT guild : public managed, public json_interface { permission base_permissions(const guild_member* member) const; /** - * @brief Get the permission overwrites for a member - * merged into a bitmask. + * @brief Get the permission overwrites for a member in a channel. * * @param base_permissions base permissions before overwrites, * from channel::base_permissions - * @param member Member to resolve the permissions for - * @param channel Channel to fetch permissions against + * @param user User to resolve the permissions for + * @param channel Channel to get permission overwrites for * @return permission Merged permissions bitmask of overwrites. - * @deprecated + * @note The method will search for the guild member in the cache by the user id. + * If the guild member is not in cache, the method will always return 0. */ - permission permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const; + permission permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const; /** - * @brief Get the permission overwrites for a member - * merged into a bitmask. + * @brief Get the permission overwrites for a member in a channel. * * @param member Member to resolve the permissions for * @param channel Channel to get permission overwrites for diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index dc9161d1d5..dcfe53e2aa 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -375,16 +375,16 @@ std::string channel::build_json(bool with_id) const { return j.dump(); } -permission channel::get_user_permissions(const user* member) const +permission channel::get_user_permissions(const user* user) const { - if (member == nullptr) + if (user == nullptr) return 0; guild* g = dpp::find_guild(guild_id); if (g == nullptr) return 0; - return g->permission_overwrites(g->base_permissions(member), member, this); + return g->permission_overwrites(g->base_permissions(user), user, this); } permission channel::get_member_overwrites(const guild_member* member) const diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index e45f287d39..4ece782635 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -539,13 +539,16 @@ std::string guild_widget::build_json(bool with_id) const { } -permission guild::base_permissions(const user* member) const +permission guild::base_permissions(const user* user) const { - if (owner_id == member->id) - return ~0; + if (user == nullptr) + return 0; + + if (owner_id == user->id) + return ~0; // owner of the guild role* everyone = dpp::find_role(id); - auto mi = members.find(member->id); + auto mi = members.find(user->id); if (mi == members.end()) return 0; guild_member gm = mi->second; @@ -570,7 +573,7 @@ permission guild::base_permissions(const guild_member *member) const { return 0; if (owner_id == member->user_id) - return ~0; + return ~0; // owner of the guild role* everyone = dpp::find_role(id); @@ -589,8 +592,11 @@ permission guild::base_permissions(const guild_member *member) const { return permissions; } -permission guild::permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const +permission guild::permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const { + if (user == nullptr || channel == nullptr) + return 0; + if (base_permissions & p_administrator) return ~0; @@ -603,7 +609,7 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u } } - auto mi = members.find(member->id); + auto mi = members.find(user->id); if (mi == members.end()) return 0; guild_member gm = mi->second; @@ -617,7 +623,7 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u continue; for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { - if ((rid == it->id && it->type == ot_role) || (member->id == it->id && it->type == ot_member)) { + if ((rid == it->id && it->type == ot_role) || (user->id == it->id && it->type == ot_member)) { allow |= it->allow; deny |= it->deny; break; From c52f069b5a9a977621e2d7a953d7cdf8c0185c32 Mon Sep 17 00:00:00 2001 From: Phil B Date: Mon, 20 Jun 2022 22:51:51 +0200 Subject: [PATCH 097/136] update --- include/dpp/channel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 9f8b465d52..d920c5b58d 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -399,7 +398,7 @@ class DPP_EXPORT channel : public managed, public json_interface { * @return permission Permissions bitmask made of bits in dpp::permissions. * @note If the guild this channel belongs to is not in the cache, the function will always return 0. */ - permission get_member_overwrites(const guild_member* member) const; + permission get_member_overwrites(const class guild_member* member) const; /** * @brief Return a map of members on the channel, built from the guild's @@ -407,6 +406,7 @@ class DPP_EXPORT channel : public managed, public json_interface { * Does not return reliable information for voice channels, use * dpp::channel::get_voice_members() instead for this. * @return A map of guild members keyed by user id. + * @note If the guild this channel belongs to is not in the cache, the function will always return 0. */ std::map get_members(); From b16244b0c8c6635d22fd0d152386f904a5388376 Mon Sep 17 00:00:00 2001 From: yuto0214w Date: Tue, 21 Jun 2022 23:16:09 +0900 Subject: [PATCH 098/136] Support webhook avatar for execute_webhook --- src/dpp/cluster/webhook.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 917d421c04..c6b6c528dd 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -78,12 +78,12 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"thread_id", thread_id}, }); std::string body; - if (!thread_name.empty()) { // only use json::parse if thread_name is set - json j = json::parse(m.build_json(false)); - j["thread_name"] = thread_name; - body = j.dump(); - } - this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, !body.empty() ? body : m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { + json j = json::parse(m.build_json(false)); + if (!thread_name.empty()) j["thread_name"] = thread_name; + if (!wh.name.empty()) j["username"] = wh.name; + if (!wh.avatar.empty()) j["avatar_url"] = wh.avatar; + body = j.dump(); + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token : token) + parameters, m_post, body, [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } From b4ace2023677f90f7cad5955d93d796fcb29f5e2 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 21 Jun 2022 18:50:39 +0100 Subject: [PATCH 099/136] start of automod stuff --- include/dpp/intents.h | 4 +++- src/dpp/cluster/webhook.cpp | 14 ++++++++++++-- src/dpp/webhook.cpp | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/dpp/intents.h b/include/dpp/intents.h index 68e38f379f..c8bcf182c0 100644 --- a/include/dpp/intents.h +++ b/include/dpp/intents.h @@ -64,8 +64,10 @@ enum intents { i_message_content = (1 << 15), /// Scheduled events i_guild_scheduled_events = (1 << 16), + /// Auto moderation configuration + i_auto_moderation_configuration = (1 << 20), /// Default D++ intents (all non-privileged intents) - i_default_intents = dpp::i_guilds | dpp::i_guild_bans | dpp::i_guild_emojis | dpp::i_guild_integrations | dpp::i_guild_webhooks | dpp::i_guild_invites | dpp::i_guild_voice_states | dpp::i_guild_messages | dpp::i_guild_message_reactions | dpp::i_guild_message_typing | dpp::i_direct_messages | dpp::i_direct_message_typing | dpp::i_direct_message_reactions | dpp::i_guild_scheduled_events, + i_default_intents = dpp::i_guilds | dpp::i_guild_bans | dpp::i_guild_emojis | dpp::i_guild_integrations | dpp::i_guild_webhooks | dpp::i_guild_invites | dpp::i_guild_voice_states | dpp::i_guild_messages | dpp::i_guild_message_reactions | dpp::i_guild_message_typing | dpp::i_direct_messages | dpp::i_direct_message_typing | dpp::i_direct_message_reactions | dpp::i_guild_scheduled_events | dpp::i_auto_moderation_configuration, /// Privileged intents requiring ID i_privileged_intents = dpp::i_guild_members | dpp::i_guild_presences | dpp::i_message_content, /// Every single intent diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 917d421c04..8797961aef 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -78,9 +78,19 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"thread_id", thread_id}, }); std::string body; - if (!thread_name.empty()) { // only use json::parse if thread_name is set + if (!thread_name.empty() || wh.image_data || wh.avatar || !wh.name.empty()) { // only use json::parse if thread_name is set json j = json::parse(m.build_json(false)); - j["thread_name"] = thread_name; + if (!thread_name.empty()) { + j["thread_name"] = thread_name; + } + if (wh.avatar) { + j["avatar_url"] = wh.avatar; + } else if (wh.image_data) { + j["avatar_url"] = *image_data; + } + if (!wh.name.empty()) { + j["username"] = wh.name; + } body = j.dump(); } this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + parameters, m_post, !body.empty() ? body : m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { diff --git a/src/dpp/webhook.cpp b/src/dpp/webhook.cpp index a5e5e53749..14ec753846 100644 --- a/src/dpp/webhook.cpp +++ b/src/dpp/webhook.cpp @@ -66,7 +66,7 @@ webhook& webhook::fill_from_json(nlohmann::json* j) { user_id = snowflake_not_null(&user, "id"); } name = string_not_null(j, "name"); - avatar = string_not_null(j, "name"); + avatar = string_not_null(j, "avatar"); token = string_not_null(j, "token"); application_id = snowflake_not_null(j, "application_id"); From 1b1684967aa6593f8225afe2dece8e40eaf030c6 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 21 Jun 2022 18:50:53 +0100 Subject: [PATCH 100/136] start of automod stuff --- include/dpp/automod.h | 142 ++++++++++++++++++++++++++++++++++++++++++ src/dpp/automod.cpp | 98 +++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 include/dpp/automod.h create mode 100644 src/dpp/automod.cpp diff --git a/include/dpp/automod.h b/include/dpp/automod.h new file mode 100644 index 0000000000..4001c43276 --- /dev/null +++ b/include/dpp/automod.h @@ -0,0 +1,142 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +enum automod_preset_type : uint8_t { + amod_preset_profanity = 1, + amod_preset_sexual_content = 2, + amod_preset_slurs = 3, +}; + +enum automod_action_type : uint8_t { + amod_action_block_message = 1, + amod_action_send_alert = 2, + amod_action_timeout = 3, +}; + +enum automod_event_type : uint8_t { + /// Trigger on message send + amod_message_send = 1, +}; + +enum automod_trigger_type : uint8_t { + amod_type_keyword = 1, + amod_type_harmful_link = 2, + amod_type_spam = 3, + amod_type_keyword_preset = 4, +}; + +struct DPP_EXPORT automod_metadata : public json_interface { + std::vector keyword_filter; + std::vector keyword_preset_types; + + virtual ~automod_metadata(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_metadata& Reference to self + */ + automod_metadata& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; + +}; + +struct DPP_EXPORT automod_action : public json_interface { + automod_action_type type; + snowflake channel_id; + int32_t duration_seconds; + + automod_action(); + + virtual ~automod_action(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_action& Reference to self + */ + automod_action& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; +}; + +class DPP_EXPORT automod_rule : public managed, public json_interface { +public: + snowflake id; //!< the id of this rule + snowflake guild_id; //!< the guild which this rule belongs to + std::string name; //!< the rule name + snowflake creator_id; //!< the user which first created this rule + automod_event_type event_type; //!< the rule event type + automod_trigger_type trigger_type; //!< the rule trigger type + automod_metadata trigger_metadata;//!< the rule trigger metadata + std::vector actions; //!< the actions which will execute when the rule is triggered + bool enabled; //!< whether the rule is enabled + std::vector exempt_roles; //!< the role ids that should not be affected by the rule (Maximum of 20) + std::vector exempt_channels;//!< the channel ids that should not be affected by the rule (Maximum of 50) + + automod_rule(); + + virtual ~automod_rule(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_rule& Reference to self + */ + automod_rule& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** A group of automod rules. + */ +typedef std::unordered_map automod_rule_map; + +}; diff --git a/src/dpp/automod.cpp b/src/dpp/automod.cpp new file mode 100644 index 0000000000..92f5842440 --- /dev/null +++ b/src/dpp/automod.cpp @@ -0,0 +1,98 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#include +#include +#include + +namespace dpp { + +using json = nlohmann::json; + +automod_action::automod_action() : channel_id(0), duration_seconds(0) +{ +} + +automod_action::~automod_action() = default; + +automod_action& automod_action::fill_from_json(nlohmann::json* j) { + type = (automod_action_type)uint8_not_null(j, "type"); + switch (type) { + case amod_action_send_alert: + channel_id = snowflake_not_null(&((*j)["metadata"]), "channel_id"); + break; + case amod_action_timeout: + duration_seconds = int32_not_null(&((*j)["metadata"]), "duration_seconds"); + break; + } + if (j->contains("user")) { + json & user = (*j)["user"]; + user_id = snowflake_not_null(&user, "id"); + } + return *this; +} + +std::string automod_action::build_json(bool with_id) const { + json j({ + { "type", type } + }); + switch (type) { + case amod_action_send_alert: + if (channel_id) { + j["metadata"] = json::object(); + j["metadata"]["channel_id"] = std::to_string(channel_id); + } + break; + case amod_action_timeout: + if (duration_seconds) { + j["metadata"] = json::object(); + j["metadata"]["duration_seconds"] = duration_seconds; + } + break; + } + return j.dump(); +} + +automod_metadata::~automod_metadata() = default; + +automod_metadata& automod_metadata::fill_from_json(nlohmann::json* j) { + return *this; +} + +std::string automod_metadata::build_json(bool with_id) const { + return "{}"; +} + +automod_rule::automod_rule() : managed(), guild_id(0), creator_id(0), event_type(amod_message_send), trigger_type(amod_type_keyword), enabled(true) +{ +} + +automod_action::~automod_action() = default; + +automod_action& ban::fill_from_json(nlohmann::json* j) { + return *this; +} + +std::string automod_action::build_json(bool with_id) const { + return "{}"; +} + +}; + From 4bf5e7a13ea98f938c0b7053b2aa4a6004d2dc2a Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 21 Jun 2022 19:10:31 +0100 Subject: [PATCH 101/136] compile errors --- include/dpp/automod.h | 4 ++-- src/dpp/automod.cpp | 26 ++++++++++++++++---------- src/dpp/cluster/webhook.cpp | 6 +++--- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/dpp/automod.h b/include/dpp/automod.h index 4001c43276..119dc5733c 100644 --- a/include/dpp/automod.h +++ b/include/dpp/automod.h @@ -54,8 +54,8 @@ enum automod_trigger_type : uint8_t { }; struct DPP_EXPORT automod_metadata : public json_interface { - std::vector keyword_filter; - std::vector keyword_preset_types; + std::vector keywords; + std::vector presets; virtual ~automod_metadata(); diff --git a/src/dpp/automod.cpp b/src/dpp/automod.cpp index 92f5842440..c3b3a01948 100644 --- a/src/dpp/automod.cpp +++ b/src/dpp/automod.cpp @@ -33,7 +33,7 @@ automod_action::automod_action() : channel_id(0), duration_seconds(0) automod_action::~automod_action() = default; automod_action& automod_action::fill_from_json(nlohmann::json* j) { - type = (automod_action_type)uint8_not_null(j, "type"); + type = (automod_action_type)int8_not_null(j, "type"); switch (type) { case amod_action_send_alert: channel_id = snowflake_not_null(&((*j)["metadata"]), "channel_id"); @@ -41,11 +41,9 @@ automod_action& automod_action::fill_from_json(nlohmann::json* j) { case amod_action_timeout: duration_seconds = int32_not_null(&((*j)["metadata"]), "duration_seconds"); break; - } - if (j->contains("user")) { - json & user = (*j)["user"]; - user_id = snowflake_not_null(&user, "id"); - } + default: + break; + }; return *this; } @@ -66,13 +64,21 @@ std::string automod_action::build_json(bool with_id) const { j["metadata"]["duration_seconds"] = duration_seconds; } break; - } + default: + break; + }; return j.dump(); } automod_metadata::~automod_metadata() = default; automod_metadata& automod_metadata::fill_from_json(nlohmann::json* j) { + for (auto k : (*j)["keyword_filter"]) { + keywords.push_back(k); + } + for (auto k : (*j)["presets"]) { + presets.push_back((automod_preset_type)k.get()); + } return *this; } @@ -84,13 +90,13 @@ automod_rule::automod_rule() : managed(), guild_id(0), creator_id(0), event_type { } -automod_action::~automod_action() = default; +automod_rule::~automod_rule() = default; -automod_action& ban::fill_from_json(nlohmann::json* j) { +automod_rule& automod_rule::fill_from_json(nlohmann::json* j) { return *this; } -std::string automod_action::build_json(bool with_id) const { +std::string automod_rule::build_json(bool with_id) const { return "{}"; } diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 8797961aef..222990a798 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -78,15 +78,15 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, {"thread_id", thread_id}, }); std::string body; - if (!thread_name.empty() || wh.image_data || wh.avatar || !wh.name.empty()) { // only use json::parse if thread_name is set + if (!thread_name.empty() || wh.image_data || !wh.avatar.empty() || !wh.name.empty()) { // only use json::parse if thread_name is set json j = json::parse(m.build_json(false)); if (!thread_name.empty()) { j["thread_name"] = thread_name; } - if (wh.avatar) { + if (!wh.avatar.empty()) { j["avatar_url"] = wh.avatar; } else if (wh.image_data) { - j["avatar_url"] = *image_data; + j["avatar_url"] = *wh.image_data; } if (!wh.name.empty()) { j["username"] = wh.name; From 974b1b13fd9e3b0400ed961cd9a318e3903939f0 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 21 Jun 2022 20:16:37 +0100 Subject: [PATCH 102/136] feat: serialisation functions for automod structs --- src/dpp/automod.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/dpp/automod.cpp b/src/dpp/automod.cpp index c3b3a01948..5f2e330b3f 100644 --- a/src/dpp/automod.cpp +++ b/src/dpp/automod.cpp @@ -83,7 +83,17 @@ automod_metadata& automod_metadata::fill_from_json(nlohmann::json* j) { } std::string automod_metadata::build_json(bool with_id) const { - return "{}"; + json j; + j["keyword_filter"] = json::array(); + j["presets"] = json::array(); + for (auto v : keywords) { + j["keyword_filter"].push_back(v); + } + for (auto v : presets) { + j["presets"].push_back((uint32_t)v); + } + return j.dump(); + } automod_rule::automod_rule() : managed(), guild_id(0), creator_id(0), event_type(amod_message_send), trigger_type(amod_type_keyword), enabled(true) @@ -93,11 +103,65 @@ automod_rule::automod_rule() : managed(), guild_id(0), creator_id(0), event_type automod_rule::~automod_rule() = default; automod_rule& automod_rule::fill_from_json(nlohmann::json* j) { + id = snowflake_not_null(j, "id"); + guild_id = snowflake_not_null(j, "guild_id"); + name = string_not_null(j, "name"); + creator_id = snowflake_not_null(j, "creator_id"); + event_type = (automod_event_type)int8_not_null(j, "event_type"); + trigger_type = (automod_trigger_type)int8_not_null(j, "trigger_type"); + if (j->at("trigger_metadata")) { + trigger_metadata.fill_from_json(&((*j)["trigger_metadata"])); + } + enabled = bool_not_null(j, "enabled"); + exempt_roles.clear(); + exempt_channels.clear(); + for (auto k : (*j)["automod_actions"]) { + actions.push_back(automod_action().fill_from_json(&k)); + } + for (auto k : (*j)["exempt_roles"]) { + exempt_roles.push_back(stoull(k.get())); + } + for (auto k : (*j)["exempt_channels"]) { + exempt_channels.push_back(stoull(k.get())); + } return *this; } std::string automod_rule::build_json(bool with_id) const { - return "{}"; + json j; + if (with_id && id) { + j["id"] = std::to_string(id); + } + if (guild_id) { + j["guild_id"] = std::to_string(guild_id); + } + j["name"] = name; + j["enabled"] = enabled; + j["event_type"] = event_type; + j["trigger_type"] = trigger_type; + j["trigger_metadata"] = json::parse(trigger_metadata.build_json()); + if (actions.size()) { + j["actions"] = json::array(); + json& act = j["actions"]; + for (auto v : actions) { + act.push_back(json::parse(v.build_json())); + } + } + if (exempt_roles.size()) { + j["exempt_roles"] = json::array(); + json& roles = j["exempt_roles"]; + for (auto v : exempt_roles) { + roles.push_back(std::to_string(v)); + } + } + if (exempt_channels.size()) { + j["exempt_channels"] = json::array(); + json& channels = j["exempt_channels"]; + for (auto v : exempt_channels) { + channels.push_back(std::to_string(v)); + } + } + return j.dump(); } }; From b652a32d1f20aa2383e600504c0df953e6ab0237 Mon Sep 17 00:00:00 2001 From: Phil B Date: Tue, 21 Jun 2022 21:41:54 +0200 Subject: [PATCH 103/136] fixed nullptr bug in guild::base_permissions & refactor --- include/dpp/channel.h | 3 +-- src/dpp/channel.cpp | 8 +++----- src/dpp/guild.cpp | 16 ++++++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index d920c5b58d..b3db86cbb0 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -387,7 +387,7 @@ class DPP_EXPORT channel : public managed, public json_interface { * * @param user The user to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. - * @note If the guild member or the guild this channel belongs to is not in cache, the method will always return 0. + * @note If the guild member is not in cache, the method will always return 0. */ permission get_user_permissions(const class user* user) const; @@ -396,7 +396,6 @@ class DPP_EXPORT channel : public managed, public json_interface { * * @param member The member to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. - * @note If the guild this channel belongs to is not in the cache, the function will always return 0. */ permission get_member_overwrites(const class guild_member* member) const; diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index dcfe53e2aa..8fac3eba06 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -375,8 +375,7 @@ std::string channel::build_json(bool with_id) const { return j.dump(); } -permission channel::get_user_permissions(const user* user) const -{ +permission channel::get_user_permissions(const user* user) const { if (user == nullptr) return 0; @@ -387,8 +386,7 @@ permission channel::get_user_permissions(const user* user) const return g->permission_overwrites(g->base_permissions(user), user, this); } -permission channel::get_member_overwrites(const guild_member* member) const -{ +permission channel::get_member_overwrites(const guild_member* member) const { if (member == nullptr) return 0; @@ -404,7 +402,7 @@ std::map channel::get_members() { guild* g = dpp::find_guild(guild_id); if (g) { for (auto m = g->members.begin(); m != g->members.end(); ++m) { - if (get_member_overwrites(&m->second) & p_view_channel) { + if (g->permission_overwrites(&m->second, this) & p_view_channel) { rv[m->second.user_id] = &(m->second); } } diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 4ece782635..8175f3902c 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -539,15 +539,17 @@ std::string guild_widget::build_json(bool with_id) const { } -permission guild::base_permissions(const user* user) const -{ +permission guild::base_permissions(const user* user) const { if (user == nullptr) return 0; if (owner_id == user->id) - return ~0; // owner of the guild + return ~0; // return all permissions if it's the owner of the guild role* everyone = dpp::find_role(id); + if (everyone == nullptr) + return 0; + auto mi = members.find(user->id); if (mi == members.end()) return 0; @@ -573,9 +575,11 @@ permission guild::base_permissions(const guild_member *member) const { return 0; if (owner_id == member->user_id) - return ~0; // owner of the guild + return ~0; // return all permissions if it's the owner of the guild role* everyone = dpp::find_role(id); + if (everyone == nullptr) + return 0; permission permissions = everyone->permissions; @@ -592,8 +596,7 @@ permission guild::base_permissions(const guild_member *member) const { return permissions; } -permission guild::permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const -{ +permission guild::permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const { if (user == nullptr || channel == nullptr) return 0; @@ -613,6 +616,7 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u if (mi == members.end()) return 0; guild_member gm = mi->second; + uint64_t allow = 0; uint64_t deny = 0; From 2cf278f0536e3cf88e77dfa39097e03c08d0f153 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 21 Jun 2022 21:23:01 +0100 Subject: [PATCH 104/136] feat: auto moderation events --- .cspell.json | 3 +- include/dpp/cluster.h | 32 +++++++++++++++ include/dpp/dispatcher.h | 53 +++++++++++++++++++++++++ include/dpp/event.h | 6 +++ include/dpp/intents.h | 9 ++++- src/dpp/discordevents.cpp | 4 ++ src/dpp/dispatcher.cpp | 5 ++- src/dpp/events/automod_rule_create.cpp | 48 ++++++++++++++++++++++ src/dpp/events/automod_rule_delete.cpp | 48 ++++++++++++++++++++++ src/dpp/events/automod_rule_execute.cpp | 48 ++++++++++++++++++++++ src/dpp/events/automod_rule_update.cpp | 48 ++++++++++++++++++++++ 11 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 src/dpp/events/automod_rule_create.cpp create mode 100644 src/dpp/events/automod_rule_delete.cpp create mode 100644 src/dpp/events/automod_rule_execute.cpp create mode 100644 src/dpp/events/automod_rule_update.cpp diff --git a/.cspell.json b/.cspell.json index 02e7209f48..dd1e99b72b 100644 --- a/.cspell.json +++ b/.cspell.json @@ -76,7 +76,8 @@ "followup", "gifv", "ctls", - "ctest" + "ctest", + "automod" ], "flagWords": [ "hte" diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 7f7f94e63d..dadd3062f1 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -1151,7 +1151,39 @@ class DPP_EXPORT cluster { */ event_router_t on_webhooks_update; + /** + * @brief Called when a new automod rule is created. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_create_t&, and returns void. + */ + event_router_t on_automod_rule_create; + + + /** + * @brief Called when an automod rule is updated. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_update_t&, and returns void. + */ + event_router_t on_automod_rule_update; + /** + * @brief Called when an automod rule is deleted. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_delete_t&, and returns void. + */ + event_router_t on_automod_rule_delete; + + /** + * @brief Called when an automod rule is triggered/executed. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_execute_t&, and returns void. + */ + event_router_t on_automod_rule_execute; + /** * @brief Called when a new member joins a guild. * diff --git a/include/dpp/dispatcher.h b/include/dpp/dispatcher.h index b366c62d37..9580c832cd 100644 --- a/include/dpp/dispatcher.h +++ b/include/dpp/dispatcher.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -231,6 +232,58 @@ struct DPP_EXPORT guild_scheduled_event_delete_t : public event_dispatch_t { scheduled_event deleted; }; +/** @brief Create automod rule */ +struct DPP_EXPORT automod_rule_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_create_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + automod_rule created; +}; + +/** @brief Update automod rule */ +struct DPP_EXPORT automod_rule_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_update_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + automod_rule updated; +}; + +/** @brief Delete automod rule */ +struct DPP_EXPORT automod_rule_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + automod_rule deleted; +}; + +/** @brief Execute/trigger automod rule */ +struct DPP_EXPORT automod_rule_execute_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_execute_t(class discord_client* client, const std::string& raw); +}; + /** @brief Create stage instance */ diff --git a/include/dpp/event.h b/include/dpp/event.h index cd945f5fbd..b82f982eae 100644 --- a/include/dpp/event.h +++ b/include/dpp/event.h @@ -142,4 +142,10 @@ event_decl(guild_scheduled_event_delete,GUILD_SCHEDULED_EVENT_DELETE); event_decl(guild_scheduled_event_user_add,GUILD_SCHEDULED_EVENT_USER_ADD); event_decl(guild_scheduled_event_user_remove,GUILD_SCHEDULED_EVENT_USER_REMOVE); +/* Auto moderation */ +event_decl(automod_rule_create, AUTO_MODERATION_RULE_CREATE); +event_decl(automod_rule_update, AUTO_MODERATION_RULE_UPDATE); +event_decl(automod_rule_delete, AUTO_MODERATION_RULE_DELETE); +event_decl(automod_rule_execute, AUTO_MODERATION_ACTION_EXECUTION); + }}; diff --git a/include/dpp/intents.h b/include/dpp/intents.h index c8bcf182c0..bc95aa15b4 100644 --- a/include/dpp/intents.h +++ b/include/dpp/intents.h @@ -66,8 +66,15 @@ enum intents { i_guild_scheduled_events = (1 << 16), /// Auto moderation configuration i_auto_moderation_configuration = (1 << 20), + /// Auto moderation configuration + i_auto_moderation_execution = (1 << 21), /// Default D++ intents (all non-privileged intents) - i_default_intents = dpp::i_guilds | dpp::i_guild_bans | dpp::i_guild_emojis | dpp::i_guild_integrations | dpp::i_guild_webhooks | dpp::i_guild_invites | dpp::i_guild_voice_states | dpp::i_guild_messages | dpp::i_guild_message_reactions | dpp::i_guild_message_typing | dpp::i_direct_messages | dpp::i_direct_message_typing | dpp::i_direct_message_reactions | dpp::i_guild_scheduled_events | dpp::i_auto_moderation_configuration, + i_default_intents = dpp::i_guilds | dpp::i_guild_bans | dpp::i_guild_emojis | dpp::i_guild_integrations | + dpp::i_guild_webhooks | dpp::i_guild_invites | dpp::i_guild_voice_states | + dpp::i_guild_messages | dpp::i_guild_message_reactions | dpp::i_guild_message_typing | + dpp::i_direct_messages | dpp::i_direct_message_typing | dpp::i_direct_message_reactions | + dpp::i_guild_scheduled_events | dpp::i_auto_moderation_configuration | + dpp::i_auto_moderation_execution, /// Privileged intents requiring ID i_privileged_intents = dpp::i_guild_members | dpp::i_guild_presences | dpp::i_message_content, /// Every single intent diff --git a/src/dpp/discordevents.cpp b/src/dpp/discordevents.cpp index 92bd66dfc3..aaa8a915c0 100644 --- a/src/dpp/discordevents.cpp +++ b/src/dpp/discordevents.cpp @@ -342,6 +342,10 @@ const std::map eventmap = { { "GUILD_SCHEDULED_EVENT_DELETE", new dpp::events::guild_scheduled_event_delete() }, { "GUILD_SCHEDULED_EVENT_USER_ADD", new dpp::events::guild_scheduled_event_user_add() }, { "GUILD_SCHEDULED_EVENT_USER_REMOVE", new dpp::events::guild_scheduled_event_user_remove() }, + { "AUTO_MODERATION_RULE_CREATE", new dpp::events::automod_rule_create() }, + { "AUTO_MODERATION_RULE_UPDATE", new dpp::events::automod_rule_update() }, + { "AUTO_MODERATION_RULE_DELETE", new dpp::events::automod_rule_delete() }, + { "AUTO_MODERATION_ACTION_EXECUTION", new dpp::events::automod_rule_execute() }, }; void discord_client::handle_event(const std::string &event, json &j, const std::string &raw) diff --git a/src/dpp/dispatcher.cpp b/src/dpp/dispatcher.cpp index 533bca4684..fcacd42677 100644 --- a/src/dpp/dispatcher.cpp +++ b/src/dpp/dispatcher.cpp @@ -336,5 +336,8 @@ event_ctor(guild_scheduled_event_update_t, event_dispatch_t); event_ctor(guild_scheduled_event_delete_t, event_dispatch_t); event_ctor(guild_scheduled_event_user_add_t, event_dispatch_t); event_ctor(guild_scheduled_event_user_remove_t, event_dispatch_t); - +event_ctor(automod_rule_create_t, event_dispatch_t); +event_ctor(automod_rule_delete_t, event_dispatch_t); +event_ctor(automod_rule_update_t, event_dispatch_t); +event_ctor(automod_rule_execute_t, event_dispatch_t); }; diff --git a/src/dpp/events/automod_rule_create.cpp b/src/dpp/events/automod_rule_create.cpp new file mode 100644 index 0000000000..4bb9a6140f --- /dev/null +++ b/src/dpp/events/automod_rule_create.cpp @@ -0,0 +1,48 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace dpp { namespace events { + +using namespace dpp; + +/** + * @brief Handle event + * + * @param client Websocket client (current shard) + * @param j JSON data for the event + * @param raw Raw JSON string + */ +void automod_rule_create::handle(discord_client* client, json &j, const std::string &raw) { + if (!client->creator->on_automod_rule_create.empty()) { + json& d = j["d"]; + automod_rule_create_t arc(client, raw); + arc.created = automod_rule().fill_from_json(&d); + client->creator->on_automod_rule_create.call(arc); + } +} + +}}; \ No newline at end of file diff --git a/src/dpp/events/automod_rule_delete.cpp b/src/dpp/events/automod_rule_delete.cpp new file mode 100644 index 0000000000..7308143e27 --- /dev/null +++ b/src/dpp/events/automod_rule_delete.cpp @@ -0,0 +1,48 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace dpp { namespace events { + +using namespace dpp; + +/** + * @brief Handle event + * + * @param client Websocket client (current shard) + * @param j JSON data for the event + * @param raw Raw JSON string + */ +void automod_rule_delete::handle(discord_client* client, json &j, const std::string &raw) { + if (!client->creator->on_automod_rule_create.empty()) { + json& d = j["d"]; + automod_rule_delete_t ard(client, raw); + ard.deleted = automod_rule().fill_from_json(&d); + client->creator->on_automod_rule_delete.call(ard); + } +} + +}}; \ No newline at end of file diff --git a/src/dpp/events/automod_rule_execute.cpp b/src/dpp/events/automod_rule_execute.cpp new file mode 100644 index 0000000000..c1c1c44914 --- /dev/null +++ b/src/dpp/events/automod_rule_execute.cpp @@ -0,0 +1,48 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace dpp { namespace events { + +using namespace dpp; + +/** + * @brief Handle event + * + * @param client Websocket client (current shard) + * @param j JSON data for the event + * @param raw Raw JSON string + */ +void automod_rule_execute::handle(discord_client* client, json &j, const std::string &raw) { + if (!client->creator->on_automod_rule_execute.empty()) { + json& d = j["d"]; + automod_rule_execute_t are(client, raw); + //arc.created = automod_rule().fill_from_json(&d); + client->creator->on_automod_rule_execute.call(are); + } +} + +}}; \ No newline at end of file diff --git a/src/dpp/events/automod_rule_update.cpp b/src/dpp/events/automod_rule_update.cpp new file mode 100644 index 0000000000..1b5fb9b73a --- /dev/null +++ b/src/dpp/events/automod_rule_update.cpp @@ -0,0 +1,48 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace dpp { namespace events { + +using namespace dpp; + +/** + * @brief Handle event + * + * @param client Websocket client (current shard) + * @param j JSON data for the event + * @param raw Raw JSON string + */ +void automod_rule_update::handle(discord_client* client, json &j, const std::string &raw) { + if (!client->creator->on_automod_rule_update.empty()) { + json& d = j["d"]; + automod_rule_update_t aru(client, raw); + aru.updated = automod_rule().fill_from_json(&d); + client->creator->on_automod_rule_update.call(aru); + } +} + +}}; \ No newline at end of file From 2b56a02e506d2a9415f49fb61d72dff5726f086e Mon Sep 17 00:00:00 2001 From: Phil B Date: Tue, 21 Jun 2022 22:47:52 +0200 Subject: [PATCH 105/136] update --- src/dpp/guild.cpp | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 8175f3902c..6b3c1b11c9 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -543,31 +543,12 @@ permission guild::base_permissions(const user* user) const { if (user == nullptr) return 0; - if (owner_id == user->id) - return ~0; // return all permissions if it's the owner of the guild - - role* everyone = dpp::find_role(id); - if (everyone == nullptr) - return 0; - auto mi = members.find(user->id); if (mi == members.end()) return 0; guild_member gm = mi->second; - permission permissions = everyone->permissions; - - for (auto& rid : gm.roles) { - role* r = dpp::find_role(rid); - if (r) { - permissions |= r->permissions; - } - } - - if (permissions & p_administrator) - return ~0; - - return permissions; + return base_permissions(&gm); } permission guild::base_permissions(const guild_member *member) const { From 8c45bec851765ceadb17f44af3da49dcd6668a1c Mon Sep 17 00:00:00 2001 From: Phil B Date: Tue, 21 Jun 2022 23:54:18 +0200 Subject: [PATCH 106/136] fixed guild::permission_overwrites didn't compute the correct permissions --- include/dpp/channel.h | 7 +++--- include/dpp/guild.h | 12 +++++----- src/dpp/guild.cpp | 52 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index b3db86cbb0..b5ba388fdf 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -383,16 +383,17 @@ class DPP_EXPORT channel : public managed, public json_interface { std::string get_mention() const; /** - * @brief Get the user permissions for a member on this channel including channel overwrites + * @brief Get the user permissions for a member on this channel, including channel overwrites. * * @param user The user to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. - * @note If the guild member is not in cache, the method will always return 0. + * @note The method will search for the guild member in the cache by the user id. + * If the guild member is not in cache, the method will always return 0. */ permission get_user_permissions(const class user* user) const; /** - * @brief Get the user permissions for a member on this channel including channel overwrites + * @brief Get the user permissions for a member on this channel, including channel overwrites. * * @param member The member to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 7fb56585a3..1baf71239c 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -540,7 +540,7 @@ class DPP_EXPORT guild : public managed, public json_interface { std::string build_json(bool with_id = false) const; /** - * @brief Get the base permissions for a member on this guild, + * @brief Compute the base permissions for a member on this guild, * before channel overwrites are applied. * * @param user User to get permissions for @@ -551,7 +551,7 @@ class DPP_EXPORT guild : public managed, public json_interface { permission base_permissions(const class user* user) const; /** - * @brief Get the base permissions for a member on this guild, + * @brief Compute the base permissions for a member on this guild, * before channel overwrites are applied. * * @param member member to get permissions for @@ -560,12 +560,12 @@ class DPP_EXPORT guild : public managed, public json_interface { permission base_permissions(const guild_member* member) const; /** - * @brief Get the permission overwrites for a member in a channel. + * @brief Compute the permission overwrites for a member in a channel, including base permissions. * * @param base_permissions base permissions before overwrites, * from channel::base_permissions * @param user User to resolve the permissions for - * @param channel Channel to get permission overwrites for + * @param channel Channel to compute permission overwrites for * @return permission Merged permissions bitmask of overwrites. * @note The method will search for the guild member in the cache by the user id. * If the guild member is not in cache, the method will always return 0. @@ -573,10 +573,10 @@ class DPP_EXPORT guild : public managed, public json_interface { permission permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const; /** - * @brief Get the permission overwrites for a member in a channel. + * @brief Compute the permission overwrites for a member in a channel, including base permissions. * * @param member Member to resolve the permissions for - * @param channel Channel to get permission overwrites for + * @param channel Channel to compute permission overwrites for * @return permission Merged permissions bitmask of overwrites. */ permission permission_overwrites(const guild_member* member, const channel* channel) const; diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index 6b3c1b11c9..b220008ef8 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -555,6 +555,8 @@ permission guild::base_permissions(const guild_member *member) const { if (member == nullptr) return 0; + /* this method is written with the help of discord's pseudocode available here https://discord.com/developers/docs/topics/permissions#permission-overwrites */ + if (owner_id == member->user_id) return ~0; // return all permissions if it's the owner of the guild @@ -581,12 +583,17 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u if (user == nullptr || channel == nullptr) return 0; + /* this method is written with the help of discord's pseudocode available here https://discord.com/developers/docs/topics/permissions#permission-overwrites */ + + // ADMINISTRATOR overrides any potential permission overwrites, so there is nothing to do here. if (base_permissions & p_administrator) return ~0; permission permissions = base_permissions; + + // find \@everyone role overwrite and apply it. for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { - if (it->id == id && it->type == ot_role) { + if (it->id == this->id && it->type == ot_role) { permissions &= ~it->deny; permissions |= it->allow; break; @@ -598,19 +605,20 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u return 0; guild_member gm = mi->second; + // Apply role specific overwrites. uint64_t allow = 0; uint64_t deny = 0; for (auto& rid : gm.roles) { - /* Skip \@everyone, calculated above */ - if (rid == id) + /* Skip \@everyone role to not break the hierarchy. It's calculated above */ + if (rid == this->id) continue; for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { - if ((rid == it->id && it->type == ot_role) || (user->id == it->id && it->type == ot_member)) { - allow |= it->allow; + if (rid == it->id && it->type == ot_role) { deny |= it->deny; + allow |= it->allow; break; } } @@ -619,6 +627,15 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u permissions &= ~deny; permissions |= allow; + // Apply member specific overwrite if exists. + for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { + if (gm.user_id == it->id && it->type == ot_member) { + permissions &= ~it->deny; + permissions |= it->allow; + break; + } + } + return permissions; } @@ -628,31 +645,37 @@ permission guild::permission_overwrites(const guild_member *member, const channe permission base_permissions = this->base_permissions(member); + /* this method is written with the help of discord's pseudocode available here https://discord.com/developers/docs/topics/permissions#permission-overwrites */ + + // ADMINISTRATOR overrides any potential permission overwrites, so there is nothing to do here. if (base_permissions & p_administrator) return ~0; permission permissions = base_permissions; + + // find \@everyone role overwrite and apply it. for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { - if (it->id == id && it->type == ot_role) { + if (it->id == this->id && it->type == ot_role) { permissions &= ~it->deny; permissions |= it->allow; break; } } + // Apply role specific overwrites. uint64_t allow = 0; uint64_t deny = 0; for (auto& rid : member->roles) { - /* Skip \@everyone, calculated above */ - if (rid == id) + /* Skip \@everyone role to not break the hierarchy. It's calculated above */ + if (rid == this->id) continue; for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { - if ((rid == it->id && it->type == ot_role) || (member->user_id == it->id && it->type == ot_member)) { - allow |= it->allow; + if (rid == it->id && it->type == ot_role) { deny |= it->deny; + allow |= it->allow; break; } } @@ -661,6 +684,15 @@ permission guild::permission_overwrites(const guild_member *member, const channe permissions &= ~deny; permissions |= allow; + // Apply member specific overwrite if exists. + for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { + if (member->user_id == it->id && it->type == ot_member) { + permissions &= ~it->deny; + permissions |= it->allow; + break; + } + } + return permissions; } From 08601ddda193d017901afaa25adfd7ef49c2b84a Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 22 Jun 2022 00:36:02 +0100 Subject: [PATCH 107/136] feat: automod_rule_execute --- include/dpp/dispatcher.h | 12 ++++++++++++ src/dpp/events/automod_rule_execute.cpp | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/dpp/dispatcher.h b/include/dpp/dispatcher.h index 9580c832cd..2d02fe8f85 100644 --- a/include/dpp/dispatcher.h +++ b/include/dpp/dispatcher.h @@ -282,6 +282,18 @@ struct DPP_EXPORT automod_rule_execute_t : public event_dispatch_t { * @param raw Raw event text as JSON */ automod_rule_execute_t(class discord_client* client, const std::string& raw); + + snowflake guild_id; //!< the id of the guild in which action was executed + automod_action action; //!< the action which was executed + snowflake rule_id; //!< the id of the rule which action belongs to + automod_trigger_type rule_trigger_type; //!< the trigger type of rule which was triggered + snowflake user_id; //!< the id of the user which generated the content which triggered the rule + snowflake channel_id; //!< Optional: the id of the channel in which user content was posted + snowflake message_id; //!< Optional: the id of any user message which content belongs to + snowflake alert_system_message_id; //!< Optional: the id of any system auto moderation messages posted as a result of this action + std::string content; //!< the user generated text content + std::string matched_keyword; //!< the word or phrase configured in the rule that triggered the rule (may be empty) + std::string matched_content; //!< the substring in content that triggered the rule (may be empty) }; diff --git a/src/dpp/events/automod_rule_execute.cpp b/src/dpp/events/automod_rule_execute.cpp index c1c1c44914..69a5d84fc6 100644 --- a/src/dpp/events/automod_rule_execute.cpp +++ b/src/dpp/events/automod_rule_execute.cpp @@ -40,7 +40,17 @@ void automod_rule_execute::handle(discord_client* client, json &j, const std::st if (!client->creator->on_automod_rule_execute.empty()) { json& d = j["d"]; automod_rule_execute_t are(client, raw); - //arc.created = automod_rule().fill_from_json(&d); + are.guild_id = snowflake_not_null(&d, "guild_id"); + are.action = dpp::automod_action().fill_from_json(&(d["action"])); + are.rule_id = snowflake_not_null(&d, "rule_id"); + are.rule_trigger_type = (automod_trigger_type)int8_not_null(&d, "rule_trigger_type"); + are.user_id = snowflake_not_null(&d, "user_id"); + are.channel_id = snowflake_not_null(&d, "channel_id"); + are.message_id = snowflake_not_null(&d, "message_id"); + are.alert_system_message_id = snowflake_not_null(&d, "alert_system_message_id"); + are.content = string_not_null(&d, "content"); + are.matched_keyword = string_not_null(&d, "matched_keyword"); + are.matched_content = string_not_null(&d, "matched_content"); client->creator->on_automod_rule_execute.call(are); } } From 6decf3010117b9b2c6c32315fc8552806f40ecb0 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 22 Jun 2022 01:29:02 +0100 Subject: [PATCH 108/136] feat: automod REST endpoints: cluster::automod_rules_get(), cluster::automod_rule_get(), cluster::automod_rule_create(), cluster::automod_rule_edit(), cluster::automod_rule_delete(), cluster::automod_rules_get_sync(), cluster::automod_rule_get_sync(), cluster::automod_rule_create_sync(), cluster::automod_rule_edit_sync(), cluster::automod_rule_delete_sync() --- include/dpp/cluster.h | 53 +++++++++++++++++++++++++- include/dpp/cluster_sync_calls.h | 64 ++++++++++++++++++++++++++++++++ src/dpp/cluster/automod.cpp | 46 +++++++++++++++++++++++ src/dpp/cluster_sync_calls.cpp | 20 ++++++++++ 4 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 src/dpp/cluster/automod.cpp diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index dadd3062f1..5f9f3422f6 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -186,7 +186,9 @@ typedef std::variant< scheduled_event, scheduled_event_map, event_member, - event_member_map + event_member_map, + automod_rule, + automod_rule_map > confirmable_t; /** @@ -3584,6 +3586,55 @@ class DPP_EXPORT cluster { */ void user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false, command_completion_event_t callback = utility::log_error()); + /** + * @brief Get all auto moderation rules for a guild + * + * @param guild_id Guild id of the auto moderation rule + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rules_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get a single auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Rule id to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_get(snowflake guild_id, snowflake rule_id, command_completion_event_t callback); + + /** + * @brief Create an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_create(snowflake guild_id, const automod_rule& r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to edit. The rule's id must be set. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_edit(snowflake guild_id, const automod_rule& r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Auto moderation rule id to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_delete(snowflake guild_id, snowflake rule_id, command_completion_event_t callback = utility::log_error()); + #include }; diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 940afd48cf..0ce26ef1b2 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -367,6 +367,70 @@ confirmation interaction_followup_edit_sync(const std::string &token, const mess */ message interaction_followup_get_sync(const std::string &token, snowflake message_id); +/** + * @brief Get all auto moderation rules for a guild + * + * @param guild_id Guild id of the auto moderation rule + * @return automod_rule_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule_map automod_rules_get_sync(snowflake guild_id); + +/** + * @brief Get a single auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Rule id to retrieve + * @return automod_rule returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule automod_rule_get_sync(snowflake guild_id, snowflake rule_id); + +/** + * @brief Create an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to create + * @return automod_rule returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule automod_rule_create_sync(snowflake guild_id, const automod_rule& r); + +/** + * @brief Edit an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to edit. The rule's id must be set. + * @return automod_rule returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule automod_rule_edit_sync(snowflake guild_id, const automod_rule& r); + +/** + * @brief Delete an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Auto moderation rule id to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation automod_rule_delete_sync(snowflake guild_id, snowflake rule_id); + /** * @brief Create a channel * diff --git a/src/dpp/cluster/automod.cpp b/src/dpp/cluster/automod.cpp new file mode 100644 index 0000000000..9cf7c627b3 --- /dev/null +++ b/src/dpp/cluster/automod.cpp @@ -0,0 +1,46 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#include +#include + +namespace dpp { + +void cluster::automod_rules_get(snowflake guild_id, command_completion_event_t callback) { + rest_request_list(this, API_PATH "/guilds", std::to_string(guild_id), "/auto-moderation/rules", m_get, "", callback); +} + +void cluster::automod_rule_get(snowflake guild_id, snowflake rule_id, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/auto-moderation/rules/" + std::to_string(rule_id), m_get, "", callback); +} + +void cluster::automod_rule_create(snowflake guild_id, const automod_rule& r, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/auto-moderation/rules", m_post, r.build_json(), callback); +} + +void cluster::automod_rule_edit(snowflake guild_id, const automod_rule& r, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/auto-moderation/rules/" + std::to_string(r.id), m_patch, r.build_json(true), callback); +} + +void cluster::automod_rule_delete(snowflake guild_id, snowflake rule_id, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "/auto-moderation/rules/" + std::to_string(rule_id), m_delete, "", callback); +} + +}; diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index 49a0df0eed..66c0659955 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -124,6 +124,26 @@ message cluster::interaction_followup_get_sync(const std::string &token, snowfla return dpp::sync(this, &cluster::interaction_followup_get, token, message_id); } +automod_rule_map cluster::automod_rules_get_sync(snowflake guild_id) { + return dpp::sync(this, &cluster::automod_rules_get, guild_id); +} + +automod_rule cluster::automod_rule_get_sync(snowflake guild_id, snowflake rule_id) { + return dpp::sync(this, &cluster::automod_rule_get, guild_id, rule_id); +} + +automod_rule cluster::automod_rule_create_sync(snowflake guild_id, const automod_rule& r) { + return dpp::sync(this, &cluster::automod_rule_create, guild_id, r); +} + +automod_rule cluster::automod_rule_edit_sync(snowflake guild_id, const automod_rule& r) { + return dpp::sync(this, &cluster::automod_rule_edit, guild_id, r); +} + +confirmation cluster::automod_rule_delete_sync(snowflake guild_id, snowflake rule_id) { + return dpp::sync(this, &cluster::automod_rule_delete, guild_id, rule_id); +} + channel cluster::channel_create_sync(const class channel &c) { return dpp::sync(this, &cluster::channel_create, c); } From 49a1cea6d8024f09e881d875ee701e5f22944b31 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 22 Jun 2022 01:52:56 +0100 Subject: [PATCH 109/136] fix: add to cspell --- .cspell.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index dd1e99b72b..a82aa58d29 100644 --- a/.cspell.json +++ b/.cspell.json @@ -77,7 +77,8 @@ "gifv", "ctls", "ctest", - "automod" + "automod", + "amod" ], "flagWords": [ "hte" From 309f70793d15787ca2b0c3bb4c4812d114e9c1ce Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 22 Jun 2022 01:53:17 +0100 Subject: [PATCH 110/136] docs: comments to document automod --- include/dpp/automod.h | 145 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 12 deletions(-) diff --git a/include/dpp/automod.h b/include/dpp/automod.h index 119dc5733c..245f72cf73 100644 --- a/include/dpp/automod.h +++ b/include/dpp/automod.h @@ -29,34 +29,91 @@ namespace dpp { +/** + * @brief Possible types of preset filter lists + */ enum automod_preset_type : uint8_t { + /** + * @brief Strong swearing + */ amod_preset_profanity = 1, + /** + * @brief Sexual phrases and words + */ amod_preset_sexual_content = 2, + /** + * @brief Racial and other slurs, hate speech + */ amod_preset_slurs = 3, }; +/** + * @brief Action types to perform on filtering + */ enum automod_action_type : uint8_t { + /** + * @brief Block the message + */ amod_action_block_message = 1, + /** + * @brief Send an alert to a given channel + */ amod_action_send_alert = 2, + /** + * @brief time out the user + */ amod_action_timeout = 3, }; +/** + * @brief Event types, only message send is currently supported + */ enum automod_event_type : uint8_t { - /// Trigger on message send + /** + * @brief Trigger on message send + */ amod_message_send = 1, }; +/** + * @brief Types of moderation to trigger + */ enum automod_trigger_type : uint8_t { + /** + * @brief Keyword filtering + */ amod_type_keyword = 1, + /** + * @brief Harmful/malware links + */ amod_type_harmful_link = 2, + /** + * @brief Spamming + */ amod_type_spam = 3, + /** + * @brief Preset lists of filter words + */ amod_type_keyword_preset = 4, }; +/** + * @brief Metadata associated with an automod action + */ struct DPP_EXPORT automod_metadata : public json_interface { + /** + * @brief Keywords to moderate + */ std::vector keywords; + /** + * @brief Preset keyword list types to moderate + * @see automod_preset_type + */ std::vector presets; + /** + * @brief Destroy the automod metadata object + */ virtual ~automod_metadata(); /** @@ -76,13 +133,34 @@ struct DPP_EXPORT automod_metadata : public json_interface { }; +/** + * @brief Represents an automod action + */ struct DPP_EXPORT automod_action : public json_interface { + /** + * @brief Type of action to take + */ automod_action_type type; + + /** + * @brief Channel ID, for type amod_action_send_alert + */ snowflake channel_id; + + /** + * @brief Silence duration in seconds, for amod_action_timeout + * + */ int32_t duration_seconds; + /** + * @brief Construct a new automod action object + */ automod_action(); + /** + * @brief Destroy the automod action object + */ virtual ~automod_action(); /** @@ -101,22 +179,65 @@ struct DPP_EXPORT automod_action : public json_interface { virtual std::string build_json(bool with_id = false) const; }; +/** + * @brief Represnets an automod rule + */ class DPP_EXPORT automod_rule : public managed, public json_interface { public: - snowflake id; //!< the id of this rule - snowflake guild_id; //!< the guild which this rule belongs to - std::string name; //!< the rule name - snowflake creator_id; //!< the user which first created this rule - automod_event_type event_type; //!< the rule event type - automod_trigger_type trigger_type; //!< the rule trigger type - automod_metadata trigger_metadata;//!< the rule trigger metadata - std::vector actions; //!< the actions which will execute when the rule is triggered - bool enabled; //!< whether the rule is enabled - std::vector exempt_roles; //!< the role ids that should not be affected by the rule (Maximum of 20) - std::vector exempt_channels;//!< the channel ids that should not be affected by the rule (Maximum of 50) + /** + * @brief the id of this rule + */ + snowflake id; + /** + * @brief the guild which this rule belongs to + */ + snowflake guild_id; + /** + * @brief the rule name + */ + std::string name; + /** + * @brief The user which first created this rule + */ + snowflake creator_id; + /** + * @brief The rule event type + */ + automod_event_type event_type; + /** + * @brief The rule trigger type + */ + automod_trigger_type trigger_type; + /** + * @brief The rule trigger metadata + * + */ + automod_metadata trigger_metadata; + /** + * @brief the actions which will execute when the rule is triggered + */ + std::vector actions; + /** + * @brief Whether the rule is enabled + */ + bool enabled; + /** + * @brief the role ids that should not be affected by the rule (Maximum of 20) + */ + std::vector exempt_roles; + /** + * @brief the channel ids that should not be affected by the rule (Maximum of 50) + */ + std::vector exempt_channels; + /** + * @brief Construct a new automod rule object + */ automod_rule(); + /** + * @brief Destroy the automod rule object + */ virtual ~automod_rule(); /** From b0e0293ea58c0a9cf6cc9712222932992a9da8dd Mon Sep 17 00:00:00 2001 From: Phil B Date: Wed, 22 Jun 2022 19:21:53 +0200 Subject: [PATCH 111/136] update --- include/dpp/channel.h | 13 ++++++++----- include/dpp/guild.h | 20 ++++++++++++++++---- src/dpp/channel.cpp | 8 +++----- src/dpp/guild.cpp | 24 ++++++++++-------------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index b5ba388fdf..912012a262 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -383,22 +383,25 @@ class DPP_EXPORT channel : public managed, public json_interface { std::string get_mention() const; /** - * @brief Get the user permissions for a member on this channel, including channel overwrites. + * @brief Get the user overall permissions for a member on this channel, including channel overwrites. * * @param user The user to resolve the permissions for * @return permission Permissions bitmask made of bits in dpp::permissions. - * @note The method will search for the guild member in the cache by the user id. + * @note Requires role cache to be enabled (it's enabled by default). + * + * @note The method will search for the guild member in the cache by the users id. * If the guild member is not in cache, the method will always return 0. */ permission get_user_permissions(const class user* user) const; /** - * @brief Get the user permissions for a member on this channel, including channel overwrites. + * @brief Get the overall user permissions for a member on this channel, including channel overwrites. * * @param member The member to resolve the permissions for - * @return permission Permissions bitmask made of bits in dpp::permissions. + * @return The permission overwrites for the member. Made of bits in dpp::permissions. + * @note Requires role cache to be enabled (it's enabled by default). */ - permission get_member_overwrites(const class guild_member* member) const; + permission get_user_permissions(const class guild_member &member) const; /** * @brief Return a map of members on the channel, built from the guild's diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 1baf71239c..f8aaf87cb8 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -542,10 +542,15 @@ class DPP_EXPORT guild : public managed, public json_interface { /** * @brief Compute the base permissions for a member on this guild, * before channel overwrites are applied. + * This method takes into consideration the following cases: + * - Guild owner + * - Guild roles including \@everyone * * @param user User to get permissions for * @return permission permissions bitmask - * @note The method will search for the guild member in the cache by the user id. + * @note Requires role cache to be enabled (it's enabled by default). + * + * @note The method will search for the guild member in the cache by the users id. * If the guild member is not in cache, the method will always return 0. */ permission base_permissions(const class user* user) const; @@ -553,11 +558,15 @@ class DPP_EXPORT guild : public managed, public json_interface { /** * @brief Compute the base permissions for a member on this guild, * before channel overwrites are applied. + * This method takes into consideration the following cases: + * - Guild owner + * - Guild roles including \@everyone * * @param member member to get permissions for * @return permission permissions bitmask + * @note Requires role cache to be enabled (it's enabled by default). */ - permission base_permissions(const guild_member* member) const; + permission base_permissions(const guild_member &member) const; /** * @brief Compute the permission overwrites for a member in a channel, including base permissions. @@ -567,7 +576,9 @@ class DPP_EXPORT guild : public managed, public json_interface { * @param user User to resolve the permissions for * @param channel Channel to compute permission overwrites for * @return permission Merged permissions bitmask of overwrites. - * @note The method will search for the guild member in the cache by the user id. + * @note Requires role cache to be enabled (it's enabled by default). + * + * @note The method will search for the guild member in the cache by the users id. * If the guild member is not in cache, the method will always return 0. */ permission permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const; @@ -578,8 +589,9 @@ class DPP_EXPORT guild : public managed, public json_interface { * @param member Member to resolve the permissions for * @param channel Channel to compute permission overwrites for * @return permission Merged permissions bitmask of overwrites. + * @note Requires role cache to be enabled (it's enabled by default). */ - permission permission_overwrites(const guild_member* member, const channel* channel) const; + permission permission_overwrites(const guild_member &member, const channel &channel) const; /** * @brief Rehash members map diff --git a/src/dpp/channel.cpp b/src/dpp/channel.cpp index 8fac3eba06..3d5c6f90b4 100644 --- a/src/dpp/channel.cpp +++ b/src/dpp/channel.cpp @@ -386,15 +386,13 @@ permission channel::get_user_permissions(const user* user) const { return g->permission_overwrites(g->base_permissions(user), user, this); } -permission channel::get_member_overwrites(const guild_member* member) const { - if (member == nullptr) - return 0; +permission channel::get_user_permissions(const guild_member &member) const { guild* g = dpp::find_guild(guild_id); if (g == nullptr) return 0; - return g->permission_overwrites(member, this); + return g->permission_overwrites(member, *this); } std::map channel::get_members() { @@ -402,7 +400,7 @@ std::map channel::get_members() { guild* g = dpp::find_guild(guild_id); if (g) { for (auto m = g->members.begin(); m != g->members.end(); ++m) { - if (g->permission_overwrites(&m->second, this) & p_view_channel) { + if (g->permission_overwrites(m->second, *this) & p_view_channel) { rv[m->second.user_id] = &(m->second); } } diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index b220008ef8..f58a1519de 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -548,16 +548,14 @@ permission guild::base_permissions(const user* user) const { return 0; guild_member gm = mi->second; - return base_permissions(&gm); + return base_permissions(gm); } -permission guild::base_permissions(const guild_member *member) const { - if (member == nullptr) - return 0; +permission guild::base_permissions(const guild_member &member) const { /* this method is written with the help of discord's pseudocode available here https://discord.com/developers/docs/topics/permissions#permission-overwrites */ - if (owner_id == member->user_id) + if (owner_id == member.user_id) return ~0; // return all permissions if it's the owner of the guild role* everyone = dpp::find_role(id); @@ -566,7 +564,7 @@ permission guild::base_permissions(const guild_member *member) const { permission permissions = everyone->permissions; - for (auto& rid : member->roles) { + for (auto& rid : member.roles) { role* r = dpp::find_role(rid); if (r) { permissions |= r->permissions; @@ -639,9 +637,7 @@ permission guild::permission_overwrites(const uint64_t base_permissions, const u return permissions; } -permission guild::permission_overwrites(const guild_member *member, const channel* channel) const { - if (member == nullptr || channel == nullptr) - return 0; +permission guild::permission_overwrites(const guild_member &member, const channel &channel) const { permission base_permissions = this->base_permissions(member); @@ -654,7 +650,7 @@ permission guild::permission_overwrites(const guild_member *member, const channe permission permissions = base_permissions; // find \@everyone role overwrite and apply it. - for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { + for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { if (it->id == this->id && it->type == ot_role) { permissions &= ~it->deny; permissions |= it->allow; @@ -666,13 +662,13 @@ permission guild::permission_overwrites(const guild_member *member, const channe uint64_t allow = 0; uint64_t deny = 0; - for (auto& rid : member->roles) { + for (auto& rid : member.roles) { /* Skip \@everyone role to not break the hierarchy. It's calculated above */ if (rid == this->id) continue; - for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { + for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { if (rid == it->id && it->type == ot_role) { deny |= it->deny; allow |= it->allow; @@ -685,8 +681,8 @@ permission guild::permission_overwrites(const guild_member *member, const channe permissions |= allow; // Apply member specific overwrite if exists. - for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) { - if (member->user_id == it->id && it->type == ot_member) { + for (auto it = channel.permission_overwrites.begin(); it != channel.permission_overwrites.end(); ++it) { + if (member.user_id == it->id && it->type == ot_member) { permissions &= ~it->deny; permissions |= it->allow; break; From 78f905fb96db117e85b2e6769ecc87c5091156bf Mon Sep 17 00:00:00 2001 From: brain Date: Fri, 24 Jun 2022 09:34:09 +0100 Subject: [PATCH 112/136] version bump --- .vscode/settings.json | 3 ++- include/dpp/version.h | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 77360b0f3a..53168bc477 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,6 +72,7 @@ "random": "cpp", "variant": "cpp", "unordered_set": "cpp", - "codecvt": "cpp" + "codecvt": "cpp", + "future": "cpp" } } \ No newline at end of file diff --git a/include/dpp/version.h b/include/dpp/version.h index b21d245db7..6c779703b2 100644 --- a/include/dpp/version.h +++ b/include/dpp/version.h @@ -21,9 +21,9 @@ #pragma once #if !defined(DPP_VERSION_LONG) -#define DPP_VERSION_LONG 0x00100011 -#define DPP_VERSION_SHORT 100011 -#define DPP_VERSION_TEXT "D++ 10.0.11 (02-Jun-2022)" +#define DPP_VERSION_LONG 0x00100012 +#define DPP_VERSION_SHORT 100012 +#define DPP_VERSION_TEXT "D++ 10.0.12 (24-Jun-2022)" #define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) #define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) From 4140dbbe41f7a45349960a85a2a648ea849ff74b Mon Sep 17 00:00:00 2001 From: brain Date: Sat, 25 Jun 2022 09:49:35 +0100 Subject: [PATCH 113/136] timeouts on REST threads are way too low --- src/dpp/queues.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dpp/queues.cpp b/src/dpp/queues.cpp index 033c0b17f1..ad03475560 100644 --- a/src/dpp/queues.cpp +++ b/src/dpp/queues.cpp @@ -234,7 +234,7 @@ void in_thread::in_loop(uint32_t index) while (!terminating) { std::mutex mtx; std::unique_lock lock{ mtx }; - in_ready.wait_for(lock, std::chrono::milliseconds(100)); + in_ready.wait_for(lock, std::chrono::seconds(1)); /* New request to be sent! */ if (!requests->globally_ratelimited) { @@ -340,7 +340,7 @@ void request_queue::out_loop() std::mutex mtx; std::unique_lock lock{ mtx }; - out_ready.wait_for(lock, std::chrono::milliseconds(50)); + out_ready.wait_for(lock, std::chrono::seconds(1)); time_t now = time(nullptr); /* A request has been completed! */ From 6fdea1670b4f8e10ec37b44da170ee1cb7cf5635 Mon Sep 17 00:00:00 2001 From: brain Date: Tue, 28 Jun 2022 19:37:33 +0100 Subject: [PATCH 114/136] fix: improve reliability of connections, handle ssl_connect dying and connect() hanging gracefully --- src/dpp/httpsclient.cpp | 32 ++++---- src/dpp/sslclient.cpp | 168 +++++++++++++++++++++++++++++----------- 2 files changed, 138 insertions(+), 62 deletions(-) diff --git a/src/dpp/httpsclient.cpp b/src/dpp/httpsclient.cpp index 839ad14ef0..9c3ffa3512 100644 --- a/src/dpp/httpsclient.cpp +++ b/src/dpp/httpsclient.cpp @@ -54,22 +54,24 @@ void https_client::connect() for (auto& [k,v] : request_headers) { map_headers += k + ": " + v + "\r\n"; } - this->write( - fmt::format( + if (this->sfd != SOCKET_ERROR) { + this->write( + fmt::format( - "{} {} HTTP/1.1\r\n" - "Host: {}\r\n" - "pragma: no-cache\r\n" - "Connection: keep-alive\r\n" - "Content-Length: {}\r\n{}" - "\r\n{}", + "{} {} HTTP/1.1\r\n" + "Host: {}\r\n" + "pragma: no-cache\r\n" + "Connection: keep-alive\r\n" + "Content-Length: {}\r\n{}" + "\r\n{}", - this->request_type, this->path, this->hostname, - this->request_body.length(), map_headers, - this->request_body - ) - ); - read_loop(); + this->request_type, this->path, this->hostname, + this->request_body.length(), map_headers, + this->request_body + ) + ); + read_loop(); + } } multipart_content https_client::build_multipart(const std::string &json, const std::vector& filenames, const std::vector& contents) { @@ -282,7 +284,7 @@ http_state https_client::get_state() { } void https_client::one_second_timer() { - if (time(nullptr) >= timeout && this->state != HTTPS_DONE) { + if ((this->sfd == SOCKET_ERROR || time(nullptr) >= timeout) && this->state != HTTPS_DONE) { keepalive = false; this->close(); } diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 71089b9a4e..279408afd6 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -58,6 +58,7 @@ #include #include #include +#include namespace dpp { @@ -104,6 +105,72 @@ thread_local std::unordered_map keepalives; #define DPP_BUFSIZE 16 * 1024 const int ERROR_STATUS = -1; +/** + * @brief Connect to TCP socket with a select() driven timeout + * + * @param sockfd socket descriptor + * @param addr address to connect to + * @param addrlen address length + * @param timeout_ms timeout in milliseconds + * @return int -1 on error, 0 on succcess just like POSIX connect() + * @throw dpp::connection_exception on failure + */ +int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, unsigned int timeout_ms) { +#ifdef _WIN32 + u_long mode = 1; + int result = ioctlsocket(sockfd, FIONBIO, &mode); + if (result != NO_ERROR) + throw dpp::connection_exception("Can't switch socket to non-blocking mode!"); +#else + int ofcmode; + ofcmode = fcntl(sockfd, F_GETFL, 0); + ofcmode |= O_NDELAY; + if (fcntl(sockfd, F_SETFL, ofcmode)) { + throw dpp::connection_exception("Can't switch socket to non-blocking mode!"); + } +#endif + int rc = (::connect(sockfd, addr, addrlen)); + if (rc == -1 && errno != EWOULDBLOCK && errno != EINPROGRESS) { + throw dpp::connection_exception(strerror(errno)); + } else { + // Set a deadline timestamp 'timeout' ms from now (needed b/c poll can be interrupted) + double deadline = dpp::utility::time_f() + (timeout_ms / 1000.0); + do { + rc = -1; + if (dpp::utility::time_f() >= deadline) { + throw dpp::connection_exception("Connection timed out"); + } + fd_set writefds, efds; + FD_ZERO(&writefds); + FD_ZERO(&efds); + SAFE_FD_SET(sockfd, &writefds); + SAFE_FD_SET(sockfd, &efds); + timeval ts; + ts.tv_sec = 0; + ts.tv_usec = 50000; + int r = select(sockfd + 1, nullptr, &writefds, &efds, &ts); + if (r > 0 && SAFE_FD_ISSET(sockfd, &writefds) && !SAFE_FD_ISSET(sockfd, &efds)) { + rc = 0; + } else if (r > 0 && SAFE_FD_ISSET(sockfd, &efds)) { + throw dpp::connection_exception(strerror(errno)); + } + } while (rc == -1); + } +#ifdef _WIN32 + mode = 0; + int result = ioctlsocket(sockfd, FIONBIO, &mode); + if (result != NO_ERROR) + throw dpp::connection_exception("Can't switch socket to blocking mode!"); +#else + ofcmode = fcntl(sockfd, F_GETFL, 0); + ofcmode &= ~O_NDELAY; + if (fcntl(sockfd, F_SETFL, ofcmode)) { + throw dpp::connection_exception("Can't switch socket to blocking mode!"); + } +#endif + return rc; +} + ssl_client::ssl_client(const std::string &_hostname, const std::string &_port, bool plaintext_downgrade, bool reuse) : nonblocking(false), sfd(INVALID_SOCKET), @@ -118,11 +185,11 @@ ssl_client::ssl_client(const std::string &_hostname, const std::string &_port, b keepalive(reuse) { #ifndef WIN32 - signal(SIGALRM, SIG_IGN); - signal(SIGHUP, SIG_IGN); - signal(SIGPIPE, SIG_IGN); - signal(SIGCHLD, SIG_IGN); - signal(SIGXFSZ, SIG_IGN); + signal(SIGALRM, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + signal(SIGXFSZ, SIG_IGN); #else // Set up winsock. WSADATA wsadata; @@ -195,7 +262,7 @@ void ssl_client::connect() /* Resolve hostname to IP */ struct hostent *host; if ((host = gethostbyname(hostname.c_str())) == nullptr) - throw dpp::exception(std::string("Couldn't resolve hostname: ") + hostname); + throw dpp::connection_exception(std::string("Couldn't resolve hostname: ") + hostname); addrinfo hints, *addrs; @@ -206,7 +273,7 @@ void ssl_client::connect() int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrs); if (status != 0) - throw dpp::exception(std::string("getaddrinfo error: ") + gai_strerror(status)); + throw dpp::connection_exception(std::string("getaddrinfo error: ") + gai_strerror(status)); /* Attempt each address in turn, if there are multiple IP addresses on the hostname */ int err = 0; @@ -215,7 +282,7 @@ void ssl_client::connect() if (sfd == ERROR_STATUS) { err = errno; continue; - } else if (::connect(sfd, addr->ai_addr, (int)addr->ai_addrlen) == 0) { + } else if (connect_with_timeout(sfd, addr->ai_addr, (int)addr->ai_addrlen, 5000) == 0) { break; } err = errno; @@ -233,7 +300,7 @@ void ssl_client::connect() /* Check if none of the IPs yielded a valid connection */ if (sfd == ERROR_STATUS) { - throw dpp::exception(strerror(err)); + throw dpp::connection_exception(strerror(err)); } if (!plaintext) { @@ -245,28 +312,36 @@ void ssl_client::connect() /* Create SSL context */ openssl_context = SSL_CTX_new(method); if (openssl_context == nullptr) - throw dpp::exception("Failed to create SSL client context!"); + throw dpp::connection_exception("Failed to create SSL client context!"); /* Do not allow SSL 3.0, TLS 1.0 or 1.1 * https://www.packetlabs.net/posts/tls-1-1-no-longer-secure/ */ if (!SSL_CTX_set_min_proto_version(openssl_context, TLS1_2_VERSION)) - throw dpp::exception("Failed to set minimum SSL version!"); + throw dpp::connection_exception("Failed to set minimum SSL version!"); } /* Create SSL session */ ssl->ssl = SSL_new(openssl_context); if (ssl->ssl == nullptr) - throw dpp::exception("SSL_new failed!"); + throw dpp::connection_exception("SSL_new failed!"); SSL_set_fd(ssl->ssl, (int)sfd); /* Server name identification (SNI) */ SSL_set_tlsext_host_name(ssl->ssl, hostname.c_str()); +#ifndef _WIN32 + /* On Linux, we can set socket timeouts so that SSL_connect eventually gives up */ + timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); +#endif status = SSL_connect(ssl->ssl); if (status != 1) { - throw dpp::exception("SSL_connect error"); + throw dpp::connection_exception("SSL_connect error"); } this->cipher = SSL_get_cipher(ssl->ssl); @@ -287,11 +362,11 @@ void ssl_client::write(const std::string &data) } else { if (plaintext) { if (sfd == INVALID_SOCKET || ::send(sfd, data.data(), data.length(), 0) != (int)data.length()) { - throw dpp::exception("write() failed"); + throw dpp::connection_exception("write() failed"); } } else { if (SSL_write(ssl->ssl, data.data(), (int)data.length()) != (int)data.length()) { - throw dpp::exception("SSL_write() failed"); + throw dpp::connection_exception("SSL_write() failed"); } } } @@ -320,13 +395,13 @@ void ssl_client::read_loop() * would cause the protocol to break. */ int r = 0; - size_t ClientToServerLength = 0, ClientToServerOffset = 0; + size_t client_to_server_length = 0, client_to_server_offset = 0; bool read_blocked_on_write = false, write_blocked_on_read = false,read_blocked = false; fd_set readfds, writefds, efds; - char ClientToServerBuffer[DPP_BUFSIZE], ServerToClientBuffer[DPP_BUFSIZE]; + char client_to_server_buffer[DPP_BUFSIZE], server_to_client_buffer[DPP_BUFSIZE]; if (sfd == INVALID_SOCKET) { - throw dpp::exception("Invalid file descriptor in read_loop()"); + throw dpp::connection_exception("Invalid file descriptor in read_loop()"); } /* Make the socket nonblocking */ @@ -334,13 +409,12 @@ void ssl_client::read_loop() u_long mode = 1; int result = ioctlsocket(sfd, FIONBIO, &mode); if (result != NO_ERROR) - throw dpp::exception("Can't switch socket to non-blocking mode!"); + throw dpp::connection_exception("Can't switch socket to non-blocking mode!"); #else - int ofcmode; - ofcmode = fcntl(sfd, F_GETFL, 0); + int ofcmode = fcntl(sfd, F_GETFL, 0); ofcmode |= O_NDELAY; if (fcntl(sfd, F_SETFL, ofcmode)) { - throw dpp::exception("Can't switch socket to non-blocking mode!"); + throw dpp::connection_exception("Can't switch socket to non-blocking mode!"); } #endif nonblocking = true; @@ -370,14 +444,18 @@ void ssl_client::read_loop() SAFE_FD_SET(cfd, &writefds); } + if (sfd == -1) { + throw dpp::connection_exception("File descriptor invalidated, connection died"); + } + /* If we're waiting for a read on the socket don't try to write to the server */ - if (ClientToServerLength || obuffer.length() || read_blocked_on_write) { + if (client_to_server_length || obuffer.length() || read_blocked_on_write) { SAFE_FD_SET(sfd,&writefds); } - + timeval ts; - ts.tv_sec = 0; - ts.tv_usec = 50000; + ts.tv_sec = 1; + ts.tv_usec = 0; r = select(FD_SETSIZE, &readfds, &writefds, &efds, &ts); if (r == 0) continue; @@ -388,12 +466,8 @@ void ssl_client::read_loop() if (custom_readable_fd && custom_readable_fd() >= 0 && SAFE_FD_ISSET(custom_readable_fd(), &readfds)) { custom_readable_ready(); } - if (custom_readable_fd && custom_readable_fd() >= 0 && SAFE_FD_ISSET(custom_readable_fd(), &efds)) { - } - if (SAFE_FD_ISSET(sfd, &efds) || sfd == INVALID_SOCKET) { - this->log(dpp::ll_error, std::string("Error on SSL connection: ") +strerror(errno)); - return; + throw new dpp::connection_exception(strerror(errno)); } /* Now check if there's data to read */ @@ -401,12 +475,12 @@ void ssl_client::read_loop() if (plaintext) { read_blocked_on_write = false; read_blocked = false; - r = ::recv(sfd, ServerToClientBuffer, DPP_BUFSIZE, 0); + r = ::recv(sfd, server_to_client_buffer, DPP_BUFSIZE, 0); if (r <= 0) { /* error or EOF */ return; } else { - buffer.append(ServerToClientBuffer, r); + buffer.append(server_to_client_buffer, r); if (!this->handle_buffer(buffer)) { return; } @@ -417,14 +491,14 @@ void ssl_client::read_loop() read_blocked_on_write = false; read_blocked = false; - r = SSL_read(ssl->ssl,ServerToClientBuffer,DPP_BUFSIZE); + r = SSL_read(ssl->ssl,server_to_client_buffer,DPP_BUFSIZE); int e = SSL_get_error(ssl->ssl,r); switch (e) { case SSL_ERROR_NONE: /* Data received, add it to the buffer */ if (r > 0) { - buffer.append(ServerToClientBuffer, r); + buffer.append(server_to_client_buffer, r); if (!this->handle_buffer(buffer)) { return; } @@ -459,37 +533,37 @@ void ssl_client::read_loop() } /* Check for input on the sendq */ - if (obuffer.length() && ClientToServerLength == 0) { - memcpy(&ClientToServerBuffer, obuffer.data(), obuffer.length() > DPP_BUFSIZE ? DPP_BUFSIZE : obuffer.length()); - ClientToServerLength = obuffer.length() > DPP_BUFSIZE ? DPP_BUFSIZE : obuffer.length(); - obuffer = obuffer.substr(ClientToServerLength, obuffer.length()); - ClientToServerOffset = 0; + if (obuffer.length() && client_to_server_length == 0) { + memcpy(&client_to_server_buffer, obuffer.data(), obuffer.length() > DPP_BUFSIZE ? DPP_BUFSIZE : obuffer.length()); + client_to_server_length = obuffer.length() > DPP_BUFSIZE ? DPP_BUFSIZE : obuffer.length(); + obuffer = obuffer.substr(client_to_server_length, obuffer.length()); + client_to_server_offset = 0; } /* If the socket is writeable... */ - if ((SAFE_FD_ISSET(sfd,&writefds) && ClientToServerLength) || (write_blocked_on_read && SAFE_FD_ISSET(sfd,&readfds))) { + if ((SAFE_FD_ISSET(sfd,&writefds) && client_to_server_length) || (write_blocked_on_read && SAFE_FD_ISSET(sfd,&readfds))) { write_blocked_on_read = false; /* Try to write */ if (plaintext) { - r = ::send(sfd, ClientToServerBuffer + ClientToServerOffset, (int)ClientToServerLength, 0); + r = ::send(sfd, client_to_server_buffer + client_to_server_offset, (int)client_to_server_length, 0); if (r < 0) { /* Write error */ return; } else { - ClientToServerLength -= r; - ClientToServerOffset += r; + client_to_server_length -= r; + client_to_server_offset += r; bytes_out += r; } } else { - r = SSL_write(ssl->ssl, ClientToServerBuffer + ClientToServerOffset, (int)ClientToServerLength); + r = SSL_write(ssl->ssl, client_to_server_buffer + client_to_server_offset, (int)client_to_server_length); switch(SSL_get_error(ssl->ssl,r)) { /* We wrote something */ case SSL_ERROR_NONE: - ClientToServerLength -= r; - ClientToServerOffset += r; + client_to_server_length -= r; + client_to_server_offset += r; bytes_out += r; break; From 4f3665d15f1a838ed11068c9ecdc4f13ec35c559 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Wed, 29 Jun 2022 09:36:18 +0100 Subject: [PATCH 115/136] fix: don't throw new, we can't catch it --- src/dpp/sslclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 279408afd6..086749e53e 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -467,7 +467,7 @@ void ssl_client::read_loop() custom_readable_ready(); } if (SAFE_FD_ISSET(sfd, &efds) || sfd == INVALID_SOCKET) { - throw new dpp::connection_exception(strerror(errno)); + throw dpp::connection_exception(strerror(errno)); } /* Now check if there's data to read */ From 45549999ea951015f94496f03dcd9433bfe27db0 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Wed, 29 Jun 2022 09:48:47 +0100 Subject: [PATCH 116/136] fix: windows breakage --- src/dpp/sslclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 086749e53e..734dc56917 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -158,7 +158,7 @@ int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addr } #ifdef _WIN32 mode = 0; - int result = ioctlsocket(sockfd, FIONBIO, &mode); + result = ioctlsocket(sockfd, FIONBIO, &mode); if (result != NO_ERROR) throw dpp::connection_exception("Can't switch socket to blocking mode!"); #else From 9d480add217a95df29ff4c64c2c927c30f012aa8 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 11:02:45 +0100 Subject: [PATCH 117/136] refactor: dns lookup improvements --- src/dpp/sslclient.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 279408afd6..2234a0ed74 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -260,10 +260,6 @@ void ssl_client::connect() if (make_new) { /* Resolve hostname to IP */ - struct hostent *host; - if ((host = gethostbyname(hostname.c_str())) == nullptr) - throw dpp::connection_exception(std::string("Couldn't resolve hostname: ") + hostname); - addrinfo hints, *addrs; memset(&hints, 0, sizeof(addrinfo)); @@ -278,7 +274,7 @@ void ssl_client::connect() /* Attempt each address in turn, if there are multiple IP addresses on the hostname */ int err = 0; for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next) { - sfd = ::socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol); + sfd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (sfd == ERROR_STATUS) { err = errno; continue; From 58d39876422a4d56801a878a299bc0df2cb1bfe7 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 11:15:35 +0100 Subject: [PATCH 118/136] refactor: dns lookup and timeout tidyups --- src/dpp/sslclient.cpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index c7ab897fc0..8d27e01028 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -60,6 +60,9 @@ #include #include +/* Maximum allowed time in milliseconds for socket read/write timeouts and connect() */ +#define SOCKET_OP_TIMEOUT 5000 + namespace dpp { /** @@ -103,6 +106,8 @@ thread_local std::unordered_map keepalives; * it'd go unused. */ #define DPP_BUFSIZE 16 * 1024 + +/* Represents a failed socket system call, e.g. connect() failure */ const int ERROR_STATUS = -1; /** @@ -147,7 +152,7 @@ int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addr SAFE_FD_SET(sockfd, &efds); timeval ts; ts.tv_sec = 0; - ts.tv_usec = 50000; + ts.tv_usec = timeout_ms * 1000; int r = select(sockfd + 1, nullptr, &writefds, &efds, &ts); if (r > 0 && SAFE_FD_ISSET(sockfd, &writefds) && !SAFE_FD_ISSET(sockfd, &efds)) { rc = 0; @@ -271,25 +276,21 @@ void ssl_client::connect() if (status != 0) throw dpp::connection_exception(std::string("getaddrinfo error: ") + gai_strerror(status)); - /* Attempt each address in turn, if there are multiple IP addresses on the hostname */ int err = 0; - for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next) { - sfd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if (sfd == ERROR_STATUS) { - err = errno; - continue; - } else if (connect_with_timeout(sfd, addr->ai_addr, (int)addr->ai_addrlen, 5000) == 0) { - break; - } + struct addrinfo *addr = addrs; + sfd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (sfd == ERROR_STATUS) { err = errno; - shutdown(sfd, 2); - #ifdef _WIN32 + } else if (connect_with_timeout(sfd, addr->ai_addr, (int)addr->ai_addrlen, SOCKET_OP_TIMEOUT) != 0) { +#ifdef _WIN32 if (sfd >= 0 && sfd < FD_SETSIZE) { closesocket(sfd); } - #else +#else + err = errno; + shutdown(sfd, 2); +#endif ::close(sfd); - #endif sfd = ERROR_STATUS; } freeaddrinfo(addrs); @@ -330,8 +331,8 @@ void ssl_client::connect() #ifndef _WIN32 /* On Linux, we can set socket timeouts so that SSL_connect eventually gives up */ timeval tv; - tv.tv_sec = 5; - tv.tv_usec = 0; + tv.tv_sec = 0; + tv.tv_usec = SOCKET_OP_TIMEOUT * 1000; setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); #endif From 094f9a09e422718d381cd4433996d0593c740647 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 11:41:53 +0100 Subject: [PATCH 119/136] refactor: move dns resolution to its own files --- include/dpp/dns.h | 48 ++++++++++++++++++++++++++++++++++++ src/dpp/dns.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++ src/dpp/sslclient.cpp | 22 ++++------------- 3 files changed, 110 insertions(+), 17 deletions(-) create mode 100644 include/dpp/dns.h create mode 100644 src/dpp/dns.cpp diff --git a/include/dpp/dns.h b/include/dpp/dns.h new file mode 100644 index 0000000000..a938073b53 --- /dev/null +++ b/include/dpp/dns.h @@ -0,0 +1,48 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include +#include + +namespace dpp { + + /** + * @brief Resolve a hostname to an addrinfo + * + * @param hostname Hostname to resolve + * @param port A port number or named service, e.g. "80" + * @return addrinfo First IP address associated with the hostname DNS record + */ + addrinfo DPP_EXPORT resolve_hostname(const std::string& hostname, const std::string& port); +}; diff --git a/src/dpp/dns.cpp b/src/dpp/dns.cpp new file mode 100644 index 0000000000..3939f44881 --- /dev/null +++ b/src/dpp/dns.cpp @@ -0,0 +1,57 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#include +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include +#include + +namespace dpp +{ + addrinfo resolve_hostname(const std::string& hostname, const std::string& port) + { + addrinfo rv, hints, *addrs; + int error; + + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if ((error = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrs))) { + throw dpp::connection_exception(std::string("getaddrinfo error: ") + gai_strerror(error)); + } + rv = *addrs; + freeaddrinfo(addrs); + return rv; + } + +}; \ No newline at end of file diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 8d27e01028..1493e5deb4 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -59,6 +59,7 @@ #include #include #include +#include /* Maximum allowed time in milliseconds for socket read/write timeouts and connect() */ #define SOCKET_OP_TIMEOUT 5000 @@ -265,23 +266,12 @@ void ssl_client::connect() if (make_new) { /* Resolve hostname to IP */ - addrinfo hints, *addrs; - - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrs); - if (status != 0) - throw dpp::connection_exception(std::string("getaddrinfo error: ") + gai_strerror(status)); - int err = 0; - struct addrinfo *addr = addrs; - sfd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + addrinfo addr = resolve_hostname(hostname, port); + sfd = ::socket(addr.ai_family, addr.ai_socktype, addr.ai_protocol); if (sfd == ERROR_STATUS) { err = errno; - } else if (connect_with_timeout(sfd, addr->ai_addr, (int)addr->ai_addrlen, SOCKET_OP_TIMEOUT) != 0) { + } else if (connect_with_timeout(sfd, addr.ai_addr, (int)addr.ai_addrlen, SOCKET_OP_TIMEOUT) != 0) { #ifdef _WIN32 if (sfd >= 0 && sfd < FD_SETSIZE) { closesocket(sfd); @@ -293,7 +283,6 @@ void ssl_client::connect() ::close(sfd); sfd = ERROR_STATUS; } - freeaddrinfo(addrs); /* Check if none of the IPs yielded a valid connection */ if (sfd == ERROR_STATUS) { @@ -336,8 +325,7 @@ void ssl_client::connect() setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); #endif - status = SSL_connect(ssl->ssl); - if (status != 1) { + if (SSL_connect(ssl->ssl) != 1) { throw dpp::connection_exception("SSL_connect error"); } From 3eadb92e0a0d999f3e03299f3904bcc953a66da8 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 12:41:50 +0100 Subject: [PATCH 120/136] feature: add dns caching --- include/dpp/dns.h | 38 ++++++++++++++++++--- src/dpp/dns.cpp | 78 +++++++++++++++++++++++++++++++++---------- src/dpp/sslclient.cpp | 6 ++-- 3 files changed, 96 insertions(+), 26 deletions(-) diff --git a/include/dpp/dns.h b/include/dpp/dns.h index a938073b53..dd759e0ba8 100644 --- a/include/dpp/dns.h +++ b/include/dpp/dns.h @@ -20,29 +20,57 @@ ************************************************************************************/ #pragma once #include -#include #ifdef _WIN32 #include #include -#include #else #include #include #include #endif #include -#include #include -#include +#include namespace dpp { + /** + * @brief Represents a cached DNS result. + * Used by the ssl_client class to store cached copies of dns lookups. + */ + struct dns_cache_entry { + /** + * @brief Resolved address information + */ + addrinfo addr; + + /** + * @brief Socket address. + * Discord only supports ipv4, but sockaddr_in6 is larger + * than sockaddr_in, sockaddr_storage will hold either. This + * means that if discord ever do support ipv6 we just flip + * one value in dns.cpp and that should be all that is needed. + */ + sockaddr_storage ai_addr; + + /** + * @brief Time at which this cache entry is invalidated + */ + time_t expire_timestamp; + }; + + /** + * @brief Cache container type + */ + using dns_cache_t = std::unordered_map; + /** * @brief Resolve a hostname to an addrinfo * * @param hostname Hostname to resolve * @param port A port number or named service, e.g. "80" * @return addrinfo First IP address associated with the hostname DNS record + * @throw dpp::connection_exception On failure to resolve hostname */ - addrinfo DPP_EXPORT resolve_hostname(const std::string& hostname, const std::string& port); + const dns_cache_entry* DPP_EXPORT resolve_hostname(const std::string& hostname, const std::string& port); }; diff --git a/src/dpp/dns.cpp b/src/dpp/dns.cpp index 3939f44881..dbb2b6f8ea 100644 --- a/src/dpp/dns.cpp +++ b/src/dpp/dns.cpp @@ -19,39 +19,81 @@ * ************************************************************************************/ +#include #include -#ifdef _WIN32 -#include -#include -#include -#else -#include -#include -#include -#endif -#include #include -#include +#include #include namespace dpp { - addrinfo resolve_hostname(const std::string& hostname, const std::string& port) + /* One hour in seconds */ + constexpr time_t one_hour = 60 * 60; + + /* Thread safety mutex for dns cache */ + std::shared_mutex dns_cache_mutex; + + /* Cache container */ + dns_cache_t dns_cache; + + const dns_cache_entry* resolve_hostname(const std::string& hostname, const std::string& port) { - addrinfo rv, hints, *addrs; + addrinfo hints, *addrs; + dns_cache_t::const_iterator iter; + time_t now = time(nullptr); int error; + + /* Thread safety scope */ + { + /* Check cache for existing DNS record. This can use a shared lock. */ + std::shared_lock dns_cache_lock(dns_cache_mutex); + iter = dns_cache.find(hostname); + if (iter != dns_cache.end() && now < iter->second->expire_timestamp) { + /* there is a cached entry that is still valid, return it */ + return iter->second; + } + } + if (iter != dns_cache.end()) { + /* there is a cached entry, but it has expired, + * delete and free it, and fall through to a new lookup. + * We must use a unique lock here as we modify the cache. + */ + std::unique_lock dns_cache_lock(dns_cache_mutex); + delete iter->second; + dns_cache.erase(iter); + } + /* The hints indicate what sort of DNS results we are interested in. + * To change this to support IPv6, one change we need to make here is + * to change AF_INET to AF_UNSPEC. Everything else should just work fine. + */ memset(&hints, 0, sizeof(addrinfo)); - hints.ai_family = AF_UNSPEC; + hints.ai_family = AF_INET; // IPv6 explicitly unsupported by Discord hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if ((error = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrs))) { throw dpp::connection_exception(std::string("getaddrinfo error: ") + gai_strerror(error)); } - rv = *addrs; - freeaddrinfo(addrs); - return rv; - } + /* Thread safety scope */ + { + /* Update cache, requires unique lock */ + std::unique_lock dns_cache_lock(dns_cache_mutex); + dns_cache_entry* cache_entry = new dns_cache_entry(); + + /* The sockaddr struct contains a bunch of raw pointers that we + * must copy to the cache, before freeing it with freeaddrinfo(). + * Icky icky C APIs. + */ + memcpy(&cache_entry->addr, addrs, sizeof(addrinfo)); + memcpy(&cache_entry->ai_addr, addrs->ai_addr, addrs->ai_addrlen); + cache_entry->expire_timestamp = now + one_hour; + dns_cache[hostname] = cache_entry; + + /* Now we're done with this horrible struct, free it and return */ + freeaddrinfo(addrs); + return cache_entry; + } + } }; \ No newline at end of file diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 1493e5deb4..80b4c36e6c 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -267,11 +267,11 @@ void ssl_client::connect() if (make_new) { /* Resolve hostname to IP */ int err = 0; - addrinfo addr = resolve_hostname(hostname, port); - sfd = ::socket(addr.ai_family, addr.ai_socktype, addr.ai_protocol); + const dns_cache_entry* addr = resolve_hostname(hostname, port); + sfd = ::socket(addr->addr.ai_family, addr->addr.ai_socktype, addr->addr.ai_protocol); if (sfd == ERROR_STATUS) { err = errno; - } else if (connect_with_timeout(sfd, addr.ai_addr, (int)addr.ai_addrlen, SOCKET_OP_TIMEOUT) != 0) { + } else if (connect_with_timeout(sfd, (sockaddr*)&addr->ai_addr, (int)addr->addr.ai_addrlen, SOCKET_OP_TIMEOUT) != 0) { #ifdef _WIN32 if (sfd >= 0 && sfd < FD_SETSIZE) { closesocket(sfd); From c5e5520067ff121fe38f0b90ddc97c733099a344 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 12:53:41 +0100 Subject: [PATCH 121/136] fix: this should not be exported to the windows dll --- include/dpp/dns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/dns.h b/include/dpp/dns.h index dd759e0ba8..2f4d239bee 100644 --- a/include/dpp/dns.h +++ b/include/dpp/dns.h @@ -72,5 +72,5 @@ namespace dpp { * @return addrinfo First IP address associated with the hostname DNS record * @throw dpp::connection_exception On failure to resolve hostname */ - const dns_cache_entry* DPP_EXPORT resolve_hostname(const std::string& hostname, const std::string& port); + const dns_cache_entry* resolve_hostname(const std::string& hostname, const std::string& port); }; From a2c12316a12f4cc83f1eb201f067c94b9603597f Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 13:07:30 +0100 Subject: [PATCH 122/136] fix docs for dns cache --- include/dpp/dns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dpp/dns.h b/include/dpp/dns.h index 2f4d239bee..23d5b1ba99 100644 --- a/include/dpp/dns.h +++ b/include/dpp/dns.h @@ -69,7 +69,7 @@ namespace dpp { * * @param hostname Hostname to resolve * @param port A port number or named service, e.g. "80" - * @return addrinfo First IP address associated with the hostname DNS record + * @return dns_cache_entry* First IP address associated with the hostname DNS record * @throw dpp::connection_exception On failure to resolve hostname */ const dns_cache_entry* resolve_hostname(const std::string& hostname, const std::string& port); From 372fb2f30a3c71ce74af2ce17213fe9bef888978 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 14:16:12 +0100 Subject: [PATCH 123/136] docs: can directly embed dot graphs into doxygen, with pan and zoom functionality. use edotor.net to build them interactively --- docpages/04_advanced_reference.md | 79 ++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/docpages/04_advanced_reference.md b/docpages/04_advanced_reference.md index cc0a9c4295..db9927eed7 100644 --- a/docpages/04_advanced_reference.md +++ b/docpages/04_advanced_reference.md @@ -28,7 +28,84 @@ Guilds are what servers are known as to the API. There can be up to **2500** of \page thread-model Thread Model -\image html DPP_Architecture.svg +\dot +digraph "Thread Model" { + graph [ranksep=1]; + "Discord Events" -> "Your Program" + + "Your Program" [style=filled, color=yellow, shape=rect] + "Cluster" [style=filled, color=yellow, shape=rect] + + subgraph cluster_4 { + style=filled; + color=lightgrey; + node [style=filled,color=lightblue] + "Your Program" + "Cluster" + label = "User Code"; + } + + subgraph cluster_0 { + style=filled; + color=lightgrey; + node [style=filled,color=lightblue] + "Shard 1" [style=filled, color=lightblue] + "Shard 2" + "Shard 3..." + label = "Shards (Each is a thread, one per 2500 discord guilds)"; + } + + subgraph cluster_1 { + style=filled + color=lightgrey + node [style=filled,color=lightblue] + "REST Requests" + "Request In Queue 1" + "Request In Queue 2" + "Request In Queue 3..." + "Request Out Queue" + label = "REST Requests (Each in queue, and the out queue, are threads)" + } + + subgraph cluster_3 { + style=filled + color=lightgrey + node [style=filled,color=lightblue] + "Discord Events" [style=filled,color=lightblue] + "User Callback Functions" + label = "Events and Callbacks" + } + + "Cluster" [shape=rect] + "REST Requests" [shape=rect] + "Request In Queue 1" [shape=rect] + "Request In Queue 2" [shape=rect] + "Request In Queue 3..." [shape=rect] + "Shard 1" [shape=rect] + "Shard 2" [shape=rect] + "Shard 3..." [shape=rect] + "Request Out Queue" [shape=rect] + "Discord Events" [shape=rect] + "User Callback Functions" [shape=rect] + + "Cluster" -> "REST Requests" + "Shard 1" -> "Discord Events" + "Shard 2" -> "Discord Events" + "Shard 3..." -> "Discord Events" + "Your Program" -> "Cluster" + "Cluster" -> "Shard 1" + "Cluster" -> "Shard 2" + "Cluster" -> "Shard 3..." + "REST Requests" -> "Request In Queue 1" + "REST Requests" -> "Request In Queue 2" + "REST Requests" -> "Request In Queue 3..." + "Request In Queue 1" -> "Request Out Queue" + "Request In Queue 2" -> "Request Out Queue" + "Request In Queue 3..." -> "Request Out Queue" + "Request Out Queue" -> "User Callback Functions" + "User Callback Functions" -> "Your Program" +} +\enddot \page coding-standards Coding Style Standards From 345e2407e4f9d42eb913154c0945e632de13d353 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 18:00:10 +0100 Subject: [PATCH 124/136] fix: allow slash commands in dm, fixes #418 --- src/dpp/slashcommand.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dpp/slashcommand.cpp b/src/dpp/slashcommand.cpp index 9ae30245a3..861b70a88c 100644 --- a/src/dpp/slashcommand.cpp +++ b/src/dpp/slashcommand.cpp @@ -561,6 +561,7 @@ void from_json(const nlohmann::json& j, interaction& i) { i.token = string_not_null(&j, "token"); i.version = int8_not_null(&j, "version"); if (j.contains("member") && !j.at("member").is_null()) { + /* Command invoked from a guild */ if (j.at("member").contains("user") && !j.at("member").at("user").is_null()) { j.at("member").at("user").get_to(i.usr); /* Caching is on; store user if needed */ @@ -584,6 +585,11 @@ void from_json(const nlohmann::json& j, interaction& i) { g->members[i.member.user_id] = i.member; } } + } else if (j.at("user") && !j.at("user").is_null()) { + /* Command invoked from a DM */ + j.at("user").get_to(i.usr); + i.member.user_id = i.usr.id; + i.member.guild_id = 0; } if (j.contains("data") && !j.at("data").is_null()) { From c858cb84d0ba5506825977d3944b0a0a6c76b5b9 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 18:39:28 +0100 Subject: [PATCH 125/136] fix: max value for command option was broken --- src/dpp/slashcommand.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dpp/slashcommand.cpp b/src/dpp/slashcommand.cpp index 861b70a88c..ada00df003 100644 --- a/src/dpp/slashcommand.cpp +++ b/src/dpp/slashcommand.cpp @@ -391,9 +391,9 @@ command_option &command_option::fill_from_json(nlohmann::json *j) { } if (j->contains("max_value")) { if ((*j)["max_value"].is_number_integer()) { - o.min_value.emplace(int64_not_null(j, "max_value")); + o.max_value.emplace(int64_not_null(j, "max_value")); } else if ((*j)["max_value"].is_number()) { - o.min_value.emplace(double_not_null(j, "max_value")); + o.max_value.emplace(double_not_null(j, "max_value")); } } o.autocomplete = bool_not_null(j, "autocomplete"); From 4eacb0c99c799fd90574835eef0fa641dfae3185 Mon Sep 17 00:00:00 2001 From: brain Date: Wed, 29 Jun 2022 21:53:26 +0100 Subject: [PATCH 126/136] feat: add interaction::app_permissions - https://github.com/discord/discord-api-docs/pull/5131 --- include/dpp/appcommand.h | 1 + src/dpp/slashcommand.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index 424823c2c0..5085e372fb 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -625,6 +625,7 @@ class DPP_EXPORT interaction : public managed, public json_interface Date: Thu, 30 Jun 2022 01:10:29 +0100 Subject: [PATCH 127/136] version bump --- include/dpp/version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dpp/version.h b/include/dpp/version.h index 6c779703b2..9b9ea098eb 100644 --- a/include/dpp/version.h +++ b/include/dpp/version.h @@ -21,9 +21,9 @@ #pragma once #if !defined(DPP_VERSION_LONG) -#define DPP_VERSION_LONG 0x00100012 -#define DPP_VERSION_SHORT 100012 -#define DPP_VERSION_TEXT "D++ 10.0.12 (24-Jun-2022)" +#define DPP_VERSION_LONG 0x00100013 +#define DPP_VERSION_SHORT 100013 +#define DPP_VERSION_TEXT "D++ 10.0.13 (30-Jun-2022)" #define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) #define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) From f88120d1fc66f144be8354cb8779eec7010153df Mon Sep 17 00:00:00 2001 From: Jakub 'Eremiell' Marek Date: Thu, 30 Jun 2022 07:47:27 +0200 Subject: [PATCH 128/136] =?UTF-8?q?=F0=9F=90=9B=20Fix=20DNS=20mutex=20incl?= =?UTF-8?q?ude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * The new DNS feature uses unique_lock with shared_mutex * This requires mutex include in gcc 11+, clang 12.0.1+ --- src/dpp/dns.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dpp/dns.cpp b/src/dpp/dns.cpp index dbb2b6f8ea..a0f106db27 100644 --- a/src/dpp/dns.cpp +++ b/src/dpp/dns.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include From 6efeb7b08307890448cdf74d3310157f833166b8 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Thu, 30 Jun 2022 09:32:15 +0100 Subject: [PATCH 129/136] bump ci g++-9 to g++-11 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fb4bbf9fb..309be56876 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: fail-fast: false # Don't fail everything if one fails. We want to test each OS/Compiler individually matrix: cfg: - - { arch: 'amd64', os: ubuntu-20.04, cpp-version: g++-9 } + - { arch: 'amd64', os: ubuntu-latest, cpp-version: g++-11 } steps: - name: Checkout D++ From 73d0909c74f8df22ba84ccd9d7857b48817c7464 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Thu, 30 Jun 2022 09:53:32 +0100 Subject: [PATCH 130/136] ci: ensure GCC 9 and 10 are also covered --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 309be56876..ae4d9f268a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,8 @@ jobs: matrix: cfg: - { arch: 'amd64', os: ubuntu-latest, cpp-version: g++-11 } + - { arch: 'amd64', os: ubuntu-latest, cpp-version: g++-10 } + - { arch: 'amd64', os: ubuntu-20.04, cpp-version: g++-9 } steps: - name: Checkout D++ From 1aec89fbfcde0652cc5a1154a7ea4265e96f6947 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Fri, 1 Jul 2022 09:40:58 +0100 Subject: [PATCH 131/136] fix: windows sucks --- src/dpp/sslclient.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index 80b4c36e6c..f2e54e8c64 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -123,10 +123,7 @@ const int ERROR_STATUS = -1; */ int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, unsigned int timeout_ms) { #ifdef _WIN32 - u_long mode = 1; - int result = ioctlsocket(sockfd, FIONBIO, &mode); - if (result != NO_ERROR) - throw dpp::connection_exception("Can't switch socket to non-blocking mode!"); + return (::connect(sockfd, addr, addrlen)); #else int ofcmode; ofcmode = fcntl(sockfd, F_GETFL, 0); @@ -162,12 +159,7 @@ int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addr } } while (rc == -1); } -#ifdef _WIN32 - mode = 0; - result = ioctlsocket(sockfd, FIONBIO, &mode); - if (result != NO_ERROR) - throw dpp::connection_exception("Can't switch socket to blocking mode!"); -#else +#ifndef _WIN32 ofcmode = fcntl(sockfd, F_GETFL, 0); ofcmode &= ~O_NDELAY; if (fcntl(sockfd, F_SETFL, ofcmode)) { From 48c933a958985658d0462bb5b1f4714a0e89288f Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Fri, 1 Jul 2022 09:43:30 +0100 Subject: [PATCH 132/136] refactor: neater change --- src/dpp/sslclient.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dpp/sslclient.cpp b/src/dpp/sslclient.cpp index f2e54e8c64..a244e84097 100644 --- a/src/dpp/sslclient.cpp +++ b/src/dpp/sslclient.cpp @@ -131,7 +131,6 @@ int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addr if (fcntl(sockfd, F_SETFL, ofcmode)) { throw dpp::connection_exception("Can't switch socket to non-blocking mode!"); } -#endif int rc = (::connect(sockfd, addr, addrlen)); if (rc == -1 && errno != EWOULDBLOCK && errno != EINPROGRESS) { throw dpp::connection_exception(strerror(errno)); @@ -159,14 +158,13 @@ int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addr } } while (rc == -1); } -#ifndef _WIN32 ofcmode = fcntl(sockfd, F_GETFL, 0); ofcmode &= ~O_NDELAY; if (fcntl(sockfd, F_SETFL, ofcmode)) { throw dpp::connection_exception("Can't switch socket to blocking mode!"); } -#endif return rc; +#endif } ssl_client::ssl_client(const std::string &_hostname, const std::string &_port, bool plaintext_downgrade, bool reuse) : From fe0134174da4d44f8f3fb3c01106642b31f495e8 Mon Sep 17 00:00:00 2001 From: yuto0214w Date: Fri, 1 Jul 2022 18:01:11 +0900 Subject: [PATCH 133/136] Fix delete_webhook_with_token --- src/dpp/cluster/webhook.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index d5088ba3f6..d7be8fb775 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -42,7 +42,7 @@ void cluster::delete_webhook_message(const class webhook &wh, snowflake message_ void cluster::delete_webhook_with_token(snowflake webhook_id, const std::string &token, command_completion_event_t callback) { - rest_request(this, API_PATH "/webhooks", std::to_string(webhook_id), utility::url_encode(token), m_get, "", callback); + rest_request(this, API_PATH "/webhooks", std::to_string(webhook_id), utility::url_encode(token), m_delete, "", callback); } From 3a60e3e061662b802930482c34c87e30ebd00737 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sat, 2 Jul 2022 21:48:21 +0200 Subject: [PATCH 134/136] typos --- docpages/03_example_programs.md | 8 ++++---- include/dpp/automod.h | 4 ++-- include/dpp/channel.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docpages/03_example_programs.md b/docpages/03_example_programs.md index 7785abc1f4..d0843a004d 100644 --- a/docpages/03_example_programs.md +++ b/docpages/03_example_programs.md @@ -594,10 +594,10 @@ To add a slash command you should use the dpp::cluster::global_command_create me or dpp::cluster::guild_command_create to create a local command (available only to one guild). When a user issues these commands the reply will arrive via the `on_slashcommand` event which you can hook, and take action -when you see your commands. It is possible to reply to an interaction by using either the dpp::interaction_create_t::reply method, +when you see your commands. It is possible to reply to an interaction by using either the dpp::slashcommand_t::reply method, or by manually instantiating an object of type dpp::interaction_response and attaching a dpp::message object to it. -dpp::interaction_create_t::reply has two overloaded versions of the method, one of which accepts simple std::string replies, for +dpp::slashcommand_t::reply has two overloaded versions of the method, one of which accepts simple std::string replies, for basic text-only messages (if your message is 'ephemeral' you must use this) and one which accepts a dpp::message for more advanced replies. Please note that at present, Discord only supports a small subset of message and embed features within an interaction response object. @@ -612,7 +612,7 @@ int main() { dpp::cluster bot("token"); - bot.on_log(dpp::utility::cout_logger()); + bot.on_log(dpp::utility::cout_logger()); /* The event is fired when someone issues your commands */ bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { @@ -732,7 +732,7 @@ int main() { dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - bot.on_log(dpp::utility::cout_logger()); + bot.on_log(dpp::utility::cout_logger()); /* Message handler to look for a command called !button */ bot.on_message_create([&bot](const dpp::message_create_t & event) { diff --git a/include/dpp/automod.h b/include/dpp/automod.h index 245f72cf73..6f2a504077 100644 --- a/include/dpp/automod.h +++ b/include/dpp/automod.h @@ -148,7 +148,7 @@ struct DPP_EXPORT automod_action : public json_interface { snowflake channel_id; /** - * @brief Silence duration in seconds, for amod_action_timeout + * @brief Silence duration in seconds (Maximum of 2419200), for amod_action_timeout * */ int32_t duration_seconds; @@ -180,7 +180,7 @@ struct DPP_EXPORT automod_action : public json_interface { }; /** - * @brief Represnets an automod rule + * @brief Represents an automod rule */ class DPP_EXPORT automod_rule : public managed, public json_interface { public: diff --git a/include/dpp/channel.h b/include/dpp/channel.h index 912012a262..e8f2284286 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -409,7 +409,6 @@ class DPP_EXPORT channel : public managed, public json_interface { * Does not return reliable information for voice channels, use * dpp::channel::get_voice_members() instead for this. * @return A map of guild members keyed by user id. - * @note If the guild this channel belongs to is not in the cache, the function will always return 0. */ std::map get_members(); From f3b9ef82f64817893a66ec1747e9abf1c89607eb Mon Sep 17 00:00:00 2001 From: Phil B Date: Sat, 2 Jul 2022 22:55:31 +0200 Subject: [PATCH 135/136] typos v2 --- include/dpp/channel.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/dpp/channel.h b/include/dpp/channel.h index e8f2284286..09bad5bd13 100644 --- a/include/dpp/channel.h +++ b/include/dpp/channel.h @@ -409,6 +409,7 @@ class DPP_EXPORT channel : public managed, public json_interface { * Does not return reliable information for voice channels, use * dpp::channel::get_voice_members() instead for this. * @return A map of guild members keyed by user id. + * @note If the guild this channel belongs to is not in the cache, the function will always return 0. */ std::map get_members(); @@ -444,7 +445,7 @@ class DPP_EXPORT channel : public managed, public json_interface { bool is_nsfw() const; /** - * @brief Returns true if the permissions are to be synched with the category it is in. + * @brief Returns true if the permissions are to be synced with the category it is in. * Used only and set manually when using the reorder channels method. * * @return true if keeping permissions From 80f4967503f892142a459dc3a1e0691922b97b48 Mon Sep 17 00:00:00 2001 From: Phil B Date: Sat, 2 Jul 2022 22:59:00 +0200 Subject: [PATCH 136/136] typos v3 --- docpages/03_example_programs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docpages/03_example_programs.md b/docpages/03_example_programs.md index d0843a004d..5f5f8c8af1 100644 --- a/docpages/03_example_programs.md +++ b/docpages/03_example_programs.md @@ -594,10 +594,10 @@ To add a slash command you should use the dpp::cluster::global_command_create me or dpp::cluster::guild_command_create to create a local command (available only to one guild). When a user issues these commands the reply will arrive via the `on_slashcommand` event which you can hook, and take action -when you see your commands. It is possible to reply to an interaction by using either the dpp::slashcommand_t::reply method, +when you see your commands. It is possible to reply to an interaction by using either the dpp::interaction_create_t::reply method, or by manually instantiating an object of type dpp::interaction_response and attaching a dpp::message object to it. -dpp::slashcommand_t::reply has two overloaded versions of the method, one of which accepts simple std::string replies, for +dpp::interaction_create_t::reply has two overloaded versions of the method, one of which accepts simple std::string replies, for basic text-only messages (if your message is 'ephemeral' you must use this) and one which accepts a dpp::message for more advanced replies. Please note that at present, Discord only supports a small subset of message and embed features within an interaction response object.