diff --git a/3rdParty/dpp/appcommand.h b/3rdParty/dpp/appcommand.h index 0409980e3d..ffc0ff5c79 100644 --- a/3rdParty/dpp/appcommand.h +++ b/3rdParty/dpp/appcommand.h @@ -19,6 +19,7 @@ * ************************************************************************************/ #pragma once +#include #include #include #include @@ -26,9 +27,10 @@ #include #include #include +#include #include #include -#include +#include #include namespace dpp { @@ -47,27 +49,59 @@ namespace dpp { * These are the possible parameter value types. */ enum command_option_type : uint8_t { - /** A sub-command */ + /** + * @brief A sub-command. + */ co_sub_command = 1, - /** A sub-command group */ + + /** + * @brief A sub-command group. + */ co_sub_command_group = 2, - /** A string value */ + + /** + * @brief A string value. + */ co_string = 3, - /** An integer value */ + + /** + * @brief An integer value. + */ co_integer = 4, - /** A boolean value */ + + /** + * @brief A boolean value. + */ co_boolean = 5, - /** A user snowflake id */ + + /** + * @brief A user snowflake id. + */ co_user = 6, - /** A channel snowflake id. Includes all channel types and categories */ + + /** + * @brief A channel snowflake id. Includes all channel types and categories. + */ co_channel = 7, - /** A role snowflake id */ + + /** + * @brief A role id (snowflake). + */ co_role = 8, - /** A mentionable. Includes users and roles */ + + /** + * @brief A mentionable (users and roles). + */ co_mentionable = 9, - /** Any double between -2^53 and 2^53 */ + + /** + * @brief Any double between -2^53 and 2^53. + */ co_number = 10, - /** File attachment type */ + + /** + * @brief File attachment type. + */ co_attachment = 11, }; @@ -90,10 +124,33 @@ typedef std::variant { - std::string name; //!< Option name (1-32 chars) - command_value value; //!< Option value - std::map name_localizations; //!< Localisations of command option name +struct DPP_EXPORT command_option_choice : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return command_option_choice& Reference to self + */ + command_option_choice& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief Option name (1-32 chars). + */ + std::string name; + + /** + * @brief Option value. + */ + command_value value; + + /** + * @brief Localisations of command option name. + */ + std::map name_localizations; /** * @brief Construct a new command option choice object @@ -105,7 +162,7 @@ struct DPP_EXPORT command_option_choice : public json_interface command_option_range; @@ -151,22 +201,92 @@ typedef std::variant command_option_range; * Adding options acts like sub-commands and can contain more * options. */ -struct DPP_EXPORT command_option : public json_interface { - command_option_type type; //!< Option type (what type of value is accepted) - std::string name; //!< Option name (1-32 chars) - std::string description; //!< Option description (1-100 chars) - bool required; //!< True if this is a mandatory parameter - bool focused; //!< True if the user is typing in this field, when sent via autocomplete - command_value value; //!< Set only by autocomplete went sent as part of an interaction - std::vector choices; //!< List of choices for multiple choice command - bool autocomplete; //!< True if this option supports auto completion - std::vector options; //!< Sub-commands - std::vector channel_types; //!< Allowed channel types for channel snowflake id options - command_option_range min_value; //!< Minimum value allowed, for co_number and co_integer types only - command_option_range max_value; //!< Maximum value allowed, for co_number and co_integer types only - std::map name_localizations; //!< Localisations of command name - std::map description_localizations; //!< Localisations of command description +struct DPP_EXPORT command_option : public json_interface { +protected: + friend struct json_interface; + /** + * @brief Fill object properties from JSON. Fills options recursively. + * + * @param j JSON to fill from + * @return command_option& Reference to self + */ + command_option& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief Option type (what type of value is accepted). + */ + command_option_type type; + + /** + * @brief Option name (1-32 chars). + */ + std::string name; + + /** + * @brief Option description (1-100 chars). + */ + std::string description; + + /** + * @brief Is this a mandatory parameter? + */ + bool required; + + /** + * @brief Is the user is typing in this field? + * + * @note This is sent via autocomplete. + */ + bool focused; + + /** + * @brief Set only by autocomplete when sent as part of an interaction. + */ + command_value value; + + /** + * @brief List of choices for multiple choice command. + */ + std::vector choices; + + /** + * @brief Does this option supports auto completion? + */ + bool autocomplete; + + /** + * @brief An array of sub-commands (options). + */ + std::vector options; + + /** + * @brief Allowed channel types for channel snowflake id options. + */ + std::vector channel_types; + + // Combines the `min_length` and `max_length` field by storing its value in the int64_t of the command_option_range + + /** + * @brief Minimum value/length that can be entered, for dpp::co_number, dpp::co_integer and dpp::co_string types only. + */ + command_option_range min_value; + + /** + * @brief Maximum value/length that can be entered, for dpp::co_number, dpp::co_integer and dpp::co_string types only. + */ + command_option_range max_value; + + /** + * @brief Localisations of command name. + */ + std::map name_localizations; + + /** + * @brief Localisations of command description. + */ + std::map description_localizations; /** * @brief Construct a new command option object @@ -181,12 +301,12 @@ struct DPP_EXPORT command_option : public json_interface { /** * @brief Add a localisation for this slash command option * @see https://discord.com/developers/docs/reference#locales - * @param language Name of language, see the list of locales linked to above. + * @param language Name of language, see the list of locales linked to above * @param _name name of slash command option in the specified language - * @param _description description of slash command option in the specified language + * @param _description description of slash command option in the specified language (optional) * @return command_option& reference to self for fluent chaining */ - command_option& add_localization(const std::string& language, const std::string& _name, const std::string& _description); + command_option& add_localization(const std::string& language, const std::string& _name, const std::string& _description = ""); /** * @brief Construct a new command option object @@ -203,39 +323,39 @@ struct DPP_EXPORT command_option : public json_interface { * * @param o choice to add * @return command_option& returns a reference to self for chaining of calls - * @throw dpp::exception command_option is an autocomplete, so choices cannot be added + * @throw dpp::logic_exception command_option is an autocomplete, so choices cannot be added */ command_option& add_choice(const command_option_choice &o); /** * @brief Set the minimum numeric value of the option. - * Only valid if the type is co_number or co_integer. + * Only valid if the type is dpp::co_number or dpp::co_integer. * @param min_v Minimum value - * @return command_option& return a reference to sef for chaining of calls + * @return command_option& returns a reference to self for chaining of calls */ command_option& set_min_value(command_option_range min_v); /** * @brief Set the maximum numeric value of the option. - * Only valid if the type is co_number or co_integer. + * Only valid if the type is dpp::co_number or dpp::co_integer. * @param max_v Maximum value - * @return command_option& return a reference to sef for chaining of calls + * @return command_option& returns a reference to self for chaining of calls */ command_option& set_max_value(command_option_range max_v); /** - * @brief Set the minimum string length of the option. - * Only valid if the type is co_string + * @brief Set the minimum string length of the option. Must be between 0 and 6000 (inclusive). + * Only valid if the type is dpp::co_string * @param min_v Minimum value - * @return command_option& return a reference to sef for chaining of calls + * @return command_option& returns a reference to self for chaining of calls */ command_option& set_min_length(command_option_range min_v); /** - * @brief Set the maximum string length of the option. - * Only valid if the type is co_string + * @brief Set the maximum string length of the option. Must be between 1 and 6000 (inclusive). + * Only valid if the type is dpp::co_string * @param max_v Maximum value - * @return command_option& return a reference to sef for chaining of calls + * @return command_option& returns a reference to self for chaining of calls */ command_option& set_max_length(command_option_range max_v); @@ -260,17 +380,9 @@ struct DPP_EXPORT command_option : public json_interface { * * @param autocomp True to enable auto completion for this option * @return command_option& return a reference to self for chaining of calls - * @throw dpp::exception You attempted to enable auto complete on a command_option that has choices added to it + * @throw dpp::logic_exception You attempted to enable auto complete on a command_option that has choices added to it */ command_option& set_auto_complete(bool autocomp); - - /** - * @brief Fill object properties from JSON. Fills options recursively. - * - * @param j JSON to fill from - * @return command_option& Reference to self - */ - command_option& fill_from_json(nlohmann::json* j); }; /** @@ -287,13 +399,56 @@ void to_json(nlohmann::json& j, const command_option& opt); * @brief Response types when responding to an interaction within on_interaction_create. */ enum interaction_response_type { - ir_pong = 1, //!< Acknowledge a Ping - ir_channel_message_with_source = 4, //!< respond to an interaction with a message - ir_deferred_channel_message_with_source = 5, //!< Acknowledge an interaction and edit a response later, the user sees a loading state - ir_deferred_update_message = 6, //!< for components, acknowledge an interaction and edit the original message later; the user does not see a loading state - ir_update_message = 7, //!< for components, edit the message the component was attached to - ir_autocomplete_reply = 8, //!< Reply to autocomplete interaction. Be sure to do this within 500ms of the interaction! - ir_modal_dialog = 9, //!< A modal dialog box + /** + * @brief Acknowledge a Ping + */ + ir_pong = 1, + + /** + * @brief Respond to an interaction with a message. + */ + ir_channel_message_with_source = 4, + + /** + * @brief Acknowledge an interaction and edit a response later, the user sees a loading state + */ + ir_deferred_channel_message_with_source = 5, + + /** + * @brief For components, acknowledge an interaction and edit the original message later; the user does not see a loading state. + */ + ir_deferred_update_message = 6, + + /** + * @brief For components, edit the message the component was attached to. + */ + ir_update_message = 7, + + /** + * @brief Reply to autocomplete interaction. + * + * @note Be sure to do this within 500ms of the interaction! + */ + ir_autocomplete_reply = 8, + + /** + * @brief A modal dialog box + * + * @note Not available for modal submit and ping interactions. + */ + ir_modal_dialog = 9, + + /** + * @brief Acknowledge a interaction with an upgrade button, only available for apps with monetization enabled. + * + * @see https://discord.com/developers/docs/monetization/entitlements#premiumrequired-interaction-response + * @note Not available for autocomplete and ping interactions. + * @warning This response does not support using `content`, `embeds`, or `attachments`, so reply with no data when using this! + * + * @depreciated Replaced with buttons with a style of cos_premium + * This interaction type may stop working at any point + */ + ir_premium_required = 10, }; /** @@ -306,60 +461,70 @@ enum interaction_response_type { * * `mymessage.flags |= dpp::m_ephemeral;` */ -struct DPP_EXPORT interaction_response : public json_interface { +struct DPP_EXPORT interaction_response : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return interaction_response& Reference to self + */ + virtual interaction_response& fill_from_json_impl(nlohmann::json* j); + /** + * @brief Build json for this object + * + * @return json JSON object + */ + virtual json to_json_impl(bool with_id = false) const; + +public: /** * @brief Response type from dpp::interaction_response_type. * Should be one of ir_pong, ir_channel_message_with_source, * or ir_deferred_channel_message_with_source. */ - interaction_response_type type; + interaction_response_type type{}; /** - * @brief A message object. This pointer is always valid - * while the containing interaction_response exists. + * @brief Message tied to this response. */ - struct message* msg; + message msg{}; /** * @brief Array of up to 25 autocomplete choices */ - std::vector autocomplete_choices; + std::vector autocomplete_choices{}; /** * @brief Construct a new interaction response object */ - interaction_response(); + interaction_response() = default; /** * @brief Construct a new interaction response object * * @param t Type of reply - * @param m Message to reply with */ - interaction_response(interaction_response_type t, const struct message& m); + interaction_response(interaction_response_type t); /** * @brief Construct a new interaction response object * * @param t Type of reply + * @param m Message to reply with */ - interaction_response(interaction_response_type t); - - /** - * @brief Fill object properties from JSON - * - * @param j JSON to fill from - * @return interaction_response& Reference to self - */ - interaction_response& fill_from_json(nlohmann::json* j); + interaction_response(interaction_response_type t, const message& m); /** - * @brief Build a json string for this object + * @brief Construct a new interaction response object * - * @return std::string JSON string + * @param t Type of reply + * @param m Message to reply with */ - virtual std::string build_json(bool with_id = false) const; + interaction_response(interaction_response_type t, message&& m); /** * @brief Add a command option choice @@ -372,7 +537,7 @@ struct DPP_EXPORT interaction_response : public json_interface { -private: +protected: + friend struct json_interface; + size_t current_row; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return interaction_response& Reference to self + */ + virtual interaction_modal_response& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build a json for this object + * @param with_id include id in json output + * + * @return json JSON object + */ + virtual json to_json_impl(bool with_id = false) const; + public: + using json_interface::fill_from_json; + using json_interface::to_json; + using json_interface::build_json; + /** * @brief Custom ID for the modal form */ std::string custom_id; /** - * @brief Title of the modal form box + * @brief Title of the modal form box (max 25 characters) */ std::string title; @@ -449,22 +637,6 @@ struct DPP_EXPORT interaction_modal_response : public interaction_response, publ */ interaction_modal_response& add_row(); - /** - * @brief Fill object properties from JSON - * - * @param j JSON to fill from - * @return interaction_response& Reference to self - */ - interaction_modal_response& fill_from_json(nlohmann::json* j); - - /** - * @brief Build a json string for this object - * @param with_id include id in json output - * - * @return std::string JSON string - */ - std::string build_json(bool with_id = false) const; - /** * @brief Destroy the interaction modal response object */ @@ -472,35 +644,42 @@ struct DPP_EXPORT interaction_modal_response : public interaction_response, publ }; /** - * @brief Resolved snowflake ids to users, guild members, roles and channels. + * @brief Resolved snowflake ids to users, guild members, roles and channels. You can use the `interaction::get_resolved_*` methods to easily get a resolved set */ struct DPP_EXPORT command_resolved { /** * @brief Resolved users + * @see interaction::get_resolved_user */ std::map users; /** * @brief Resolved guild members + * @see interaction::get_resolved_member */ std::map members; /** - * @brief Resolved total guild member permissions in the channel, including overwrites + * @brief Resolved total guild member permissions including channel overwrites, permissions from roles and administrator privileges + * @see interaction::get_resolved_permission */ std::map member_permissions; /** * @brief Resolved roles + * @see interaction::get_resolved_role */ std::map roles; /** * @brief Resolved channels + * @see interaction::get_resolved_channel */ std::map channels; /** * @brief Resolved messages + * @see interaction::get_resolved_message */ std::map messages; /** * @brief Resolved attachments + * @see interaction::get_resolved_attachment */ std::map attachments; }; @@ -511,25 +690,44 @@ struct DPP_EXPORT command_resolved { * the command on a channel or in DM. */ struct DPP_EXPORT command_data_option { - std::string name; //!< the name of the parameter - command_option_type type; //!< value of ApplicationCommandOptionType - command_value value; //!< Optional: the value of the pair - std::vector options; //!< Optional: present if this option is a group or subcommand - bool focused; //!< Optional: true if this option is the currently focused option for autocomplete + /** + * @brief The name of the parameter. + */ + std::string name; + + /** + * @brief The type of option (value of ApplicationCommandOptionType). + */ + command_option_type type; + + /** + * @brief Optional: the value of the pair + */ + command_value value; + + /** + * @brief Optional: present if this option is a group or subcommand + */ + std::vector options; + + /** + * @brief Optional: true if this option is the currently focused option for autocomplete + */ + bool focused; /** * @brief Check if the value variant holds std::monostate and options vector is empty (i.e. the option wasn't supplied) * @return bool true, if value variant holds std::monostate and options vector is empty */ bool empty() { - return std::holds_alternative(value) && options.empty(); + return std::holds_alternative(value) && options.empty(); } /** * @brief Get an option value by index * - * @tparam Type to get from the parameter - * @param index index number of parameter + * @tparam T Type to get from the parameter + * @param index index of the option * @return T returned type */ template T& get_value(size_t index) { @@ -550,21 +748,75 @@ void from_json(const nlohmann::json& j, command_data_option& cdo); /** Types of interaction in the dpp::interaction class */ enum interaction_type { - it_ping = 1, //!< ping - it_application_command = 2, //!< application command (slash command) - it_component_button = 3, //!< button click or select menu chosen (component interaction) - it_autocomplete = 4, //!< Autocomplete interaction - it_modal_submit = 5, //!< Modal form submission + /** + * @brief A ping interaction. + */ + it_ping = 1, + + /** + * @brief Application command (slash command) interaction. + */ + it_application_command = 2, + + /** + * @brief Button click or select menu chosen (component interaction) + */ + it_component_button = 3, + + /** + * @brief Autocomplete interaction. + */ + it_autocomplete = 4, + + /** + * @brief Modal form submission. + */ + it_modal_submit = 5, +}; + +/* +* @brief Context type where the interaction can be used or triggered from, e.g. guild, user etc +*/ +enum interaction_context_type { + /** + * @brief Interaction can be used within servers + */ + itc_guild = 0, + + /** + * @brief Interaction can be used within DMs with the app's bot user + */ + itc_bot_dm = 1, + + /** + * @brief Interaction can be used within Group DMs and DMs other than the app's bot user + */ + itc_private_channel = 2, }; /** * @brief Right-click context menu types */ enum slashcommand_contextmenu_type { - ctxm_none = 0, //!< Undefined context menu type - ctxm_chat_input = 1, //!< DEFAULT, these are the slash commands you're used to - ctxm_user = 2, //!< Add command to user context menu - ctxm_message = 3 //!< Add command to message context menu + /** + * @brief Undefined context menu type + */ + ctxm_none = 0, + + /** + * @brief DEFAULT: these are the generic slash commands (e.g. /ping, /pong, etc) + */ + ctxm_chat_input = 1, + + /** + * @brief A slashcommand that goes in the user context menu. + */ + ctxm_user = 2, + + /** + * @brief A slashcommand that goes in the message context menu. + */ + ctxm_message = 3 }; /** @@ -573,17 +825,38 @@ enum slashcommand_contextmenu_type { * with the interaction. */ struct DPP_EXPORT command_interaction { - snowflake id; //!< the ID of the invoked command - std::string name; //!< the name of the invoked command - std::vector options; //!< Optional: the params + values from the user - slashcommand_contextmenu_type type; //!< type of the command interaction - dpp::snowflake target_id; //!< Non-zero target ID for context menu actions. e.g. user id or message id whom clicked or tapped with the context menu https://discord.com/developers/docs/interactions/application-commands#user-commands + /** + * @brief The ID of the invoked command. + */ + snowflake id; + + /** + * @brief The name of the invoked command. + */ + std::string name; + + /** + * @brief Optional: the params + values from the user. + */ + std::vector options; + + /** + * @brief The type of command interaction. + */ + slashcommand_contextmenu_type type; + + /** + * @brief Non-zero target ID for context menu actions (e.g. user id or message id whom clicked or tapped with the context menu). + * + * @see https://discord.com/developers/docs/interactions/application-commands#user-commands + */ + dpp::snowflake target_id; /** * @brief Get an option value by index * - * @tparam Type to get from the parameter - * @param index index number of parameter + * @tparam T Type to get from the parameter + * @param index index of the option * @return T returned type */ template T& get_value(size_t index) { @@ -617,10 +890,12 @@ struct DPP_EXPORT component_interaction { * @brief Component type (dpp::component_type) */ uint8_t component_type; + /** * @brief Custom ID set when created */ std::string custom_id; + /** * @brief Possible values for a drop down list */ @@ -659,7 +934,9 @@ void from_json(const nlohmann::json& j, autocomplete_interaction& ai); * into the events on_form_submit, on_slashcommand, on_user_context_menu, * on_button_click, on_select_menu, etc. */ -class DPP_EXPORT interaction : public managed, public json_interface { +class DPP_EXPORT interaction : public managed, public json_interface { +protected: + friend struct json_interface; /** * @brief Get a resolved object from the resolved set @@ -679,26 +956,125 @@ class DPP_EXPORT interaction : public managed, public json_interfacesecond; } + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return interaction& Reference to self + */ + interaction& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build a json for this object + * + * @param with_id True if to include the ID in the JSON + * @return json JSON object + */ + virtual json to_json_impl(bool with_id = false) const; + public: - snowflake application_id; //!< id of the application this interaction is for - uint8_t type; //!< the type of interaction (dpp::interaction_type) - std::variant data; //!< Optional: the command data payload - snowflake guild_id; //!< Optional: the guild it was sent from - snowflake channel_id; //!< Optional: the channel it was sent from - snowflake message_id; //!< Originating message id for context menu actions - permission app_permissions; //!< Permissions of the bot in the channel/guild where this command was issued - message msg; //!< Originating message for context menu actions - guild_member member; //!< Optional: guild member data for the invoking user, including permissions - user usr; //!< User object for the invoking user - std::string token; //!< a continuation token for responding to the interaction - uint8_t version; //!< read-only property, always 1 - command_resolved resolved; //!< Resolved user/role etc - std::string locale; //!< User's locale (language) - std::string guild_locale; //!< Guild's locale (language) - for guild interactions only - cache_policy_t cache_policy; //!< Cache policy from cluster - - /** - * @brief Construct a new interaction object + /** + * @brief Context where the interaction was triggered from + */ + std::map authorizing_integration_owners; + + /** + * @brief Context where the interaction was triggered from + */ + std::optional context; + + /** + * @brief ID of the application this interaction is for. + */ + snowflake application_id; + + /** + * @brief The type of interaction from dpp::interaction_type. + */ + uint8_t type; + + /** + * @brief Optional: the command data payload. + */ + std::variant data; + + /** + * @brief Optional: the guild it was sent from. + */ + snowflake guild_id; + + /** + * @brief Optional: the channel it was sent from + */ + snowflake channel_id; + + /** + * @brief Optional: The partial channel object where it was sent from. + */ + dpp::channel channel; + + /** + * @brief Originating message id for context menu actions. + */ + snowflake message_id; + + /** + * @brief Permissions of the bot in the channel/guild where this command was issued. + */ + permission app_permissions; + + /** + * @brief Originating message for context menu actions. + */ + message msg; + + /** + * @brief Optional: guild member data for the invoking user, including permissions. Filled when the interaction is invoked in a guild + */ + guild_member member; + + /** + * @brief User object for the invoking user. + */ + user usr; + + /** + * @brief A continuation token for responding to the interaction. + */ + std::string token; + + /** + * @brief Read-only property, always 1. + */ + uint8_t version; + + /** + * @brief Resolved data e.g. users, members, roles, channels, permissions, etc. + */ + command_resolved resolved; + + /** + * @brief User's [locale](https://discord.com/developers/docs/reference#locales) (language). + */ + std::string locale; + + /** + * @brief Guild's locale (language) - for guild interactions only. + */ + std::string guild_locale; + + /** + * @brief Cache policy from cluster. + */ + cache_policy_t cache_policy; + + /** + * @brief For monetized apps, any entitlements for the invoking user, representing access to premium SKUs. + */ + std::vector entitlements; + + /** + * @brief Construct a new interaction object. */ interaction(); @@ -787,7 +1163,7 @@ class DPP_EXPORT interaction : public managed, public json_interface { +class DPP_EXPORT command_permission : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return command_permission& Reference to self + */ + command_permission &fill_from_json_impl(nlohmann::json *j); + public: - snowflake id; //!< the ID of the role or user - command_permission_type type; //!< the type of permission - bool permission; //!< true to allow, false, to disallow + /** + * @brief The ID of the role/user. + */ + snowflake id; /** - * @brief Construct a new command permission object + * @brief The type of permission. + */ + command_permission_type type; + + /** + * @brief True to allow, false to disallow. + */ + bool permission; + + /** + * @brief Construct a new command permission object. */ command_permission() = default; @@ -918,14 +1323,6 @@ class DPP_EXPORT command_permission : public json_interface * @param permission True to allow, false, to disallow */ command_permission(snowflake id, const command_permission_type t, bool permission); - - /** - * @brief Fill object properties from JSON - * - * @param j JSON to fill from - * @return command_permission& Reference to self - */ - command_permission &fill_from_json(nlohmann::json *j); }; /** @@ -941,27 +1338,45 @@ void to_json(nlohmann::json& j, const command_permission& cp); /** * @brief Returned when fetching the permissions for a command in a guild. */ -class DPP_EXPORT guild_command_permissions : public json_interface { +class DPP_EXPORT guild_command_permissions : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return guild_command_permissions& Reference to self + */ + guild_command_permissions &fill_from_json_impl(nlohmann::json *j); + public: - snowflake id; //!< the id of the command - snowflake application_id; //!< the id of the application the command belongs to - snowflake guild_id; //!< the id of the guild - std::vector permissions; //!< the permissions for the command in the guild + /** + * @brief The id of the command. + */ + snowflake id; /** - * @brief Construct a new guild command permissions object + * @brief The id of the application the command belongs to. */ - guild_command_permissions(); + snowflake application_id; - virtual ~guild_command_permissions() = default; + /** + * @brief The id of the guild. + */ + snowflake guild_id; /** - * @brief Fill object properties from JSON - * - * @param j JSON to fill from - * @return guild_command_permissions& Reference to self + * @brief The permissions for the command, in the guild. */ - guild_command_permissions &fill_from_json(nlohmann::json *j); + std::vector permissions; + + /** + * @brief Construct a new guild command permissions object + */ + guild_command_permissions(); + + virtual ~guild_command_permissions() = default; }; @@ -979,7 +1394,26 @@ void to_json(nlohmann::json& j, const guild_command_permissions& gcp); * @brief Represents an application command, created by your bot * either globally, or on a guild. */ -class DPP_EXPORT slashcommand : public managed, public json_interface { +class DPP_EXPORT slashcommand : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return slashcommand& Reference to self + */ + slashcommand& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build a json for this object + * + * @param with_id True if to include the ID in the JSON + * @return json JSON object + */ + json to_json_impl(bool with_id = false) const; + public: /** * @brief Application id (usually matches your bots id) @@ -1041,10 +1475,21 @@ class DPP_EXPORT slashcommand : public managed, public json_interface integration_types; + + /** + * @brief Interaction context(s) where the command can be used, only for globally-scoped commands. By default, all interaction context types included for new commands. + */ + std::vector contexts; + /** * @brief True if this command should be allowed in a DM * D++ defaults this to false. Cannot be set to true in a guild * command, only a global command. + * @deprecated Use dpp::slashcommand_t::set_interaction_contexts instead */ bool dm_permission; @@ -1068,20 +1513,29 @@ class DPP_EXPORT slashcommand : public managed, public json_interface contexts); + /** * @brief Adds a permission to the command * @@ -1181,22 +1643,6 @@ class DPP_EXPORT slashcommand : public managed, public json_interface slashcommand_map; */ typedef std::unordered_map guild_command_permissions_map; -}; +} diff --git a/3rdParty/dpp/application.h b/3rdParty/dpp/application.h index f84ff4be0d..8d9b7325f9 100644 --- a/3rdParty/dpp/application.h +++ b/3rdParty/dpp/application.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -20,14 +21,18 @@ ************************************************************************************/ #pragma once +#include #include #include #include #include #include +#include #include -#include +#include #include +#include +#include namespace dpp { @@ -35,9 +40,13 @@ namespace dpp { * @brief status of a member of a team who maintain a bot/application */ enum team_member_status : uint8_t { - /// User was invited to the team + /** + * @brief User was invited to the team. + */ tms_invited = 1, - /// User has accepted membership onto the team + /** + * @brief User has accepted membership onto the team. + */ tms_accepted = 2 }; @@ -45,23 +54,54 @@ enum team_member_status : uint8_t { * @brief Flags for a bot or application */ enum application_flags : uint32_t { - /// Has gateway presence intent + /** + * @brief Indicates if an app uses the Auto Moderation API + */ + apf_application_automod_rule_create_badge = (1 << 6), + + /** + * @brief Has gateway presence intent + */ apf_gateway_presence = (1 << 12), - /// Has gateway presence intent for <100 guilds + + /** + * @brief Has gateway presence intent for <100 guilds + */ apf_gateway_presence_limited = (1 << 13), - /// Has guild members intent + + /** + * @brief Has guild members intent + */ apf_gateway_guild_members = (1 << 14), - /// Has guild members intent for <100 guilds + + /** + * @brief Has guild members intent for <100 guilds + */ apf_gateway_guild_members_limited = (1 << 15), - /// Verification is pending + + /** + * @brief Verification is pending + */ apf_verification_pending_guild_limit = (1 << 16), - /// Embedded + + /** + * @brief Embedded + */ apf_embedded = (1 << 17), - /// Has approval for message content + + /** + * @brief Has approval for message content + */ apf_gateway_message_content = (1 << 18), - /// Has message content, but <100 guilds + + /** + * @brief Has message content, but <100 guilds + */ apf_gateway_message_content_limited = (1 << 19), - /// Indicates if the app has registered global application commands + + /** + * @brief Indicates if the app has registered global application commands + */ apf_application_command_badge = (1 << 23) }; @@ -69,8 +109,45 @@ enum application_flags : uint32_t { * @brief Represents the settings for the bot/application's in-app authorization link */ struct DPP_EXPORT application_install_params { - 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 + /** + * @brief A bitmask of dpp::permissions to request for the bot role. + */ + permission permissions; + + /** + * @brief The scopes as strings to add the application to the server with. + * + * @see https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes + */ + std::vector scopes; +}; + +/** + * @brief Team member role types for application team members. + * + * These are hard coded to string forms by discord. If further types are added, + * this enum will be extended to support them. + */ +enum team_member_role_t : uint8_t { + /** + * @brief Team owner. + */ + tmr_owner, + + /** + * @brief Team admin. + */ + tmr_admin, + + /** + * @brief Developer + */ + tmr_developer, + + /** + * @brief Read-Only + */ + tmr_readonly, }; /** @@ -78,10 +155,30 @@ struct DPP_EXPORT application_install_params { */ class DPP_EXPORT team_member { public: - team_member_status membership_state; //!< the user's membership state on the team - std::string permissions; //!< will always be [""] - snowflake team_id; //!< the id of the parent team of which they are a member - user member_user; //!< the avatar, discriminator, id, and username of the user + /** + * @brief The user's membership state on the team. + */ + team_member_status membership_state; + + /** + * @brief Will always be "". + */ + std::string permissions; + + /** + * @brief The id of the parent team of which they are a member. + */ + snowflake team_id; + + /** + * @brief The avatar, discriminator, id, and username, of the user. + */ + user member_user; + + /** + * @brief The role of the user in the team. + */ + team_member_role_t member_role; }; /** @@ -89,39 +186,270 @@ class DPP_EXPORT team_member { */ class DPP_EXPORT app_team { public: - utility::iconhash icon; //!< a hash of the image of the team's icon (may be empty) - snowflake id; //!< the unique id of the team - std::vector members; //!< the members of the team - std::string name; //!< the name of the team - snowflake owner_user_id; //!< the user id of the current team owner + /** + * @brief A hash of the image of the team's icon (may be empty). + */ + utility::iconhash icon; + + /** + * @brief The id of the team. + */ + snowflake id; + + /** + * @brief The members of the team. + */ + std::vector members; + + /** + * @brief The name of the team. + */ + std::string name; + + /** + * @brief The user id of the current team owner. + */ + snowflake owner_user_id; +}; + +/** + * @brief Configuration object for an app installation + */ +struct DPP_EXPORT integration_configuration { + std::optional oauth2_install_params; }; /** * @brief The application class represents details of a bot application */ -class DPP_EXPORT application : public managed, public json_interface { +class DPP_EXPORT application : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + application& fill_from_json_impl(nlohmann::json* j); + public: - std::string name; //!< the name of the app - utility::iconhash icon; //!< the icon hash of the app (may be empty) - std::string description; //!< the description of the app - std::string rpc_origins; //!< Optional: an array of rpc origin urls, if rpc is enabled - bool bot_public; //!< when false only app owner can join the app's bot to guilds - bool bot_require_code_grant; //!< when true the app's bot will only join upon completion of the full oauth2 code grant flow - std::string terms_of_service_url; //!< Optional: the url of the app's terms of service - std::string privacy_policy_url; //!< Optional: the url of the app's privacy policy - user owner; //!< Optional: partial user object containing info on the owner of the application - std::string summary; //!< if this application is a game sold on Discord, this field will be the summary field for the store page of its primary sku @deprecated Will be removed in v11 - std::string verify_key; //!< the hex encoded key for verification in interactions and the GameSDK's GetTicket - app_team team; //!< if the application belongs to a team, this will be a list of the members of that team (may be empty) - snowflake guild_id; //!< Optional: if this application is a game sold on Discord, this field will be the guild to which it has been linked - snowflake primary_sku_id; //!< Optional: if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists - std::string slug; //!< Optional: if this application is a game sold on Discord, this field will be the URL slug that links to the store page - utility::iconhash cover_image; //!< Optional: the application's default rich presence invite cover image hash - uint32_t flags; //!< Optional: the application's public flags - std::vector 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 - std::string role_connections_verification_url; //!< The application's role connection verification entry point, which when configured will render the app as a verification method in the guild role verification configuration + /** + * @brief The name of the app. + */ + std::string name; + + /** + * @brief The icon hash of the app (may be empty). + */ + utility::iconhash icon; + + /** + * @brief The description of the app. + */ + std::string description; + + /** + * @brief Optional: an array of rpc origin urls, if rpc is enabled. + */ + std::vector rpc_origins; + + /** + * @brief When false, only app owner add the bot to guilds. + */ + bool bot_public; + + /** + * @brief When true, the app's bot will only join upon completion of the full oauth2 code grant flow + */ + bool bot_require_code_grant; + + /** + * @brief Optional: Partial user object for the bot user associated with the app. + */ + user bot; + + /** + * @brief Optional: the url of the app's terms of service. + */ + std::string terms_of_service_url; + + /** + * @brief Optional: the url of the app's privacy policy. + */ + std::string privacy_policy_url; + + /** + * @brief Optional: partial user object containing info on the owner of the application. + */ + user owner; + + /** + * @brief If this application is a game sold on Discord, this field will be the summary field for the store page of its primary SKU. + * + * @deprecated Will be removed in v11 + */ + std::string summary; + + /** + * @brief The hex encoded key for verification in interactions and the GameSDK's GetTicket. + */ + std::string verify_key; + + /** + * @brief If the application belongs to a team, this will be a list of the members of that team (may be empty). + */ + app_team team; + + /** + * @brief Optional: if this application is a game sold on Discord, this field will be the guild to which it has been linked. + */ + snowflake guild_id; + + /** + * @brief Partial object of the associated guild. + */ + guild guild_obj; + + /** + * @brief Optional: if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists. + */ + snowflake primary_sku_id; + + /** + * @brief Optional: if this application is a game sold on Discord, this field will be the URL slug that links to the store page. + */ + std::string slug; + + /** + * @brief Optional: the application's default rich presence invite cover image hash + */ + utility::iconhash cover_image; + + /** + * @brief Optional: the application's public flags. + */ + uint32_t flags; + + /** + * @brief Optional: Approximate count of guilds the app has been added to. + */ + uint64_t approximate_guild_count; + + /** + * @brief Optional: Approximate count of users that have installed the app + */ + uint64_t approximate_user_install_count; + + /** + * @brief Optional: Array of redirect URIs for the app. + */ + std::vector redirect_uris; + + /** + * @brief Optional: Interactions endpoint URL for the app. + */ + std::string interactions_endpoint_url; + + /** + * @brief The application's role connection verification entry point + * which, when configured, will render the app as a verification method + * in the guild role verification configuration. + */ + std::string role_connections_verification_url; + + /** + * @brief Up to 5 tags describing the content and functionality of the application. + */ + std::vector tags; + + /** + * @brief Settings for the application's default in-app authorization link, if enabled. + */ + application_install_params install_params; + + /** + * @brief Default scopes and permissions for each supported installation context + */ + std::map integration_types_config; + + /** + * @brief The application's default custom authorization link, if enabled. + */ + std::string custom_install_url; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t discoverability_state; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint32_t discovery_eligibility_flags; + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t explicit_content_filter; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t creator_monetization_state; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + bool integration_public; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + bool integration_require_code_grant; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + std::vector interactions_event_types; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t interactions_version; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + bool is_monetized; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint32_t monetization_eligibility_flags; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t monetization_state; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + bool hook; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t rpc_application_state; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t store_application_state; + + /** + * @warning This variable is not documented by discord, we have no idea what it means and how it works. Use at your own risk. + */ + uint8_t verification_state; /** Constructor */ application(); @@ -129,34 +457,34 @@ class DPP_EXPORT application : public managed, public json_interface application_map; -}; +} diff --git a/3rdParty/dpp/auditlog.h b/3rdParty/dpp/auditlog.h index 49ecbedfba..2b2c517906 100644 --- a/3rdParty/dpp/auditlog.h +++ b/3rdParty/dpp/auditlog.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,7 +23,7 @@ #pragma once #include #include -#include +#include #include #include @@ -32,183 +33,449 @@ namespace dpp { * @brief Defines types of audit log entry */ enum audit_type { - /// Guild update - aut_guild_update = 1, - /// Channel create - aut_channel_create = 10, - /// Channel update - aut_channel_update = 11, - /// Channel delete - aut_channel_delete = 12, - /// Channel overwrite create - aut_channel_overwrite_create = 13, - /// Channel overwrite update - aut_channel_overwrite_update = 14, - /// Channel overwrite delete - aut_channel_overwrite_delete = 15, - /// Channel member kick - aut_member_kick = 20, - /// Channel member prune - aut_member_prune = 21, - /// Channel member ban add - aut_member_ban_add = 22, - /// Channel member ban remove - aut_member_ban_remove = 23, - /// Guild member update - aut_member_update = 24, - /// Guild member role update - aut_member_role_update = 25, - /// Guild member move - aut_member_move = 26, - /// Guild member voice disconnect - aut_member_disconnect = 27, - /// Guild bot add - aut_bot_add = 28, - /// Guild role create - aut_role_create = 30, - /// Guild role update - aut_role_update = 31, - /// Guild role delete - aut_role_delete = 32, - /// Guild invite create - aut_invite_create = 40, - /// Guild invite update - aut_invite_update = 41, - /// Guild invite delete - aut_invite_delete = 42, - /// Guild webhook create - aut_webhook_create = 50, - /// Guild webhook update - aut_webhook_update = 51, - /// Guild webhook delete - aut_webhook_delete = 52, - /// Guild emoji create - aut_emoji_create = 60, - /// Guild emoji update - aut_emoji_update = 61, - /// Guild emoji delete - aut_emoji_delete = 62, - /// Guild message delete - aut_message_delete = 72, - /// Guild message bulk delete - aut_message_bulk_delete = 73, - /// Guild message pin - aut_message_pin = 74, - /// Guild message unpin - aut_message_unpin = 75, - /// Guild integration create - aut_integration_create = 80, - /// Guild integration update - aut_integration_update = 81, - /// Guild integration delete - aut_integration_delete = 82, - /// Stage instance create - aut_stage_instance_create = 83, - /// Stage instance update - aut_stage_instance_update = 84, - /// stage instance delete - aut_stage_instance_delete = 85, - /// Sticker create - aut_sticker_create = 90, - /// Sticker update - aut_sticker_update = 91, - /// Sticker delete - aut_sticker_delete = 92, - /// Scheduled event creation - aut_guild_scheduled_event_create = 100, - /// Scheduled event update - aut_guild_scheduled_event_update = 101, - /// Scheduled event deletion - aut_guild_scheduled_event_delete = 102, - /// Thread create - aut_thread_create = 110, - /// Thread update - aut_thread_update = 111, - /// Thread delete - aut_thread_delete = 112, - /// Application command permissions update - aut_appcommand_permission_update = 121, - /// Auto moderation rule creation - aut_automod_rule_create = 140, - /// Auto moderation rule update - aut_automod_rule_update = 141, - /// Auto moderation rule deletion - aut_automod_rule_delete = 142, - /// Message was blocked by Auto Moderation - aut_automod_block_message = 143, - /// Message was flagged by Auto Moderation - aut_automod_flag_to_channel = 144, - /// Member was timed out by Auto Moderation - aut_automod_user_communication_disabled = 145, + /** + * @brief Guild update + */ + aut_guild_update = 1, + + /** + * @brief Channel create + */ + aut_channel_create = 10, + + /** + * @brief Channel update + */ + aut_channel_update = 11, + + /** + * @brief Channel delete + */ + aut_channel_delete = 12, + + /** + * @brief Channel overwrite create + */ + aut_channel_overwrite_create = 13, + + /** + * @brief Channel overwrite update + */ + aut_channel_overwrite_update = 14, + + /** + * @brief Channel overwrite delete + */ + aut_channel_overwrite_delete = 15, + + /** + * @brief Channel member kick + */ + aut_member_kick = 20, + + /** + * @brief Channel member prune + */ + aut_member_prune = 21, + + /** + * @brief Channel member ban add + */ + aut_member_ban_add = 22, + + /** + * @brief Channel member ban remove + */ + aut_member_ban_remove = 23, + + /** + * @brief Guild member update + */ + aut_member_update = 24, + + /** + * @brief Guild member role update + */ + aut_member_role_update = 25, + + /** + * @brief Guild member move + */ + aut_member_move = 26, + + /** + * @brief Guild member voice disconnect + */ + aut_member_disconnect = 27, + + /** + * @brief Guild bot add + */ + aut_bot_add = 28, + + /** + * @brief Guild role create + */ + aut_role_create = 30, + + /** + * @brief Guild role update + */ + aut_role_update = 31, + + /** + * @brief Guild role delete + */ + aut_role_delete = 32, + + /** + * @brief Guild invite create + */ + aut_invite_create = 40, + + /** + * @brief Guild invite update + */ + aut_invite_update = 41, + + /** + * @brief Guild invite delete + */ + aut_invite_delete = 42, + + /** + * @brief Guild webhook create + */ + aut_webhook_create = 50, + + /** + * @brief Guild webhook update + */ + aut_webhook_update = 51, + + /** + * @brief Guild webhook delete + */ + aut_webhook_delete = 52, + + /** + * @brief Guild emoji create + */ + aut_emoji_create = 60, + + /** + * @brief Guild emoji update + */ + aut_emoji_update = 61, + + /** + * @brief Guild emoji delete + */ + aut_emoji_delete = 62, + + /** + * @brief Guild message delete + */ + aut_message_delete = 72, + + /** + * @brief Guild message bulk delete + */ + aut_message_bulk_delete = 73, + + /** + * @brief Guild message pin + */ + aut_message_pin = 74, + + /** + * @brief Guild message unpin + */ + aut_message_unpin = 75, + + /** + * @brief Guild integration create + */ + aut_integration_create = 80, + + /** + * @brief Guild integration update + */ + aut_integration_update = 81, + + /** + * @brief Guild integration delete + */ + aut_integration_delete = 82, + + /** + * @brief Stage instance create + */ + aut_stage_instance_create = 83, + + /** + * @brief Stage instance update + */ + aut_stage_instance_update = 84, + + /** + * @brief stage instance delete + */ + aut_stage_instance_delete = 85, + + /** + * @brief Sticker create + */ + aut_sticker_create = 90, + + /** + * @brief Sticker update + */ + aut_sticker_update = 91, + + /** + * @brief Sticker delete + */ + aut_sticker_delete = 92, + + /** + * @brief Scheduled event creation + */ + aut_guild_scheduled_event_create = 100, + + /** + * @brief Scheduled event update + */ + aut_guild_scheduled_event_update = 101, + + /** + * @brief Scheduled event deletion + */ + aut_guild_scheduled_event_delete = 102, + + /** + * @brief Thread create + */ + aut_thread_create = 110, + + /** + * @brief Thread update + */ + aut_thread_update = 111, + + /** + * @brief Thread delete + */ + aut_thread_delete = 112, + + /** + * @brief Application command permissions update + */ + aut_appcommand_permission_update = 121, + + /** + * @brief Auto moderation rule creation + */ + aut_automod_rule_create = 140, + + /** + * @brief Auto moderation rule update + */ + aut_automod_rule_update = 141, + + /** + * @brief Auto moderation rule deletion + */ + aut_automod_rule_delete = 142, + + /** + * @brief Message was blocked by Auto Moderation + */ + aut_automod_block_message = 143, + + /** + * @brief Message was flagged by Auto Moderation + */ + aut_automod_flag_to_channel = 144, + + /** + * @brief Member was timed out by Auto Moderation + */ + aut_automod_user_communication_disabled = 145, + + /** + * @brief Creator monetization request was created + */ + aut_creator_monetization_request_created = 150, + + /** + * @brief Creator monetization terms were accepted + */ + aut_creator_monetization_terms_accepted = 151, }; /** * @brief Defines audit log changes */ struct DPP_EXPORT audit_change { - /// Optional: Serialised new value of the change, e.g. for nicknames, the new nickname - std::string new_value; - /// Optional: Serialised old value of the change, e.g. for nicknames, the old nickname - std::string old_value; /** - * The property name that was changed, e.g. `nick` for nickname changes - * @note For dpp::aut_appcommand_permission_update updates the key is the id of the user, channel, role, or a permission constant that was updated instead of an actual property name + * @brief Optional: Serialised new value of the change, e.g. for nicknames, the new nickname. + */ + std::string new_value; + + /** + * @brief Optional: Serialised old value of the change, e.g. for nicknames, the old nickname. */ - std::string key; + std::string old_value; + + /** + * @brief The property name that was changed (e.g. `nick` for nickname changes). + * @note For dpp::aut_appcommand_permission_update updates the key is the id of the user, channel, role, or a permission constant that was updated instead of an actual property name. + */ + std::string key; }; /** * @brief Extra information for an audit log entry */ struct DPP_EXPORT audit_extra { - std::string automod_rule_name; //!< Name of the Auto Moderation rule that was triggered - std::string automod_rule_trigger_type; //!< Trigger type of the Auto Moderation rule that was triggered - std::string delete_member_days; //!< number of days after which inactive members were kicked - std::string members_removed; //!< number of members removed by the prune - snowflake channel_id; //!< channel in which the entities were targeted - snowflake message_id; //!< id of the message that was targeted - std::string count; //!< number of entities that were targeted - snowflake id; //!< id of the overwritten entity - std::string type; //!< type of overwritten entity - "0" for "role" or "1" for "member" - std::string role_name; //!< name of the role if type is "0" (not present if type is "1") - snowflake application_id; //!< ID of the app whose permissions were targeted + /** + * @brief Name of the Auto Moderation rule that was triggered. + */ + std::string automod_rule_name; + + /** + * @brief Trigger type of the Auto Moderation rule that was triggered. + */ + std::string automod_rule_trigger_type; + + /** + * @brief Number of days after which inactive members were kicked. + */ + std::string delete_member_days; + + /** + * @brief Number of members removed by the prune. + */ + std::string members_removed; + + /** + * @brief Channel in which the entities were targeted. + */ + snowflake channel_id; + + /** + * @brief ID of the message that was targeted. + */ + snowflake message_id; + + /** + * @brief Number of entities that were targeted. + */ + std::string count; + + /** + * @brief ID of the overwritten entity. + */ + snowflake id; + + /** + * @brief Type of overwritten entity - "0" for "role" or "1" for "member" + */ + std::string type; + + /** + * @brief Name of the role if type is "0" (not present if type is "1"). + */ + std::string role_name; + + /** + * @brief ID of the app whose permissions were targeted + */ + snowflake application_id; }; /** * @brief An individual audit log entry */ -struct DPP_EXPORT audit_entry { - snowflake id; //!< id of the entry +struct DPP_EXPORT audit_entry : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + audit_entry& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief ID of the entry. + */ + snowflake id; + /** * ID of the affected entity (webhook, user, role, etc.) (may be empty) * @note For dpp::audit_type::aut_appcommand_permission_update updates, it's the command ID or the app ID */ - snowflake target_id; - std::vector changes; //!< Optional: changes made to the target_id - snowflake user_id; //!< the user or app that made the changes (may be empty) - audit_type type; //!< type of action that occurred - std::optional extra; //!< Optional: additional info for certain action types - std::string reason; //!< Optional: the reason for the change (1-512 characters) + snowflake target_id; + + /** + * @brief Optional: changes made to the target_id. + */ + std::vector changes; + + /** + * @brief The user or app that made the changes (may be empty). + */ + snowflake user_id; + + /** + * @brief Type of action that occurred. + */ + audit_type type; + + /** + * @brief Optional: additional info for certain action types. + */ + std::optional extra; + + /** + * @brief Optional: the reason for the change (1-512 characters). + */ + std::string reason; + + /** Constructor */ + audit_entry(); + + /** Destructor */ + virtual ~audit_entry() = default; }; /** * @brief The auditlog class represents the audit log entries of a guild. */ -class DPP_EXPORT auditlog : public json_interface { +class DPP_EXPORT auditlog : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + auditlog& fill_from_json_impl(nlohmann::json* j); + public: - std::vector entries; //!< Audit log entries - + /** + * @brief Audit log entries. + */ + std::vector entries; + /** Constructor */ auditlog() = default; /** Destructor */ virtual ~auditlog() = default; - - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - auditlog& fill_from_json(nlohmann::json* j); }; -}; +} diff --git a/3rdParty/dpp/automod.h b/3rdParty/dpp/automod.h index f04dba4230..a16c92a5fa 100644 --- a/3rdParty/dpp/automod.h +++ b/3rdParty/dpp/automod.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -24,7 +25,7 @@ #include #include #include -#include +#include #include namespace dpp { @@ -37,10 +38,12 @@ 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 */ @@ -52,15 +55,19 @@ enum automod_preset_type : uint8_t { */ enum automod_action_type : uint8_t { /** - * @brief Block the message + * @brief Blocks the message and prevents it from being posted. + * A custom explanation can be specified and shown to members whenever their message is blocked */ amod_action_block_message = 1, + /** * @brief Send an alert to a given channel */ amod_action_send_alert = 2, + /** - * @brief time out the user + * @brief timeout the user + * @note Can only be set up for rules with trigger types of dpp::amod_type_keyword and dpp::amod_type_mention_spam */ amod_action_timeout = 3, }; @@ -80,37 +87,60 @@ enum automod_event_type : uint8_t { */ enum automod_trigger_type : uint8_t { /** - * @brief Check if content contains words from a user defined list of keywords + * @brief Check if content contains words from a user defined list of keywords (max 6 of this type per guild) */ amod_type_keyword = 1, + /** * @brief Harmful/malware links * @deprecated Removed by Discord */ amod_type_harmful_link = 2, + /** - * @brief Check if content represents generic spam + * @brief Check if content represents generic spam (max 1 of this type per guild) */ amod_type_spam = 3, + /** - * @brief Check if content contains words from discord pre-defined wordsets + * @brief Check if content contains words from discord pre-defined wordsets (max 1 of this type per guild) */ amod_type_keyword_preset = 4, + /** - * @brief Check if content contains more mentions than allowed + * @brief Check if content contains more mentions than allowed (max 1 of this type per guild) */ amod_type_mention_spam = 5, }; /** - * @brief Metadata associated with an automod action + * @brief Metadata associated with an automod action. Different fields are relevant based on the value of dpp::automod_rule::trigger_type. */ struct DPP_EXPORT automod_metadata : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_metadata& Reference to self + */ + automod_metadata& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build a json for this object + * + * @return json JSON object + */ + virtual json to_json_impl(bool with_id = false) const; + +public: /** * @brief @brief Substrings which will be searched for in content (Maximum of 1000). * * Each keyword can be a phrase which contains multiple words. - * All keywords are case insensitive and can be up to 30 characters. + * All keywords are case insensitive and can be up to 60 characters. * * Wildcard symbols (`*`) can be used to customize how each keyword will be matched. * @@ -119,31 +149,31 @@ struct DPP_EXPORT automod_metadata : public json_interface { * Prefix - word must start with the keyword * * | keyword | matches | - * |----------|-------------------------------------| - * | cat* | catch, Catapult, CAttLE | - * | the mat* | the matrix | - * - * Suffix - word must end with the keyword - * - * | keyword | matches | - * |----------|--------------------------| - * | *cat | wildcat, copyCat | - * | *the mat | breathe mat | - * - * Anywhere - keyword can appear anywhere in the content - * - * | keyword | matches | - * |-----------|-----------------------------| - * | \*cat* | location, eduCation | - * | \*the mat* | breathe matter | - * - * Whole Word - keyword is a full word or phrase and must be surrounded by whitespace at the beginning and end - * - * | keyword | matches | - * |---------|-------------| - * | cat | Cat | - * | the mat | the mat | - * + * |----------|-------------------------------------| + * | cat* | catch, Catapult, CAttLE | + * | the mat* | the matrix | + * + * Suffix - word must end with the keyword + * + * | keyword | matches | + * |----------|--------------------------| + * | *cat | wildcat, copyCat | + * | *the mat | breathe mat | + * + * Anywhere - keyword can appear anywhere in the content + * + * | keyword | matches | + * |-----------|-----------------------------| + * | \*cat* | location, eduCation | + * | \*the mat* | breathe matter | + * + * Whole Word - keyword is a full word or phrase and must be surrounded by whitespace at the beginning and end + * + * | keyword | matches | + * |---------|-------------| + * | cat | Cat | + * | the mat | the mat | + * */ std::vector keywords; @@ -162,10 +192,10 @@ struct DPP_EXPORT automod_metadata : public json_interface { std::vector presets; /** - * @brief Substrings which should not trigger the rule. + * @brief Substrings which should not trigger the rule (Maximum of 100 for the trigger type dpp::amod_type_keyword, Maximum of 1000 for the trigger type dpp::amod_type_keyword_preset). * * Each keyword can be a phrase which contains multiple words. - * All keywords are case insensitive and can be up to 30 characters. + * All keywords are case insensitive and can be up to 60 characters. * * Wildcard symbols (`*`) can be used to customize how each keyword will be matched. * @@ -174,31 +204,31 @@ struct DPP_EXPORT automod_metadata : public json_interface { * Prefix - word must start with the keyword * * | keyword | matches | - * |----------|-------------------------------------| - * | cat* | catch, Catapult, CAttLE | - * | the mat* | the matrix | - * - * Suffix - word must end with the keyword - * - * | keyword | matches | - * |----------|--------------------------| - * | *cat | wildcat, copyCat | - * | *the mat | breathe mat | - * - * Anywhere - keyword can appear anywhere in the content - * - * | keyword | matches | - * |-----------|-----------------------------| - * | \*cat* | location, eduCation | - * | \*the mat* | breathe matter | - * - * Whole Word - keyword is a full word or phrase and must be surrounded by whitespace at the beginning and end - * - * | keyword | matches | - * |---------|-------------| - * | cat | Cat | - * | the mat | the mat | - * + * |----------|-------------------------------------| + * | cat* | catch, Catapult, CAttLE | + * | the mat* | the matrix | + * + * Suffix - word must end with the keyword + * + * | keyword | matches | + * |----------|--------------------------| + * | *cat | wildcat, copyCat | + * | *the mat | breathe mat | + * + * Anywhere - keyword can appear anywhere in the content + * + * | keyword | matches | + * |-----------|-----------------------------| + * | \*cat* | location, eduCation | + * | \*the mat* | breathe matter | + * + * Whole Word - keyword is a full word or phrase and must be surrounded by whitespace at the beginning and end + * + * | keyword | matches | + * |---------|-------------| + * | cat | Cat | + * | the mat | the mat | + * */ std::vector allow_list; @@ -207,6 +237,11 @@ struct DPP_EXPORT automod_metadata : public json_interface { */ uint8_t mention_total_limit; + /** + * @brief Whether to automatically detect mention raids + */ + bool mention_raid_protection_enabled; + /** * @brief Construct a new automod metadata object */ @@ -216,43 +251,50 @@ struct DPP_EXPORT automod_metadata : public json_interface { * @brief Destroy the automod metadata object */ virtual ~automod_metadata(); +}; + +/** + * @brief Represents an automod action + */ +struct DPP_EXPORT automod_action : public json_interface { +protected: + friend struct json_interface; /** * @brief Fill object properties from JSON * * @param j JSON to fill from - * @return automod_metadata& Reference to self + * @return automod_action& Reference to self */ - automod_metadata& fill_from_json(nlohmann::json* j); + automod_action& fill_from_json_impl(nlohmann::json* j); /** - * @brief Build a json string for this object + * @brief Build a json for this object * - * @return std::string JSON string + * @return json JSON object */ - virtual std::string build_json(bool with_id = false) const; - -}; + virtual json to_json_impl(bool with_id = false) const; -/** - * @brief Represents an automod action - */ -struct DPP_EXPORT automod_action : public json_interface { +public: /** * @brief Type of action to take */ automod_action_type type; /** - * @brief Channel ID, for type dpp::amod_action_send_alert + * @brief Channel ID to which user content should be logged, for type dpp::amod_action_send_alert */ snowflake channel_id; + /** + * @brief Additional explanation that will be shown to members whenever their message is blocked. For type dpp::amod_action_block_message + */ + std::string custom_message; + /** * @brief Timeout duration in seconds (Maximum of 2419200), for dpp::amod_action_timeout - * */ - int32_t duration_seconds; + uint32_t duration_seconds; /** * @brief Construct a new automod action object @@ -263,68 +305,81 @@ struct DPP_EXPORT automod_action : public json_interface { * @brief Destroy the automod action object */ virtual ~automod_action(); +}; + +/** + * @brief Represents an automod rule + */ +class DPP_EXPORT automod_rule : public managed, public json_interface { +protected: + friend struct json_interface; /** * @brief Fill object properties from JSON * * @param j JSON to fill from - * @return automod_action& Reference to self + * @return automod_rule& Reference to self */ - automod_action& fill_from_json(nlohmann::json* j); + automod_rule& fill_from_json_impl(nlohmann::json* j); /** * @brief Build a json string for this object * - * @return std::string JSON string + * @return json JSON object */ - virtual std::string build_json(bool with_id = false) const; -}; + virtual json to_json_impl(bool with_id = false) const; -/** - * @brief Represents an automod rule - */ -class DPP_EXPORT automod_rule : public managed, public json_interface { public: /** * @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) */ @@ -339,25 +394,10 @@ class DPP_EXPORT automod_rule : public managed, public json_interface automod_rule_map; -}; +} diff --git a/3rdParty/dpp/ban.h b/3rdParty/dpp/ban.h index 5695bc74a6..e8c6cc4f5d 100644 --- a/3rdParty/dpp/ban.h +++ b/3rdParty/dpp/ban.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,7 +23,7 @@ #pragma once #include #include -#include +#include #include #include @@ -33,35 +34,36 @@ namespace dpp { * */ class DPP_EXPORT ban : public json_interface { -public: - /** The ban reason */ - std::string reason; - /** User ID the ban applies to */ - snowflake user_id; - - /** Constructor */ - ban(); - - /** Destructor */ - virtual ~ban() = default; +protected: + friend struct json_interface; /** Read class values from json object * @param j A json object to read from * @return A reference to self */ - ban& fill_from_json(nlohmann::json* j); + ban& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief The ban reason. + */ + std::string reason; /** - * @brief Build json representation of a ban - * @param with_id Include ID in json - * - * @return std::string stringified json + * @brief User ID the ban applies to. */ - std::string build_json(bool with_id = false) const; + snowflake user_id; + + /** Constructor */ + ban(); + + /** Destructor */ + virtual ~ban() = default; }; -/** A group of bans +/** + * @brief A group of bans. The key is the user ID. */ typedef std::unordered_map ban_map; -}; +} diff --git a/3rdParty/dpp/bignum.h b/3rdParty/dpp/bignum.h new file mode 100644 index 0000000000..dc8bc59166 --- /dev/null +++ b/3rdParty/dpp/bignum.h @@ -0,0 +1,101 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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 + +namespace dpp { + +/** + * @brief This contains the OpenSSL structs. It is not public, + * so that the public interface doesn't depend on OpenSSL directly. + */ +struct openssl_bignum; + +/** +* @brief An arbitrary length integer number. + * Officially, the Discord documentation says that permission values can be any arbitrary + * number of digits. At time of writing there are only 50 bits of permissions, but this is + * set to grow larger and potentially past 64 bits. They will continue to send this data + * as a huge single integer at that point, because this is obviously sensible. /s + * + * @note dpp::bignumber uses OpenSSL BN_* under the hood, as we include openssl anyway + * for HTTPS. +*/ +class DPP_EXPORT bignumber { + /** + * @brief Internal opaque struct to contain OpenSSL things + */ + std::shared_ptr ssl_bn{nullptr}; +public: + /** + * @brief Construct a new bignumber object + */ + bignumber() = default; + + /** + * @brief Parse a std::string of an arbitrary length number into + * a bignumber. + * @param number_string string representation of a number. The + * number must be an integer, and can be positive or negative. + * @note Prefixing number_string with 0x will parse it as hexadecimal. + * This is not case sensitive. + */ + bignumber(const std::string& number_string); + + /** + * @brief Build a bignumber from a vector of 64 bit values. + * The values are accepted in "reverse order", so the first vector + * entry at index 0 is the leftmost 64 bits of the bignum. + * The vector can be any arbitrary length. + * @param bits Vector of 64 bit values which represent the number + */ + bignumber(std::vector bits); + + /** + * @brief Default destructor + */ + ~bignumber() = default; + + /** + * @brief Get the string representation of the bignumber. + * @param hex If false (the default) the number is returned in + * decimal, else if this parameter is true, it will be returned + * as hex (without leading '0x') + * @return String representation of bignumber + */ + [[nodiscard]] std::string get_number(bool hex = false) const; + + /** + * @brief Get the array of 64 bit values that represents the + * bignumber. This is what we should use to store bignumbers + * in memory, not this bignumber class itself, as the bignumber + * class instantiates OpenSSL structs and takes significantly + * more ram than just a vector. + * @return Vector of 64 bit values representing the bignumber + */ + [[nodiscard]] std::vector get_binary() const; +}; + +} diff --git a/3rdParty/dpp/cache.h b/3rdParty/dpp/cache.h index 59b3b4e77d..f1d519c107 100644 --- a/3rdParty/dpp/cache.h +++ b/3rdParty/dpp/cache.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -64,7 +65,7 @@ template class cache { /** * @brief Construct a new cache object. * - * Caches must contain classes derived from dpp::managed. + * @note Caches must contain classes derived from dpp::managed. */ cache() { cache_map = new std::unordered_map; @@ -108,7 +109,7 @@ template class cache { } else if (object != existing->second) { /* Flag old pointer for deletion and replace */ std::lock_guard delete_lock(deletion_mutex); - deletion_queue[existing->second] = time(NULL); + deletion_queue[existing->second] = time(nullptr); (*cache_map)[object->id] = object; } } @@ -133,7 +134,7 @@ template class cache { auto existing = cache_map->find(object->id); if (existing != cache_map->end()) { cache_map->erase(existing); - deletion_queue[object] = time(NULL); + deletion_queue[object] = time(nullptr); } } @@ -249,17 +250,18 @@ template class cache { */ size_t bytes() { std::shared_lock l(cache_mutex); - return sizeof(this) + (cache_map->bucket_count() * sizeof(size_t)); + return sizeof(*this) + (cache_map->bucket_count() * sizeof(size_t)); } }; -/** Run garbage collection across all caches removing deleted items +/** + * Run garbage collection across all caches removing deleted items * that have been deleted over 60 seconds ago. */ void DPP_EXPORT garbage_collection(); -#define cache_decl(type, setter, getter, counter) DPP_EXPORT class type * setter (snowflake id); DPP_EXPORT cache * getter (); DPP_EXPORT uint64_t counter (); +#define cache_decl(type, setter, getter, counter) /** Find an object in the cache by id. @return type* Pointer to the object or nullptr when it's not found */ DPP_EXPORT class type * setter (snowflake id); DPP_EXPORT cache * getter (); /** Get the amount of cached type objects. */ DPP_EXPORT uint64_t counter (); /* Declare major caches */ cache_decl(user, find_user, get_user_cache, get_user_count); @@ -268,5 +270,5 @@ cache_decl(role, find_role, get_role_cache, get_role_count); cache_decl(channel, find_channel, get_channel_cache, get_channel_count); cache_decl(emoji, find_emoji, get_emoji_cache, get_emoji_count); -}; +} diff --git a/3rdParty/dpp/channel.h b/3rdParty/dpp/channel.h index 8c72dcaed9..8f033fc692 100644 --- a/3rdParty/dpp/channel.h +++ b/3rdParty/dpp/channel.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -25,8 +26,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -36,27 +36,81 @@ namespace dpp { /** @brief Flag integers as received from and sent to discord */ enum channel_type : uint8_t { - CHANNEL_TEXT = 0, //!< a text channel within a server - DM = 1, //!< a direct message between users - CHANNEL_VOICE = 2, //!< a voice channel within a server + /** + * @brief A text channel within a server. + */ + CHANNEL_TEXT = 0, + + /** + * @brief A direct message between users. + */ + DM = 1, + + /** + * @brief A voice channel within a server. + */ + CHANNEL_VOICE = 2, + /** * @brief a direct message between multiple users - * @deprecated this channel type was intended to be used with the now deprecated GameBridge SDK. Existing group dms with bots will continue to function, but newly created channels will be unusable + * @deprecated this channel type was intended to be used with the now deprecated GameBridge SDK. + * Existing group dms with bots will continue to function, but newly created channels will be unusable. */ - GROUP_DM = 3, - CHANNEL_CATEGORY = 4, //!< an organizational category that contains up to 50 channels - CHANNEL_ANNOUNCEMENT = 5, //!< a channel that users can follow and crosspost into their own server + GROUP_DM = 3, + /** - * @brief a channel in which game developers can sell their game on Discord - * @deprecated store channels are deprecated by Discord + * @brief An organizational category that contains up to 50 channels. + */ + CHANNEL_CATEGORY = 4, + + /** + * @brief A channel that users can follow and cross-post into their own server. + */ + CHANNEL_ANNOUNCEMENT = 5, + + /** + * @brief A channel in which game developers can sell their game on Discord. + * @deprecated Store channels are deprecated by Discord. + */ + CHANNEL_STORE = 6, + + /** + * @brief A temporary sub-channel within a `GUILD_ANNOUNCEMENT` channel. + */ + CHANNEL_ANNOUNCEMENT_THREAD = 10, + + /** + * @brief A temporary sub-channel within a `GUILD_TEXT` or `GUILD_FORUM` channel. + */ + CHANNEL_PUBLIC_THREAD = 11, + + /** + * @brief A temporary sub-channel within a `GUILD_TEXT` channel + * that is only viewable by those invited and those with the `MANAGE_THREADS` permission. + */ + CHANNEL_PRIVATE_THREAD = 12, + + /** + * @brief A "stage" channel, like a voice channel with one authorised speaker. + */ + CHANNEL_STAGE = 13, + + /** + * @brief The channel in a hub containing the listed servers. + * + * @see https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ + */ + CHANNEL_DIRECTORY = 14, + + /** + * @brief Forum channel that can only contain threads. */ - CHANNEL_STORE = 6, - CHANNEL_ANNOUNCEMENT_THREAD = 10, //!< a temporary sub-channel within a GUILD_ANNOUNCEMENT channel - CHANNEL_PUBLIC_THREAD = 11, //!< a temporary sub-channel within a GUILD_TEXT or GUILD_FORUM channel - CHANNEL_PRIVATE_THREAD = 12, //!< a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission - CHANNEL_STAGE = 13, //!< a "stage" channel, like a voice channel with one authorised speaker - CHANNEL_DIRECTORY = 14, //!< the channel in a [hub](https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ) containing the listed servers - CHANNEL_FORUM = 15 //!< forum channel that can only contain threads + CHANNEL_FORUM = 15, + + /** + * @brief Media channel that can only contain threads, similar to forum channels. + */ + CHANNEL_MEDIA = 16, }; /** @brief Our flags as stored in the object @@ -65,36 +119,52 @@ enum channel_type : uint8_t { * shuffle these values upwards by one bit. */ enum channel_flags : uint16_t { - /// NSFW Gated Channel + /* Note that bits 1 to 4 are used for the channel type mask */ + /** + * @brief NSFW Gated Channel + */ c_nsfw = 0b0000000000010000, - /// Video quality forced to 720p + + /** + * @brief Video quality forced to 720p + */ c_video_quality_720p = 0b0000000000100000, - /// Lock permissions (only used when updating channel positions) + + /** + * @brief Lock permissions (only used when updating channel positions) + */ c_lock_permissions = 0b0000000001000000, - /// Thread is pinned to the top of its parent forum channel + + /** + * @brief Thread is pinned to the top of its parent forum or media channel + */ c_pinned_thread = 0b0000000010000000, - /// Whether a tag is required to be specified when creating a thread in a forum channel. Tags are specified in the thread::applied_tags field. + + /** + * @brief Whether a tag is required to be specified when creating a thread in a forum or media channel. + * Tags are specified in the thread::applied_tags field. + */ c_require_tag = 0b0000000100000000, - /* Note that the 9th and 10th bit are used for the forum layout type */ -}; -/** - * @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 is pinned to the top of its parent forum channel - dc_pinned_thread = 1 << 1, - /// Whether a tag is required to be specified when creating a thread in a forum channel. Tags are specified in the thread::applied_tags field. - dc_require_tag = 1 << 4, + /* Note that the 9th and 10th bit are used for the forum layout type. */ + /** + * @brief When set hides the embedded media download options. Available only for media channels + */ + c_hide_media_download_options = 0b0001000000000000, }; /** * @brief Types for sort posts in a forum channel */ enum default_forum_sort_order_t : uint8_t { - /// Sort forum posts by activity (default) + /** + * @brief Sort forum posts by activity (default) + */ so_latest_activity = 0, - /// Sort forum posts by creation time (from most recent to oldest) + + /** + * @brief Sort forum posts by creation time (from most recent to oldest) + */ so_creation_date = 1, }; @@ -102,18 +172,34 @@ enum default_forum_sort_order_t : uint8_t { * @brief Types of forum layout views that indicates how the threads in a forum channel will be displayed for users by default */ enum forum_layout_type : uint8_t { - fl_not_set = 0, //!< No default has been set for the forum channel - fl_list_view = 1, //!< Display posts as a list - fl_gallery_view = 2, //!< Display posts as a collection of tiles + /** + * @brief No default has been set for the forum channel + */ + fl_not_set = 0, + + /** + * @brief Display posts as a list + */ + fl_list_view = 1, + + /** + * @brief Display posts as a collection of tiles + */ + fl_gallery_view = 2, }; /** * @brief channel permission overwrite types */ enum overwrite_type : uint8_t { - /// Role + /** + * @brief Role + */ ot_role = 0, - /// Member + + /** + * @brief Member + */ ot_member = 1 }; @@ -121,13 +207,24 @@ enum overwrite_type : uint8_t { * @brief Channel permission overwrites */ struct DPP_EXPORT permission_overwrite { - /// ID of the role or the member + /** + * @brief ID of the role or the member + */ snowflake id; - /// Bitmask of allowed permissions + + /** + * @brief Bitmask of allowed permissions + */ permission allow; - /// Bitmask of denied permissions + + /** + * @brief Bitmask of denied permissions + */ permission deny; - /// Type of overwrite. See dpp::overwrite_type + + /** + * @brief Type of overwrite. See dpp::overwrite_type + */ uint8_t type; /** @@ -145,69 +242,70 @@ struct DPP_EXPORT permission_overwrite { permission_overwrite(snowflake id, uint64_t allow, uint64_t deny, overwrite_type type); }; - -/** - * @brief metadata for threads - */ -struct DPP_EXPORT thread_metadata { - /// Timestamp when the thread's archive status was last changed, used for calculating recent activity - time_t archive_timestamp; - /// The duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 - uint16_t auto_archive_duration; - /// Whether a thread is archived - bool archived; - /// Whether a thread is locked. When a thread is locked, only users with `MANAGE_THREADS` can unarchive it - bool locked; - /// Whether non-moderators can add other non-moderators. Only for private threads - bool invitable; -}; - /** * @brief Auto archive duration of threads which will stop showing in the channel list after the specified period of inactivity. * Defined as an enum to fit into 1 byte. Internally it'll be translated to minutes to match the API */ enum auto_archive_duration_t : uint8_t { - /// Auto archive duration of 1 hour. (60 minutes) + /** + * @brief Auto archive duration of 1 hour (60 minutes). + */ arc_1_hour = 1, - /// Auto archive duration of 1 day. (1440 minutes) + + /** + * @brief Auto archive duration of 1 day (1440 minutes). + */ arc_1_day = 2, - /// Auto archive duration of 3 days. (4320 minutes) + + /** + * @brief Auto archive duration of 3 days (4320 minutes). + */ arc_3_days = 3, - /// Auto archive duration of 1 week. (10080 minutes) + + /** + * @brief Auto archive duration of 1 week (10080 minutes). + */ arc_1_week = 4, }; /** - * @brief represents membership of a user with a thread + * @brief Represents a tag that is able to be applied to a thread in a forum or media channel */ -struct DPP_EXPORT thread_member -{ - /// ID of the thread member is part of - snowflake thread_id; - /// ID of the member - snowflake user_id; - /// The time when user last joined the thread - time_t joined; - /// Any user-thread settings, currently only used for notifications - uint32_t flags; - - /** - * @brief Read struct values from a json object +struct DPP_EXPORT forum_tag : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read struct values from a json object * @param j json to read values from - * @return A reference to self + * @return A reference to self */ - thread_member& fill_from_json(nlohmann::json* j); -}; + forum_tag& fill_from_json_impl(nlohmann::json* j); -/** - * @brief Represents a tag that is able to be applied to a thread in a forum channel - */ -struct DPP_EXPORT forum_tag : public managed { - /** The name of the tag (0-20 characters) */ + /** + * @brief Build json for this forum_tag object + * + * @param with_id include the ID in the json + * @return json JSON object + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief The name of the tag (0-20 characters). + */ std::string name; - /** The emoji of the tag. Contains either nothing, the id of a guild's custom emoji or the unicode character of the emoji */ + + /** + * @brief The emoji of the tag. + * Contains either nothing, the id of a guild's custom emoji or the unicode character of the emoji. + */ std::variant emoji; - /** Whether this tag can only be added to or removed from threads by a member with the `MANAGE_THREADS` permission */ + + /** + * @brief Whether this tag can only be added to or removed from threads + * by a member with the `MANAGE_THREADS` permission. + */ bool moderated; /** Constructor */ @@ -221,22 +319,7 @@ struct DPP_EXPORT forum_tag : public managed { forum_tag(const std::string& name); /** Destructor */ - virtual ~forum_tag(); - - /** - * @brief Read struct values from a json object - * @param j json to read values from - * @return A reference to self - */ - forum_tag& fill_from_json(nlohmann::json* j); - - /** - * @brief Build json for this forum_tag object - * - * @param with_id include the ID in the json - * @return std::string JSON string - */ - std::string build_json(bool with_id = false) const; + virtual ~forum_tag() = default; /** * @brief Set name of this forum_tag object @@ -249,60 +332,98 @@ struct DPP_EXPORT forum_tag : public managed { forum_tag& set_name(const std::string& name); }; -/** @brief A group of thread member objects*/ -typedef std::unordered_map thread_member_map; - /** * @brief A definition of a discord channel. * There are one of these for every channel type except threads. Threads are * special snowflakes. Get it? A Discord pun. Hahaha. .... I'll get my coat. */ -class DPP_EXPORT channel : public managed, public json_interface { +class DPP_EXPORT channel : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + channel& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build json for this channel object + * + * @param with_id include the ID in the json + * @return json JSON object + */ + virtual json to_json_impl(bool with_id = false) const; + + static constexpr uint16_t CHANNEL_TYPE_MASK = 0b0000000000001111; + public: - /** Channel name (1-100 characters) */ + /** + * @brief Channel name (1-100 characters). + */ std::string name; - /** Channel topic (0-4096 characters for forum channels, 0-1024 characters for all others) */ + /** + * @brief Channel topic (0-4096 characters for forum and media channels, 0-1024 characters for all others). + */ std::string topic; /** - * @brief Voice region if set for voice channel, otherwise empty string + * @brief Voice region if set for voice channel, otherwise empty string. */ std::string rtc_region; - /** DM recipients */ + /** + * @brief DM recipients. + */ std::vector recipients; - /** Permission overwrites to apply to base permissions */ + /** + * @brief Permission overwrites to apply to base permissions. + */ std::vector permission_overwrites; - /** A set of tags that can be used in a forum channel */ + /** + * @brief A set of tags that can be used in a forum or media channel. + */ std::vector available_tags; /** - * @brief The emoji to show as the default reaction button on a forum post. - * Contains either nothing, the id of a guild's custom emoji or the unicode character of the emoji + * @brief The emoji to show as the default reaction button on a thread in a forum or media channel. + * Contains either nothing, the id of a guild's custom emoji or the unicode character of the emoji. */ std::variant default_reaction; /** - * @brief Channel icon (for group DMs) + * @brief Channel icon (for group DMs). */ utility::iconhash icon; - /** User ID of the creator for group DMs or threads */ + /** + * @brief User ID of the creator for group DMs or threads. + */ snowflake owner_id; - /** Parent ID (for guild channels: id of the parent category, for threads: id of the text channel this thread was created) */ + /** + * @brief Parent ID (for guild channels: id of the parent category, for threads: id of the text channel this thread was created). + */ snowflake parent_id; - /** Guild id of the guild that owns the channel */ + /** + * @brief Guild id of the guild that owns the channel. + */ snowflake guild_id; - /** ID of last message to be sent to the channel (may not point to an existing or valid message or thread) */ + /** + * @brief ID of last message to be sent to the channel. + * + * @warning may not point to an existing or valid message/thread. + */ snowflake last_message_id; - /** Timestamp of last pinned message */ + /** + * @brief Timestamp of last pinned message. + */ time_t last_pin_timestamp; /** @@ -313,31 +434,47 @@ class DPP_EXPORT channel : public managed, public json_interface { */ permission permissions; - /** Sorting position, lower number means higher up the list */ + /** + * @brief Sorting position, lower number means higher up the list + */ uint16_t position; - /** the bitrate (in kilobits) of the voice channel */ + /** + * @brief The bitrate (in kilobits) of the voice channel. + */ uint16_t bitrate; - /** amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected*/ + /** + * @brief Amount of seconds a user has to wait before sending another message (0-21600). + * Bots, as well as users with the permission manage_messages or manage_channel, are unaffected + */ uint16_t rate_limit_per_user; - /** The initial `rate_limit_per_user` to set on newly created threads in a channel. This field is copied to the thread at creation time and does not live update */ + /** + * @brief The initial `rate_limit_per_user` to set on newly created threads in a channel. + * This field is copied to the thread at creation time and does not live update. + */ uint16_t default_thread_rate_limit_per_user; /** * @brief Default duration, copied onto newly created threads. Used by the clients, not the API. - * Threads will stop showing in the channel list after the specified period of inactivity. Defaults to dpp::arc_1_day + * Threads will stop showing in the channel list after the specified period of inactivity. Defaults to dpp::arc_1_day. */ auto_archive_duration_t default_auto_archive_duration; - /** the default sort order type used to order posts in forum channels */ + /** + * @brief The default sort order type used to order posts in forum and media channels. + */ default_forum_sort_order_t default_sort_order; - /** Flags bitmap (dpp::channel_flags) */ + /** + * @brief Flags bitmap (dpp::channel_flags) + */ uint16_t flags; - - /** Maximum user limit for voice channels (0-99) */ + + /** + * @brief Maximum user limit for voice channels (0-99) + */ uint8_t user_limit; /** Constructor */ @@ -353,20 +490,6 @@ class DPP_EXPORT channel : public managed, public json_interface { */ static std::string get_mention(const snowflake& id); - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - channel& fill_from_json(nlohmann::json* j); - - /** - * @brief Build json for this channel object - * - * @param with_id include the ID in the json - * @return std::string JSON string - */ - virtual std::string build_json(bool with_id = false) const; - /** * @brief Set name of this channel object * @@ -404,6 +527,14 @@ class DPP_EXPORT channel : public managed, public json_interface { */ channel& set_default_forum_layout(forum_layout_type layout_type); + /** + * @brief Set the default forum sort order for the forum channel + * + * @param sort_order The sort order + * @return Reference to self, so these method calls may be chained + */ + channel& set_default_sort_order(default_forum_sort_order_t sort_order); + /** * @brief Set flags for this channel object * @@ -494,16 +625,39 @@ class DPP_EXPORT channel : public managed, public json_interface { channel& set_rate_limit_per_user(const uint16_t rate_limit_per_user); /** - * @brief Add a permission_overwrite to this channel object - * - * @param id ID of the role or the member you want to add overwrite for + * @brief Add permission overwrites for a user or role. + * If the channel already has permission overwrites for the passed target, the existing ones will be adjusted by the passed permissions + * + * @param target ID of the role or the member you want to adjust overwrites for * @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 + * @param allowed_permissions bitmask of dpp::permissions you want to allow for this user/role in this channel. Note: You can use the dpp::permission class + * @param denied_permissions bitmask of dpp::permissions you want to deny for this user/role in this channel. Note: You can use the dpp::permission class * - * @return Reference to self, so these method calls may be chained + * @return Reference to self, so these method calls may be chained + */ + channel& add_permission_overwrite(const snowflake target, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); + /** + * @brief Set permission overwrites for a user or role on this channel object. Old permission overwrites for the target will be overwritten + * + * @param target ID of the role or the member you want to set overwrites for + * @param type type of overwrite + * @param allowed_permissions bitmask of allowed dpp::permissions for this user/role in this channel. Note: You can use the dpp::permission class + * @param denied_permissions bitmask of denied dpp::permissions for this user/role in this channel. Note: You can use the dpp::permission class + * + * @return Reference to self, so these method calls may be chained + * + * @note If both `allowed_permissions` and `denied_permissions` parameters are 0, the permission overwrite for the target will be removed + */ + channel& set_permission_overwrite(const snowflake target, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); + /** + * @brief Remove channel specific permission overwrites of a user or role + * + * @param target ID of the role or the member you want to remove permission overwrites of + * @param type type of overwrite + * + * @return Reference to self, so these method calls may be chained */ - channel& add_permission_overwrite(const snowflake id, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); + channel& remove_permission_overwrite(const snowflake target, const overwrite_type type); /** * @brief Get the channel type @@ -574,10 +728,19 @@ class DPP_EXPORT channel : public managed, public json_interface { /** * @brief Get the channel's icon url (if its a group DM), otherwise returns an empty string * - * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized icon is returned. - * @return std::string icon url or empty string + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_icon_url(uint16_t size = 0) const; + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief Returns string of URL to channel + * + * @return string of URL to channel + */ + std::string get_url() const; /** * @brief Returns true if the channel is NSFW gated @@ -636,6 +799,13 @@ class DPP_EXPORT channel : public managed, public json_interface { */ bool is_forum() const; + /** + * @brief Returns true if the channel is a media channel + * + * @return true if media channel + */ + bool is_media_channel() const; + /** * @brief Returns true if the channel is an announcement channel * @@ -686,101 +856,15 @@ class DPP_EXPORT channel : public managed, public json_interface { */ bool is_tag_required() const; -}; - -/** @brief A definition of a discord thread. - * A thread is a superset of a channel. Not to be confused with `std::thread`! - */ -class DPP_EXPORT thread : public channel { -public: - /** - * @brief Thread member of current user if joined to the thread. - * Note this is only set by certain api calls otherwise contains default data - */ - thread_member member; - - /** Thread metadata (threads) */ - thread_metadata metadata; - - /** Created message. Only filled within the cluster::thread_create_in_forum() method */ - message msg; - - /** - * A list of dpp::forum_tag IDs that have been applied to a thread in a forum channel - */ - std::vector applied_tags; - - /** - * @brief Number of messages ever sent in the thread. - * It's similar to thread::message_count on message creation, but will not decrement the number when a message is deleted - */ - uint32_t total_messages_sent; - - /** - * @brief Number of messages (not including the initial message or deleted messages) of the thread. - * For threads created before July 1, 2022, the message count is inaccurate when it's greater than 50. - */ - uint8_t message_count; - - /** Approximate count of members in a thread (threads) */ - uint8_t member_count; - - /** - * @brief Construct a new thread object - */ - thread(); - - /** - * @brief Returns true if the thread is within an announcement channel - * - * @return true if announcement thread - */ - bool is_news_thread() const; - - /** - * @brief Returns true if the channel is a public thread - * - * @return true if public thread - */ - bool is_public_thread() const; - /** - * @brief Returns true if the channel is a private thread + * @brief Returns true if embedded media download options are hidden in a media channel * - * @return true if private thread + * @return true, if embedded media download options are hidden in a media channel */ - bool is_private_thread() const; - - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - thread& fill_from_json(nlohmann::json* j); - - /** - * @brief Destroy the thread object - */ - virtual ~thread(); - - /** - * @brief Build json for this thread object - * - * @param with_id include the ID in the json - * @return std::string JSON string - */ - std::string build_json(bool with_id = false) const; + bool is_download_options_hidden() const; }; - -/** - * @brief Serialize a thread_metadata object to json - * - * @param j JSON object to serialize to - * @param tmdata object to serialize - */ -void to_json(nlohmann::json& j, const thread_metadata& tmdata); - /** * @brief Serialize a permission_overwrite object to json * @@ -794,10 +878,5 @@ void to_json(nlohmann::json& j, const permission_overwrite& po); */ typedef std::unordered_map channel_map; -/** - * @brief A group of threads - */ -typedef std::unordered_map thread_map; - -}; +} diff --git a/3rdParty/dpp/cluster.h b/3rdParty/dpp/cluster.h index c52898962e..b66e55b5bb 100644 --- a/3rdParty/dpp/cluster.h +++ b/3rdParty/dpp/cluster.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,17 +39,15 @@ #include #include #include -#include +#include #include #include #include #include #include #include -#include #include - -using json = nlohmann::json; +#include namespace dpp { @@ -135,6 +133,38 @@ class DPP_EXPORT cluster { */ timer_next_t next_timer; + /** + * @brief Mutex to work with named_commands and synchronize read write access + */ + std::shared_mutex named_commands_mutex; + + /** + * @brief Typedef for slashcommand handler type + */ + using slashcommand_handler_t = std::function; + +#ifdef DPP_CORO + /** + * @brief Typedef for coroutines based slashcommand handler type + */ + using co_slashcommand_handler_t = std::function(const slashcommand_t&)>; + + /** + * @brief Typedef for variant of coroutines based slashcommand handler type and regular version of it + */ + using slashcommand_handler_variant = std::variant; + + /** + * @brief Container to store relation between command name and it's handler + */ + std::map named_commands; +#else + /** + * @brief Container to store relation between command name and it's handler + */ + std::map named_commands; +#endif + /** * @brief Tick active timers */ @@ -204,6 +234,11 @@ class DPP_EXPORT cluster { */ std::condition_variable terminating; + /** + * @brief The time (in seconds) that a request is allowed to take. + */ + uint16_t request_timeout = 20; + /** * @brief Constructor for creating a cluster. All but the token are optional. * @param token The bot token to use for all HTTP commands and websocket connections @@ -213,12 +248,12 @@ class DPP_EXPORT cluster { * @param cluster_id The ID of this cluster, should be between 0 and MAXCLUSTERS-1 * @param maxclusters The total number of clusters that are active, which may be on separate processes or even separate machines. * @param compressed Whether or not to use compression for shards on this cluster. Saves a ton of bandwidth at the cost of some CPU - * @param policy Set the user caching policy for the cluster, either lazy (only cache users/members when they message the bot) or aggressive (request whole member lists on seeing new guilds too) + * @param policy Set the caching policy for the cluster, either lazy (only cache users/members when they message the bot) or aggressive (request whole member lists on seeing new guilds too) * @param request_threads The number of threads to allocate for making HTTP requests to Discord. This defaults to 12. You can increase this at runtime via the object returned from get_rest(). * @param request_threads_raw The number of threads to allocate for making HTTP requests to sites outside of Discord. This defaults to 1. You can increase this at runtime via the object returned from get_raw_rest(). * @throw dpp::exception Thrown on windows, if WinSock fails to initialise, or on any other system if a dpp::request_queue fails to construct */ - cluster(const std::string& token, uint32_t intents = i_default_intents, uint32_t shards = 0, uint32_t cluster_id = 0, uint32_t maxclusters = 1, bool compressed = true, cache_policy_t policy = { cp_aggressive, cp_aggressive, cp_aggressive }, uint32_t request_threads = 12, uint32_t request_threads_raw = 1); + cluster(const std::string& token, uint32_t intents = i_default_intents, uint32_t shards = 0, uint32_t cluster_id = 0, uint32_t maxclusters = 1, bool compressed = true, cache_policy_t policy = cache_policy::cpol_default, uint32_t request_threads = 12, uint32_t request_threads_raw = 1); /** * @brief dpp::cluster is non-copyable @@ -230,6 +265,16 @@ class DPP_EXPORT cluster { */ cluster(const cluster&&) = delete; + /** + * @brief dpp::cluster is non-copyable + */ + cluster& operator=(const cluster&) = delete; + + /** + * @brief dpp::cluster is non-moveable + */ + cluster& operator=(const cluster&&) = delete; + /** * @brief Destroy the cluster object */ @@ -261,6 +306,7 @@ class DPP_EXPORT cluster { * * @param mode websocket protocol to use, either ws_json or ws_etf. * @return cluster& Reference to self for chaining. + * @throw dpp::logic_exception If called after the cluster is started (this is not supported) */ cluster& set_websocket_protocol(websocket_protocol_t mode); @@ -316,7 +362,7 @@ class DPP_EXPORT cluster { * * @return cluster& Reference to self for chaining. */ - cluster& set_default_gateway(std::string& default_gateway); + cluster& set_default_gateway(const std::string& default_gateway); /** * @brief Log a message to whatever log the user is using. @@ -346,6 +392,16 @@ class DPP_EXPORT cluster { */ bool stop_timer(timer t); +#ifdef DPP_CORO + /** + * @brief Get an awaitable to wait a certain amount of seconds. Use the co_await keyword on its return value to suspend the coroutine until the timer ends + * + * @param seconds How long to wait for + * @return async Object that can be co_await-ed to suspend the function for a certain time + */ + [[nodiscard]] async co_sleep(uint64_t seconds); +#endif + /** * @brief Get the dm channel for a user id * @@ -401,17 +457,79 @@ class DPP_EXPORT cluster { */ const shard_list& get_shards(); + /** + * @brief Sets the request timeout. + * + * @param timeout The length of time (in seconds) that requests are allowed to take. Default: 20. + * + * @return cluster& Reference to self for chaining. + */ + cluster& set_request_timeout(uint16_t timeout); + /* Functions for attaching to event handlers */ + /** + * @brief Register a slash command handler. + * + * @param name The name of the slash command to register + * @param handler A handler function of type `slashcommand_handler_t` + * + * @return bool Returns `true` if the command was registered successfully, or `false` if + * the command with the same name already exists + */ + bool register_command(const std::string& name, const slashcommand_handler_t handler); + +#ifdef DPP_CORO + /** + * @brief Register a coroutine-based slash command handler. + * + * @param name The name of the slash command to register. + * @param handler A coroutine handler function of type `co_slashcommand_handler_t`. + * + * @return bool Returns `true` if the command was registered successfully, or `false` if + * the command with the same name already exists. + */ + template + std::enable_if_t, dpp::task>, bool> + register_command(const std::string& name, F&& handler){ + std::unique_lock lk(named_commands_mutex); + auto [_, inserted] = named_commands.try_emplace(name, std::forward(handler)); + return inserted; + }; +#endif + + /** + * @brief Unregister a slash command. + * + * This function unregisters (removes) a previously registered slash command by name. + * If the command is successfully removed, it returns `true`. + * + * @param name The name of the slash command to unregister. + * + * @return bool Returns `true` if the command was successfully unregistered, or `false` + * if the command was not found. + */ + bool unregister_command(const std::string& name); + /** * @brief on voice state update event * + * @see https://discord.com/developers/docs/topics/gateway-events#voice-state-update * @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 voice_state_update_t&, and returns void. */ event_router_t on_voice_state_update; - + /** + * @brief on voice client platform event + * After a client connects, or on joining a vc, you will receive the platform type of each client. This is either desktop + * or mobile. + * + * @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 voice_client_disconnect_t&, and returns void. + */ + event_router_t on_voice_client_platform; + /** * @brief on voice client disconnect event * @@ -456,6 +574,7 @@ class DPP_EXPORT cluster { * Interactions are created by discord when commands you have registered are issued * by a user. For an example of this in action please see \ref slashcommands * + * @see https://discord.com/developers/docs/topics/gateway-events#interaction-create * @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. * @@ -540,6 +659,7 @@ class DPP_EXPORT cluster { * explicitly with dpp::cluster::guild_delete, or via the guild being unavailable due to * an outage. * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-delete * @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 guild_delete_t&, and returns void. */ @@ -552,6 +672,7 @@ class DPP_EXPORT cluster { * channel should not be retained long-term as they will be deleted by the garbage * collector. * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-delete * @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 channel_delete_t&, and returns void. */ @@ -563,6 +684,7 @@ class DPP_EXPORT cluster { * The new channel details have already been applied to the guild when you * receive this event. * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-update * @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 channel_update_t&, and returns void. */ @@ -573,6 +695,7 @@ class DPP_EXPORT cluster { * @brief Called when a shard is connected and ready. * A set of cluster::on_guild_create events will follow this event. * + * @see https://discord.com/developers/docs/topics/gateway-events#ready * @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. */ @@ -584,6 +707,7 @@ class DPP_EXPORT cluster { * The message has already been deleted from Discord when you * receive this event. * + * @see https://discord.com/developers/docs/topics/gateway-events#message-delete * @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_delete_t&, and returns void. */ @@ -593,6 +717,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a user leaves a guild (either through being kicked, or choosing to leave) * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-member-remove * @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 guild_member_remove_t&, and returns void. */ @@ -604,6 +729,7 @@ class DPP_EXPORT cluster { * A resumed session does not need to re-synchronise guilds, members, etc. * This is generally non-fatal and informational only. * + * @see https://discord.com/developers/docs/topics/gateway-events#resumed * @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 resumed_t&, and returns void. */ @@ -613,6 +739,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a new role is created on a guild. * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-role-create * @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 guild_role_create_t&, and returns void. */ @@ -622,6 +749,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a user is typing on a channel. * + * @see https://discord.com/developers/docs/topics/gateway-events#typing-start * @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 typing_start_t&, and returns void. */ @@ -631,6 +759,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a new reaction is added to a message. * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-add * @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_reaction_add_t&, and returns void. */ @@ -642,6 +771,7 @@ class DPP_EXPORT cluster { * D++ will request these for all new guilds if needed, after the cluster::on_guild_create * events. * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk * @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 guild_members_chunk_t&, and returns void. */ @@ -651,6 +781,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a single reaction is removed from a message. * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove * @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_reaction_remove_t&, and returns void. */ @@ -660,7 +791,11 @@ class DPP_EXPORT cluster { /** * @brief Called when a new guild is created. * D++ will request members for the guild for its cache using guild_members_chunk. + * + * @warning If the cache policy has disabled guild caching, the pointer in this event will become invalid after the + * event ends. You should make a copy of any data you wish to preserve beyond this. * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-create * @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 guild_create_t&, and returns void. */ @@ -670,6 +805,10 @@ class DPP_EXPORT cluster { /** * @brief Called when a new channel is created on a guild. * + * @warning If the cache policy has disabled channel caching, the pointer in this event will become invalid after the + * event ends. You should make a copy of any data you wish to preserve beyond this. + * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-create * @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 channel_create_t&, and returns void. */ @@ -679,6 +818,7 @@ class DPP_EXPORT cluster { /** * @brief Called when all reactions for a particular emoji are removed from a message. * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-emoji * @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_reaction_remove_emoji_t&, and returns void. */ @@ -688,6 +828,7 @@ class DPP_EXPORT cluster { /** * @brief Called when multiple messages are deleted from a channel or DM. * + * @see https://discord.com/developers/docs/topics/gateway-events#message-delete-bulk * @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_delete_bulk_t&, and returns void. */ @@ -697,6 +838,9 @@ class DPP_EXPORT cluster { /** * @brief Called when an existing role is updated on a guild. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-role-update * @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 guild_role_update_t&, and returns void. */ @@ -706,6 +850,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a role is deleted in a guild. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-role-delete * @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 guild_role_delete_t&, and returns void. */ @@ -717,6 +864,7 @@ class DPP_EXPORT cluster { * Note that the pinned message is not returned to this event, just the timestamp * of the last pinned message. * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-pins-update * @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 channel_pins_update_t&, and returns void. */ @@ -726,6 +874,7 @@ class DPP_EXPORT cluster { /** * @brief Called when all reactions are removed from a message. * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-all * @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_reaction_remove_all_t&, and returns void. */ @@ -737,6 +886,8 @@ class DPP_EXPORT cluster { * This will be sent either when we establish a new voice channel connection, * or as discord rearrange their infrastructure. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * * @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 voice_server_update_t&, and returns void. */ @@ -747,6 +898,9 @@ class DPP_EXPORT cluster { * @brief Called when new emojis are added to a guild. * The complete set of emojis is sent every time. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update * @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 guild_emojis_update_t&, and returns void. */ @@ -757,6 +911,9 @@ class DPP_EXPORT cluster { * @brief Called when new stickers are added to a guild. * The complete set of stickers is sent every time. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update * @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 guild_stickers_update_t&, and returns void. */ @@ -770,6 +927,7 @@ class DPP_EXPORT cluster { * increase your bot's CPU usage. If you don't need them it is recommended to not ask * for them. * + * @see https://discord.com/developers/docs/topics/gateway-events#presence-update * @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 presence_update_t&, and returns void. */ @@ -779,6 +937,7 @@ class DPP_EXPORT cluster { /** * @brief Called when the webhooks for a guild are updated. * + * @see https://discord.com/developers/docs/topics/gateway-events#webhooks-update * @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 webhooks_update_t&, and returns void. */ @@ -787,6 +946,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a new automod rule is created. * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-create * @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. */ @@ -796,6 +956,7 @@ class DPP_EXPORT cluster { /** * @brief Called when an automod rule is updated. * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-update * @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. */ @@ -804,6 +965,7 @@ class DPP_EXPORT cluster { /** * @brief Called when an automod rule is deleted. * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-delete * @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. */ @@ -812,6 +974,7 @@ class DPP_EXPORT cluster { /** * @brief Called when an automod rule is triggered/executed. * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-action-execution * @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. */ @@ -820,6 +983,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a new member joins a guild. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-member-add * @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 guild_member_add_t&, and returns void. */ @@ -829,6 +995,7 @@ class DPP_EXPORT cluster { /** * @brief Called when an invite is deleted from a guild. * + * @see https://discord.com/developers/docs/topics/gateway-events#invite-delete * @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 invite_delete_t&, and returns void. */ @@ -838,6 +1005,11 @@ class DPP_EXPORT cluster { /** * @brief Called when details of a guild are updated. * + * @warning If the cache policy has disabled guild caching, the pointer in this event will become invalid after the + * event ends. You should make a copy of any data you wish to preserve beyond this. If the guild cache is disabled, + * only changed elements in the updated guild object will be set. all other values will be empty or defaults. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-update * @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 guild_update_t&, and returns void. */ @@ -850,6 +1022,9 @@ class DPP_EXPORT cluster { * An integration is a connection to a guild of a user's associated accounts, * e.g. youtube or twitch, for automatic assignment of roles etc. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update * @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 guild_integrations_update_t&, and returns void. */ @@ -859,6 +1034,9 @@ class DPP_EXPORT cluster { /** * @brief Called when details of a guild member (e.g. their roles or nickname) are updated. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-member-update * @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 guild_member_update_t&, and returns void. */ @@ -868,6 +1046,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a new invite is created for a guild. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#invite-create * @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 invite_create_t&, and returns void. */ @@ -877,6 +1058,7 @@ class DPP_EXPORT cluster { /** * @brief Called when a message is updated (edited). * + * @see https://discord.com/developers/docs/topics/gateway-events#message-update * @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_update_t&, and returns void. */ @@ -888,6 +1070,7 @@ class DPP_EXPORT cluster { * 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. * + * @see https://discord.com/developers/docs/topics/gateway-events#user-update * @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 user_update_t&, and returns void. */ @@ -901,15 +1084,45 @@ class DPP_EXPORT cluster { * the roadmap to be supported as it consumes excessive amounts of RAM. * For an example for caching of messages, please see \ref caching-messages * + * @see https://discord.com/developers/docs/topics/gateway-events#message-create * @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. */ event_router_t on_message_create; + /** + * @brief Called when a vote is added to a message poll. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-poll-vote-add + * @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_poll_vote_add_t&, and returns void. + */ + event_router_t on_message_poll_vote_add; + + /** + * @brief Called when a vote is removed from a message poll. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-poll-vote-remove + * @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_poll_vote_remove_t&, and returns void. + */ + event_router_t on_message_poll_vote_remove; + + /** + * @brief Called when a guild audit log entry is created. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create + * @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 guild_audit_log_entry_create_t&, and returns void. + */ + event_router_t on_guild_audit_log_entry_create; /** * @brief Called when a ban is added to a guild. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-ban-add * @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 guild_ban_add_t&, and returns void. */ @@ -919,6 +1132,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a ban is removed from a guild. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove * @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 guild_ban_remove_t&, and returns void. */ @@ -930,6 +1146,9 @@ class DPP_EXPORT cluster { * An integration is a connection to a guild of a user's associated accounts, * e.g. youtube or twitch, for automatic assignment of roles etc. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#integration-create * @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 integration_create_t&, and returns void. */ @@ -942,6 +1161,9 @@ class DPP_EXPORT cluster { * An integration is a connection to a guild of a user's associated accounts, * e.g. youtube or twitch, for automatic assignment of roles etc. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#integration-update * @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 integration_update_t&, and returns void. */ @@ -953,6 +1175,9 @@ class DPP_EXPORT cluster { * An integration is a connection to a guild of a user's associated accounts, * e.g. youtube or twitch, for automatic assignment of roles etc. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#integration-delete * @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 integration_delete_t&, and returns void. */ @@ -963,6 +1188,9 @@ class DPP_EXPORT cluster { * @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 * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-create * @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. */ @@ -972,6 +1200,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a thread is updated * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-update * @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_update_t&, and returns void. */ @@ -981,6 +1212,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a thread is deleted * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-delete * @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_delete_t&, and returns void. */ @@ -991,6 +1225,9 @@ class DPP_EXPORT cluster { * @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 * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-list-sync * @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. */ @@ -1000,6 +1237,9 @@ class DPP_EXPORT cluster { /** * @brief Called when current user's thread member object is updated * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-member-update * @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_member_update_t&, and returns void. */ @@ -1009,6 +1249,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a thread's member list is updated (without GUILD_MEMBERS intent, is only called for current user) * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-members-update * @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_members_update_t&, and returns void. */ @@ -1018,6 +1261,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a new scheduled event is created * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create * @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 guild_scheduled_event_create_t&, and returns void. */ @@ -1027,6 +1273,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a new scheduled event is updated * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update * @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 guild_scheduled_event_update_t&, and returns void. */ @@ -1036,6 +1285,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a new scheduled event is deleted * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete * @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 guild_scheduled_event_delete_t&, and returns void. */ @@ -1045,6 +1297,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a user is added to a scheduled event * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add * @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 guild_scheduled_event_user_add_t&, and returns void. */ @@ -1052,8 +1307,11 @@ class DPP_EXPORT cluster { /** - * @brief Called when a user is removed to a scheduled event + * @brief Called when a user is removed from a scheduled event + * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove * @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 guild_scheduled_event_user_remove_t&, and returns void. */ @@ -1063,31 +1321,26 @@ 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 + * with XChaCha20-Poly1305, 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 * of dpp::voice_buffer_send_t to determine if you should fill the buffer with more * content. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * * @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 voice_buffer_send_t&, and returns void. */ event_router_t on_voice_buffer_send; - /** - * @brief Called when a user is talking on a voice channel. - * - * @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 voice_user_talking_t&, and returns void. - */ - event_router_t on_voice_user_talking; - - /** * @brief Called when a voice channel is connected and ready to send audio. * Note that this is not directly attached to the READY event of the websocket, * as there is further connection that needs to be done before audio is ready to send. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * * @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 voice_ready_t&, and returns void. */ @@ -1132,8 +1385,11 @@ class DPP_EXPORT cluster { /** * @brief Called when a new stage instance is created on a stage channel. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#stage-instance-create * @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 stage_instance_create_t&, and returns void. */ event_router_t on_stage_instance_create; @@ -1141,6 +1397,9 @@ class DPP_EXPORT cluster { /** * @brief Called when a stage instance is updated. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#stage-instance-update * @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 stage_instance_update_t&, and returns void. */ @@ -1150,11 +1409,44 @@ class DPP_EXPORT cluster { /** * @brief Called when an existing stage instance is deleted from a stage channel. * + * @warning If the cache policy has disabled guild caching, the pointer to the guild in this event may be nullptr. + * + * @see https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete * @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 stage_instance_delete_t&, and returns void. */ event_router_t on_stage_instance_delete; + /** + * @brief Called when a user subscribes to an SKU. + * + * @see https://discord.com/developers/docs/monetization/entitlements#new-entitlement + * @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 channel_delete_t&, and returns void. + */ + event_router_t on_entitlement_create; + + + /** + * @brief Called when a user's subscription renews for the next billing period. + * The `ends_at` field will have an updated value with the new expiration date. + * + * @see https://discord.com/developers/docs/monetization/entitlements#updated-entitlement + * @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 channel_update_t&, and returns void. + */ + event_router_t on_entitlement_update; + + /** + * @brief Called when a user's entitlement is deleted. + * These events are infrequent and only occur if Discord issues a refund, or Discord removes an entitlement via "internal tooling". + * Entitlements **are not deleted** when they expire. + * + * @see https://discord.com/developers/docs/monetization/entitlements#deleted-entitlement + * @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 channel_update_t&, and returns void. + */ + event_router_t on_entitlement_delete; /** * @brief Post a REST request. Where possible use a helper method instead like message_create @@ -1167,8 +1459,10 @@ class DPP_EXPORT cluster { * @param callback Function to call when the HTTP call completes. The callback parameter will contain amongst other things, the decoded json. * @param filename Filename to post for POST requests (for uploading files) * @param filecontent File content to post for POST requests (for uploading files) + * @param filemimetype File content to post for POST requests (for uploading files) + * @param protocol HTTP protocol to use (1.0 and 1.1 are supported) */ - void post_rest(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::string &filename = "", const std::string &filecontent = ""); + void post_rest(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::string &filename = "", const std::string &filecontent = "", const std::string &filemimetype = "", const std::string& protocol = "1.1"); /** * @brief Post a multipart REST request. Where possible use a helper method instead like message_create @@ -1179,10 +1473,9 @@ class DPP_EXPORT cluster { * @param method Method, e.g. GET, POST * @param postdata Post data (usually JSON encoded) * @param callback Function to call when the HTTP call completes. The callback parameter will contain amongst other things, the decoded json. - * @param filename List of filenames to post for POST requests (for uploading files) - * @param filecontent List of file content to post for POST requests (for uploading files) + * @param file_data List of files to post for POST requests (for uploading files) */ - void post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &filename = {}, const std::vector &filecontent = {}); + void post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &file_data = {}); /** * @brief Make a HTTP(S) request. For use when wanting asynchronous access to HTTP APIs outside of Discord. @@ -1193,8 +1486,10 @@ class DPP_EXPORT cluster { * @param postdata POST data * @param mimetype MIME type of POST data * @param headers Headers to send with the request + * @param protocol HTTP protocol to use (1.1 and 1.0 are supported) + * @param request_timeout How many seconds before the connection is considered failed if not finished */ - void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}); + void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5); /** * @brief Respond to a slash command @@ -1219,15 +1514,26 @@ class DPP_EXPORT cluster { */ void interaction_response_edit(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); + /** + * @brief Get the original response to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response + * @param token Token for the interaction webhook + * @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 interaction_response_get_original(const std::string &token, command_completion_event_t callback = utility::log_error()); + /** * @brief Create a followup message to a slash command - * + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response * @param token Token for the interaction webhook * @param m followup message to create * @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 interaction_followup_create(const std::string &token, const message &m, command_completion_event_t callback); + void interaction_followup_create(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); /** * @brief Edit original followup message to a slash command @@ -1242,8 +1548,9 @@ class DPP_EXPORT cluster { void interaction_followup_edit_original(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); /** - * @brief - * + * @brief Delete the initial interaction response + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response * @param token Token for the interaction webhook * @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(). @@ -1253,6 +1560,8 @@ class DPP_EXPORT cluster { /** * @brief Edit followup message to a slash command * The message ID in the message you pass should be correctly set to that of a followup message you previously sent + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message * @param token Token for the interaction webhook * @param m message to edit, the ID should be set * @param callback Function to call when the API call completes. @@ -1262,12 +1571,25 @@ class DPP_EXPORT cluster { /** * @brief Get the followup message to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message * @param token Token for the interaction webhook * @param message_id message to retrieve * @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 interaction_followup_get(const std::string &token, snowflake message_id, command_completion_event_t callback); + + /** + * @brief Get the original followup message to a slash command + * This is an alias for cluster::interaction_response_get_original + * @see cluster::interaction_response_get_original + * + * @param token Token for the interaction webhook + * @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 interaction_followup_get_original(const std::string &token, command_completion_event_t callback = utility::log_error()); /** * @brief Create a global slash command (a bot can have a maximum of 100 of these). @@ -1296,12 +1618,13 @@ class DPP_EXPORT cluster { * @param guild_id Guild to get the audit log of * @param user_id Entries from a specific user ID. Set this to `0` will fetch any user * @param action_type Entries for a specific dpp::audit_type. Set this to `0` will fetch any type - * @param before Entries that preceded a specific audit log entry ID. Used for paginating + * @param before Entries with ID less than a specific audit log entry ID. Used for paginating + * @param after Entries with ID greater than a specific audit log entry ID. Used for paginating * @param limit Maximum number of entries (between 1-100) to return * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::auditlog 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_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, uint32_t limit, command_completion_event_t callback); + void guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit, command_completion_event_t callback); /** * @brief Create a slash command local to a guild @@ -1315,7 +1638,6 @@ class DPP_EXPORT cluster { */ void guild_command_create(const slashcommand &s, snowflake guild_id, command_completion_event_t callback = utility::log_error()); - /** * @brief Create/overwrite guild slash commands. * Any existing guild slash commands on this guild will be deleted and replaced with these. @@ -1329,19 +1651,38 @@ class DPP_EXPORT cluster { */ void guild_bulk_command_create(const std::vector &commands, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + /** + * @brief Delete all existing guild slash commands. + * + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param guild_id Guild ID to delete the slash commands in. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand_map object in confirmation_callback_t::value **which will be empty, meaning there are no commands**. 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_bulk_command_delete(snowflake guild_id, command_completion_event_t callback = utility::log_error()); + /** * @brief Create/overwrite global slash commands. * Any existing global slash commands will be deleted and replaced with these. * * @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. Updates will be available in all guilds after 1 hour. + * overwriting existing commands that are registered globally for this application. * Commands that do not already exist will count toward daily application command create limits. * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::slashcommand_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 global_bulk_command_create(const std::vector &commands, command_completion_event_t callback = utility::log_error()); + /** + * @brief Delete all existing global slash commands. + * + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand_map object in confirmation_callback_t::value **which will be empty, meaning there are no commands**. 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 global_bulk_command_delete(command_completion_event_t callback = utility::log_error()); + /** * @brief Edit a global slash command (a bot can have a maximum of 100 of these) * @@ -1534,6 +1875,15 @@ class DPP_EXPORT cluster { */ void message_edit(const struct message &m, command_completion_event_t callback = utility::log_error()); + /** + * @brief Edit the flags of a message on a channel. The callback function is called when the message has been edited + * + * @param m Message to edit the flags of + * @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 message_edit_flags(const struct message &m, command_completion_event_t callback = utility::log_error()); + /** * @brief Add a reaction to a message. The reaction string must be either an `emojiname:id` or a unicode character. * @@ -1704,6 +2054,54 @@ class DPP_EXPORT cluster { */ void message_delete_bulk(const std::vector &message_ids, snowflake channel_id, command_completion_event_t callback = utility::log_error()); + /** + * @brief Get a list of users that voted for this specific answer. + * + * @param m Message that contains the poll to retrieve the answers from + * @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id) + * @param after Users after this ID should be retrieved if this is set to non-zero + * @param limit This number of users maximum should be returned, up to 100 + * @param callback Function to call when the API call completes. + * @see https://discord.com/developers/docs/resources/poll#get-answer-voters + * On success the callback will contain a dpp::user_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 poll_get_answer_voters(const message& m, uint32_t answer_id, snowflake after, uint64_t limit, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a list of users that voted for this specific answer. + * + * @param message_id ID of the message with the poll to retrieve the answers from + * @param channel_id ID of the channel with the poll to retrieve the answers from + * @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id) + * @param after Users after this ID should be retrieved if this is set to non-zero + * @param limit This number of users maximum should be returned, up to 100 + * @param callback Function to call when the API call completes. + * @see https://discord.com/developers/docs/resources/poll#get-answer-voters + * On success the callback will contain a dpp::user_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 poll_get_answer_voters(snowflake message_id, snowflake channel_id, uint32_t answer_id, snowflake after, uint64_t limit, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Immediately end a poll. + * + * @param m Message that contains the poll + * @param callback Function to call when the API call completes. + * @see https://discord.com/developers/docs/resources/poll#end-poll + * On success the callback will contain a dpp::message object representing the message containing the poll 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 poll_end(const message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Immediately end a poll. + * + * @param message_id ID of the message with the poll to end + * @param channel_id ID of the channel with the poll to end + * @param callback Function to call when the API call completes. + * @see https://discord.com/developers/docs/resources/poll#end-poll + * On success the callback will contain a dpp::message object representing the message containing the poll 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 poll_end(snowflake message_id, snowflake channel_id, command_completion_event_t callback = utility::log_error()); + /** * @brief Get a channel * @@ -1809,11 +2207,11 @@ class DPP_EXPORT cluster { * @brief Get details about an invite * * @see https://discord.com/developers/docs/resources/invite#get-invite - * @param invite Invite code to get information on + * @param invite_code Invite code to get information on * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::invite 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 invite_get(const std::string &invite, command_completion_event_t callback); + void invite_get(const std::string &invite_code, command_completion_event_t callback); /** * @brief Delete an invite @@ -2154,6 +2552,19 @@ 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 Remove the timeout of a guild member. + * A shortcut for guild_member_timeout(guild_id, user_id, 0, callback) + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove the member timeout from + * @param user_id User ID to remove the timeout for + * @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_timeout_remove(snowflake guild_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + /** * @brief Add guild ban * @@ -2250,7 +2661,7 @@ class DPP_EXPORT cluster { * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::dtemplate 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_template_create(snowflake guild_id, const std::string &name, const std::string &description, command_completion_event_t callback); + void guild_template_create(snowflake guild_id, const std::string &name, const std::string &description, command_completion_event_t callback = utility::log_error()); /** * @brief Syncs the template to the guild's current state. @@ -2338,7 +2749,7 @@ class DPP_EXPORT cluster { /** * @brief Get all emojis for a guild * - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emojis + * @see https://discord.com/developers/docs/resources/emoji#list-guild-emojis * @param guild_id Guild ID to get emojis for * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::emoji_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(). @@ -2373,7 +2784,7 @@ class DPP_EXPORT cluster { * @brief Edit a single emoji. * * You must ensure that the emoji passed contained image data using the emoji::load_image() method. - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji + * @see https://discord.com/developers/docs/resources/emoji#modify-guild-emoji * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param guild_id Guild ID to edit emoji on * @param newemoji Emoji to edit @@ -2394,6 +2805,55 @@ class DPP_EXPORT cluster { */ void guild_emoji_delete(snowflake guild_id, snowflake emoji_id, command_completion_event_t callback = utility::log_error()); + /** + * @brief List all Application Emojis + * + * @see https://discord.com/developers/docs/resources/emoji#list-application-emojis + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji_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 application_emojis_get(command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get an Application Emoji + * + * @see https://discord.com/developers/docs/resources/emoji#get-application-emoji + * @param emoji_id The ID of the Emoji to get. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji 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 application_emoji_get(snowflake emoji_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create an Application Emoji + * + * @see https://discord.com/developers/docs/resources/emoji#create-application-emoji + * @param newemoji The emoji to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji 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 application_emoji_create(const class emoji& newemoji, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit an Application Emoji + * + * @see https://discord.com/developers/docs/resources/emoji#modify-application-emoji + * @param newemoji The emoji to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji 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 application_emoji_edit(const class emoji& newemoji, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete an Application Emoji + * + * @see https://discord.com/developers/docs/resources/emoji#delete-application-emoji + * @param emoji_id The emoji's 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 application_emoji_delete(snowflake emoji_id, command_completion_event_t callback = utility::log_error()); + /** * @brief Get prune counts * @@ -2463,6 +2923,8 @@ class DPP_EXPORT cluster { * @param guild_id Guild ID to get integrations for * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::integration_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(). + * + * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. */ void guild_get_integrations(snowflake guild_id, command_completion_event_t callback); @@ -2541,6 +3003,57 @@ class DPP_EXPORT cluster { */ void guild_get_vanity(snowflake guild_id, command_completion_event_t callback); + /** + * @brief Get the guild's onboarding configuration + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param guild_id The guild to pull the onboarding configuration from. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::onboarding object in confirmation_callback_t::value filled to match the vanity url. 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_get_onboarding(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::onboarding object in confirmation_callback_t::value filled to match the vanity url. 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_edit_onboarding(const struct onboarding& o, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::welcome_screen object in confirmation_callback_t::value filled to match the vanity url. 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_get_welcome_screen(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::welcome_screen object in confirmation_callback_t::value filled to match the vanity url. 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_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled, command_completion_event_t callback = utility::log_error()); + /** * @brief Create a webhook * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. @@ -2795,7 +3308,7 @@ class DPP_EXPORT cluster { void user_application_role_connection_update(snowflake application_id, const application_role_connection &connection, command_completion_event_t callback = utility::log_error()); /** - * @brief Get a user by id + * @brief Get a user by id, without using the cache * * @see https://discord.com/developers/docs/resources/user#get-user * @param user_id User ID to retrieve @@ -2808,6 +3321,20 @@ class DPP_EXPORT cluster { */ void user_get(snowflake user_id, command_completion_event_t callback); + /** + * @brief Get a user by id, checking in the cache first + * + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user_identified 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(). + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note If the user is found in the cache, special values set in `dpp::user_identified` will be undefined. This call should be used + * where you want to for example resolve a user who may no longer be in the bot's guilds, for something like a ban log message. + */ + void user_get_cached(snowflake user_id, command_completion_event_t callback); + /** * @brief Get current (bot) user * @@ -2862,19 +3389,24 @@ class DPP_EXPORT cluster { void current_user_get_guilds(command_completion_event_t callback); /** - * @brief Edit current (bot) user + * @brief Edit current (bot) user. + * + * Modify the requester's user account settings. Returns a dpp::user object on success. + * Fires a User Update Gateway event. + * + * @note There appears to be no limit to the image size, however, if your image cannot be processed/uploaded in time, you will receive a malformed http request. * - * Modifies the current member in a guild. Returns the updated guild_member object on success. - * Fires a `Guild Member Update` Gateway event. * @see https://discord.com/developers/docs/resources/user#modify-current-user * @param nickname Nickname to set - * @param image_blob Avatar data to upload (NOTE: Very heavily rate limited!) - * @param type Type of image for avatar + * @param avatar_blob Avatar data to upload + * @param avatar_type Type of image for avatar. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @param banner_blob Banner data to upload + * @param banner_type Type of image for Banner. It can be one of `i_gif`, `i_jpg` or `i_png`. * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::user 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(). - * @throw dpp::exception Image data is larger than the maximum size of 256 kilobytes + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes */ - void current_user_edit(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png, command_completion_event_t callback = utility::log_error()); + void current_user_edit(const std::string &nickname, const std::string& avatar_blob = "", const image_type avatar_type = i_png, const std::string& banner_blob = "", const image_type banner_type = i_png, command_completion_event_t callback = utility::log_error()); /** * @brief Get current user DM channels @@ -2903,7 +3435,7 @@ class DPP_EXPORT cluster { void current_user_leave_guild(snowflake guild_id, command_completion_event_t callback = utility::log_error()); /** - * @brief Create a thread in forum channel + * @brief Create a thread in a forum or media channel * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * * @see https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel @@ -2922,7 +3454,7 @@ class DPP_EXPORT cluster { * @brief Create a thread * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * - * @see https://discord.com/developers/docs/resources/guild#create-guild-channel + * @see https://discord.com/developers/docs/resources/channel#start-thread-without-message * @param thread_name Name of the thread * @param channel_id Channel in which thread to create * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) @@ -2934,10 +3466,21 @@ class DPP_EXPORT cluster { */ void thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user, command_completion_event_t callback = utility::log_error()); + /** + * @brief Edit a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/topics/threads#editing-deleting-threads + * @param t Thread to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::thread 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 thread_edit(const thread &t, command_completion_event_t callback = utility::log_error()); + /** * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#start-thread-from-message * @param thread_name Name of the thread * @param channel_id Channel in which thread to create * @param message_id message to start thread with @@ -2950,7 +3493,7 @@ class DPP_EXPORT cluster { /** * @brief Join a thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#join-thread * @param thread_id Thread ID to join * @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(). @@ -2959,7 +3502,7 @@ class DPP_EXPORT cluster { /** * @brief Leave a thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#leave-thread * @param thread_id Thread ID to leave * @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(). @@ -2968,7 +3511,7 @@ class DPP_EXPORT cluster { /** * @brief Add a member to a thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#add-thread-member * @param thread_id Thread ID to add to * @param user_id Member ID to add * @param callback Function to call when the API call completes. @@ -2978,7 +3521,7 @@ class DPP_EXPORT cluster { /** * @brief Remove a member from a thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#remove-thread-member * @param thread_id Thread ID to remove from * @param user_id Member ID to remove * @param callback Function to call when the API call completes. @@ -2988,7 +3531,7 @@ class DPP_EXPORT cluster { /** * @brief Get a thread member - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#get-thread-member * @param thread_id Thread to get member for * @param user_id ID of the user to get * @param callback Function to call when the API call completes @@ -2998,7 +3541,7 @@ class DPP_EXPORT cluster { /** * @brief Get members of a thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-thread-members * @param thread_id Thread to get members for * @param callback Function to call when the API call completes * On success the callback will contain a dpp::thread_member_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(). @@ -3006,19 +3549,19 @@ class DPP_EXPORT cluster { void thread_members_get(snowflake thread_id, command_completion_event_t callback); /** - * @brief Get active threads in a guild (Sorted by ID in descending order) - * @see https://discord.com/developers/docs/topics/threads + * @brief Get all active threads in the guild, including public and private threads. Threads are ordered by their id, in descending order. + * @see https://discord.com/developers/docs/resources/guild#list-active-guild-threads * @param guild_id Guild to get active threads for * @param callback Function to call when the API call completes - * On success the callback will contain a dpp::thread_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(). + * On success the callback will contain a dpp::active_threads 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 threads_get_active(snowflake guild_id, command_completion_event_t callback); /** * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @param callback Function to call when the API call completes * On success the callback will contain a dpp::thread_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(). @@ -3027,9 +3570,9 @@ class DPP_EXPORT cluster { /** * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @param callback Function to call when the API call completes * On success the callback will contain a dpp::thread_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(). @@ -3038,8 +3581,7 @@ class DPP_EXPORT cluster { /** * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) - - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads * @param channel_id Channel to get public archived threads for * @param before_id Get threads before this id * @param limit Number of threads to get @@ -3048,6 +3590,15 @@ class DPP_EXPORT cluster { */ void threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit, command_completion_event_t callback); + /** + * @brief Get the thread specified by thread_id. This uses the same call as dpp::cluster::channel_get but returns a thread object. + * @see https://discord.com/developers/docs/resources/channel#get-channel + * @param thread_id The id of the thread to obtain. + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::thread 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 thread_get(snowflake thread_id, command_completion_event_t callback); + /** * @brief Create a sticker in a guild * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. @@ -3056,7 +3607,7 @@ class DPP_EXPORT cluster { * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::sticker 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_sticker_create(sticker &s, command_completion_event_t callback = utility::log_error()); + void guild_sticker_create(const sticker &s, command_completion_event_t callback = utility::log_error()); /** * @brief Modify a sticker in a guild @@ -3066,7 +3617,7 @@ class DPP_EXPORT cluster { * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::sticker 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_sticker_modify(sticker &s, command_completion_event_t callback = utility::log_error()); + void guild_sticker_modify(const sticker &s, command_completion_event_t callback = utility::log_error()); /** * @brief Delete a sticker from a guild @@ -3100,7 +3651,7 @@ class DPP_EXPORT cluster { /** * @brief Get all guild stickers - * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @see https://discord.com/developers/docs/resources/sticker#list-guild-stickers * @param guild_id Guild ID of the guild where the sticker is * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::sticker_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(). @@ -3108,8 +3659,8 @@ class DPP_EXPORT cluster { void guild_stickers_get(snowflake guild_id, command_completion_event_t callback); /** - * @brief Get sticker packs - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @brief Get a list of available sticker packs + * @see https://discord.com/developers/docs/resources/sticker#list-sticker-packs * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::sticker_pack_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(). */ @@ -3259,6 +3810,16 @@ class DPP_EXPORT cluster { */ void current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0, command_completion_event_t callback = utility::log_error()); + /** + * @brief Get the bot's voice state in a guild without a Gateway connection + * + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * @param guild_id Guild to get the voice state for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::voicestate 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 current_user_get_voice_state(snowflake guild_id, command_completion_event_t callback); + /** * @brief Set a user's voice state on a stage channel * @@ -3282,6 +3843,17 @@ 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 a user's voice state in a guild without a Gateway connection + * + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * @param guild_id Guild to get the voice state for + * @param user_id The user to get the voice state of + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::voicestate 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 user_get_voice_state(snowflake guild_id, snowflake user_id, command_completion_event_t callback); + /** * @brief Get all auto moderation rules for a guild * @@ -3331,6 +3903,78 @@ class DPP_EXPORT cluster { */ void automod_rule_delete(snowflake guild_id, snowflake rule_id, command_completion_event_t callback = utility::log_error()); + /** + * @brief Returns all entitlements for a given app, active and expired. + * + * @see https://discord.com/developers/docs/monetization/entitlements#list-entitlements + * @param user_id User ID to look up entitlements for. + * @param sku_ids List of SKU IDs to check entitlements for. + * @param before_id Retrieve entitlements before this entitlement ID. + * @param after_id Retrieve entitlements after this entitlement ID. + * @param limit Number of entitlements to return, 1-100 (default 100). + * @param guild_id Guild ID to look up entitlements for. + * @param exclude_ended Whether ended entitlements should be excluded from the search. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji_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 entitlements_get(snowflake user_id = 0, const std::vector& sku_ids = {}, snowflake before_id = 0, snowflake after_id = 0, uint8_t limit = 100, snowflake guild_id = 0, bool exclude_ended = false, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Creates a test entitlement to a given SKU for a given guild or user. + * Discord will act as though that user or guild has entitlement to your premium offering. + * + * @see https://discord.com/developers/docs/monetization/entitlements#create-test-entitlement + * @param new_entitlement The entitlement to create. + * Make sure your dpp::entitlement_type (inside your dpp::entitlement object) matches the type of the owner_id + * (if type is guild, owner_id is a guild id), otherwise it won't work! + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::entitlement 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 entitlement_test_create(const class entitlement& new_entitlement, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Deletes a currently-active test entitlement. + * Discord will act as though that user or guild no longer has entitlement to your premium offering. + * + * @see https://discord.com/developers/docs/monetization/entitlements#delete-test-entitlement + * @param entitlement_id The test entitlement 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 entitlement_test_delete(snowflake entitlement_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed. + * + * @see https://discord.com/developers/docs/monetization/entitlements#consume-an-entitlement + * @param entitlement_id The entitlement to mark as consumed. + * @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 entitlement_consume(snowflake entitlement_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Returns all SKUs for a given application. + * @note Because of how Discord's SKU and subscription systems work, you will see two SKUs for your premium offering. + * For integration and testing entitlements, you should use the SKU with type: 5. + * + * @see https://discord.com/developers/docs/monetization/skus#list-skus + * @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 skus_get(command_completion_event_t callback = utility::log_error()); + + /** + * @brief Set the status of a voice channel. + * + * @see https://github.com/discord/discord-api-docs/pull/6400 (please replace soon). + * @param channel_id The channel to update. + * @param status The new status for the channel. + * @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_set_voice_status(snowflake channel_id, const std::string& status, command_completion_event_t callback = utility::log_error()); + #include #ifdef DPP_CORO #include @@ -3338,4 +3982,4 @@ class DPP_EXPORT cluster { }; -}; +} diff --git a/3rdParty/dpp/cluster_coro_calls.h b/3rdParty/dpp/cluster_coro_calls.h index 46b305a519..8ddf0392a4 100644 --- a/3rdParty/dpp/cluster_coro_calls.h +++ b/3rdParty/dpp/cluster_coro_calls.h @@ -33,14 +33,22 @@ * @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. Updates will be available in all guilds after 1 hour. + * overwriting existing commands that are registered globally for this application. * Commands that do not already exist will count toward daily application command create limits. * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_global_bulk_command_create(const std::vector &commands) { - return dpp::awaitable(this, [&] (auto cc) { this->global_bulk_command_create(commands, cc); }); -} +[[nodiscard]] async co_global_bulk_command_create(const std::vector &commands); + +/** + * @brief Delete all existing global slash commands. + * + * @see dpp::cluster::global_bulk_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_bulk_command_delete(); /** * @brief Create a global slash command (a bot can have a maximum of 100 of these). @@ -51,9 +59,7 @@ auto inline co_global_bulk_command_create(const std::vector &comma * @return slashcommand returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_create(const slashcommand &s) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_create(s, cc); }); -} +[[nodiscard]] async co_global_command_create(const slashcommand &s); /** * @brief Get a global slash command @@ -64,9 +70,7 @@ auto inline co_global_command_create(const slashcommand &s) { * @return slashcommand returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_get(snowflake id) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_get(id, cc); }); -} +[[nodiscard]] async co_global_command_get(snowflake id); /** * @brief Delete a global slash command (a bot can have a maximum of 100 of these) @@ -77,9 +81,7 @@ auto inline co_global_command_get(snowflake id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_delete(snowflake id) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_delete(id, cc); }); -} +[[nodiscard]] async co_global_command_delete(snowflake id); /** * @brief Edit a global slash command (a bot can have a maximum of 100 of these) @@ -90,9 +92,7 @@ auto inline co_global_command_delete(snowflake id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_edit(const slashcommand &s) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_edit(s, cc); }); -} +[[nodiscard]] async co_global_command_edit(const slashcommand &s); /** * @brief Get the application's global slash commands @@ -102,9 +102,7 @@ auto inline co_global_command_edit(const slashcommand &s) { * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_global_commands_get() { - return dpp::awaitable(this, [&] (auto cc) { this->global_commands_get(cc); }); -} +[[nodiscard]] async co_global_commands_get(); /** * @brief Create/overwrite guild slash commands. @@ -118,9 +116,18 @@ auto inline co_global_commands_get() { * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_bulk_command_create(const std::vector &commands, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_bulk_command_create(commands, guild_id, cc); }); -} +[[nodiscard]] async co_guild_bulk_command_create(const std::vector &commands, snowflake guild_id); + +/** + * @brief Delete all existing guild slash commands. + * + * @see dpp::cluster::guild_bulk_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param guild_id Guild ID to delete the slash commands in. + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_bulk_command_delete(snowflake guild_id); /** * @brief Get all slash command permissions of a guild @@ -131,9 +138,7 @@ auto inline co_guild_bulk_command_create(const std::vector &comman * @return guild_command_permissions_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_commands_get_permissions(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_commands_get_permissions(guild_id, cc); }); -} +[[nodiscard]] async co_guild_commands_get_permissions(snowflake guild_id); /** * @brief Edit/Overwrite the permissions of all existing slash commands in a guild @@ -149,9 +154,7 @@ auto inline co_guild_commands_get_permissions(snowflake guild_id) { * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead * \memberof dpp::cluster */ -auto inline co_guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_bulk_command_edit_permissions(commands, guild_id, cc); }); -} +[[nodiscard]] async co_guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id); /** * @brief Create a slash command local to a guild @@ -164,9 +167,7 @@ auto inline co_guild_bulk_command_edit_permissions(const std::vectorguild_command_create(s, guild_id, cc); }); -} +[[nodiscard]] async co_guild_command_create(const slashcommand &s, snowflake guild_id); /** * @brief Delete a slash command local to a guild @@ -178,9 +179,7 @@ auto inline co_guild_command_create(const slashcommand &s, snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_delete(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_delete(id, guild_id, cc); }); -} +[[nodiscard]] async co_guild_command_delete(snowflake id, snowflake guild_id); /** * @brief Edit slash command permissions of a guild @@ -193,9 +192,7 @@ auto inline co_guild_command_delete(snowflake id, snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_edit_permissions(const slashcommand &s, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_edit_permissions(s, guild_id, cc); }); -} +[[nodiscard]] async co_guild_command_edit_permissions(const slashcommand &s, snowflake guild_id); /** * @brief Get a slash command of a guild @@ -208,9 +205,7 @@ auto inline co_guild_command_edit_permissions(const slashcommand &s, snowflake g * @return slashcommand returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_get(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_get(id, guild_id, cc); }); -} +[[nodiscard]] async co_guild_command_get(snowflake id, snowflake guild_id); /** * @brief Get the permissions for a slash command of a guild @@ -222,9 +217,7 @@ auto inline co_guild_command_get(snowflake id, snowflake guild_id) { * @return guild_command_permissions returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_get_permissions(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_get_permissions(id, guild_id, cc); }); -} +[[nodiscard]] async co_guild_command_get_permissions(snowflake id, snowflake guild_id); /** * @brief Edit a slash command local to a guild @@ -236,9 +229,7 @@ auto inline co_guild_command_get_permissions(snowflake id, snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_edit(const slashcommand &s, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_edit(s, guild_id, cc); }); -} +[[nodiscard]] async co_guild_command_edit(const slashcommand &s, snowflake guild_id); /** * @brief Get the application's slash commands for a guild @@ -250,9 +241,7 @@ auto inline co_guild_command_edit(const slashcommand &s, snowflake guild_id) { * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_commands_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_commands_get(guild_id, cc); }); -} +[[nodiscard]] async co_guild_commands_get(snowflake guild_id); /** * @brief Respond to a slash command @@ -265,9 +254,7 @@ auto inline co_guild_commands_get(snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_response_create(interaction_id, token, r, cc); }); -} +[[nodiscard]] async co_interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r); /** * @brief Edit response to a slash command @@ -279,21 +266,30 @@ auto inline co_interaction_response_create(snowflake interaction_id, const std:: * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_response_edit(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_response_edit(token, m, cc); }); -} +[[nodiscard]] async co_interaction_response_edit(const std::string &token, const message &m); + +/** + * @brief Get the original response to a slash command + * + * @see dpp::cluster::interaction_response_get_original + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_response_get_original(const std::string &token); /** * @brief Create a followup message to a slash command - * + * + * @see dpp::cluster::interaction_followup_create + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response * @param token Token for the interaction webhook * @param m followup message to create * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_create(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_create(token, m, cc); }); -} +[[nodiscard]] async co_interaction_followup_create(const std::string &token, const message &m); /** * @brief Edit original followup message to a slash command @@ -306,43 +302,55 @@ auto inline co_interaction_followup_create(const std::string &token, const messa * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_edit_original(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_edit_original(token, m, cc); }); -} +[[nodiscard]] async co_interaction_followup_edit_original(const std::string &token, const message &m); /** - * @brief - * + * @brief Delete the initial interaction response + * + * @see dpp::cluster::interaction_followup_delete + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response * @param token Token for the interaction webhook * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_delete(const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_delete(token, cc); }); -} +[[nodiscard]] async co_interaction_followup_delete(const std::string &token); /** * @brief Edit followup message to a slash command * The message ID in the message you pass should be correctly set to that of a followup message you previously sent + * + * @see dpp::cluster::interaction_followup_edit + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message * @param token Token for the interaction webhook * @param m message to edit, the ID should be set * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_edit(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_edit(token, m, cc); }); -} +[[nodiscard]] async co_interaction_followup_edit(const std::string &token, const message &m); /** * @brief Get the followup message to a slash command + * + * @see dpp::cluster::interaction_followup_get + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message * @param token Token for the interaction webhook * @param message_id message to retrieve * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_get(const std::string &token, snowflake message_id) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_get(token, message_id, cc); }); -} +[[nodiscard]] async co_interaction_followup_get(const std::string &token, snowflake message_id); + +/** + * @brief Get the original followup message to a slash command + * This is an alias for cluster::interaction_response_get_original + * @see dpp::cluster::interaction_followup_get_original + * @see cluster::interaction_response_get_original + * + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_get_original(const std::string &token); /** * @brief Get all auto moderation rules for a guild @@ -351,9 +359,7 @@ auto inline co_interaction_followup_get(const std::string &token, snowflake mess * @return automod_rule_map returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rules_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rules_get(guild_id, cc); }); -} +[[nodiscard]] async co_automod_rules_get(snowflake guild_id); /** * @brief Get a single auto moderation rule @@ -363,9 +369,7 @@ auto inline co_automod_rules_get(snowflake guild_id) { * @return automod_rule returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_get(snowflake guild_id, snowflake rule_id) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_get(guild_id, rule_id, cc); }); -} +[[nodiscard]] async co_automod_rule_get(snowflake guild_id, snowflake rule_id); /** * @brief Create an auto moderation rule @@ -375,9 +379,7 @@ auto inline co_automod_rule_get(snowflake guild_id, snowflake rule_id) { * @return automod_rule returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_create(snowflake guild_id, const automod_rule& r) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_create(guild_id, r, cc); }); -} +[[nodiscard]] async co_automod_rule_create(snowflake guild_id, const automod_rule& r); /** * @brief Edit an auto moderation rule @@ -387,9 +389,7 @@ auto inline co_automod_rule_create(snowflake guild_id, const automod_rule& r) { * @return automod_rule returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_edit(snowflake guild_id, const automod_rule& r) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_edit(guild_id, r, cc); }); -} +[[nodiscard]] async co_automod_rule_edit(snowflake guild_id, const automod_rule& r); /** * @brief Delete an auto moderation rule @@ -399,9 +399,7 @@ auto inline co_automod_rule_edit(snowflake guild_id, const automod_rule& r) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_delete(snowflake guild_id, snowflake rule_id) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_delete(guild_id, rule_id, cc); }); -} +[[nodiscard]] async co_automod_rule_delete(snowflake guild_id, snowflake rule_id); /** * @brief Create a channel @@ -419,9 +417,7 @@ auto inline co_automod_rule_delete(snowflake guild_id, snowflake rule_id) { * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_create(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_create(c, cc); }); -} +[[nodiscard]] async co_channel_create(const class channel &c); /** * @brief Remove a permission from a channel @@ -433,9 +429,7 @@ auto inline co_channel_create(const class channel &c) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_delete_permission(const class channel &c, snowflake overwrite_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_delete_permission(c, overwrite_id, cc); }); -} +[[nodiscard]] async co_channel_delete_permission(const class channel &c, snowflake overwrite_id); /** * @brief Delete a channel @@ -446,9 +440,39 @@ auto inline co_channel_delete_permission(const class channel &c, snowflake overw * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_delete(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_delete(channel_id, cc); }); -} +[[nodiscard]] async co_channel_delete(snowflake channel_id); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @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 bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @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 bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); /** * @brief Edit multiple channels positions @@ -463,9 +487,7 @@ auto inline co_channel_delete(snowflake channel_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_edit_positions(const std::vector &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_edit_positions(c, cc); }); -} +[[nodiscard]] async co_channel_edit_positions(const std::vector &c); /** * @brief Edit a channel @@ -476,9 +498,7 @@ auto inline co_channel_edit_positions(const std::vector &c) { * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_edit(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_edit(c, cc); }); -} +[[nodiscard]] async co_channel_edit(const class channel &c); /** * @brief Follow an announcement (news) channel @@ -489,9 +509,7 @@ auto inline co_channel_edit(const class channel &c) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_follow_news(const class channel &c, snowflake target_channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_follow_news(c, target_channel_id, cc); }); -} +[[nodiscard]] async co_channel_follow_news(const class channel &c, snowflake target_channel_id); /** * @brief Get a channel @@ -502,9 +520,7 @@ auto inline co_channel_follow_news(const class channel &c, snowflake target_chan * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_get(snowflake c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_get(c, cc); }); -} +[[nodiscard]] async co_channel_get(snowflake c); /** * @brief Create invite for a channel @@ -513,12 +529,10 @@ auto inline co_channel_get(snowflake c) { * @see https://discord.com/developers/docs/resources/channel#create-channel-invite * @param c Channel to create an invite on * @param i Invite to create - * @return confirmation returned object on completion + * @return invite returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_invite_create(const class channel &c, const class invite &i) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_invite_create(c, i, cc); }); -} +[[nodiscard]] async co_channel_invite_create(const class channel &c, const class invite &i); /** * @brief Get invites for a channel @@ -529,9 +543,27 @@ auto inline co_channel_invite_create(const class channel &c, const class invite * @return invite_map returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_invites_get(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_invites_get(c, cc); }); -} +[[nodiscard]] async co_channel_invites_get(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param c Channel to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_typing(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param cid Channel ID to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_typing(snowflake cid); /** * @brief Get all channels for a guild @@ -542,9 +574,19 @@ auto inline co_channel_invites_get(const class channel &c) { * @return channel_map returned object on completion * \memberof dpp::cluster */ -auto inline co_channels_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channels_get(guild_id, cc); }); -} +[[nodiscard]] async co_channels_get(snowflake guild_id); + +/** + * @brief Set the status of a voice channel. + * + * @see dpp::cluster::channel_set_voice_status + * @see https://github.com/discord/discord-api-docs/pull/6400 (please replace soon). + * @param channel_id The channel to update. + * @param status The new status for the channel. + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_set_voice_status(snowflake channel_id, const std::string& status); /** * @brief Create a dm channel @@ -554,9 +596,7 @@ auto inline co_channels_get(snowflake guild_id) { * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_create_dm_channel(snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->create_dm_channel(user_id, cc); }); -} +[[nodiscard]] async co_create_dm_channel(snowflake user_id); /** * @brief Get current user DM channels @@ -564,9 +604,7 @@ auto inline co_create_dm_channel(snowflake user_id) { * @return channel_map returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_get_dms() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_get_dms(cc); }); -} +[[nodiscard]] async co_current_user_get_dms(); /** * @brief Create a direct message, also create the channel for the direct message if needed @@ -580,9 +618,7 @@ auto inline co_current_user_get_dms() { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_direct_message_create(snowflake user_id, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->direct_message_create(user_id, m, cc); }); -} +[[nodiscard]] async co_direct_message_create(snowflake user_id, const message &m); /** * @brief Adds a recipient to a Group DM using their access token @@ -595,9 +631,7 @@ auto inline co_direct_message_create(snowflake user_id, const message &m) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick) { - return dpp::awaitable(this, [&] (auto cc) { this->gdm_add(channel_id, user_id, access_token, nick, cc); }); -} +[[nodiscard]] async co_gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick); /** * @brief Removes a recipient from a Group DM @@ -608,9 +642,7 @@ auto inline co_gdm_add(snowflake channel_id, snowflake user_id, const std::strin * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_gdm_remove(snowflake channel_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->gdm_remove(channel_id, user_id, cc); }); -} +[[nodiscard]] async co_gdm_remove(snowflake channel_id, snowflake user_id); /** * @brief Create single emoji. @@ -624,9 +656,7 @@ auto inline co_gdm_remove(snowflake channel_id, snowflake user_id) { * @return emoji returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_create(snowflake guild_id, const class emoji& newemoji) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_create(guild_id, newemoji, cc); }); -} +[[nodiscard]] async co_guild_emoji_create(snowflake guild_id, const class emoji& newemoji); /** * @brief Delete a guild emoji @@ -639,25 +669,21 @@ auto inline co_guild_emoji_create(snowflake guild_id, const class emoji& newemoj * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_delete(guild_id, emoji_id, cc); }); -} +[[nodiscard]] async co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id); /** * @brief Edit a single emoji. * * You must ensure that the emoji passed contained image data using the emoji::load_image() method. * @see dpp::cluster::guild_emoji_edit - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji + * @see https://discord.com/developers/docs/resources/emoji#modify-guild-emoji * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param guild_id Guild ID to edit emoji on * @param newemoji Emoji to edit * @return emoji returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_edit(guild_id, newemoji, cc); }); -} +[[nodiscard]] async co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji); /** * @brief Get a single emoji @@ -669,22 +695,126 @@ auto inline co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji) * @return emoji returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_get(snowflake guild_id, snowflake emoji_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_get(guild_id, emoji_id, cc); }); -} +[[nodiscard]] async co_guild_emoji_get(snowflake guild_id, snowflake emoji_id); /** * @brief Get all emojis for a guild * * @see dpp::cluster::guild_emojis_get - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emojis + * @see https://discord.com/developers/docs/resources/emoji#list-guild-emojis * @param guild_id Guild ID to get emojis for * @return emoji_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emojis_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emojis_get(guild_id, cc); }); -} +[[nodiscard]] async co_guild_emojis_get(snowflake guild_id); + +/** + * @brief List all Application Emojis + * + * @see dpp::cluster::application_emojis_get + * @see https://discord.com/developers/docs/resources/emoji#list-application-emojis + * @return emoji_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_emojis_get(); + +/** + * @brief Get an Application Emoji + * + * @see dpp::cluster::application_emoji_get + * @see https://discord.com/developers/docs/resources/emoji#get-application-emoji + * @param emoji_id The ID of the Emoji to get. + * @return emoji returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_emoji_get(snowflake emoji_id); + +/** + * @brief Create an Application Emoji + * + * @see dpp::cluster::application_emoji_create + * @see https://discord.com/developers/docs/resources/emoji#create-application-emoji + * @param newemoji The emoji to create + * @return emoji returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_emoji_create(const class emoji& newemoji); + +/** + * @brief Edit an Application Emoji + * + * @see dpp::cluster::application_emoji_edit + * @see https://discord.com/developers/docs/resources/emoji#modify-application-emoji + * @param newemoji The emoji to edit + * @return emoji returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_emoji_edit(const class emoji& newemoji); + +/** + * @brief Delete an Application Emoji + * + * @see dpp::cluster::application_emoji_delete + * @see https://discord.com/developers/docs/resources/emoji#delete-application-emoji + * @param emoji_id The emoji's ID to delete. + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_emoji_delete(snowflake emoji_id); + +/** + * @brief Returns all entitlements for a given app, active and expired. + * + * @see dpp::cluster::entitlements_get + * @see https://discord.com/developers/docs/monetization/entitlements#list-entitlements + * @param user_id User ID to look up entitlements for. + * @param sku_ids List of SKU IDs to check entitlements for. + * @param before_id Retrieve entitlements before this entitlement ID. + * @param after_id Retrieve entitlements after this entitlement ID. + * @param limit Number of entitlements to return, 1-100 (default 100). + * @param guild_id Guild ID to look up entitlements for. + * @param exclude_ended Whether ended entitlements should be excluded from the search. + * @return entitlement_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_entitlements_get(snowflake user_id = 0, const std::vector& sku_ids = {}, snowflake before_id = 0, snowflake after_id = 0, uint8_t limit = 100, snowflake guild_id = 0, bool exclude_ended = false); + +/** + * @brief Creates a test entitlement to a given SKU for a given guild or user. + * Discord will act as though that user or guild has entitlement to your premium offering. + * + * @see dpp::cluster::entitlement_test_create + * @see https://discord.com/developers/docs/monetization/entitlements#create-test-entitlement + * @param new_entitlement The entitlement to create. + * Make sure your dpp::entitlement_type (inside your dpp::entitlement object) matches the type of the owner_id + * (if type is guild, owner_id is a guild id), otherwise it won't work! + * @return entitlement returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_entitlement_test_create(const class entitlement& new_entitlement); + +/** + * @brief Deletes a currently-active test entitlement. + * Discord will act as though that user or guild no longer has entitlement to your premium offering. + * + * @see dpp::cluster::entitlement_test_delete + * @see https://discord.com/developers/docs/monetization/entitlements#delete-test-entitlement + * @param entitlement_id The test entitlement to delete. + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_entitlement_test_delete(snowflake entitlement_id); + +/** + * @brief For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed. + * + * @see dpp::cluster::entitlement_consume + * @see https://discord.com/developers/docs/monetization/entitlements#consume-an-entitlement + * @param entitlement_id The entitlement to mark as consumed. + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_entitlement_consume(snowflake entitlement_id); /** * @brief Get the gateway information for the bot using the token @@ -693,9 +823,7 @@ auto inline co_guild_emojis_get(snowflake guild_id) { * @return gateway returned object on completion * \memberof dpp::cluster */ -auto inline co_get_gateway_bot() { - return dpp::awaitable(this, [&] (auto cc) { this->get_gateway_bot(cc); }); -} +[[nodiscard]] async co_get_gateway_bot(); /** * @brief Modify current member @@ -711,9 +839,7 @@ auto inline co_get_gateway_bot() { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_current_member_edit(snowflake guild_id, const std::string &nickname) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_current_member_edit(guild_id, nickname, cc); }); -} +[[nodiscard]] async co_guild_current_member_edit(snowflake guild_id, const std::string &nickname); /** * @brief Get the audit log for a guild @@ -723,14 +849,13 @@ auto inline co_guild_current_member_edit(snowflake guild_id, const std::string & * @param guild_id Guild to get the audit log of * @param user_id Entries from a specific user ID. Set this to `0` will fetch any user * @param action_type Entries for a specific dpp::audit_type. Set this to `0` will fetch any type - * @param before Entries that preceded a specific audit log entry ID. Used for paginating + * @param before Entries with ID less than a specific audit log entry ID. Used for paginating + * @param after Entries with ID greater than a specific audit log entry ID. Used for paginating * @param limit Maximum number of entries (between 1-100) to return * @return auditlog returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, uint32_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_auditlog_get(guild_id, user_id, action_type, before, limit, cc); }); -} +[[nodiscard]] async co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit); /** * @brief Add guild ban @@ -746,9 +871,7 @@ auto inline co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_ * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_ban_add(guild_id, user_id, delete_message_seconds, cc); }); -} +[[nodiscard]] async co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0); /** * @brief Delete guild ban @@ -763,9 +886,7 @@ auto inline co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t del * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_ban_delete(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_ban_delete(guild_id, user_id, cc); }); -} +[[nodiscard]] async co_guild_ban_delete(snowflake guild_id, snowflake user_id); /** * @brief Create a guild @@ -788,9 +909,7 @@ auto inline co_guild_ban_delete(snowflake guild_id, snowflake user_id) { * @return guild returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_create(const class guild &g) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_create(g, cc); }); -} +[[nodiscard]] async co_guild_create(const class guild &g); /** * @brief Delete a guild @@ -803,9 +922,7 @@ auto inline co_guild_create(const class guild &g) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_delete(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_delete(guild_id, cc); }); -} +[[nodiscard]] async co_guild_delete(snowflake guild_id); /** * @brief Delete guild integration @@ -821,9 +938,7 @@ auto inline co_guild_delete(snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_delete_integration(snowflake guild_id, snowflake integration_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_delete_integration(guild_id, integration_id, cc); }); -} +[[nodiscard]] async co_guild_delete_integration(snowflake guild_id, snowflake integration_id); /** * @brief Edit a guild @@ -838,9 +953,7 @@ auto inline co_guild_delete_integration(snowflake guild_id, snowflake integratio * @return guild returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_edit(const class guild &g) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_edit(g, cc); }); -} +[[nodiscard]] async co_guild_edit(const class guild &g); /** * @brief Edit guild widget @@ -855,9 +968,7 @@ auto inline co_guild_edit(const class guild &g) { * @return guild_widget returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_edit_widget(snowflake guild_id, const class guild_widget &gw) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_edit_widget(guild_id, gw, cc); }); -} +[[nodiscard]] async co_guild_edit_widget(snowflake guild_id, const class guild_widget &gw); /** * @brief Get single guild ban @@ -870,9 +981,7 @@ auto inline co_guild_edit_widget(snowflake guild_id, const class guild_widget &g * @return ban returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_ban(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_ban(guild_id, user_id, cc); }); -} +[[nodiscard]] async co_guild_get_ban(snowflake guild_id, snowflake user_id); /** * @brief Get guild ban list @@ -888,14 +997,10 @@ auto inline co_guild_get_ban(snowflake guild_id, snowflake user_id) { * @return ban_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_bans(guild_id, before, after, limit, cc); }); -} +[[nodiscard]] async co_guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit); -auto inline co_guild_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get(snowflake guild_id); /** * @brief Get guild integrations @@ -906,16 +1011,14 @@ auto inline co_guild_get(snowflake guild_id) { * @see https://discord.com/developers/docs/resources/guild#get-guild-integrations * @param guild_id Guild ID to get integrations for * @return integration_map returned object on completion + * + * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. * \memberof dpp::cluster */ -auto inline co_guild_get_integrations(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_integrations(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get_integrations(snowflake guild_id); -auto inline co_guild_get_preview(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_preview(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get_preview(snowflake guild_id); /** * @brief Get guild vanity url, if enabled @@ -927,9 +1030,7 @@ auto inline co_guild_get_preview(snowflake guild_id) { * @return invite returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_vanity(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_vanity(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get_vanity(snowflake guild_id); /** * @brief Get guild widget @@ -942,9 +1043,7 @@ auto inline co_guild_get_vanity(snowflake guild_id) { * @return guild_widget returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_widget(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_widget(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get_widget(snowflake guild_id); /** * @brief Modify guild integration @@ -957,9 +1056,7 @@ auto inline co_guild_get_widget(snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_modify_integration(snowflake guild_id, const class integration &i) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_modify_integration(guild_id, i, cc); }); -} +[[nodiscard]] async co_guild_modify_integration(snowflake guild_id, const class integration &i); /** * @brief Get prune counts @@ -976,9 +1073,7 @@ auto inline co_guild_modify_integration(snowflake guild_id, const class integrat * @return prune returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_prune_counts(guild_id, pruneinfo, cc); }); -} +[[nodiscard]] async co_guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo); /** * @brief Begin guild prune @@ -997,9 +1092,7 @@ auto inline co_guild_get_prune_counts(snowflake guild_id, const struct prune& pr * @return prune returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_begin_prune(guild_id, pruneinfo, cc); }); -} +[[nodiscard]] async co_guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo); /** * @brief Change current user nickname @@ -1016,9 +1109,7 @@ auto inline co_guild_begin_prune(snowflake guild_id, const struct prune& prunein * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_set_nickname(snowflake guild_id, const std::string &nickname) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_set_nickname(guild_id, nickname, cc); }); -} +[[nodiscard]] async co_guild_set_nickname(snowflake guild_id, const std::string &nickname); /** * @brief Sync guild integration @@ -1030,9 +1121,62 @@ auto inline co_guild_set_nickname(snowflake guild_id, const std::string &nicknam * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sync_integration(snowflake guild_id, snowflake integration_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sync_integration(guild_id, integration_id, cc); }); -} +[[nodiscard]] async co_guild_sync_integration(snowflake guild_id, snowflake integration_id); + +/** + * @brief Get the guild's onboarding configuration + * + * @see dpp::cluster::guild_get_onboarding + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param guild_id The guild to pull the onboarding configuration from. + * @return onboarding returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_onboarding(snowflake guild_id); + +/** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see dpp::cluster::guild_edit_onboarding + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit_onboarding(const struct onboarding& o); + +/** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see dpp::cluster::guild_get_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_welcome_screen(snowflake guild_id); + +/** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see dpp::cluster::guild_edit_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled); /** * @brief Add guild member. Needs a specific oauth2 scope, from which you get the access_token. @@ -1052,9 +1196,7 @@ auto inline co_guild_sync_integration(snowflake guild_id, snowflake integration_ * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_add_member(const guild_member& gm, const std::string &access_token) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_add_member(gm, access_token, cc); }); -} +[[nodiscard]] async co_guild_add_member(const guild_member& gm, const std::string &access_token); /** * @brief Edit the properties of an existing guild member @@ -1070,9 +1212,7 @@ auto inline co_guild_add_member(const guild_member& gm, const std::string &acces * @return guild_member returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_edit_member(const guild_member& gm) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_edit_member(gm, cc); }); -} +[[nodiscard]] async co_guild_edit_member(const guild_member& gm); /** * @brief Get a guild member @@ -1083,9 +1223,7 @@ auto inline co_guild_edit_member(const guild_member& gm) { * @return guild_member returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_member(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_member(guild_id, user_id, cc); }); -} +[[nodiscard]] async co_guild_get_member(snowflake guild_id, snowflake user_id); /** * @brief Get all guild members @@ -1099,9 +1237,7 @@ auto inline co_guild_get_member(snowflake guild_id, snowflake user_id) { * @return guild_member_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake after) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_members(guild_id, limit, after, cc); }); -} +[[nodiscard]] async co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake after); /** * @brief Add role to guild member @@ -1117,9 +1253,7 @@ auto inline co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake a * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_add_role(guild_id, user_id, role_id, cc); }); -} +[[nodiscard]] async co_guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Remove (kick) a guild member @@ -1135,9 +1269,7 @@ auto inline co_guild_member_add_role(snowflake guild_id, snowflake user_id, snow * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_delete(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_delete(guild_id, user_id, cc); }); -} +[[nodiscard]] async co_guild_member_delete(snowflake guild_id, snowflake user_id); /** * @brief Remove (kick) a guild member @@ -1152,9 +1284,7 @@ auto inline co_guild_member_delete(snowflake guild_id, snowflake user_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_kick(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_kick(guild_id, user_id, cc); }); -} +[[nodiscard]] async co_guild_member_kick(snowflake guild_id, snowflake user_id); /** * @brief Set the timeout of a guild member @@ -1169,9 +1299,21 @@ auto inline co_guild_member_kick(snowflake guild_id, snowflake user_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_timeout(guild_id, user_id, communication_disabled_until, cc); }); -} +[[nodiscard]] async co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); + +/** + * @brief Remove the timeout of a guild member. + * A shortcut for guild_member_timeout(guild_id, user_id, 0, callback) + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_timeout_remove + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove the member timeout from + * @param user_id User ID to remove the timeout for + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_timeout_remove(snowflake guild_id, snowflake user_id); /** * @brief Remove role from guild member @@ -1188,9 +1330,7 @@ auto inline co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_ * @deprecated Use dpp::cluster::guild_member_remove_role instead * \memberof dpp::cluster */ -auto inline co_guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_delete_role(guild_id, user_id, role_id, cc); }); -} +[[nodiscard]] async co_guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Remove role from guild member @@ -1206,9 +1346,7 @@ auto inline co_guild_member_delete_role(snowflake guild_id, snowflake user_id, s * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_remove_role(guild_id, user_id, role_id, cc); }); -} +[[nodiscard]] async co_guild_member_remove_role(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. @@ -1225,9 +1363,7 @@ auto inline co_guild_member_remove_role(snowflake guild_id, snowflake user_id, s * @return guild_member returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_move(channel_id, guild_id, user_id, cc); }); -} +[[nodiscard]] async co_guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id); /** * @brief Search for guild members based on whether their username or nickname starts with the given string. @@ -1241,9 +1377,7 @@ auto inline co_guild_member_move(const snowflake channel_id, const snowflake gui * @return guild_member_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_search_members(guild_id, query, limit, cc); }); -} +[[nodiscard]] async co_guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit); /** * @brief Get guild invites @@ -1256,19 +1390,46 @@ auto inline co_guild_search_members(snowflake guild_id, const std::string& query * @return invite_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_invites(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_invites(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get_invites(snowflake guild_id); -auto inline co_invite_delete(const std::string &invitecode) { - return dpp::awaitable(this, [&] (auto cc) { this->invite_delete(invitecode, cc); }); -} +[[nodiscard]] async co_invite_delete(const std::string &invitecode); +/** + * @brief Get details about an invite + * + * @see dpp::cluster::invite_get + * @see https://discord.com/developers/docs/resources/invite#get-invite + * @param invite_code Invite code to get information on + * @return invite returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_invite_get(const std::string &invite_code); -auto inline co_invite_get(const std::string &invitecode) { - return dpp::awaitable(this, [&] (auto cc) { this->invite_get(invitecode, cc); }); -} +/** + * @brief Add a reaction to a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/resources/channel#create-reaction + * @param m Message to add a reaction to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_add_reaction(const struct message &m, const std::string &reaction); + +/** + * @brief Add a reaction to a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/topics/gateway#message-reaction-add + * @param message_id Message to add reactions to + * @param channel_id Channel to add reactions to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_add_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Send a message to a channel. The callback function is called when the message has been sent @@ -1279,9 +1440,7 @@ auto inline co_invite_get(const std::string &invitecode) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_create(const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->message_create(m, cc); }); -} +[[nodiscard]] async co_message_create(const struct message &m); /** * @brief Crosspost a message. The callback function is called when the message has been sent @@ -1293,9 +1452,30 @@ auto inline co_message_create(const message &m) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_crosspost(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_crosspost(message_id, channel_id, cc); }); -} +[[nodiscard]] async co_message_crosspost(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete all reactions on a message + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param m Message to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_all_reactions(const struct message &m); + +/** + * @brief Delete all reactions on a message by id + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_all_reactions(snowflake message_id, snowflake channel_id); /** * @brief Bulk delete messages from a channel. The callback function is called when the message has been edited @@ -1310,9 +1490,7 @@ auto inline co_message_crosspost(snowflake message_id, snowflake channel_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_bulk(const std::vector& message_ids, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_bulk(message_ids, channel_id, cc); }); -} +[[nodiscard]] async co_message_delete_bulk(const std::vector &message_ids, snowflake channel_id); /** * @brief Delete a message from a channel. The callback function is called when the message has been edited @@ -1325,9 +1503,84 @@ auto inline co_message_delete_bulk(const std::vector& message_ids, sn * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete(message_id, channel_id, cc); }); -} +[[nodiscard]] async co_message_delete(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete own reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param m Message to delete own reaction from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_own_reaction(const struct message &m, const std::string &reaction); + +/** + * @brief Delete own reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_own_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param m Message to delete a user's reaction from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction(const struct message &m, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param m Message to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction_emoji(const struct message &m, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction_emoji(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Edit a message on a channel. The callback function is called when the message has been edited @@ -1338,9 +1591,16 @@ auto inline co_message_delete(snowflake message_id, snowflake channel_id) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_edit(const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->message_edit(m, cc); }); -} +[[nodiscard]] async co_message_edit(const struct message &m); + +/** + * @brief Edit the flags of a message on a channel. The callback function is called when the message has been edited + * + * @param m Message to edit the flags of + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_edit_flags(const struct message &m); /** * @brief Get a message @@ -1352,9 +1612,38 @@ auto inline co_message_edit(const message &m) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_get(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_get(message_id, channel_id, cc); }); -} +[[nodiscard]] async co_message_get(snowflake message_id, snowflake channel_id); + +/** + * @brief Get reactions on a message for a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param m Message to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return user_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_get_reactions(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit); + +/** + * @brief Get reactions on a message for a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param message_id Message to get reactions for + * @param channel_id Channel to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return emoji_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_get_reactions(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit); /** * @brief Pin a message @@ -1366,9 +1655,7 @@ auto inline co_message_get(snowflake message_id, snowflake channel_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_pin(snowflake channel_id, snowflake message_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_pin(channel_id, message_id, cc); }); -} +[[nodiscard]] async co_message_pin(snowflake channel_id, snowflake message_id); /** * @brief Get multiple messages. @@ -1385,9 +1672,7 @@ auto inline co_message_pin(snowflake channel_id, snowflake message_id) { * @return message_map returned object on completion * \memberof dpp::cluster */ -auto inline co_messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->messages_get(channel_id, around, before, after, limit, cc); }); -} +[[nodiscard]] async co_messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); /** * @brief Unpin a message @@ -1399,9 +1684,59 @@ auto inline co_messages_get(snowflake channel_id, snowflake around, snowflake be * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_unpin(snowflake channel_id, snowflake message_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_unpin(channel_id, message_id, cc); }); -} +[[nodiscard]] async co_message_unpin(snowflake channel_id, snowflake message_id); + +/** + * @brief Get a list of users that voted for this specific answer. + * + * @param m Message that contains the poll to retrieve the answers from + * @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id) + * @param after Users after this ID should be retrieved if this is set to non-zero + * @param limit This number of users maximum should be returned, up to 100 + * @return user_map returned object on completion + * @see dpp::cluster::poll_get_answer_voters + * @see https://discord.com/developers/docs/resources/poll#get-answer-voters + * \memberof dpp::cluster + */ +[[nodiscard]] async co_poll_get_answer_voters(const message& m, uint32_t answer_id, snowflake after, uint64_t limit); + +/** + * @brief Get a list of users that voted for this specific answer. + * + * @param message_id ID of the message with the poll to retrieve the answers from + * @param channel_id ID of the channel with the poll to retrieve the answers from + * @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id) + * @param after Users after this ID should be retrieved if this is set to non-zero + * @param limit This number of users maximum should be returned, up to 100 + * @return user_map returned object on completion + * @see dpp::cluster::poll_get_answer_voters + * @see https://discord.com/developers/docs/resources/poll#get-answer-voters + * \memberof dpp::cluster + */ +[[nodiscard]] async co_poll_get_answer_voters(snowflake message_id, snowflake channel_id, uint32_t answer_id, snowflake after, uint64_t limit); + +/** + * @brief Immediately end a poll. + * + * @param m Message that contains the poll + * @return message returned object on completion + * @see dpp::cluster::poll_end + * @see https://discord.com/developers/docs/resources/poll#end-poll + * \memberof dpp::cluster + */ +[[nodiscard]] async co_poll_end(const message &m); + +/** + * @brief Immediately end a poll. + * + * @param message_id ID of the message with the poll to end + * @param channel_id ID of the channel with the poll to end + * @return message returned object on completion + * @see dpp::cluster::poll_end + * @see https://discord.com/developers/docs/resources/poll#end-poll + * \memberof dpp::cluster + */ +[[nodiscard]] async co_poll_end(snowflake message_id, snowflake channel_id); /** * @brief Get a channel's pins @@ -1411,9 +1746,7 @@ auto inline co_message_unpin(snowflake channel_id, snowflake message_id) { * @return message_map returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_pins_get(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_pins_get(channel_id, cc); }); -} +[[nodiscard]] async co_channel_pins_get(snowflake channel_id); /** * @brief Create a role on a guild @@ -1428,9 +1761,7 @@ auto inline co_channel_pins_get(snowflake channel_id) { * @return role returned object on completion * \memberof dpp::cluster */ -auto inline co_role_create(const class role &r) { - return dpp::awaitable(this, [&] (auto cc) { this->role_create(r, cc); }); -} +[[nodiscard]] async co_role_create(const class role &r); /** * @brief Delete a role @@ -1445,9 +1776,7 @@ auto inline co_role_create(const class role &r) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_role_delete(snowflake guild_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->role_delete(guild_id, role_id, cc); }); -} +[[nodiscard]] async co_role_delete(snowflake guild_id, snowflake role_id); /** * @brief Edit a role on a guild @@ -1461,9 +1790,7 @@ auto inline co_role_delete(snowflake guild_id, snowflake role_id) { * @return role returned object on completion * \memberof dpp::cluster */ -auto inline co_role_edit(const class role &r) { - return dpp::awaitable(this, [&] (auto cc) { this->role_edit(r, cc); }); -} +[[nodiscard]] async co_role_edit(const class role &r); /** * @brief Edit multiple role's position in a guild. Returns a list of all roles of the guild on success. @@ -1479,9 +1806,7 @@ auto inline co_role_edit(const class role &r) { * @return role_map returned object on completion * \memberof dpp::cluster */ -auto inline co_roles_edit_position(snowflake guild_id, const std::vector &roles) { - return dpp::awaitable(this, [&] (auto cc) { this->roles_edit_position(guild_id, roles, cc); }); -} +[[nodiscard]] async co_roles_edit_position(snowflake guild_id, const std::vector &roles); /** * @brief Get a role for a guild @@ -1492,9 +1817,54 @@ auto inline co_roles_edit_position(snowflake guild_id, const std::vector & * @return role_map returned object on completion * \memberof dpp::cluster */ -auto inline co_roles_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->roles_get(guild_id, cc); }); -} +[[nodiscard]] async co_roles_get(snowflake guild_id); + +/** + * @brief Get the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_get + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_role_connection_get(snowflake application_id); + +/** + * @brief Update the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_update + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records + * @param application_id The application ID + * @param connection_metadata The application role connection metadata to update + * @return application_role_connection returned object on completion + * @note An application can have a maximum of 5 metadata records. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_role_connection_update(snowflake application_id, const std::vector &connection_metadata); + +/** + * @brief Get user application role connection + * + * @see dpp::cluster::user_application_role_connection_get + * @see https://discord.com/developers/docs/resources/user#get-user-application-role-connection + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_application_role_connection_get(snowflake application_id); + +/** + * @brief Update user application role connection + * + * @see dpp::cluster::user_application_role_connection_update + * @see https://discord.com/developers/docs/resources/user#update-user-application-role-connection + * @param application_id The application ID + * @param connection The application role connection to update + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_application_role_connection_update(snowflake application_id, const application_role_connection &connection); /** * @brief Get all scheduled events for a guild @@ -1504,9 +1874,7 @@ auto inline co_roles_get(snowflake guild_id) { * @return scheduled_event_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_events_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_events_get(guild_id, cc); }); -} +[[nodiscard]] async co_guild_events_get(snowflake guild_id); /** * @brief Create a scheduled event on a guild @@ -1517,9 +1885,7 @@ auto inline co_guild_events_get(snowflake guild_id) { * @return scheduled_event returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_create(const scheduled_event& event) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_create(event, cc); }); -} +[[nodiscard]] async co_guild_event_create(const scheduled_event& event); /** * @brief Delete a scheduled event from a guild @@ -1531,9 +1897,7 @@ auto inline co_guild_event_create(const scheduled_event& event) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_delete(snowflake event_id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_delete(event_id, guild_id, cc); }); -} +[[nodiscard]] async co_guild_event_delete(snowflake event_id, snowflake guild_id); /** * @brief Edit/modify a scheduled event on a guild @@ -1544,9 +1908,7 @@ auto inline co_guild_event_delete(snowflake event_id, snowflake guild_id) { * @return scheduled_event returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_edit(const scheduled_event& event) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_edit(event, cc); }); -} +[[nodiscard]] async co_guild_event_edit(const scheduled_event& event); /** * @brief Get a scheduled event for a guild @@ -1558,14 +1920,22 @@ auto inline co_guild_event_edit(const scheduled_event& event) { * @return scheduled_event returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_get(snowflake guild_id, snowflake event_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_get(guild_id, event_id, cc); }); -} +[[nodiscard]] async co_guild_event_get(snowflake guild_id, snowflake event_id); + +/** + * @brief Returns all SKUs for a given application. + * @note Because of how Discord's SKU and subscription systems work, you will see two SKUs for your premium offering. + * For integration and testing entitlements, you should use the SKU with type: 5. + * + * @see dpp::cluster::skus_get + * @see https://discord.com/developers/docs/monetization/skus#list-skus + * @return sku_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_skus_get(); -auto inline co_stage_instance_create(const stage_instance& si) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_create(si, cc); }); -} +[[nodiscard]] async co_stage_instance_create(const stage_instance& si); /** * @brief Get the stage instance associated with the channel id, if it exists. @@ -1575,14 +1945,10 @@ auto inline co_stage_instance_create(const stage_instance& si) { * @return stage_instance returned object on completion * \memberof dpp::cluster */ -auto inline co_stage_instance_get(const snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_get(channel_id, cc); }); -} +[[nodiscard]] async co_stage_instance_get(const snowflake channel_id); -auto inline co_stage_instance_edit(const stage_instance& si) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_edit(si, cc); }); -} +[[nodiscard]] async co_stage_instance_edit(const stage_instance& si); /** * @brief Delete a stage instance. @@ -1593,9 +1959,7 @@ auto inline co_stage_instance_edit(const stage_instance& si) { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * \memberof dpp::cluster */ -auto inline co_stage_instance_delete(const snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_delete(channel_id, cc); }); -} +[[nodiscard]] async co_stage_instance_delete(const snowflake channel_id); /** * @brief Create a sticker in a guild @@ -1606,9 +1970,7 @@ auto inline co_stage_instance_delete(const snowflake channel_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_create(sticker &s) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_create(s, cc); }); -} +[[nodiscard]] async co_guild_sticker_create(const sticker &s); /** * @brief Delete a sticker from a guild @@ -1620,9 +1982,7 @@ auto inline co_guild_sticker_create(sticker &s) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_delete(sticker_id, guild_id, cc); }); -} +[[nodiscard]] async co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id); /** * @brief Get a guild sticker @@ -1633,9 +1993,7 @@ auto inline co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_get(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_get(id, guild_id, cc); }); -} +[[nodiscard]] async co_guild_sticker_get(snowflake id, snowflake guild_id); /** * @brief Modify a sticker in a guild @@ -1646,21 +2004,17 @@ auto inline co_guild_sticker_get(snowflake id, snowflake guild_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_modify(sticker &s) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_modify(s, cc); }); -} +[[nodiscard]] async co_guild_sticker_modify(const sticker &s); /** * @brief Get all guild stickers * @see dpp::cluster::guild_stickers_get - * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @see https://discord.com/developers/docs/resources/sticker#list-guild-stickers * @param guild_id Guild ID of the guild where the sticker is * @return sticker_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_stickers_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_stickers_get(guild_id, cc); }); -} +[[nodiscard]] async co_guild_stickers_get(snowflake guild_id); /** * @brief Get a nitro sticker @@ -1670,20 +2024,16 @@ auto inline co_guild_stickers_get(snowflake guild_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_nitro_sticker_get(snowflake id) { - return dpp::awaitable(this, [&] (auto cc) { this->nitro_sticker_get(id, cc); }); -} +[[nodiscard]] async co_nitro_sticker_get(snowflake id); /** - * @brief Get sticker packs + * @brief Get a list of available sticker packs * @see dpp::cluster::sticker_packs_get - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @see https://discord.com/developers/docs/resources/sticker#list-sticker-packs * @return sticker_pack_map returned object on completion * \memberof dpp::cluster */ -auto inline co_sticker_packs_get() { - return dpp::awaitable(this, [&] (auto cc) { this->sticker_packs_get(cc); }); -} +[[nodiscard]] async co_sticker_packs_get(); /** * @brief Create a new guild based on a template. @@ -1695,9 +2045,7 @@ auto inline co_sticker_packs_get() { * @return guild returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_create_from_template(const std::string &code, const std::string &name) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_create_from_template(code, name, cc); }); -} +[[nodiscard]] async co_guild_create_from_template(const std::string &code, const std::string &name); /** * @brief Creates a template for the guild @@ -1710,9 +2058,7 @@ auto inline co_guild_create_from_template(const std::string &code, const std::st * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_create(snowflake guild_id, const std::string &name, const std::string &description) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_create(guild_id, name, description, cc); }); -} +[[nodiscard]] async co_guild_template_create(snowflake guild_id, const std::string &name, const std::string &description); /** * @brief Deletes the template @@ -1724,9 +2070,7 @@ auto inline co_guild_template_create(snowflake guild_id, const std::string &name * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_delete(snowflake guild_id, const std::string &code) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_delete(guild_id, code, cc); }); -} +[[nodiscard]] async co_guild_template_delete(snowflake guild_id, const std::string &code); /** * @brief Modifies the template's metadata. @@ -1740,9 +2084,7 @@ auto inline co_guild_template_delete(snowflake guild_id, const std::string &code * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_modify(guild_id, code, name, description, cc); }); -} +[[nodiscard]] async co_guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); /** * @brief Get guild templates @@ -1753,9 +2095,7 @@ auto inline co_guild_template_modify(snowflake guild_id, const std::string &code * @return dtemplate_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_templates_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_templates_get(guild_id, cc); }); -} +[[nodiscard]] async co_guild_templates_get(snowflake guild_id); /** * @brief Syncs the template to the guild's current state. @@ -1767,9 +2107,7 @@ auto inline co_guild_templates_get(snowflake guild_id) { * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_sync(snowflake guild_id, const std::string &code) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_sync(guild_id, code, cc); }); -} +[[nodiscard]] async co_guild_template_sync(snowflake guild_id, const std::string &code); /** * @brief Get a template @@ -1779,119 +2117,118 @@ auto inline co_guild_template_sync(snowflake guild_id, const std::string &code) * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_template_get(const std::string &code) { - return dpp::awaitable(this, [&] (auto cc) { this->template_get(code, cc); }); -} +[[nodiscard]] async co_template_get(const std::string &code); /** * @brief Join a thread * @see dpp::cluster::current_user_join_thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#join-thread * @param thread_id Thread ID to join * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_join_thread(snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_join_thread(thread_id, cc); }); -} +[[nodiscard]] async co_current_user_join_thread(snowflake thread_id); /** * @brief Leave a thread * @see dpp::cluster::current_user_leave_thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#leave-thread * @param thread_id Thread ID to leave * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_leave_thread(snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_leave_thread(thread_id, cc); }); -} +[[nodiscard]] async co_current_user_leave_thread(snowflake thread_id); /** - * @brief Get active threads in a channel (Sorted by ID in descending order) + * @brief Get all active threads in the guild, including public and private threads. Threads are ordered by their id, in descending order. * @see dpp::cluster::threads_get_active - * @see https://discord.com/developers/docs/topics/threads - * @param channel_id Channel to get active threads for - * @return thread_map returned object on completion + * @see https://discord.com/developers/docs/resources/guild#list-active-guild-threads + * @param guild_id Guild to get active threads for + * @return active_threads returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_active(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_active(channel_id, cc); }); -} +[[nodiscard]] async co_threads_get_active(snowflake guild_id); /** * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) * @see dpp::cluster::threads_get_joined_private_archived - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads * @param channel_id Channel to get public archived threads for * @param before_id Get threads before this id * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_joined_private_archived(channel_id, before_id, limit, cc); }); -} +[[nodiscard]] async co_threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit); /** * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) * @see dpp::cluster::threads_get_private_archived - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_private_archived(channel_id, before_timestamp, limit, cc); }); -} +[[nodiscard]] async co_threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit); /** * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) * @see dpp::cluster::threads_get_public_archived - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_public_archived(channel_id, before_timestamp, limit, cc); }); -} +[[nodiscard]] async co_threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit); /** * @brief Get a thread member * @see dpp::cluster::thread_member_get - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#get-thread-member * @param thread_id Thread to get member for * @param user_id ID of the user to get * @return thread_member returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_member_get(const snowflake thread_id, const snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_member_get(thread_id, user_id, cc); }); -} +[[nodiscard]] async co_thread_member_get(const snowflake thread_id, const snowflake user_id); /** * @brief Get members of a thread * @see dpp::cluster::thread_members_get - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-thread-members * @param thread_id Thread to get members for * @return thread_member_map returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_members_get(snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_members_get(thread_id, cc); }); -} +[[nodiscard]] async co_thread_members_get(snowflake thread_id); + +/** + * @brief Create a thread in a forum or media channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_create_in_forum + * @see https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel + * @param thread_name Name of the forum thread + * @param channel_id Forum channel in which thread to create + * @param msg The message to start the thread with + * @param auto_archive_duration Duration to automatically archive the thread after recent activity + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @param applied_tags List of IDs of forum tags (dpp::forum_tag) to apply to this thread + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_create_in_forum(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}); /** * @brief Create a thread * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * * @see dpp::cluster::thread_create - * @see https://discord.com/developers/docs/resources/guild#create-guild-channel + * @see https://discord.com/developers/docs/resources/channel#start-thread-without-message * @param thread_name Name of the thread * @param channel_id Channel in which thread to create * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) @@ -1901,15 +2238,25 @@ auto inline co_thread_members_get(snowflake thread_id) { * @return thread returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_create(thread_name, channel_id, auto_archive_duration, thread_type, invitable, rate_limit_per_user, cc); }); -} +[[nodiscard]] async co_thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user); + +/** + * @brief Edit a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_edit + * @see https://discord.com/developers/docs/topics/threads#editing-deleting-threads + * @param t Thread to edit + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_edit(const thread &t); /** * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @see dpp::cluster::thread_create_with_message - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#start-thread-from-message * @param thread_name Name of the thread * @param channel_id Channel in which thread to create * @param message_id message to start thread with @@ -1918,53 +2265,60 @@ auto inline co_thread_create(const std::string& thread_name, snowflake channel_i * @return thread returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_create_with_message(thread_name, channel_id, message_id, auto_archive_duration, rate_limit_per_user, cc); }); -} +[[nodiscard]] async co_thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user); /** * @brief Add a member to a thread * @see dpp::cluster::thread_member_add - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#add-thread-member * @param thread_id Thread ID to add to * @param user_id Member ID to add * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_member_add(snowflake thread_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_member_add(thread_id, user_id, cc); }); -} +[[nodiscard]] async co_thread_member_add(snowflake thread_id, snowflake user_id); /** * @brief Remove a member from a thread * @see dpp::cluster::thread_member_remove - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#remove-thread-member * @param thread_id Thread ID to remove from * @param user_id Member ID to remove * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_member_remove(snowflake thread_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_member_remove(thread_id, user_id, cc); }); -} +[[nodiscard]] async co_thread_member_remove(snowflake thread_id, snowflake user_id); /** - * @brief Edit current (bot) user + * @brief Get the thread specified by thread_id. This uses the same call as dpp::cluster::channel_get but returns a thread object. + * @see dpp::cluster::thread_get + * @see https://discord.com/developers/docs/resources/channel#get-channel + * @param thread_id The id of the thread to obtain. + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_get(snowflake thread_id); + +/** + * @brief Edit current (bot) user. + * + * Modify the requester's user account settings. Returns a dpp::user object on success. + * Fires a User Update Gateway event. + * + * @note There appears to be no limit to the image size, however, if your image cannot be processed/uploaded in time, you will receive a malformed http request. * - * Modifies the current member in a guild. Returns the updated guild_member object on success. - * Fires a `Guild Member Update` Gateway event. * @see dpp::cluster::current_user_edit * @see https://discord.com/developers/docs/resources/user#modify-current-user * @param nickname Nickname to set - * @param image_blob Avatar data to upload (NOTE: Very heavily rate limited!) - * @param type Type of image for avatar + * @param avatar_blob Avatar data to upload + * @param avatar_type Type of image for avatar. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @param banner_blob Banner data to upload + * @param banner_type Type of image for Banner. It can be one of `i_gif`, `i_jpg` or `i_png`. * @return user returned object on completion - * @throw dpp::exception Image data is larger than the maximum size of 256 kilobytes + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes * \memberof dpp::cluster */ -auto inline co_current_user_edit(const std::string &nickname, const std::string& image_blob, const image_type type) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_edit(nickname, image_blob, type, cc); }); -} +[[nodiscard]] async co_current_user_edit(const std::string &nickname, const std::string& avatar_blob = "", const image_type avatar_type = i_png, const std::string& banner_blob = "", const image_type banner_type = i_png); /** * @brief Get current (bot) application @@ -1974,9 +2328,7 @@ auto inline co_current_user_edit(const std::string &nickname, const std::string& * @return application returned object on completion * \memberof dpp::cluster */ -auto inline co_current_application_get() { - return dpp::awaitable(this, [&] (auto cc) { this->current_application_get(cc); }); -} +[[nodiscard]] async co_current_application_get(); /** * @brief Get current (bot) user @@ -1988,9 +2340,7 @@ auto inline co_current_application_get() { * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. * \memberof dpp::cluster */ -auto inline co_current_user_get() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_get(cc); }); -} +[[nodiscard]] async co_current_user_get(); /** * @brief Set the bot's voice state on a stage channel @@ -2015,9 +2365,18 @@ auto inline co_current_user_get() { * @throw std::logic_exception You attempted to set a request_to_speak_timestamp in the past which is not the value of 0. * \memberof dpp::cluster */ -auto inline co_current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress, time_t request_to_speak_timestamp) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_set_voice_state(guild_id, channel_id, suppress, request_to_speak_timestamp, cc); }); -} +[[nodiscard]] async co_current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); + +/** + * @brief Get the bot's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::current_user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * @param guild_id Guild to get the voice state for + * @return voicestate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_get_voice_state(snowflake guild_id); /** * @brief Set a user's voice state on a stage channel @@ -2041,9 +2400,19 @@ auto inline co_current_user_set_voice_state(snowflake guild_id, snowflake channe * @param suppress True if the user's audio should be suppressed, false if it should not * \memberof dpp::cluster */ -auto inline co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress) { - return dpp::awaitable(this, [&] (auto cc) { this->user_set_voice_state(user_id, guild_id, channel_id, suppress, cc); }); -} +[[nodiscard]] async co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); + +/** + * @brief Get a user's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * @param guild_id Guild to get the voice state for + * @param user_id The user to get the voice state of + * @return voicestate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_get_voice_state(snowflake guild_id, snowflake user_id); /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). @@ -2054,9 +2423,7 @@ auto inline co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowf * @return connection_map returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_connections_get() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_connections_get(cc); }); -} +[[nodiscard]] async co_current_user_connections_get(); /** * @brief Get current (bot) user guilds @@ -2065,9 +2432,7 @@ auto inline co_current_user_connections_get() { * @return guild_map returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_get_guilds() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_get_guilds(cc); }); -} +[[nodiscard]] async co_current_user_get_guilds(); /** * @brief Leave a guild @@ -2077,12 +2442,10 @@ auto inline co_current_user_get_guilds() { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_leave_guild(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_leave_guild(guild_id, cc); }); -} +[[nodiscard]] async co_current_user_leave_guild(snowflake guild_id); /** - * @brief Get a user by id + * @brief Get a user by id, without using the cache * * @see dpp::cluster::user_get * @see https://discord.com/developers/docs/resources/user#get-user @@ -2094,9 +2457,22 @@ auto inline co_current_user_leave_guild(snowflake guild_id) { * Call `dpp::find_user` instead that looks up the user in the cache rather than a REST call. * \memberof dpp::cluster */ -auto inline co_user_get(snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->user_get(user_id, cc); }); -} +[[nodiscard]] async co_user_get(snowflake user_id); + +/** + * @brief Get a user by id, checking in the cache first + * + * @see dpp::cluster::user_get_cached + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note If the user is found in the cache, special values set in `dpp::user_identified` will be undefined. This call should be used + * where you want to for example resolve a user who may no longer be in the bot's guilds, for something like a ban log message. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_get_cached(snowflake user_id); /** * @brief Get all voice regions @@ -2105,9 +2481,7 @@ auto inline co_user_get(snowflake user_id) { * @return voiceregion_map returned object on completion * \memberof dpp::cluster */ -auto inline co_get_voice_regions() { - return dpp::awaitable(this, [&] (auto cc) { this->get_voice_regions(cc); }); -} +[[nodiscard]] async co_get_voice_regions(); /** * @brief Get guild voice regions. @@ -2122,22 +2496,10 @@ auto inline co_get_voice_regions() { * @return voiceregion_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_voice_regions(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_voice_regions(guild_id, cc); }); -} +[[nodiscard]] async co_guild_get_voice_regions(snowflake guild_id); -/** - * @brief Create a webhook - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::create_webhook - * @see https://discord.com/developers/docs/resources/webhook#create-webhook - * @param w Webhook to create - * @return webhook returned object on completion - * \memberof dpp::cluster - */ -auto inline co_create_webhook(const class webhook &w) { - return dpp::awaitable(this, [&] (auto cc) { this->create_webhook(w, cc); }); -} + +[[nodiscard]] async co_create_webhook(const class webhook &wh); /** * @brief Delete a webhook @@ -2148,9 +2510,7 @@ auto inline co_create_webhook(const class webhook &w) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_delete_webhook(snowflake webhook_id) { - return dpp::awaitable(this, [&] (auto cc) { this->delete_webhook(webhook_id, cc); }); -} +[[nodiscard]] async co_delete_webhook(snowflake webhook_id); /** * @brief Delete webhook message @@ -2163,9 +2523,7 @@ auto inline co_delete_webhook(snowflake webhook_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->delete_webhook_message(wh, message_id, thread_id, cc); }); -} +[[nodiscard]] async co_delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Delete webhook with token @@ -2176,9 +2534,7 @@ auto inline co_delete_webhook_message(const class webhook &wh, snowflake message * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_delete_webhook_with_token(snowflake webhook_id, const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->delete_webhook_with_token(webhook_id, token, cc); }); -} +[[nodiscard]] async co_delete_webhook_with_token(snowflake webhook_id, const std::string &token); /** * @brief Edit webhook @@ -2189,9 +2545,7 @@ auto inline co_delete_webhook_with_token(snowflake webhook_id, const std::string * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_edit_webhook(const class webhook& wh) { - return dpp::awaitable(this, [&] (auto cc) { this->edit_webhook(wh, cc); }); -} +[[nodiscard]] async co_edit_webhook(const class webhook& wh); /** * @brief Edit webhook message @@ -2210,9 +2564,7 @@ auto inline co_edit_webhook(const class webhook& wh) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_edit_webhook_message(const class webhook &wh, const struct message& m, snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->edit_webhook_message(wh, m, thread_id, cc); }); -} +[[nodiscard]] async co_edit_webhook_message(const class webhook &wh, const struct message &m, snowflake thread_id = 0); /** * @brief Edit webhook with token (token is encapsulated in the webhook object) @@ -2222,9 +2574,7 @@ auto inline co_edit_webhook_message(const class webhook &wh, const struct messag * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_edit_webhook_with_token(const class webhook& wh) { - return dpp::awaitable(this, [&] (auto cc) { this->edit_webhook_with_token(wh, cc); }); -} +[[nodiscard]] async co_edit_webhook_with_token(const class webhook& wh); /** * @brief Execute webhook @@ -2240,9 +2590,7 @@ auto inline co_edit_webhook_with_token(const class webhook& wh) { * @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 */ -auto inline co_execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, const std::string& thread_name) { - return dpp::awaitable(this, [&] (auto cc) { this->execute_webhook(wh, m, wait, thread_id, thread_name, cc); }); -} +[[nodiscard]] async co_execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = ""); /** * @brief Get channel webhooks @@ -2252,9 +2600,7 @@ auto inline co_execute_webhook(const class webhook &wh, const struct message& m, * @return webhook_map returned object on completion * \memberof dpp::cluster */ -auto inline co_get_channel_webhooks(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_channel_webhooks(channel_id, cc); }); -} +[[nodiscard]] async co_get_channel_webhooks(snowflake channel_id); /** * @brief Get guild webhooks @@ -2264,9 +2610,7 @@ auto inline co_get_channel_webhooks(snowflake channel_id) { * @return webhook_map returned object on completion * \memberof dpp::cluster */ -auto inline co_get_guild_webhooks(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_guild_webhooks(guild_id, cc); }); -} +[[nodiscard]] async co_get_guild_webhooks(snowflake guild_id); /** * @brief Get webhook @@ -2276,9 +2620,7 @@ auto inline co_get_guild_webhooks(snowflake guild_id) { * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_get_webhook(snowflake webhook_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_webhook(webhook_id, cc); }); -} +[[nodiscard]] async co_get_webhook(snowflake webhook_id); /** * @brief Get webhook message @@ -2291,9 +2633,7 @@ auto inline co_get_webhook(snowflake webhook_id) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_webhook_message(wh, message_id, thread_id, cc); }); -} +[[nodiscard]] async co_get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Get webhook using token @@ -2304,13 +2644,9 @@ auto inline co_get_webhook_message(const class webhook &wh, snowflake message_id * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_get_webhook_with_token(snowflake webhook_id, const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->get_webhook_with_token(webhook_id, token, cc); }); -} +[[nodiscard]] async co_get_webhook_with_token(snowflake webhook_id, const std::string &token); /* End of auto-generated definitions */ -auto inline co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}) { - return dpp::awaitable(this, [&] (auto cc) { this->request(url, method, cc, mimetype, headers); }); -} +[[nodiscard]] async co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5); diff --git a/3rdParty/dpp/cluster_sync_calls.h b/3rdParty/dpp/cluster_sync_calls.h index 51adeed1e5..4d1c5a32d7 100644 --- a/3rdParty/dpp/cluster_sync_calls.h +++ b/3rdParty/dpp/cluster_sync_calls.h @@ -33,15 +33,30 @@ * @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. Updates will be available in all guilds after 1 hour. + * overwriting existing commands that are registered globally for this application. * Commands that do not already exist will count toward daily application command create limits. * @return slashcommand_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand_map global_bulk_command_create_sync(const std::vector &commands); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand_map global_bulk_command_create_sync(const std::vector &commands); + +/** + * @brief Delete all existing global slash commands. + * + * @see dpp::cluster::global_bulk_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand_map global_bulk_command_delete_sync(); /** * @brief Create a global slash command (a bot can have a maximum of 100 of these). @@ -52,10 +67,11 @@ slashcommand_map global_bulk_command_create_sync(const std::vector * @return slashcommand returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand global_command_create_sync(const slashcommand &s); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand global_command_create_sync(const slashcommand &s); /** * @brief Get a global slash command @@ -66,10 +82,11 @@ slashcommand global_command_create_sync(const slashcommand &s); * @return slashcommand returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand global_command_get_sync(snowflake id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand global_command_get_sync(snowflake id); /** * @brief Delete a global slash command (a bot can have a maximum of 100 of these) @@ -80,10 +97,11 @@ slashcommand global_command_get_sync(snowflake id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 global_command_delete_sync(snowflake id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation global_command_delete_sync(snowflake id); /** * @brief Edit a global slash command (a bot can have a maximum of 100 of these) @@ -94,10 +112,11 @@ confirmation global_command_delete_sync(snowflake id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 global_command_edit_sync(const slashcommand &s); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation global_command_edit_sync(const slashcommand &s); /** * @brief Get the application's global slash commands @@ -107,10 +126,11 @@ confirmation global_command_edit_sync(const slashcommand &s); * @return slashcommand_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand_map global_commands_get_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand_map global_commands_get_sync(); /** * @brief Create/overwrite guild slash commands. @@ -124,10 +144,26 @@ slashcommand_map global_commands_get_sync(); * @return slashcommand_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand_map guild_bulk_command_create_sync(const std::vector &commands, snowflake guild_id); + +/** + * @brief Delete all existing guild slash commands. + * + * @see dpp::cluster::guild_bulk_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param guild_id Guild ID to delete the slash commands in. + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand_map guild_bulk_command_create_sync(const std::vector &commands, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand_map guild_bulk_command_delete_sync(snowflake guild_id); /** * @brief Get all slash command permissions of a guild @@ -138,10 +174,11 @@ slashcommand_map guild_bulk_command_create_sync(const std::vector * @return guild_command_permissions_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_command_permissions_map guild_commands_get_permissions_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_command_permissions_map guild_commands_get_permissions_sync(snowflake guild_id); /** * @brief Edit/Overwrite the permissions of all existing slash commands in a guild @@ -157,10 +194,11 @@ guild_command_permissions_map guild_commands_get_permissions_sync(snowflake guil * @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 + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_command_permissions_map guild_bulk_command_edit_permissions_sync(const std::vector &commands, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_command_permissions_map guild_bulk_command_edit_permissions_sync(const std::vector &commands, snowflake guild_id); /** * @brief Create a slash command local to a guild @@ -173,10 +211,11 @@ guild_command_permissions_map guild_bulk_command_edit_permissions_sync(const std * @return slashcommand returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand guild_command_create_sync(const slashcommand &s, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand guild_command_create_sync(const slashcommand &s, snowflake guild_id); /** * @brief Delete a slash command local to a guild @@ -188,10 +227,11 @@ slashcommand guild_command_create_sync(const slashcommand &s, snowflake guild_id * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_command_delete_sync(snowflake id, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_command_delete_sync(snowflake id, snowflake guild_id); /** * @brief Edit slash command permissions of a guild @@ -204,10 +244,11 @@ confirmation guild_command_delete_sync(snowflake id, snowflake guild_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_command_edit_permissions_sync(const slashcommand &s, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_command_edit_permissions_sync(const slashcommand &s, snowflake guild_id); /** * @brief Get a slash command of a guild @@ -220,10 +261,11 @@ confirmation guild_command_edit_permissions_sync(const slashcommand &s, snowflak * @return slashcommand returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand guild_command_get_sync(snowflake id, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand guild_command_get_sync(snowflake id, snowflake guild_id); /** * @brief Get the permissions for a slash command of a guild @@ -235,10 +277,11 @@ slashcommand guild_command_get_sync(snowflake id, snowflake guild_id); * @return guild_command_permissions returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_command_permissions guild_command_get_permissions_sync(snowflake id, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_command_permissions guild_command_get_permissions_sync(snowflake id, snowflake guild_id); /** * @brief Edit a slash command local to a guild @@ -250,10 +293,11 @@ guild_command_permissions guild_command_get_permissions_sync(snowflake id, snowf * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_command_edit_sync(const slashcommand &s, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_command_edit_sync(const slashcommand &s, snowflake guild_id); /** * @brief Get the application's slash commands for a guild @@ -265,10 +309,11 @@ confirmation guild_command_edit_sync(const slashcommand &s, snowflake guild_id); * @return slashcommand_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -slashcommand_map guild_commands_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") slashcommand_map guild_commands_get_sync(snowflake guild_id); /** * @brief Respond to a slash command @@ -281,10 +326,11 @@ slashcommand_map guild_commands_get_sync(snowflake guild_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_response_create_sync(snowflake interaction_id, const std::string &token, const interaction_response &r); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation interaction_response_create_sync(snowflake interaction_id, const std::string &token, const interaction_response &r); /** * @brief Edit response to a slash command @@ -296,23 +342,42 @@ confirmation interaction_response_create_sync(snowflake interaction_id, const st * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation interaction_response_edit_sync(const std::string &token, const message &m); + +/** + * @brief Get the original response to a slash command + * + * @see dpp::cluster::interaction_response_get_original + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_response_edit_sync(const std::string &token, const message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message interaction_response_get_original_sync(const std::string &token); /** * @brief Create a followup message to a slash command - * + * + * @see dpp::cluster::interaction_followup_create + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response * @param token Token for the interaction webhook * @param m followup message to create * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_followup_create_sync(const std::string &token, const message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation interaction_followup_create_sync(const std::string &token, const message &m); /** * @brief Edit original followup message to a slash command @@ -325,47 +390,75 @@ confirmation interaction_followup_create_sync(const std::string &token, const me * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_followup_edit_original_sync(const std::string &token, const message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation interaction_followup_edit_original_sync(const std::string &token, const message &m); /** - * @brief - * + * @brief Delete the initial interaction response + * + * @see dpp::cluster::interaction_followup_delete + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response * @param token Token for the interaction webhook * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_followup_delete_sync(const std::string &token); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation interaction_followup_delete_sync(const std::string &token); /** * @brief Edit followup message to a slash command * The message ID in the message you pass should be correctly set to that of a followup message you previously sent + * + * @see dpp::cluster::interaction_followup_edit + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message * @param token Token for the interaction webhook * @param m message to edit, the ID should be set * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_followup_edit_sync(const std::string &token, const message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation interaction_followup_edit_sync(const std::string &token, const message &m); /** * @brief Get the followup message to a slash command + * + * @see dpp::cluster::interaction_followup_get + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message * @param token Token for the interaction webhook * @param message_id message to retrieve * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 interaction_followup_get_sync(const std::string &token, snowflake message_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message interaction_followup_get_sync(const std::string &token, snowflake message_id); + +/** + * @brief Get the original followup message to a slash command + * This is an alias for cluster::interaction_response_get_original + * @see dpp::cluster::interaction_followup_get_original + * @see cluster::interaction_response_get_original + * + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message interaction_followup_get_original_sync(const std::string &token); /** * @brief Get all auto moderation rules for a guild @@ -374,10 +467,11 @@ message interaction_followup_get_sync(const std::string &token, snowflake messag * @return automod_rule_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") automod_rule_map automod_rules_get_sync(snowflake guild_id); /** * @brief Get a single auto moderation rule @@ -387,10 +481,11 @@ automod_rule_map automod_rules_get_sync(snowflake guild_id); * @return automod_rule returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") automod_rule automod_rule_get_sync(snowflake guild_id, snowflake rule_id); /** * @brief Create an auto moderation rule @@ -400,10 +495,11 @@ automod_rule automod_rule_get_sync(snowflake guild_id, snowflake rule_id); * @return automod_rule returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") automod_rule automod_rule_create_sync(snowflake guild_id, const automod_rule& r); /** * @brief Edit an auto moderation rule @@ -413,10 +509,11 @@ automod_rule automod_rule_create_sync(snowflake guild_id, const automod_rule& r) * @return automod_rule returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") automod_rule automod_rule_edit_sync(snowflake guild_id, const automod_rule& r); /** * @brief Delete an auto moderation rule @@ -426,10 +523,11 @@ automod_rule automod_rule_edit_sync(snowflake guild_id, const automod_rule& r); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation automod_rule_delete_sync(snowflake guild_id, snowflake rule_id); /** * @brief Create a channel @@ -447,10 +545,11 @@ confirmation automod_rule_delete_sync(snowflake guild_id, snowflake rule_id); * @return channel returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -channel channel_create_sync(const class channel &c); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") channel channel_create_sync(const class channel &c); /** * @brief Remove a permission from a channel @@ -462,10 +561,11 @@ channel channel_create_sync(const class channel &c); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 channel_delete_permission_sync(const class channel &c, snowflake overwrite_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_delete_permission_sync(const class channel &c, snowflake overwrite_id); /** * @brief Delete a channel @@ -476,10 +576,51 @@ confirmation channel_delete_permission_sync(const class channel &c, snowflake ov * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_delete_sync(snowflake channel_id); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @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 bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_edit_permissions_sync(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @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 bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 channel_delete_sync(snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_edit_permissions_sync(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); /** * @brief Edit multiple channels positions @@ -494,10 +635,11 @@ confirmation channel_delete_sync(snowflake channel_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 channel_edit_positions_sync(const std::vector &c); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_edit_positions_sync(const std::vector &c); /** * @brief Edit a channel @@ -508,10 +650,11 @@ confirmation channel_edit_positions_sync(const std::vector &c); * @return channel returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -channel channel_edit_sync(const class channel &c); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") channel channel_edit_sync(const class channel &c); /** * @brief Follow an announcement (news) channel @@ -522,10 +665,11 @@ channel channel_edit_sync(const class channel &c); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 channel_follow_news_sync(const class channel &c, snowflake target_channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_follow_news_sync(const class channel &c, snowflake target_channel_id); /** * @brief Get a channel @@ -536,10 +680,11 @@ confirmation channel_follow_news_sync(const class channel &c, snowflake target_c * @return channel returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -channel channel_get_sync(snowflake c); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") channel channel_get_sync(snowflake c); /** * @brief Create invite for a channel @@ -548,13 +693,14 @@ channel channel_get_sync(snowflake c); * @see https://discord.com/developers/docs/resources/channel#create-channel-invite * @param c Channel to create an invite on * @param i Invite to create - * @return confirmation returned object on completion + * @return invite returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 channel_invite_create_sync(const class channel &c, const class invite &i); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") invite channel_invite_create_sync(const class channel &c, const class invite &i); /** * @brief Get invites for a channel @@ -565,10 +711,39 @@ confirmation channel_invite_create_sync(const class channel &c, const class invi * @return invite_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") invite_map channel_invites_get_sync(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param c Channel to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -invite_map channel_invites_get_sync(const class channel &c); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_typing_sync(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param cid Channel ID to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_typing_sync(snowflake cid); /** * @brief Get all channels for a guild @@ -579,10 +754,27 @@ invite_map channel_invites_get_sync(const class channel &c); * @return channel_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -channel_map channels_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") channel_map channels_get_sync(snowflake guild_id); + +/** + * @brief Set the status of a voice channel. + * + * @see dpp::cluster::channel_set_voice_status + * @see https://github.com/discord/discord-api-docs/pull/6400 (please replace soon). + * @param channel_id The channel to update. + * @param status The new status for the channel. + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation channel_set_voice_status_sync(snowflake channel_id, const std::string& status); /** * @brief Create a dm channel @@ -592,10 +784,11 @@ channel_map channels_get_sync(snowflake guild_id); * @return channel returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -channel create_dm_channel_sync(snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") channel create_dm_channel_sync(snowflake user_id); /** * @brief Get current user DM channels @@ -603,10 +796,11 @@ channel create_dm_channel_sync(snowflake user_id); * @return channel_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -channel_map current_user_get_dms_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") channel_map current_user_get_dms_sync(); /** * @brief Create a direct message, also create the channel for the direct message if needed @@ -620,10 +814,11 @@ channel_map current_user_get_dms_sync(); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 direct_message_create_sync(snowflake user_id, const message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message direct_message_create_sync(snowflake user_id, const message &m); /** * @brief Adds a recipient to a Group DM using their access token @@ -636,10 +831,11 @@ message direct_message_create_sync(snowflake user_id, const message &m); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 gdm_add_sync(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation gdm_add_sync(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick); /** * @brief Removes a recipient from a Group DM @@ -650,10 +846,11 @@ confirmation gdm_add_sync(snowflake channel_id, snowflake user_id, const std::st * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 gdm_remove_sync(snowflake channel_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation gdm_remove_sync(snowflake channel_id, snowflake user_id); /** * @brief Create single emoji. @@ -667,10 +864,11 @@ confirmation gdm_remove_sync(snowflake channel_id, snowflake user_id); * @return emoji returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -emoji guild_emoji_create_sync(snowflake guild_id, const class emoji& newemoji); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji guild_emoji_create_sync(snowflake guild_id, const class emoji& newemoji); /** * @brief Delete a guild emoji @@ -683,27 +881,29 @@ emoji guild_emoji_create_sync(snowflake guild_id, const class emoji& newemoji); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_emoji_delete_sync(snowflake guild_id, snowflake emoji_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_emoji_delete_sync(snowflake guild_id, snowflake emoji_id); /** * @brief Edit a single emoji. * * You must ensure that the emoji passed contained image data using the emoji::load_image() method. * @see dpp::cluster::guild_emoji_edit - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji + * @see https://discord.com/developers/docs/resources/emoji#modify-guild-emoji * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param guild_id Guild ID to edit emoji on * @param newemoji Emoji to edit * @return emoji returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -emoji guild_emoji_edit_sync(snowflake guild_id, const class emoji& newemoji); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji guild_emoji_edit_sync(snowflake guild_id, const class emoji& newemoji); /** * @brief Get a single emoji @@ -715,24 +915,170 @@ emoji guild_emoji_edit_sync(snowflake guild_id, const class emoji& newemoji); * @return emoji returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -emoji guild_emoji_get_sync(snowflake guild_id, snowflake emoji_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji guild_emoji_get_sync(snowflake guild_id, snowflake emoji_id); /** * @brief Get all emojis for a guild * * @see dpp::cluster::guild_emojis_get - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emojis + * @see https://discord.com/developers/docs/resources/emoji#list-guild-emojis * @param guild_id Guild ID to get emojis for * @return emoji_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji_map guild_emojis_get_sync(snowflake guild_id); + +/** + * @brief List all Application Emojis + * + * @see dpp::cluster::application_emojis_get + * @see https://discord.com/developers/docs/resources/emoji#list-application-emojis + * @return emoji_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -emoji_map guild_emojis_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji_map application_emojis_get_sync(); + +/** + * @brief Get an Application Emoji + * + * @see dpp::cluster::application_emoji_get + * @see https://discord.com/developers/docs/resources/emoji#get-application-emoji + * @param emoji_id The ID of the Emoji to get. + * @return emoji returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji application_emoji_get_sync(snowflake emoji_id); + +/** + * @brief Create an Application Emoji + * + * @see dpp::cluster::application_emoji_create + * @see https://discord.com/developers/docs/resources/emoji#create-application-emoji + * @param newemoji The emoji to create + * @return emoji returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji application_emoji_create_sync(const class emoji& newemoji); + +/** + * @brief Edit an Application Emoji + * + * @see dpp::cluster::application_emoji_edit + * @see https://discord.com/developers/docs/resources/emoji#modify-application-emoji + * @param newemoji The emoji to edit + * @return emoji returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji application_emoji_edit_sync(const class emoji& newemoji); + +/** + * @brief Delete an Application Emoji + * + * @see dpp::cluster::application_emoji_delete + * @see https://discord.com/developers/docs/resources/emoji#delete-application-emoji + * @param emoji_id The emoji's ID to delete. + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation application_emoji_delete_sync(snowflake emoji_id); + +/** + * @brief Returns all entitlements for a given app, active and expired. + * + * @see dpp::cluster::entitlements_get + * @see https://discord.com/developers/docs/monetization/entitlements#list-entitlements + * @param user_id User ID to look up entitlements for. + * @param sku_ids List of SKU IDs to check entitlements for. + * @param before_id Retrieve entitlements before this entitlement ID. + * @param after_id Retrieve entitlements after this entitlement ID. + * @param limit Number of entitlements to return, 1-100 (default 100). + * @param guild_id Guild ID to look up entitlements for. + * @param exclude_ended Whether ended entitlements should be excluded from the search. + * @return entitlement_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") entitlement_map entitlements_get_sync(snowflake user_id = 0, const std::vector& sku_ids = {}, snowflake before_id = 0, snowflake after_id = 0, uint8_t limit = 100, snowflake guild_id = 0, bool exclude_ended = false); + +/** + * @brief Creates a test entitlement to a given SKU for a given guild or user. + * Discord will act as though that user or guild has entitlement to your premium offering. + * + * @see dpp::cluster::entitlement_test_create + * @see https://discord.com/developers/docs/monetization/entitlements#create-test-entitlement + * @param new_entitlement The entitlement to create. + * Make sure your dpp::entitlement_type (inside your dpp::entitlement object) matches the type of the owner_id + * (if type is guild, owner_id is a guild id), otherwise it won't work! + * @return entitlement returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") entitlement entitlement_test_create_sync(const class entitlement& new_entitlement); + +/** + * @brief Deletes a currently-active test entitlement. + * Discord will act as though that user or guild no longer has entitlement to your premium offering. + * + * @see dpp::cluster::entitlement_test_delete + * @see https://discord.com/developers/docs/monetization/entitlements#delete-test-entitlement + * @param entitlement_id The test entitlement to delete. + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation entitlement_test_delete_sync(snowflake entitlement_id); + +/** + * @brief For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed. + * + * @see dpp::cluster::entitlement_consume + * @see https://discord.com/developers/docs/monetization/entitlements#consume-an-entitlement + * @param entitlement_id The entitlement to mark as consumed. + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation entitlement_consume_sync(snowflake entitlement_id); /** * @brief Get the gateway information for the bot using the token @@ -741,10 +1087,11 @@ emoji_map guild_emojis_get_sync(snowflake guild_id); * @return gateway returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -gateway get_gateway_bot_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") gateway get_gateway_bot_sync(); /** * @brief Modify current member @@ -760,10 +1107,11 @@ gateway get_gateway_bot_sync(); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_current_member_edit_sync(snowflake guild_id, const std::string &nickname); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_current_member_edit_sync(snowflake guild_id, const std::string &nickname); /** * @brief Get the audit log for a guild @@ -773,15 +1121,17 @@ confirmation guild_current_member_edit_sync(snowflake guild_id, const std::strin * @param guild_id Guild to get the audit log of * @param user_id Entries from a specific user ID. Set this to `0` will fetch any user * @param action_type Entries for a specific dpp::audit_type. Set this to `0` will fetch any type - * @param before Entries that preceded a specific audit log entry ID. Used for paginating + * @param before Entries with ID less than a specific audit log entry ID. Used for paginating + * @param after Entries with ID greater than a specific audit log entry ID. Used for paginating * @param limit Maximum number of entries (between 1-100) to return * @return auditlog returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -auditlog guild_auditlog_get_sync(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, uint32_t limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") auditlog guild_auditlog_get_sync(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit); /** * @brief Add guild ban @@ -797,10 +1147,11 @@ auditlog guild_auditlog_get_sync(snowflake guild_id, snowflake user_id, uint32_t * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_seconds = 0); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0); /** * @brief Delete guild ban @@ -815,10 +1166,11 @@ confirmation guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_delete_sync(snowflake guild_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_ban_delete_sync(snowflake guild_id, snowflake user_id); /** * @brief Create a guild @@ -841,10 +1193,11 @@ confirmation guild_ban_delete_sync(snowflake guild_id, snowflake user_id); * @return guild returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild guild_create_sync(const class guild &g); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild guild_create_sync(const class guild &g); /** * @brief Delete a guild @@ -857,10 +1210,11 @@ guild guild_create_sync(const class guild &g); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_delete_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_delete_sync(snowflake guild_id); /** * @brief Delete guild integration @@ -876,10 +1230,11 @@ confirmation guild_delete_sync(snowflake guild_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_delete_integration_sync(snowflake guild_id, snowflake integration_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_delete_integration_sync(snowflake guild_id, snowflake integration_id); /** * @brief Edit a guild @@ -894,10 +1249,11 @@ confirmation guild_delete_integration_sync(snowflake guild_id, snowflake integra * @return guild returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild guild_edit_sync(const class guild &g); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild guild_edit_sync(const class guild &g); /** * @brief Edit guild widget @@ -912,10 +1268,11 @@ guild guild_edit_sync(const class guild &g); * @return guild_widget returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_widget guild_edit_widget_sync(snowflake guild_id, const class guild_widget &gw); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_widget guild_edit_widget_sync(snowflake guild_id, const class guild_widget &gw); /** * @brief Get single guild ban @@ -928,10 +1285,11 @@ guild_widget guild_edit_widget_sync(snowflake guild_id, const class guild_widget * @return ban returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -ban guild_get_ban_sync(snowflake guild_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") ban guild_get_ban_sync(snowflake guild_id, snowflake user_id); /** * @brief Get guild ban list @@ -947,13 +1305,14 @@ ban guild_get_ban_sync(snowflake guild_id, snowflake user_id); * @return ban_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -ban_map guild_get_bans_sync(snowflake guild_id, snowflake before, snowflake after, snowflake limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") ban_map guild_get_bans_sync(snowflake guild_id, snowflake before, snowflake after, snowflake limit); -guild guild_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild guild_get_sync(snowflake guild_id); /** * @brief Get guild integrations @@ -964,15 +1323,18 @@ guild guild_get_sync(snowflake guild_id); * @see https://discord.com/developers/docs/resources/guild#get-guild-integrations * @param guild_id Guild ID to get integrations for * @return integration_map returned object on completion + * + * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -integration_map guild_get_integrations_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") integration_map guild_get_integrations_sync(snowflake guild_id); -guild guild_get_preview_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild guild_get_preview_sync(snowflake guild_id); /** * @brief Get guild vanity url, if enabled @@ -984,10 +1346,11 @@ guild guild_get_preview_sync(snowflake guild_id); * @return invite returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -invite guild_get_vanity_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") invite guild_get_vanity_sync(snowflake guild_id); /** * @brief Get guild widget @@ -1000,10 +1363,11 @@ invite guild_get_vanity_sync(snowflake guild_id); * @return guild_widget returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_widget guild_get_widget_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_widget guild_get_widget_sync(snowflake guild_id); /** * @brief Modify guild integration @@ -1016,10 +1380,11 @@ guild_widget guild_get_widget_sync(snowflake guild_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_modify_integration_sync(snowflake guild_id, const class integration &i); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_modify_integration_sync(snowflake guild_id, const class integration &i); /** * @brief Get prune counts @@ -1036,10 +1401,11 @@ confirmation guild_modify_integration_sync(snowflake guild_id, const class integ * @return prune returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -prune guild_get_prune_counts_sync(snowflake guild_id, const struct prune& pruneinfo); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") prune guild_get_prune_counts_sync(snowflake guild_id, const struct prune& pruneinfo); /** * @brief Begin guild prune @@ -1058,10 +1424,11 @@ prune guild_get_prune_counts_sync(snowflake guild_id, const struct prune& prunei * @return prune returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -prune guild_begin_prune_sync(snowflake guild_id, const struct prune& pruneinfo); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") prune guild_begin_prune_sync(snowflake guild_id, const struct prune& pruneinfo); /** * @brief Change current user nickname @@ -1078,10 +1445,11 @@ prune guild_begin_prune_sync(snowflake guild_id, const struct prune& pruneinfo); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_set_nickname_sync(snowflake guild_id, const std::string &nickname); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_set_nickname_sync(snowflake guild_id, const std::string &nickname); /** * @brief Sync guild integration @@ -1093,10 +1461,82 @@ confirmation guild_set_nickname_sync(snowflake guild_id, const std::string &nick * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_sync_integration_sync(snowflake guild_id, snowflake integration_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_sync_integration_sync(snowflake guild_id, snowflake integration_id); + +/** + * @brief Get the guild's onboarding configuration + * + * @see dpp::cluster::guild_get_onboarding + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param guild_id The guild to pull the onboarding configuration from. + * @return onboarding returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") onboarding guild_get_onboarding_sync(snowflake guild_id); + +/** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see dpp::cluster::guild_edit_onboarding + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") onboarding guild_edit_onboarding_sync(const struct onboarding& o); + +/** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see dpp::cluster::guild_get_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dpp::welcome_screen guild_get_welcome_screen_sync(snowflake guild_id); + +/** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see dpp::cluster::guild_edit_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dpp::welcome_screen guild_edit_welcome_screen_sync(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled); /** * @brief Add guild member. Needs a specific oauth2 scope, from which you get the access_token. @@ -1116,10 +1556,11 @@ confirmation guild_sync_integration_sync(snowflake guild_id, snowflake integrati * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_add_member_sync(const guild_member& gm, const std::string &access_token); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_add_member_sync(const guild_member& gm, const std::string &access_token); /** * @brief Edit the properties of an existing guild member @@ -1135,10 +1576,11 @@ confirmation guild_add_member_sync(const guild_member& gm, const std::string &ac * @return guild_member returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_member guild_edit_member_sync(const guild_member& gm); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_member guild_edit_member_sync(const guild_member& gm); /** * @brief Get a guild member @@ -1149,10 +1591,11 @@ guild_member guild_edit_member_sync(const guild_member& gm); * @return guild_member returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_member guild_get_member_sync(snowflake guild_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_member guild_get_member_sync(snowflake guild_id, snowflake user_id); /** * @brief Get all guild members @@ -1166,10 +1609,11 @@ guild_member guild_get_member_sync(snowflake guild_id, snowflake user_id); * @return guild_member_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_member_map guild_get_members_sync(snowflake guild_id, uint16_t limit, snowflake after); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_member_map guild_get_members_sync(snowflake guild_id, uint16_t limit, snowflake after); /** * @brief Add role to guild member @@ -1185,10 +1629,11 @@ guild_member_map guild_get_members_sync(snowflake guild_id, uint16_t limit, snow * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_add_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_member_add_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Remove (kick) a guild member @@ -1204,10 +1649,11 @@ confirmation guild_member_add_role_sync(snowflake guild_id, snowflake user_id, s * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_delete_sync(snowflake guild_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_member_delete_sync(snowflake guild_id, snowflake user_id); /** * @brief Remove (kick) a guild member @@ -1222,10 +1668,11 @@ confirmation guild_member_delete_sync(snowflake guild_id, snowflake user_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_kick_sync(snowflake guild_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_member_kick_sync(snowflake guild_id, snowflake user_id); /** * @brief Set the timeout of a guild member @@ -1240,10 +1687,29 @@ confirmation guild_member_kick_sync(snowflake guild_id, snowflake user_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_timeout_sync(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_member_timeout_sync(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); + +/** + * @brief Remove the timeout of a guild member. + * A shortcut for guild_member_timeout(guild_id, user_id, 0, callback) + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_timeout_remove + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove the member timeout from + * @param user_id User ID to remove the timeout for + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_member_timeout_remove_sync(snowflake guild_id, snowflake user_id); /** * @brief Remove role from guild member @@ -1260,10 +1726,11 @@ confirmation guild_member_timeout_sync(snowflake guild_id, snowflake user_id, ti * @deprecated Use dpp::cluster::guild_member_remove_role instead * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_delete_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_member_delete_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Remove role from guild member @@ -1279,10 +1746,11 @@ confirmation guild_member_delete_role_sync(snowflake guild_id, snowflake user_id * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") 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. @@ -1299,10 +1767,11 @@ confirmation guild_member_remove_role_sync(snowflake guild_id, snowflake user_id * @return guild_member returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_member guild_member_move_sync(const snowflake channel_id, const snowflake guild_id, const snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_member guild_member_move_sync(const snowflake channel_id, const snowflake guild_id, const snowflake user_id); /** * @brief Search for guild members based on whether their username or nickname starts with the given string. @@ -1316,10 +1785,11 @@ guild_member guild_member_move_sync(const snowflake channel_id, const snowflake * @return guild_member_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_member_map guild_search_members_sync(snowflake guild_id, const std::string& query, uint16_t limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_member_map guild_search_members_sync(snowflake guild_id, const std::string& query, uint16_t limit); /** * @brief Get guild invites @@ -1332,16 +1802,62 @@ guild_member_map guild_search_members_sync(snowflake guild_id, const std::string * @return invite_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -invite_map guild_get_invites_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") invite_map guild_get_invites_sync(snowflake guild_id); -invite invite_delete_sync(const std::string &invitecode); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") invite invite_delete_sync(const std::string &invitecode); +/** + * @brief Get details about an invite + * + * @see dpp::cluster::invite_get + * @see https://discord.com/developers/docs/resources/invite#get-invite + * @param invite_code Invite code to get information on + * @return invite returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") invite invite_get_sync(const std::string &invite_code); -invite invite_get_sync(const std::string &invitecode); +/** + * @brief Add a reaction to a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/resources/channel#create-reaction + * @param m Message to add a reaction to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_add_reaction_sync(const struct message &m, const std::string &reaction); + +/** + * @brief Add a reaction to a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/topics/gateway#message-reaction-add + * @param message_id Message to add reactions to + * @param channel_id Channel to add reactions to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_add_reaction_sync(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Send a message to a channel. The callback function is called when the message has been sent @@ -1352,10 +1868,11 @@ invite invite_get_sync(const std::string &invitecode); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_create_sync(const struct message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message message_create_sync(const struct message &m); /** * @brief Crosspost a message. The callback function is called when the message has been sent @@ -1367,10 +1884,42 @@ message message_create_sync(const struct message &m); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message message_crosspost_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete all reactions on a message + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param m Message to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_crosspost_sync(snowflake message_id, snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_all_reactions_sync(const struct message &m); + +/** + * @brief Delete all reactions on a message by id + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_all_reactions_sync(snowflake message_id, snowflake channel_id); /** * @brief Bulk delete messages from a channel. The callback function is called when the message has been edited @@ -1385,10 +1934,11 @@ message message_crosspost_sync(snowflake message_id, snowflake channel_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_delete_bulk_sync(const std::vector &message_ids, snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_bulk_sync(const std::vector &message_ids, snowflake channel_id); /** * @brief Delete a message from a channel. The callback function is called when the message has been edited @@ -1401,10 +1951,112 @@ confirmation message_delete_bulk_sync(const std::vector &message_ids, * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_delete_sync(snowflake message_id, snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete own reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param m Message to delete own reaction from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_own_reaction_sync(const struct message &m, const std::string &reaction); + +/** + * @brief Delete own reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_own_reaction_sync(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param m Message to delete a user's reaction from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_reaction_sync(const struct message &m, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_reaction_sync(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param m Message to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_reaction_emoji_sync(const struct message &m, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_delete_reaction_emoji_sync(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Edit a message on a channel. The callback function is called when the message has been edited @@ -1415,10 +2067,24 @@ confirmation message_delete_sync(snowflake message_id, snowflake channel_id); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message message_edit_sync(const struct message &m); + +/** + * @brief Edit the flags of a message on a channel. The callback function is called when the message has been edited + * + * @param m Message to edit the flags of + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_edit_sync(const struct message &m); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message message_edit_flags_sync(const struct message &m); /** * @brief Get a message @@ -1430,10 +2096,50 @@ message message_edit_sync(const struct message &m); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_get_sync(snowflake message_id, snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message message_get_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Get reactions on a message for a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param m Message to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return user_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user_map message_get_reactions_sync(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit); + +/** + * @brief Get reactions on a message for a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param message_id Message to get reactions for + * @param channel_id Channel to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return emoji_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") emoji_map message_get_reactions_sync(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit); /** * @brief Pin a message @@ -1445,10 +2151,11 @@ message message_get_sync(snowflake message_id, snowflake channel_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_pin_sync(snowflake channel_id, snowflake message_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_pin_sync(snowflake channel_id, snowflake message_id); /** * @brief Get multiple messages. @@ -1465,10 +2172,11 @@ confirmation message_pin_sync(snowflake channel_id, snowflake message_id); * @return message_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_map messages_get_sync(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message_map messages_get_sync(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); /** * @brief Unpin a message @@ -1480,10 +2188,79 @@ message_map messages_get_sync(snowflake channel_id, snowflake around, snowflake * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation message_unpin_sync(snowflake channel_id, snowflake message_id); + +/** + * @brief Get a list of users that voted for this specific answer. + * + * @param m Message that contains the poll to retrieve the answers from + * @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id) + * @param after Users after this ID should be retrieved if this is set to non-zero + * @param limit This number of users maximum should be returned, up to 100 + * @return user_map returned object on completion + * @see dpp::cluster::poll_get_answer_voters + * @see https://discord.com/developers/docs/resources/poll#get-answer-voters + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user_map poll_get_answer_voters_sync(const message& m, uint32_t answer_id, snowflake after, uint64_t limit); + +/** + * @brief Get a list of users that voted for this specific answer. + * + * @param message_id ID of the message with the poll to retrieve the answers from + * @param channel_id ID of the channel with the poll to retrieve the answers from + * @param answer_id ID of the answer to retrieve votes from (see poll_answer::answer_id) + * @param after Users after this ID should be retrieved if this is set to non-zero + * @param limit This number of users maximum should be returned, up to 100 + * @return user_map returned object on completion + * @see dpp::cluster::poll_get_answer_voters + * @see https://discord.com/developers/docs/resources/poll#get-answer-voters + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user_map poll_get_answer_voters_sync(snowflake message_id, snowflake channel_id, uint32_t answer_id, snowflake after, uint64_t limit); + +/** + * @brief Immediately end a poll. + * + * @param m Message that contains the poll + * @return message returned object on completion + * @see dpp::cluster::poll_end + * @see https://discord.com/developers/docs/resources/poll#end-poll + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 message_unpin_sync(snowflake channel_id, snowflake message_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message poll_end_sync(const message &m); + +/** + * @brief Immediately end a poll. + * + * @param message_id ID of the message with the poll to end + * @param channel_id ID of the channel with the poll to end + * @return message returned object on completion + * @see dpp::cluster::poll_end + * @see https://discord.com/developers/docs/resources/poll#end-poll + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message poll_end_sync(snowflake message_id, snowflake channel_id); /** * @brief Get a channel's pins @@ -1493,10 +2270,11 @@ confirmation message_unpin_sync(snowflake channel_id, snowflake message_id); * @return message_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_map channel_pins_get_sync(snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message_map channel_pins_get_sync(snowflake channel_id); /** * @brief Create a role on a guild @@ -1511,10 +2289,11 @@ message_map channel_pins_get_sync(snowflake channel_id); * @return role returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -role role_create_sync(const class role &r); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") role role_create_sync(const class role &r); /** * @brief Delete a role @@ -1529,10 +2308,11 @@ role role_create_sync(const class role &r); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 role_delete_sync(snowflake guild_id, snowflake role_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation role_delete_sync(snowflake guild_id, snowflake role_id); /** * @brief Edit a role on a guild @@ -1546,10 +2326,11 @@ confirmation role_delete_sync(snowflake guild_id, snowflake role_id); * @return role returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -role role_edit_sync(const class role &r); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") role role_edit_sync(const class role &r); /** * @brief Edit multiple role's position in a guild. Returns a list of all roles of the guild on success. @@ -1565,10 +2346,11 @@ role role_edit_sync(const class role &r); * @return role_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -role_map roles_edit_position_sync(snowflake guild_id, const std::vector &roles); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") role_map roles_edit_position_sync(snowflake guild_id, const std::vector &roles); /** * @brief Get a role for a guild @@ -1579,10 +2361,43 @@ role_map roles_edit_position_sync(snowflake guild_id, const std::vector &r * @return role_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -role_map roles_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") role_map roles_get_sync(snowflake guild_id); + +/** + * @brief Get the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_get + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") application_role_connection application_role_connection_get_sync(snowflake application_id); + +/** + * @brief Update the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_update + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records + * @param application_id The application ID + * @param connection_metadata The application role connection metadata to update + * @return application_role_connection returned object on completion + * @note An application can have a maximum of 5 metadata records. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") application_role_connection application_role_connection_update_sync(snowflake application_id, const std::vector &connection_metadata); /** * @brief Get user application role connection @@ -1593,10 +2408,11 @@ role_map roles_get_sync(snowflake guild_id); * @return application_role_connection returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -application_role_connection user_application_role_connection_get_sync(snowflake application_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") application_role_connection user_application_role_connection_get_sync(snowflake application_id); /** * @brief Update user application role connection @@ -1608,10 +2424,11 @@ application_role_connection user_application_role_connection_get_sync(snowflake * @return application_role_connection returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -application_role_connection user_application_role_connection_update_sync(snowflake application_id, const application_role_connection &connection); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") application_role_connection user_application_role_connection_update_sync(snowflake application_id, const application_role_connection &connection); /** * @brief Get all scheduled events for a guild @@ -1621,10 +2438,11 @@ application_role_connection user_application_role_connection_update_sync(snowfla * @return scheduled_event_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -scheduled_event_map guild_events_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") scheduled_event_map guild_events_get_sync(snowflake guild_id); /** * @brief Create a scheduled event on a guild @@ -1635,10 +2453,11 @@ scheduled_event_map guild_events_get_sync(snowflake guild_id); * @return scheduled_event returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -scheduled_event guild_event_create_sync(const scheduled_event& event); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") scheduled_event guild_event_create_sync(const scheduled_event& event); /** * @brief Delete a scheduled event from a guild @@ -1650,10 +2469,11 @@ scheduled_event guild_event_create_sync(const scheduled_event& event); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_event_delete_sync(snowflake event_id, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_event_delete_sync(snowflake event_id, snowflake guild_id); /** * @brief Edit/modify a scheduled event on a guild @@ -1664,10 +2484,11 @@ confirmation guild_event_delete_sync(snowflake event_id, snowflake guild_id); * @return scheduled_event returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -scheduled_event guild_event_edit_sync(const scheduled_event& event); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") scheduled_event guild_event_edit_sync(const scheduled_event& event); /** * @brief Get a scheduled event for a guild @@ -1679,13 +2500,30 @@ scheduled_event guild_event_edit_sync(const scheduled_event& event); * @return scheduled_event returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") scheduled_event guild_event_get_sync(snowflake guild_id, snowflake event_id); + +/** + * @brief Returns all SKUs for a given application. + * @note Because of how Discord's SKU and subscription systems work, you will see two SKUs for your premium offering. + * For integration and testing entitlements, you should use the SKU with type: 5. + * + * @see dpp::cluster::skus_get + * @see https://discord.com/developers/docs/monetization/skus#list-skus + * @return sku_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -scheduled_event guild_event_get_sync(snowflake guild_id, snowflake event_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sku_map skus_get_sync(); -stage_instance stage_instance_create_sync(const stage_instance& si); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") stage_instance stage_instance_create_sync(const stage_instance& si); /** * @brief Get the stage instance associated with the channel id, if it exists. @@ -1695,13 +2533,14 @@ stage_instance stage_instance_create_sync(const stage_instance& si); * @return stage_instance returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -stage_instance stage_instance_get_sync(const snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") stage_instance stage_instance_get_sync(const snowflake channel_id); -stage_instance stage_instance_edit_sync(const stage_instance& si); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") stage_instance stage_instance_edit_sync(const stage_instance& si); /** * @brief Delete a stage instance. @@ -1712,10 +2551,11 @@ stage_instance stage_instance_edit_sync(const stage_instance& si); * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 stage_instance_delete_sync(const snowflake channel_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation stage_instance_delete_sync(const snowflake channel_id); /** * @brief Create a sticker in a guild @@ -1726,10 +2566,11 @@ confirmation stage_instance_delete_sync(const snowflake channel_id); * @return sticker returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -sticker guild_sticker_create_sync(sticker &s); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sticker guild_sticker_create_sync(const sticker &s); /** * @brief Delete a sticker from a guild @@ -1741,10 +2582,11 @@ sticker guild_sticker_create_sync(sticker &s); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_sticker_delete_sync(snowflake sticker_id, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_sticker_delete_sync(snowflake sticker_id, snowflake guild_id); /** * @brief Get a guild sticker @@ -1755,10 +2597,11 @@ confirmation guild_sticker_delete_sync(snowflake sticker_id, snowflake guild_id) * @return sticker returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -sticker guild_sticker_get_sync(snowflake id, snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sticker guild_sticker_get_sync(snowflake id, snowflake guild_id); /** * @brief Modify a sticker in a guild @@ -1769,23 +2612,25 @@ sticker guild_sticker_get_sync(snowflake id, snowflake guild_id); * @return sticker returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -sticker guild_sticker_modify_sync(sticker &s); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sticker guild_sticker_modify_sync(const sticker &s); /** * @brief Get all guild stickers * @see dpp::cluster::guild_stickers_get - * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @see https://discord.com/developers/docs/resources/sticker#list-guild-stickers * @param guild_id Guild ID of the guild where the sticker is * @return sticker_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -sticker_map guild_stickers_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sticker_map guild_stickers_get_sync(snowflake guild_id); /** * @brief Get a nitro sticker @@ -1795,22 +2640,24 @@ sticker_map guild_stickers_get_sync(snowflake guild_id); * @return sticker returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -sticker nitro_sticker_get_sync(snowflake id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sticker nitro_sticker_get_sync(snowflake id); /** - * @brief Get sticker packs + * @brief Get a list of available sticker packs * @see dpp::cluster::sticker_packs_get - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @see https://discord.com/developers/docs/resources/sticker#list-sticker-packs * @return sticker_pack_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -sticker_pack_map sticker_packs_get_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") sticker_pack_map sticker_packs_get_sync(); /** * @brief Create a new guild based on a template. @@ -1822,10 +2669,11 @@ sticker_pack_map sticker_packs_get_sync(); * @return guild returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild guild_create_from_template_sync(const std::string &code, const std::string &name); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild guild_create_from_template_sync(const std::string &code, const std::string &name); /** * @brief Creates a template for the guild @@ -1838,10 +2686,11 @@ guild guild_create_from_template_sync(const std::string &code, const std::string * @return dtemplate returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -dtemplate guild_template_create_sync(snowflake guild_id, const std::string &name, const std::string &description); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dtemplate guild_template_create_sync(snowflake guild_id, const std::string &name, const std::string &description); /** * @brief Deletes the template @@ -1853,10 +2702,11 @@ dtemplate guild_template_create_sync(snowflake guild_id, const std::string &name * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_template_delete_sync(snowflake guild_id, const std::string &code); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation guild_template_delete_sync(snowflake guild_id, const std::string &code); /** * @brief Modifies the template's metadata. @@ -1870,10 +2720,11 @@ confirmation guild_template_delete_sync(snowflake guild_id, const std::string &c * @return dtemplate returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -dtemplate guild_template_modify_sync(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dtemplate guild_template_modify_sync(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); /** * @brief Get guild templates @@ -1884,10 +2735,11 @@ dtemplate guild_template_modify_sync(snowflake guild_id, const std::string &code * @return dtemplate_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -dtemplate_map guild_templates_get_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dtemplate_map guild_templates_get_sync(snowflake guild_id); /** * @brief Syncs the template to the guild's current state. @@ -1899,10 +2751,11 @@ dtemplate_map guild_templates_get_sync(snowflake guild_id); * @return dtemplate returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -dtemplate guild_template_sync_sync(snowflake guild_id, const std::string &code); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dtemplate guild_template_sync_sync(snowflake guild_id, const std::string &code); /** * @brief Get a template @@ -1912,124 +2765,133 @@ dtemplate guild_template_sync_sync(snowflake guild_id, const std::string &code); * @return dtemplate returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -dtemplate template_get_sync(const std::string &code); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") dtemplate template_get_sync(const std::string &code); /** * @brief Join a thread * @see dpp::cluster::current_user_join_thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#join-thread * @param thread_id Thread ID to join * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 current_user_join_thread_sync(snowflake thread_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation current_user_join_thread_sync(snowflake thread_id); /** * @brief Leave a thread * @see dpp::cluster::current_user_leave_thread - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#leave-thread * @param thread_id Thread ID to leave * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 current_user_leave_thread_sync(snowflake thread_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation current_user_leave_thread_sync(snowflake thread_id); /** - * @brief Get active threads in a guild (Sorted by ID in descending order) + * @brief Get all active threads in the guild, including public and private threads. Threads are ordered by their id, in descending order. * @see dpp::cluster::threads_get_active - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/guild#list-active-guild-threads * @param guild_id Guild to get active threads for - * @return thread_map returned object on completion + * @return active_threads returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread_map threads_get_active_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") active_threads threads_get_active_sync(snowflake guild_id); /** * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) * @see dpp::cluster::threads_get_joined_private_archived - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads * @param channel_id Channel to get public archived threads for * @param before_id Get threads before this id * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread_map threads_get_joined_private_archived_sync(snowflake channel_id, snowflake before_id, uint16_t limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread_map threads_get_joined_private_archived_sync(snowflake channel_id, snowflake before_id, uint16_t limit); /** * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) * @see dpp::cluster::threads_get_private_archived - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread_map threads_get_private_archived_sync(snowflake channel_id, time_t before_timestamp, uint16_t limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread_map threads_get_private_archived_sync(snowflake channel_id, time_t before_timestamp, uint16_t limit); /** * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) * @see dpp::cluster::threads_get_public_archived - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread_map threads_get_public_archived_sync(snowflake channel_id, time_t before_timestamp, uint16_t limit); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread_map threads_get_public_archived_sync(snowflake channel_id, time_t before_timestamp, uint16_t limit); /** * @brief Get a thread member * @see dpp::cluster::thread_member_get - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#get-thread-member * @param thread_id Thread to get member for * @param user_id ID of the user to get * @return thread_member returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread_member thread_member_get_sync(const snowflake thread_id, const snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread_member thread_member_get_sync(const snowflake thread_id, const snowflake user_id); /** * @brief Get members of a thread * @see dpp::cluster::thread_members_get - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#list-thread-members * @param thread_id Thread to get members for * @return thread_member_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread_member_map thread_members_get_sync(snowflake thread_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread_member_map thread_members_get_sync(snowflake thread_id); /** - * @brief Create a thread in forum channel + * @brief Create a thread in a forum or media channel * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * * @see dpp::cluster::thread_create_in_forum @@ -2043,17 +2905,18 @@ thread_member_map thread_members_get_sync(snowflake thread_id); * @return thread returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread thread_create_in_forum_sync(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread thread_create_in_forum_sync(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}); /** * @brief Create a thread * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * * @see dpp::cluster::thread_create - * @see https://discord.com/developers/docs/resources/guild#create-guild-channel + * @see https://discord.com/developers/docs/resources/channel#start-thread-without-message * @param thread_name Name of the thread * @param channel_id Channel in which thread to create * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) @@ -2063,16 +2926,33 @@ thread thread_create_in_forum_sync(const std::string& thread_name, snowflake cha * @return thread returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread thread_create_sync(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread thread_create_sync(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user); + +/** + * @brief Edit a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_edit + * @see https://discord.com/developers/docs/topics/threads#editing-deleting-threads + * @param t Thread to edit + * @return thread returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread thread_edit_sync(const thread &t); /** * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @see dpp::cluster::thread_create_with_message - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#start-thread-from-message * @param thread_name Name of the thread * @param channel_id Channel in which thread to create * @param message_id message to start thread with @@ -2081,57 +2961,80 @@ thread thread_create_sync(const std::string& thread_name, snowflake channel_id, * @return thread returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -thread thread_create_with_message_sync(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread thread_create_with_message_sync(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user); /** * @brief Add a member to a thread * @see dpp::cluster::thread_member_add - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#add-thread-member * @param thread_id Thread ID to add to * @param user_id Member ID to add * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 thread_member_add_sync(snowflake thread_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation thread_member_add_sync(snowflake thread_id, snowflake user_id); /** * @brief Remove a member from a thread * @see dpp::cluster::thread_member_remove - * @see https://discord.com/developers/docs/topics/threads + * @see https://discord.com/developers/docs/resources/channel#remove-thread-member * @param thread_id Thread ID to remove from * @param user_id Member ID to remove * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 thread_member_remove_sync(snowflake thread_id, snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation thread_member_remove_sync(snowflake thread_id, snowflake user_id); /** - * @brief Edit current (bot) user + * @brief Get the thread specified by thread_id. This uses the same call as dpp::cluster::channel_get but returns a thread object. + * @see dpp::cluster::thread_get + * @see https://discord.com/developers/docs/resources/channel#get-channel + * @param thread_id The id of the thread to obtain. + * @return thread returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") thread thread_get_sync(snowflake thread_id); + +/** + * @brief Edit current (bot) user. + * + * Modify the requester's user account settings. Returns a dpp::user object on success. + * Fires a User Update Gateway event. + * + * @note There appears to be no limit to the image size, however, if your image cannot be processed/uploaded in time, you will receive a malformed http request. * - * Modifies the current member in a guild. Returns the updated guild_member object on success. - * Fires a `Guild Member Update` Gateway event. * @see dpp::cluster::current_user_edit * @see https://discord.com/developers/docs/resources/user#modify-current-user * @param nickname Nickname to set - * @param image_blob Avatar data to upload (NOTE: Very heavily rate limited!) - * @param type Type of image for avatar + * @param avatar_blob Avatar data to upload + * @param avatar_type Type of image for avatar. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @param banner_blob Banner data to upload + * @param banner_type Type of image for Banner. It can be one of `i_gif`, `i_jpg` or `i_png`. * @return user returned object on completion - * @throw dpp::exception Image data is larger than the maximum size of 256 kilobytes + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -user current_user_edit_sync(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user current_user_edit_sync(const std::string &nickname, const std::string& avatar_blob = "", const image_type avatar_type = i_png, const std::string& banner_blob = "", const image_type banner_type = i_png); /** * @brief Get current (bot) application @@ -2141,10 +3044,11 @@ user current_user_edit_sync(const std::string &nickname, const std::string& imag * @return application returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -application current_application_get_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") application current_application_get_sync(); /** * @brief Get current (bot) user @@ -2156,10 +3060,11 @@ application current_application_get_sync(); * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -user_identified current_user_get_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user_identified current_user_get_sync(); /** * @brief Set the bot's voice state on a stage channel @@ -2184,10 +3089,26 @@ user_identified current_user_get_sync(); * @throw std::logic_exception You attempted to set a request_to_speak_timestamp in the past which is not the value of 0. * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation current_user_set_voice_state_sync(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); + +/** + * @brief Get the bot's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::current_user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-current-user-voice-state + * @param guild_id Guild to get the voice state for + * @return voicestate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 current_user_set_voice_state_sync(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") voicestate current_user_get_voice_state_sync(snowflake guild_id); /** * @brief Set a user's voice state on a stage channel @@ -2211,10 +3132,27 @@ confirmation current_user_set_voice_state_sync(snowflake guild_id, snowflake cha * @param suppress True if the user's audio should be suppressed, false if it should not * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation user_set_voice_state_sync(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); + +/** + * @brief Get a user's voice state in a guild without a Gateway connection + * + * @see dpp::cluster::user_get_voice_state + * @see https://discord.com/developers/docs/resources/voice#get-user-voice-state + * @param guild_id Guild to get the voice state for + * @param user_id The user to get the voice state of + * @return voicestate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 user_set_voice_state_sync(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") voicestate user_get_voice_state_sync(snowflake guild_id, snowflake user_id); /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). @@ -2225,10 +3163,11 @@ confirmation user_set_voice_state_sync(snowflake user_id, snowflake guild_id, sn * @return connection_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -connection_map current_user_connections_get_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") connection_map current_user_connections_get_sync(); /** * @brief Get current (bot) user guilds @@ -2237,10 +3176,11 @@ connection_map current_user_connections_get_sync(); * @return guild_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -guild_map current_user_get_guilds_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") guild_map current_user_get_guilds_sync(); /** * @brief Leave a guild @@ -2250,13 +3190,14 @@ guild_map current_user_get_guilds_sync(); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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 current_user_leave_guild_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation current_user_leave_guild_sync(snowflake guild_id); /** - * @brief Get a user by id + * @brief Get a user by id, without using the cache * * @see dpp::cluster::user_get * @see https://discord.com/developers/docs/resources/user#get-user @@ -2268,10 +3209,30 @@ confirmation current_user_leave_guild_sync(snowflake guild_id); * Call `dpp::find_user` instead that looks up the user in the cache rather than a REST call. * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -user_identified user_get_sync(snowflake user_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user_identified user_get_sync(snowflake user_id); + +/** + * @brief Get a user by id, checking in the cache first + * + * @see dpp::cluster::user_get_cached + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note If the user is found in the cache, special values set in `dpp::user_identified` will be undefined. This call should be used + * where you want to for example resolve a user who may no longer be in the bot's guilds, for something like a ban log message. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. + * @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. + */ +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") user_identified user_get_cached_sync(snowflake user_id); /** * @brief Get all voice regions @@ -2280,10 +3241,11 @@ user_identified user_get_sync(snowflake user_id); * @return voiceregion_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -voiceregion_map get_voice_regions_sync(); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") voiceregion_map get_voice_regions_sync(); /** * @brief Get guild voice regions. @@ -2298,24 +3260,14 @@ voiceregion_map get_voice_regions_sync(); * @return voiceregion_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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. */ -voiceregion_map guild_get_voice_regions_sync(snowflake guild_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") voiceregion_map guild_get_voice_regions_sync(snowflake guild_id); -/** - * @brief Create a webhook - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::create_webhook - * @see https://discord.com/developers/docs/resources/webhook#create-webhook - * @param w Webhook to create - * @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 create_webhook_sync(const class webhook &w); + +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook create_webhook_sync(const class webhook &wh); /** * @brief Delete a webhook @@ -2326,10 +3278,11 @@ webhook create_webhook_sync(const class webhook &w); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_sync(snowflake webhook_id); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation delete_webhook_sync(snowflake webhook_id); /** * @brief Delete webhook message @@ -2342,10 +3295,11 @@ confirmation delete_webhook_sync(snowflake webhook_id); * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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, snowflake thread_id = 0); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Delete webhook with token @@ -2356,10 +3310,11 @@ confirmation delete_webhook_message_sync(const class webhook &wh, snowflake mess * @return confirmation returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_with_token_sync(snowflake webhook_id, const std::string &token); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") confirmation delete_webhook_with_token_sync(snowflake webhook_id, const std::string &token); /** * @brief Edit webhook @@ -2370,10 +3325,11 @@ confirmation delete_webhook_with_token_sync(snowflake webhook_id, const std::str * @return webhook returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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_sync(const class webhook& wh); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook edit_webhook_sync(const class webhook& wh); /** * @brief Edit webhook message @@ -2392,10 +3348,11 @@ webhook edit_webhook_sync(const class webhook& wh); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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, snowflake thread_id = 0); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") 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) @@ -2405,10 +3362,11 @@ message edit_webhook_message_sync(const class webhook &wh, const struct message * @return webhook returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook edit_webhook_with_token_sync(const class webhook& wh); /** * @brief Execute webhook @@ -2424,10 +3382,11 @@ webhook edit_webhook_with_token_sync(const class webhook& wh); * @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 + * @deprecated This function is deprecated, please use coroutines instead. * @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, const std::string& thread_name = ""); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") 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 @@ -2437,10 +3396,11 @@ message execute_webhook_sync(const class webhook &wh, const struct message &m, b * @return webhook_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook_map get_channel_webhooks_sync(snowflake channel_id); /** * @brief Get guild webhooks @@ -2450,10 +3410,11 @@ webhook_map get_channel_webhooks_sync(snowflake channel_id); * @return webhook_map returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook_map get_guild_webhooks_sync(snowflake guild_id); /** * @brief Get webhook @@ -2463,10 +3424,11 @@ webhook_map get_guild_webhooks_sync(snowflake guild_id); * @return webhook returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook get_webhook_sync(snowflake webhook_id); /** * @brief Get webhook message @@ -2479,10 +3441,11 @@ webhook get_webhook_sync(snowflake webhook_id); * @return message returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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, snowflake message_id, snowflake thread_id = 0); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") message get_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Get webhook using token @@ -2493,10 +3456,11 @@ message get_webhook_message_sync(const class webhook &wh, snowflake message_id, * @return webhook returned object on completion * \memberof dpp::cluster * @throw dpp::rest_exception upon failure to execute REST function + * @deprecated This function is deprecated, please use coroutines instead. * @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); +DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev/coro-introduction.html") webhook get_webhook_with_token_sync(snowflake webhook_id, const std::string &token); /* End of auto-generated definitions */ diff --git a/3rdParty/dpp/collector.h b/3rdParty/dpp/collector.h index 72ddd89a4e..ffa18154e7 100644 --- a/3rdParty/dpp/collector.h +++ b/3rdParty/dpp/collector.h @@ -1,22 +1,23 @@ -/* - * Discord erlpack - tidied up for D++, Craig Edwards 2021. - * - * MessagePack system dependencies modified for erlpack. - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki +/************************************************************************************ * - * 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 + * D++, A Lightweight C++ library for Discord * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * - * 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. - */ + * 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 @@ -54,14 +55,24 @@ namespace dpp { template class collector { protected: - /// Owning cluster + /** + * @brief Owning cluster. + */ class cluster* owner; private: - /// Timed listener + /** + * @brief Timed listener. + */ timed_listener, std::function>* tl; - /// stored list + + /** + * @brief Stored list. + */ std::vector stored; - /// Trigger flag + + /** + * @brief Trigger flag. + */ bool triggered; public: /** @@ -80,7 +91,7 @@ template class collector stored.push_back(*v); } }; - tl = new dpp::timed_listener, std::function>(cl, duration, event, f, [this](dpp::timer timer_handle) { + tl = new dpp::timed_listener, std::function>(cl, duration, event, f, [this]([[maybe_unused]] dpp::timer timer_handle) { if (!triggered) { triggered = true; completed(stored); @@ -152,16 +163,35 @@ template class collector */ class collected_reaction : public managed { public: - /// Reacting user + /** + * @brief Reacting user. + */ user react_user; - /// Reacting guild + + /** + * @brief Reacting guild. + */ guild* react_guild{}; - /// Reacting guild member + + /** + * @brief Reacting guild member. + */ guild_member react_member; - /// Reacting channel + + /** + * @brief Reacting channel. + */ channel* react_channel{}; - /// Reacted emoji + + /** + * @brief Reacted emoji. + */ emoji react_emoji; + + /** + * @brief Optional: ID of the user who authored the message which was reacted to. + */ + snowflake message_author_id{}; }; /** @@ -236,7 +266,14 @@ class message_collector : public message_collector_t { * Collects message reactions during a set timeframe and returns them in a list via the completed() method. */ class reaction_collector : public reaction_collector_t { + /** + * @brief The ID of the message. + */ snowflake message_id; + + /** + * @brief The reaction. + */ collected_reaction react; public: /** @@ -272,6 +309,7 @@ class reaction_collector : public reaction_collector_t { react.react_member = element.reacting_member; react.react_channel = element.reacting_channel; react.react_emoji = element.reacting_emoji; + react.message_author_id = element.message_author_id; return &react; } else { return nullptr; @@ -432,4 +470,4 @@ class scheduled_event_collector : public scheduled_event_collector_t { virtual ~scheduled_event_collector() = default; }; -}; \ No newline at end of file +} diff --git a/3rdParty/dpp/colors.h b/3rdParty/dpp/colors.h index a57e08a273..e7d8833991 100644 --- a/3rdParty/dpp/colors.h +++ b/3rdParty/dpp/colors.h @@ -22,59 +22,724 @@ #include - /** - * @brief The main namespace for D++ functions. classes and types +/** + * @brief The main namespace for D++ functions. classes and types */ namespace dpp { - /** - * @brief predefined color constants - */ - namespace colors { - const uint32_t - white = 0xFFFFFF, - discord_white = 0xFFFFFE, - light_gray = 0xC0C0C0, - gray = 0x808080, - dark_gray = 0x404040, - black = 0x000000, - discord_black = 0x000001, - red = 0xFF0000, - pink = 0xFFAFAF, - orange = 0xFFC800, - yellow = 0xFFFF00, - green = 0x00FF00, - magenta = 0xFF00FF, - cyan = 0x00FFFF, - blue = 0x0000FF, - light_sea_green = 0x1ABC9C, - medium_sea_green = 0x2ECC71, - summer_sky = 0x3498DB, - deep_lilac = 0x9B59B6, - ruby = 0xE91E63, - moon_yellow = 0xF1C40F, - tahiti_gold = 0xE67E22, - cinnabar = 0xE74C3C, - submarine = 0x95A5A6, - blue_aquamarine = 0x607D8B, - deep_sea = 0x11806A, - sea_green = 0x1F8B4C, - endeavour = 0x206694, - vivid_violet = 0x71368A, - jazzberry_jam = 0xAD1457, - dark_goldenrod = 0xC27C0E, - rust = 0xA84300, - brown = 0x992D22, - gray_chateau = 0x979C9F, - bismark = 0x546E7A, - sti_blue = 0x0E4BEF, - wrx_blue = 0x00247D, - rallyart_crimson = 0xE60012, - lime = 0x00FF00, - forest_green = 0x228B22, - cadmium_green = 0x097969, - aquamarine = 0x7FFFD4, - blue_green = 0x088F8F, - raspberry = 0xE30B5C, - scarlet_red = 0xFF2400; - }; -}; + +/** + * @brief predefined color constants. + */ +namespace colors { + const uint32_t + white = 0xFFFFFF, + discord_white = 0xFFFFFE, + light_gray = 0xC0C0C0, + gray = 0x808080, + dark_gray = 0x404040, + black = 0x000000, + discord_black = 0x000001, + red = 0xFF0000, + pink = 0xFFAFAF, + orange = 0xFFC800, + yellow = 0xFFFF00, + green = 0x00FF00, + magenta = 0xFF00FF, + cyan = 0x00FFFF, + blue = 0x0000FF, + light_sea_green = 0x1ABC9C, + medium_sea_green = 0x2ECC71, + summer_sky = 0x3498DB, + deep_lilac = 0x9B59B6, + ruby = 0xE91E63, + moon_yellow = 0xF1C40F, + tahiti_gold = 0xE67E22, + cinnabar = 0xE74C3C, + submarine = 0x95A5A6, + blue_aquamarine = 0x607D8B, + deep_sea = 0x11806A, + sea_green = 0x1F8B4C, + endeavour = 0x206694, + vivid_violet = 0x71368A, + jazzberry_jam = 0xAD1457, + dark_goldenrod = 0xC27C0E, + rust = 0xA84300, + brown = 0x992D22, + gray_chateau = 0x979C9F, + bismark = 0x546E7A, + sti_blue = 0x0E4BEF, + wrx_blue = 0x00247D, + rallyart_crimson = 0xE60012, + lime = 0x00FF00, + forest_green = 0x228B22, + cadmium_green = 0x097969, + aquamarine = 0x7FFFD4, + blue_green = 0x088F8F, + raspberry = 0xE30B5C, + scarlet_red = 0xFF2400, + night = 0x0C090A, + charcoal = 0x34282C, + oil = 0x3B3131, + light_black = 0x454545, + black_cat = 0x413839, + iridium = 0x3D3C3A, + black_eel = 0x463E3F, + black_cow = 0x4C4646, + gray_wolf = 0x504A4B, + grey_wolf = 0x504A4B, + vampire_gray = 0x565051, + vampire_grey = 0x565051, + iron_gray = 0x52595D, + iron_grey = 0x52595D, + gray_dolphin = 0x5C5858, + grey_dolphin = 0x5C5858, + carbon_gray = 0x625D5D, + carbon_grey = 0x625D5D, + ash_gray = 0x666362, + ash_grey = 0x666362, + dim_gray = 0x696969, + dim_grey = 0x696969, + nardo_gray = 0x686A6C, + nardo_grey = 0x686A6C, + cloudy_gray = 0x6D6968, + cloudy_grey = 0x6D6968, + smokey_gray = 0x726E6D, + smokey_grey = 0x726E6D, + alien_gray = 0x736F6E, + alien_grey = 0x736F6E, + sonic_silver = 0x757575, + platinum_gray = 0x797979, + platinum_grey = 0x797979, + granite = 0x837E7C, + battleship_gray = 0x848482, + battleship_grey = 0x848482, + gunmetal_gray = 0x8D918D, + gunmetal_grey = 0x8D918D, + gray_cloud = 0xB6B6B4, + grey_cloud = 0xB6B6B4, + silver = 0xC0C0C0, + pale_silver = 0xC9C0BB, + gray_goose = 0xD1D0CE, + grey_goose = 0xD1D0CE, + platinum_silver = 0xCECECE, + silver_white = 0xDADBDD, + gainsboro = 0xDCDCDC, + platinum = 0xE5E4E2, + metallic_silver = 0xBCC6CC, + blue_gray = 0x98AFC7, + blue_grey = 0x98AFC7, + roman_silver = 0x838996, + light_slate_gray = 0x778899, + light_slate_grey = 0x778899, + slate_gray = 0x708090, + slate_grey = 0x708090, + rat_gray = 0x6D7B8D, + slate_granite_gray = 0x657383, + slate_granite_grey = 0x657383, + jet_gray = 0x616D7E, + jet_grey = 0x616D7E, + mist_blue = 0x646D7E, + marble_blue = 0x566D7E, + slate_blue_grey = 0x737CA1, + slate_blue_gray = 0x737CA1, + light_purple_blue = 0x728FCE, + azure_blue = 0x4863A0, + blue_jay = 0x2B547E, + charcoal_blue = 0x36454F, + dark_blue_grey = 0x29465B, + dark_slate = 0x2B3856, + deep_sea_blue = 0x123456, + night_blue = 0x151B54, + midnight_blue = 0x191970, + navy = 0x000080, + denim_dark_blue = 0x151B8D, + dark_blue = 0x00008B, + lapis_blue = 0x15317E, + new_midnight_blue = 0x0000A0, + earth_blue = 0x0000A5, + cobalt_blue = 0x0020C2, + medium_blue = 0x0000CD, + blueberry_blue = 0x0041C2, + canary_blue = 0x2916F5, + samco_blue = 0x0002FF, + bright_blue = 0x0909FF, + blue_orchid = 0x1F45FC, + sapphire_blue = 0x2554C7, + blue_eyes = 0x1569C7, + bright_navy_blue = 0x1974D2, + balloon_blue = 0x2B60DE, + royal_blue = 0x4169E1, + ocean_blue = 0x2B65EC, + blue_ribbon = 0x306EFF, + blue_dress = 0x157DEC, + neon_blue = 0x1589FF, + dodger_blue = 0x1E90FF, + glacial_blue_ice = 0x368BC1, + steel_blue = 0x4682B4, + silk_blue = 0x488AC7, + windows_blue = 0x357EC7, + blue_ivy = 0x3090C7, + blue_koi = 0x659EC7, + columbia_blue = 0x87AFC7, + baby_blue = 0x95B9C7, + cornflower_blue = 0x6495ED, + sky_blue_dress = 0x6698FF, + iceberg = 0x56A5EC, + butterfly_blue = 0x38ACEC, + deep_sky_blue = 0x00BFFF, + midday_blue = 0x3BB9FF, + crystal_blue = 0x5CB3FF, + denim_blue = 0x79BAEC, + day_sky_blue = 0x82CAFF, + light_sky_blue = 0x87CEFA, + sky_blue = 0x87CEEB, + jeans_blue = 0xA0CFEC, + blue_angel = 0xB7CEEC, + pastel_blue = 0xB4CFEC, + light_day_blue = 0xADDFFF, + sea_blue = 0xC2DFFF, + heavenly_blue = 0xC6DEFF, + robin_egg_blue = 0xBDEDFF, + powder_blue = 0xB0E0E6, + coral_blue = 0xAFDCEC, + light_blue = 0xADD8E6, + light_steel_blue = 0xB0CFDE, + gulf_blue = 0xC9DFEC, + pastel_light_blue = 0xD5D6EA, + lavender_blue = 0xE3E4FA, + white_blue = 0xDBE9FA, + lavender = 0xE6E6FA, + water = 0xEBF4FA, + alice_blue = 0xF0F8FF, + ghost_white = 0xF8F8FF, + azure = 0xF0FFFF, + light_cyan = 0xE0FFFF, + light_slate = 0xCCFFFF, + electric_blue = 0x9AFEFF, + tron_blue = 0x7DFDFE, + blue_zircon = 0x57FEFF, + aqua = 0x00FFFF, + bright_cyan = 0x0AFFFF, + celeste = 0x50EBEC, + blue_diamond = 0x4EE2EC, + bright_turquoise = 0x16E2F5, + blue_lagoon = 0x8EEBEC, + pale_turquoise = 0xAFEEEE, + pale_blue_lily = 0xCFECEC, + light_teal = 0xB3D9D9, + tiffany_blue = 0x81D8D0, + blue_hosta = 0x77BFC7, + cyan_opaque = 0x92C7C7, + northern_lights_blue = 0x78C7C7, + medium_aquamarine = 0x66CDAA, + magic_mint = 0xAAF0D1, + light_aquamarine = 0x93FFE8, + bright_teal = 0x01F9C6, + turquoise = 0x40E0D0, + medium_turquoise = 0x48D1CC, + deep_turquoise = 0x48CCCD, + jellyfish = 0x46C7C7, + blue_turquoise = 0x43C6DB, + dark_turquoise = 0x00CED1, + macaw_blue_green = 0x43BFC7, + seafoam_green = 0x3EA99F, + cadet_blue = 0x5F9EA0, + blue_chill = 0x3B9C9C, + dark_cyan = 0x008B8B, + teal_green = 0x00827F, + teal = 0x008080, + teal_blue = 0x007C80, + medium_teal = 0x045F5F, + dark_teal = 0x045D5D, + deep_teal = 0x033E3E, + dark_slate_gray = 0x25383C, + dark_slate_grey = 0x25383C, + gunmetal = 0x2C3539, + blue_moss_green = 0x3C565B, + beetle_green = 0x4C787E, + grayish_turquoise = 0x5E7D7E, + greenish_blue = 0x307D7E, + aquamarine_stone = 0x348781, + sea_turtle_green = 0x438D80, + dull_sea_green = 0x4E8975, + dark_green_blue = 0x1F6357, + deep_sea_green = 0x306754, + bottle_green = 0x006A4E, + elf_green = 0x1B8A6B, + dark_mint = 0x31906E, + jade = 0x00A36C, + earth_green = 0x34A56F, + chrome_green = 0x1AA260, + emerald = 0x50C878, + mint = 0x3EB489, + metallic_green = 0x7C9D8E, + camouflage_green = 0x78866B, + sage_green = 0x848B79, + hazel_green = 0x617C58, + venom_green = 0x728C00, + olive_drab = 0x6B8E23, + olive = 0x808000, + dark_olive_green = 0x556B2F, + military_green = 0x4E5B31, + green_leaves = 0x3A5F0B, + army_green = 0x4B5320, + fern_green = 0x667C26, + fall_forest_green = 0x4E9258, + irish_green = 0x08A04B, + pine_green = 0x387C44, + medium_forest_green = 0x347235, + jungle_green = 0x347C2C, + cactus_green = 0x227442, + dark_green = 0x006400, + deep_green = 0x056608, + deep_emerald_green = 0x046307, + hunter_green = 0x355E3B, + dark_forest_green = 0x254117, + lotus_green = 0x004225, + seaweed_green = 0x437C17, + shamrock_green = 0x347C17, + green_onion = 0x6AA121, + moss_green = 0x8A9A5B, + grass_green = 0x3F9B0B, + green_pepper = 0x4AA02C, + dark_lime_green = 0x41A317, + parrot_green = 0x12AD2B, + clover_green = 0x3EA055, + dinosaur_green = 0x73A16C, + green_snake = 0x6CBB3C, + alien_green = 0x6CC417, + green_apple = 0x4CC417, + lime_green = 0x32CD32, + pea_green = 0x52D017, + kelly_green = 0x4CC552, + zombie_green = 0x54C571, + green_peas = 0x89C35C, + dollar_bill_green = 0x85BB65, + frog_green = 0x99C68E, + turquoise_green = 0xA0D6B4, + dark_sea_green = 0x8FBC8F, + basil_green = 0x829F82, + gray_green = 0xA2AD9C, + iguana_green = 0x9CB071, + citron_green = 0x8FB31D, + acid_green = 0xB0BF1A, + avocado_green = 0xB2C248, + pistachio_green = 0x9DC209, + salad_green = 0xA1C935, + yellow_green = 0x9ACD32, + pastel_green = 0x77DD77, + hummingbird_green = 0x7FE817, + nebula_green = 0x59E817, + stoplight_go_green = 0x57E964, + neon_green = 0x16F529, + jade_green = 0x5EFB6E, + lime_mint_green = 0x36F57F, + spring_green = 0x00FF7F, + medium_spring_green = 0x00FA9A, + emerald_green = 0x5FFB17, + lawn_green = 0x7CFC00, + bright_green = 0x66FF00, + chartreuse = 0x7FFF00, + yellow_lawn_green = 0x87F717, + aloe_vera_green = 0x98F516, + dull_green_yellow = 0xB1FB17, + lemon_green = 0xADF802, + green_yellow = 0xADFF2F, + chameleon_green = 0xBDF516, + neon_yellow_green = 0xDAEE01, + yellow_green_grosbeak = 0xE2F516, + tea_green = 0xCCFB5D, + slime_green = 0xBCE954, + algae_green = 0x64E986, + light_green = 0x90EE90, + dragon_green = 0x6AFB92, + pale_green = 0x98FB98, + mint_green = 0x98FF98, + green_thumb = 0xB5EAAA, + organic_brown = 0xE3F9A6, + light_jade = 0xC3FDB8, + light_mint_green = 0xC2E5D3, + light_rose_green = 0xDBF9DB, + chrome_white = 0xE8F1D4, + honeydew = 0xF0FFF0, + mint_cream = 0xF5FFFA, + lemon_chiffon = 0xFFFACD, + parchment = 0xFFFFC2, + cream = 0xFFFFCC, + cream_white = 0xFFFDD0, + light_goldenrod_yellow = 0xFAFAD2, + light_yellow = 0xFFFFE0, + beige = 0xF5F5DC, + cornsilk = 0xFFF8DC, + blonde = 0xFBF6D9, + champagne = 0xF7E7CE, + antique_white = 0xFAEBD7, + papaya_whip = 0xFFEFD5, + blanched_almond = 0xFFEBCD, + bisque = 0xFFE4C4, + wheat = 0xF5DEB3, + moccasin = 0xFFE4B5, + peach = 0xFFE5B4, + light_orange = 0xFED8B1, + peach_puff = 0xFFDAB9, + coral_peach = 0xFBD5AB, + navajo_white = 0xFFDEAD, + golden_blonde = 0xFBE7A1, + golden_silk = 0xF3E3C3, + dark_blonde = 0xF0E2B6, + light_gold = 0xF1E5AC, + vanilla = 0xF3E5AB, + tan_brown = 0xECE5B6, + dirty_white = 0xE8E4C9, + pale_goldenrod = 0xEEE8AA, + khaki = 0xF0E68C, + cardboard_brown = 0xEDDA74, + harvest_gold = 0xEDE275, + sun_yellow = 0xFFE87C, + corn_yellow = 0xFFF380, + pastel_yellow = 0xFAF884, + neon_yellow = 0xFFFF33, + canary_yellow = 0xFFEF00, + banana_yellow = 0xF5E216, + mustard_yellow = 0xFFDB58, + golden_yellow = 0xFFDF00, + bold_yellow = 0xF9DB24, + rubber_ducky_yellow = 0xFFD801, + gold = 0xFFD700, + bright_gold = 0xFDD017, + chrome_gold = 0xFFCE44, + golden_brown = 0xEAC117, + deep_yellow = 0xF6BE00, + macaroni_and_cheese = 0xF2BB66, + saffron = 0xFBB917, + neon_gold = 0xFDBD01, + beer = 0xFBB117, + yellow_orange = 0xFFAE42, + orange_yellow = 0xFFAE42, + cantaloupe = 0xFFA62F, + cheese_orange = 0xFFA600, + brown_sand = 0xEE9A4D, + sandy_brown = 0xF4A460, + brown_sugar = 0xE2A76F, + camel_brown = 0xC19A6B, + deer_brown = 0xE6BF83, + burly_wood = 0xDEB887, + tan = 0xD2B48C, + light_french_beige = 0xC8AD7F, + sand = 0xC2B280, + sage = 0xBCB88A, + fall_leaf_brown = 0xC8B560, + ginger_brown = 0xC9BE62, + bronze_gold = 0xC9AE5D, + dark_khaki = 0xBDB76B, + olive_green = 0xBAB86C, + brass = 0xB5A642, + cookie_brown = 0xC7A317, + metallic_gold = 0xD4AF37, + bee_yellow = 0xE9AB17, + school_bus_yellow = 0xE8A317, + goldenrod = 0xDAA520, + orange_gold = 0xD4A017, + caramel = 0xC68E17, + cinnamon = 0xC58917, + peru = 0xCD853F, + bronze = 0xCD7F32, + tiger_orange = 0xC88141, + copper = 0xB87333, + dark_gold = 0xAA6C39, + metallic_bronze = 0xA97142, + dark_almond = 0xAB784E, + wood = 0x966F33, + oak_brown = 0x806517, + antique_bronze = 0x665D1E, + hazel = 0x8E7618, + dark_yellow = 0x8B8000, + dark_moccasin = 0x827839, + khaki_green = 0x8A865D, + millennium_jade = 0x93917C, + dark_beige = 0x9F8C76, + bullet_shell = 0xAF9B60, + army_brown = 0x827B60, + sandstone = 0x786D5F, + taupe = 0x483C32, + mocha = 0x493D26, + milk_chocolate = 0x513B1C, + gray_brown = 0x3D3635, + dark_coffee = 0x3B2F2F, + old_burgundy = 0x43302E, + western_charcoal = 0x49413F, + bakers_brown = 0x5C3317, + dark_brown = 0x654321, + sepia_brown = 0x704214, + dark_bronze = 0x804A00, + coffee = 0x6F4E37, + brown_bear = 0x835C3B, + red_dirt = 0x7F5217, + sepia = 0x7F462C, + sienna = 0xA0522D, + saddle_brown = 0x8B4513, + dark_sienna = 0x8A4117, + sangria = 0x7E3817, + blood_red = 0x7E3517, + chestnut = 0x954535, + coral_brown = 0x9E4638, + chestnut_red = 0xC34A2C, + mahogany = 0xC04000, + red_gold = 0xEB5406, + red_fox = 0xC35817, + dark_bisque = 0xB86500, + light_brown = 0xB5651D, + petra_gold = 0xB76734, + copper_red = 0xCB6D51, + orange_salmon = 0xC47451, + chocolate = 0xD2691E, + sedona = 0xCC6600, + papaya_orange = 0xE56717, + halloween_orange = 0xE66C2C, + neon_orange = 0xFF6700, + bright_orange = 0xFF5F1F, + pumpkin_orange = 0xF87217, + carrot_orange = 0xF88017, + dark_orange = 0xFF8C00, + construction_cone_orange = 0xF87431, + indian_saffron = 0xFF7722, + sunrise_orange = 0xE67451, + mango_orange = 0xFF8040, + coral = 0xFF7F50, + basket_ball_orange = 0xF88158, + light_salmon_rose = 0xF9966B, + light_salmon = 0xFFA07A, + dark_salmon = 0xE9967A, + tangerine = 0xE78A61, + light_copper = 0xDA8A67, + salmon_pink = 0xFF8674, + salmon = 0xFA8072, + peach_pink = 0xF98B88, + light_coral = 0xF08080, + pastel_red = 0xF67280, + pink_coral = 0xE77471, + bean_red = 0xF75D59, + valentine_red = 0xE55451, + indian_red = 0xCD5C5C, + tomato = 0xFF6347, + shocking_orange = 0xE55B3C, + orange_red = 0xFF4500, + neon_red = 0xFD1C03, + ruby_red = 0xF62217, + ferrari_red = 0xF70D1A, + fire_engine_red = 0xF62817, + lava_red = 0xE42217, + love_red = 0xE41B17, + grapefruit = 0xDC381F, + cherry_red = 0xC24641, + chilli_pepper = 0xC11B17, + fire_brick = 0xB22222, + tomato_sauce_red = 0xB21807, + carbon_red = 0xA70D2A, + cranberry = 0x9F000F, + saffron_red = 0x931314, + crimson_red = 0x990000, + red_wine = 0x990012, + wine_red = 0x990012, + dark_red = 0x8B0000, + maroon = 0x800000, + burgundy = 0x8C001A, + vermilion = 0x7E191B, + deep_red = 0x800517, + red_blood = 0x660000, + blood_night = 0x551606, + dark_scarlet = 0x560319, + black_bean = 0x3D0C02, + chocolate_brown = 0x3F000F, + midnight = 0x2B1B17, + purple_lily = 0x550A35, + purple_maroon = 0x810541, + plum_pie = 0x7D0541, + plum_velvet = 0x7D0552, + dark_raspberry = 0x872657, + velvet_maroon = 0x7E354D, + rosy_finch = 0x7F4E52, + dull_purple = 0x7F525D, + puce = 0x7F5A58, + rose_dust = 0x997070, + pastel_brown = 0xB1907F, + rosy_pink = 0xB38481, + rosy_brown = 0xBC8F8F, + khaki_rose = 0xC5908E, + lipstick_pink = 0xC48793, + pink_brown = 0xC48189, + old_rose = 0xC08081, + dusty_pink = 0xD58A94, + pink_daisy = 0xE799A3, + rose = 0xE8ADAA, + dusty_rose = 0xC9A9A6, + silver_pink = 0xC4AEAD, + gold_pink = 0xE6C7C2, + rose_gold = 0xECC5C0, + deep_peach = 0xFFCBA4, + pastel_orange = 0xF8B88B, + desert_sand = 0xEDC9AF, + unbleached_silk = 0xFFDDCA, + pig_pink = 0xFDD7E4, + pale_pink = 0xF2D4D7, + blush = 0xFFE6E8, + misty_rose = 0xFFE4E1, + pink_bubble_gum = 0xFFDFDD, + light_rose = 0xFBCFCD, + light_red = 0xFFCCCB, + warm_pink = 0xF6C6BD, + deep_rose = 0xFBBBB9, + light_pink = 0xFFB6C1, + soft_pink = 0xFFB8BF, + donut_pink = 0xFAAFBE, + baby_pink = 0xFAAFBA, + flamingo_pink = 0xF9A7B0, + pastel_pink = 0xFEA3AA, + rose_pink = 0xE7A1B0, + pink_rose = 0xE7A1B0, + cadillac_pink = 0xE38AAE, + carnation_pink = 0xF778A1, + pastel_rose = 0xE5788F, + blush_red = 0xE56E94, + pale_violet_red = 0xDB7093, + purple_pink = 0xD16587, + tulip_pink = 0xC25A7C, + bashful_pink = 0xC25283, + dark_pink = 0xE75480, + dark_hot_pink = 0xF660AB, + hot_pink = 0xFF69B4, + watermelon_pink = 0xFC6C85, + violet_red = 0xF6358A, + hot_deep_pink = 0xF52887, + bright_pink = 0xFF007F, + deep_pink = 0xFF1493, + neon_pink = 0xF535AA, + chrome_pink = 0xFF33AA, + neon_hot_pink = 0xFD349C, + pink_cupcake = 0xE45E9D, + royal_pink = 0xE759AC, + dimorphotheca_magenta = 0xE3319D, + pink_lemonade = 0xE4287C, + red_pink = 0xFA2A55, + crimson = 0xDC143C, + bright_maroon = 0xC32148, + rose_red = 0xC21E56, + rogue_pink = 0xC12869, + burnt_pink = 0xC12267, + pink_violet = 0xCA226B, + magenta_pink = 0xCC338B, + medium_violet_red = 0xC71585, + dark_carnation_pink = 0xC12283, + raspberry_purple = 0xB3446C, + pink_plum = 0xB93B8F, + orchid = 0xDA70D6, + deep_mauve = 0xDF73D4, + violet = 0xEE82EE, + fuchsia_pink = 0xFF77FF, + bright_neon_pink = 0xF433FF, + fuchsia = 0xFF00FF, + crimson_purple = 0xE238EC, + heliotrope_purple = 0xD462FF, + tyrian_purple = 0xC45AEC, + medium_orchid = 0xBA55D3, + purple_flower = 0xA74AC7, + orchid_purple = 0xB048B5, + rich_lilac = 0xB666D2, + pastel_violet = 0xD291BC, + mauve_taupe = 0x915F6D, + viola_purple = 0x7E587E, + eggplant = 0x614051, + plum_purple = 0x583759, + grape = 0x5E5A80, + purple_navy = 0x4E5180, + slate_blue = 0x6A5ACD, + blue_lotus = 0x6960EC, + blurple = 0x5865F2, + light_slate_blue = 0x736AFF, + medium_slate_blue = 0x7B68EE, + periwinkle_purple = 0x7575CF, + very_peri = 0x6667AB, + bright_grape = 0x6F2DA8, + purple_amethyst = 0x6C2DC7, + bright_purple = 0x6A0DAD, + deep_periwinkle = 0x5453A6, + dark_slate_blue = 0x483D8B, + purple_haze = 0x4E387E, + purple_iris = 0x571B7E, + dark_purple = 0x4B0150, + deep_purple = 0x36013F, + midnight_purple = 0x2E1A47, + purple_monster = 0x461B7E, + indigo = 0x4B0082, + blue_whale = 0x342D7E, + rebecca_purple = 0x663399, + purple_jam = 0x6A287E, + dark_magenta = 0x8B008B, + purple = 0x800080, + french_lilac = 0x86608E, + dark_orchid = 0x9932CC, + dark_violet = 0x9400D3, + purple_violet = 0x8D38C9, + jasmine_purple = 0xA23BEC, + purple_daffodil = 0xB041FF, + clematis_violet = 0x842DCE, + blue_violet = 0x8A2BE2, + purple_sage_bush = 0x7A5DC7, + lovely_purple = 0x7F38EC, + neon_purple = 0x9D00FF, + purple_plum = 0x8E35EF, + aztech_purple = 0x893BFF, + medium_purple = 0x9370DB, + light_purple = 0x8467D7, + crocus_purple = 0x9172EC, + purple_mimosa = 0x9E7BFF, + periwinkle = 0xCCCCFF, + pale_lilac = 0xDCD0FF, + lavender_purple = 0x967BB6, + rose_purple = 0xB09FCA, + lilac = 0xC8A2C8, + mauve = 0xE0B0FF, + bright_lilac = 0xD891EF, + purple_dragon = 0xC38EC7, + plum = 0xDDA0DD, + blush_pink = 0xE6A9EC, + pastel_purple = 0xF2A2E8, + blossom_pink = 0xF9B7FF, + wisteria_purple = 0xC6AEC7, + purple_thistle = 0xD2B9D3, + thistle = 0xD8BFD8, + purple_white = 0xDFD3E3, + periwinkle_pink = 0xE9CFEC, + cotton_candy = 0xFCDFFF, + lavender_pinocchio = 0xEBDDE2, + dark_white = 0xE1D9D1, + ash_white = 0xE9E4D4, + white_chocolate = 0xEDE6D6, + soft_ivory = 0xFAF0DD, + off_white = 0xF8F0E3, + pearl_white = 0xF8F6F0, + red_white = 0xF3E8EA, + lavender_blush = 0xFFF0F5, + pearl = 0xFDEEF4, + egg_shell = 0xFFF9E3, + old_lace = 0xFEF0E3, + linen = 0xFAF0E6, + sea_shell = 0xFFF5EE, + bone_white = 0xF9F6EE, + rice = 0xFAF5EF, + floral_white = 0xFFFAF0, + ivory = 0xFFFFF0, + white_gold = 0xFFFFF4, + light_white = 0xFFFFF7, + white_smoke = 0xF5F5F5, + cotton = 0xFBFBF9, + snow = 0xFFFAFA, + milk_white = 0xFEFCFF, + half_white = 0xFFFEFA; +} // namespace colors + +/** + * @brief Predefined colour constants, same as colors but for the british. + */ +namespace colours = colors; +} // namespace dpp diff --git a/3rdParty/dpp/commandhandler.h b/3rdParty/dpp/commandhandler.h index 1bebc899f5..a244c18c25 100644 --- a/3rdParty/dpp/commandhandler.h +++ b/3rdParty/dpp/commandhandler.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -50,6 +50,7 @@ struct DPP_EXPORT resolved_user { * @brief Holds user information */ dpp::user user; + /** * @brief Holds member information */ @@ -69,13 +70,40 @@ typedef std::variant> bulk_registration_list_guild; + /** * @brief List of global commands to bulk register */ @@ -384,9 +418,11 @@ class DPP_EXPORT commandhandler { */ void thinking(command_source source, command_completion_event_t callback = utility::log_error()); - /* Easter egg */ + /** + * @brief Easter egg (redefinition of dpp::commandhandler::thinking). + */ void thonk(command_source source, command_completion_event_t callback = utility::log_error()); }; -}; +} diff --git a/3rdParty/dpp/coro.h b/3rdParty/dpp/coro.h index 43f4d4fe96..bc9c140d2b 100644 --- a/3rdParty/dpp/coro.h +++ b/3rdParty/dpp/coro.h @@ -1,158 +1,29 @@ -#ifdef DPP_CORO -#pragma once -#include -#include - -namespace dpp { - - /** - * @brief Shorthand for the coroutine handle's type - */ - using handle_type = std::coroutine_handle; - - class cluster; - - /** - * @brief Return type for coroutines - */ - struct task { - /** - * @brief Required nested promise_type for coroutines - */ - using promise_type = dpp::promise; - }; - - /** - * @brief Implementation of promise_type for dpp's coroutines - */ - struct promise { - /** - * @brief A pointer to the cluster making the requests in the coroutine - */ - cluster* bot = nullptr; - - /** - * @brief The result of the last co_await-ed function - */ - confirmation_callback_t callback; - - /** - * @brief Construct a new promise object - */ - promise() = default; - - /** - * @brief Construct a new promise object - * - * @param ev Base type of all events, only used to get the dpp::cluster pointer - */ - promise(const dpp::event_dispatch_t& ev) : bot(ev.from->creator) { } - - /** - * @brief Get the return object - * - * @return task dpp::task type - */ - task get_return_object() { - return {}; - } - - /** - * @brief Function called when the coroutine is first suspended, never suspends - * - * @return std::suspend_never Never suspend this coroutine at the first suspend point - */ - std::suspend_never initial_suspend() noexcept { - return {}; - } +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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. + * + ************************************************************************************/ - /** - * @brief Function called when the coroutine reaches its last suspension point - * - * @return std::suspend_never Never suspend this coroutine at the final suspend point - */ - std::suspend_never final_suspend() noexcept { - return {}; - } - - /** - * @brief Function called when the coroutine returns nothing - */ - void return_void() noexcept {} - - /** - * @brief Function called when coroutine throws a un-catch-ed exception. Does nothing - */ - void unhandled_exception() { - /* try { std::rethrow_exception(std::current_exception()); } */ - /* catch (const std::exception& e) { std::cout << e.what() << '\n'; } */ - } - }; - - /** - * @brief A co_await-able struct which returns the result of stored api call when co_await-ed. Meant to be opaque to the user - * - * @tparam T The type of the function (lambda if auto-generated by the php script) handling the making of api call - */ - template - struct awaitable { - /** - * @brief Pointer to the nested promise object of the coroutine, used for storing and retrieving the result of an api call - */ - promise* p; - - /** - * @brief Pointer to the cluster making the api request - */ - cluster* bot; - - /** - * @brief The function handling the making of request, using the cluster pointer - */ - T api_req; - - /** - * @brief Construct a new awaitable object - * - * @param cl pointer to the cluster making the api request - * @param api_call a function to invoke with the cluster pointer, handles the making of request - */ - awaitable(cluster* cl, T api_call) : bot{cl}, api_req{api_call} {} - - /** - * @brief First function called when this object is co_await-ed, its return type tells if the coroutine should be immediately suspended - * - * @return bool false, signifying immediate suspension - */ - bool await_ready() noexcept { - return false; - } - - /** - * @brief Function called when the coroutine is suspended, makes the api request and queues the resumption of the suspended coroutine, storing the result in promise object - * - * @param handle the handle to the suspended coroutine - */ - void await_suspend(handle_type handle) { - /* p = &handle.promise(); */ - /* if (!p->bot) p->bot = bot; */ - api_req([handle](const confirmation_callback_t& cback) { handle.promise().callback = cback; handle.resume(); }); - } - - /** - * @brief Function called when the coroutine is resumed by its handle, handles the retrieval and return of result from promise object - * - * @return confirmation_callback_t the result of the api call - */ - confirmation_callback_t await_resume() { - return p->callback; - } - }; - -}; +#pragma once -/* template<> */ -/* struct std::coroutine_traits { */ -/* using promise_type = dpp::promise; */ -/* }; */ -#endif +#include "coro/awaitable.h" +#include "coro/async.h" +#include "coro/coroutine.h" +#include "coro/job.h" +#include "coro/task.h" +#include "coro/when_any.h" diff --git a/3rdParty/dpp/coro/async.h b/3rdParty/dpp/coro/async.h new file mode 100644 index 0000000000..d3e973c1df --- /dev/null +++ b/3rdParty/dpp/coro/async.h @@ -0,0 +1,189 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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 + +namespace dpp { + +struct async_dummy : awaitable_dummy { + std::shared_ptr dummy_shared_state = nullptr; +}; + +} + +#ifdef DPP_CORO + +#include "coro.h" + +#include +#include +#include +#include +#include + +namespace dpp { + +namespace detail { + +namespace async { + +/** + * @brief Shared state of the async and its callback, to be used across threads. + */ +template +struct callback { + /** + * @brief Promise object to set the result into + */ + std::shared_ptr> promise{nullptr}; + + /** + * @brief Call operator, sets the value in the promise and notifies any awaiter + * + * @param v Callback value + */ + template + void operator()(const U& v) const requires (std::convertible_to) { + promise->set_value(v); + } + + /** + * @brief Call operator, sets the value in the promise and notifies any awaiter + * + * @param v Callback value + */ + template + void operator()(U&& v) const requires (std::convertible_to) { + promise->set_value(std::move(v)); + } + + /** + * @brief Call operator, sets the value in the promise and notifies any awaiter + */ + void operator()() const requires (std::is_void_v) + { + promise->set_value(); + } +}; + +} // namespace async + +} // namespace detail + +struct confirmation_callback_t; + +/** + * @class async async.h coro/async.h + * @brief A co_await-able object handling an API call in parallel with the caller. + * + * This class is the return type of the dpp::cluster::co_* methods, but it can also be created manually to wrap any async call. + * + * @remark - The coroutine may be resumed in another thread, do not rely on thread_local variables. + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. Please report any to GitHub issues or to the D++ Discord server. + * @tparam R The return type of the API call. Defaults to confirmation_callback_t + */ +template +class async : public awaitable { + /** + * @brief Callable object to pass to API calls + */ + detail::async::callback api_callback{}; + + /** + * @brief Internal promise constructor, grabs a promise object for the callback to use + */ + explicit async(std::shared_ptr> &&promise) : awaitable{promise.get()}, api_callback{std::move(promise)} {} + +public: + using awaitable::awaitable; // use awaitable's constructors + using awaitable::operator=; // use async_base's assignment operator + using awaitable::await_ready; // expose await_ready as public + + /** + * @brief The return type of the API call. Defaults to confirmation_callback_t + */ + using result_type = R; + + /** + * @brief Construct an async object wrapping an object method, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param obj The object to call the method on + * @param fun The method of the object to call. Its last parameter must be a callback taking a parameter of type R + * @param args Parameters to pass to the method, excluding the callback + */ + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + explicit async(Obj &&obj, Fun &&fun, Args&&... args) : async{std::make_shared>()} { + std::invoke(std::forward(fun), std::forward(obj), std::forward(args)..., api_callback); + } + + /** + * @brief Construct an async object wrapping an invokeable object, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param fun The object to call using std::invoke. Its last parameter must be a callable taking a parameter of type R + * @param args Parameters to pass to the object, excluding the callback + */ + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + explicit async(Fun &&fun, Args&&... args) : async{std::make_shared>()} { + std::invoke(std::forward(fun), std::forward(args)..., api_callback); + } + + /** + * @brief Copy constructor is disabled. + */ + async(const async&) = delete; + + /** + * @brief Move constructor, moves the awaitable async object + */ + async(async&&) = default; + + /** + * @brief Copy assignment operator is disabled. + */ + async& operator=(const async&) = delete; + + /** + * @brief Move assignment operator, moves the awaitable async object + */ + async& operator=(async&&) = default; + + /** + * @brief Destructor, signals to the callback that the async object is gone and shouldn't be notified of the result + */ + ~async() { + this->abandon(); + } +}; + +DPP_CHECK_ABI_COMPAT(async<>, async_dummy); + +} + +#endif /* DPP_CORO */ diff --git a/3rdParty/dpp/coro/awaitable.h b/3rdParty/dpp/coro/awaitable.h new file mode 100644 index 0000000000..6c7e8d7315 --- /dev/null +++ b/3rdParty/dpp/coro/awaitable.h @@ -0,0 +1,733 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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 + +namespace dpp { + +struct awaitable_dummy { + int *promise_dummy = nullptr; +}; + +} + +#ifdef DPP_CORO + +#include + +// Do not include as coro.h includes or depending on clang version +#include +#include +#include +#include +#include +#include + +namespace dpp { + +namespace detail::promise { + +/** + * @brief State of a promise + */ +enum state_flags { + /** + * @brief Promise is empty + */ + sf_none = 0b0000000, + + /** + * @brief Promise has spawned an awaitable + */ + sf_has_awaitable = 0b00000001, + + /** + * @brief Promise is being awaited + */ + sf_awaited = 0b00000010, + + /** + * @brief Promise has a result + */ + sf_ready = 0b00000100, + + /** + * @brief Promise has completed, no more results are expected + */ + sf_done = 0b00001000, + + /** + * @brief Promise was broken - future or promise is gone + */ + sf_broken = 0b0010000 +}; + +template +class promise_base; + +/** + * @brief Empty result from void-returning awaitable + */ +struct empty{}; + +/** + * @brief Variant for the 3 conceptual values of a coroutine: + */ +template +using result_t = std::variant, empty, T>, std::exception_ptr>; + +template +void spawn_sync_wait_job(auto* awaitable, std::condition_variable &cv, auto&& result); + +} /* namespace detail::promise */ + +template +class basic_awaitable { +protected: + /** + * @brief Implementation for sync_wait. This is code used by sync_wait, sync_wait_for, sync_wait_until. + * + * @tparam Timed Whether the wait function times out or not + * @param do_wait Function to do the actual wait on the cv + * @return If T is void, returns a boolean for which true means the awaitable completed, false means it timed out. + * @return If T is non-void, returns a std::optional for which an absence of value means timed out. + */ + template + auto sync_wait_impl(auto&& do_wait) { + using result_type = decltype(detail::co_await_resolve(std::declval()).await_resume()); + using variant_type = detail::promise::result_t; + variant_type result; + std::condition_variable cv; + + detail::promise::spawn_sync_wait_job(static_cast(this), cv, result); + do_wait(cv, result); + /* + * Note: we use .index() here to support dpp::promise & dpp::promise :D + */ + if (result.index() == 2) { + std::rethrow_exception(std::get<2>(result)); + } + if constexpr (!Timed) { // no timeout + if constexpr (!std::is_void_v) { + return std::get<1>(result); + } + } else { // timeout + if constexpr (std::is_void_v) { + return result.index() == 1 ? true : false; + } else { + return result.index() == 1 ? std::optional{std::get<1>(result)} : std::nullopt; + } + } + } + +public: + /** + * @brief Blocks this thread and waits for the awaitable to finish. + * + * @attention This will BLOCK THE THREAD. It is likely you want to use co_await instead. + * @return If T is void, returns a boolean for which true means the awaitable completed, false means it timed out. + * @return If T is non-void, returns a std::optional for which an absence of value means timed out. + */ + auto sync_wait() { + return sync_wait_impl([](std::condition_variable &cv, auto&& result) { + std::mutex m{}; + std::unique_lock lock{m}; + cv.wait(lock, [&result] { return result.index() != 0; }); + }); + } + + /** + * @brief Blocks this thread and waits for the awaitable to finish. + * + * @attention This will BLOCK THE THREAD. It is likely you want to use co_await instead. + * @param duration Maximum duration to wait for + * @return If T is void, returns a boolean for which true means the awaitable completed, false means it timed out. + * @return If T is non-void, returns a std::optional for which an absence of value means timed out. + */ + template + auto sync_wait_for(const std::chrono::duration& duration) { + return sync_wait_impl([duration](std::condition_variable &cv, auto&& result) { + std::mutex m{}; + std::unique_lock lock{m}; + cv.wait_for(lock, duration, [&result] { return result.index() != 0; }); + }); + } + + /** + * @brief Blocks this thread and waits for the awaitable to finish. + * + * @attention This will BLOCK THE THREAD. It is likely you want to use co_await instead. + * @param time Maximum time point to wait for + * @return If T is void, returns a boolean for which true means the awaitable completed, false means it timed out. + * @return If T is non-void, returns a std::optional for which an absence of value means timed out. + */ + template + auto sync_wait_until(const std::chrono::time_point &time) { + return sync_wait_impl([time](std::condition_variable &cv, auto&& result) { + std::mutex m{}; + std::unique_lock lock{m}; + cv.wait_until(lock, time, [&result] { return result.index() != 0; }); + }); + } +}; + +/** + * @brief Generic awaitable class, represents a future value that can be co_await-ed on. + * + * Roughly equivalent of std::future for coroutines, with the crucial distinction that the future does not own a reference to a "shared state". + * It holds a non-owning reference to the promise, which must be kept alive for the entire lifetime of the awaitable. + * + * @tparam T Type of the asynchronous value + * @see promise + */ +template +class awaitable : public basic_awaitable> { +protected: + friend class detail::promise::promise_base; + + using shared_state = detail::promise::promise_base; + using state_flags = detail::promise::state_flags; + + /** + * @brief The type of the result produced by this task. + */ + using result_type = T; + + /** + * @brief Non-owning pointer to the promise, which must be kept alive for the entire lifetime of the awaitable. + */ + shared_state *state_ptr = nullptr; + + /** + * @brief Construct from a promise. + * + * @param promise The promise to refer to. + */ + awaitable(shared_state *promise) noexcept : state_ptr{promise} {} + + /** + * @brief Abandons the promise. + * + * Set the promise's state to broken and unlinks this awaitable. + * + * @return uint8_t Flags previously held before setting them to broken + */ + uint8_t abandon(); + /** + * @brief Awaiter returned by co_await. + * + * Contains the await_ready, await_suspend and await_resume functions required by the C++ standard. + * This class is CRTP-like, in that it will refer to an object derived from awaitable. + * + * @tparam Derived Type of reference to refer to the awaitable. + */ + template + struct awaiter { + Derived awaitable_obj; + + /** + * @brief First function called by the standard library when co_await-ing this object. + * + * @throws dpp::logic_exception If the awaitable's valid() would return false. + * @return bool Whether the result is ready, in which case we don't need to suspend + */ + bool await_ready() const; + + /** + * @brief Second function called by the standard library when co_await-ing this object. + * + * @throws dpp::logic_exception If the awaitable's valid() would return false. + * At this point the coroutine frame was allocated and suspended. + * + * @return bool Whether we do need to suspend or not + */ + bool await_suspend(detail::std_coroutine::coroutine_handle<> handle); + + /** + * @brief Third and final function called by the standard library when co_await-ing this object, after resuming. + * + * @throw ? Any exception that occured during the retrieval of the value will be thrown + * @return T The result. + */ + T await_resume(); + }; + +public: + /** + * @brief Construct an empty awaitable. + * + * Such an awaitable must be assigned a promise before it can be awaited. + */ + awaitable() = default; + + /** + * @brief Copy construction is disabled. + */ + awaitable(const awaitable&) = delete; + + /** + * @brief Move from another awaitable. + * + * @param rhs The awaitable to move from, left in an unspecified state after this. + */ + awaitable(awaitable&& rhs) noexcept : state_ptr(std::exchange(rhs.state_ptr, nullptr)) { + } + + /** + * @brief Title :) + * + * We use this in the destructor + */ + void if_this_causes_an_invalid_read_your_promise_was_destroyed_before_your_awaitable____check_your_promise_lifetime() { + abandon(); + } + + /** + * @brief Destructor. + * + * May signal to the promise that it was destroyed. + */ + ~awaitable(); + + /** + * @brief Copy assignment is disabled. + */ + awaitable& operator=(const awaitable&) = delete; + + /** + * @brief Move from another awaitable. + * + * @param rhs The awaitable to move from, left in an unspecified state after this. + * @return *this + */ + awaitable& operator=(awaitable&& rhs) noexcept { + abandon(); + state_ptr = std::exchange(rhs.state_ptr, nullptr); + return *this; + } + + /** + * @brief Check whether this awaitable refers to a valid promise. + * + * @return bool Whether this awaitable refers to a valid promise or not + */ + bool valid() const noexcept; + + /** + * @brief Check whether or not co_await-ing this would suspend the caller, i.e. if we have the result or not + * + * @return bool Whether we already have the result or not + */ + bool await_ready() const; + + /** + * @brief Overload of the co_await operator. + * + * @return Returns an @ref awaiter referencing this awaitable. + */ + template + requires (std::is_base_of_v>) + friend awaiter operator co_await(Derived& obj) noexcept { + return {obj}; + } + + /** + * @brief Overload of the co_await operator. Returns an @ref awaiter referencing this awaitable. + * + * @return Returns an @ref awaiter referencing this awaitable. + */ + template + requires (std::is_base_of_v>) + friend awaiter operator co_await(Derived&& obj) noexcept { + return {std::move(obj)}; + } +}; + +namespace detail::promise { + +/** + * @brief Base class defining logic common to all promise types, aka the "write" end of an awaitable. + */ +template +class promise_base { +protected: + friend class awaitable; + + /** + * @brief Variant representing one of either 3 states of the result value : empty, result, exception. + */ + using storage_type = result_t; + + /** + * @brief State of the result value. + * + * @see storage_type + * + * @note use .index() instead of std::holds_alternative to support promise_base and promise_base :) + */ + storage_type value = std::monostate{}; + + /** + * @brief State of the awaitable tied to this promise. + */ + std::atomic state = sf_none; + + /** + * @brief Coroutine handle currently awaiting the completion of this promise. + */ + std_coroutine::coroutine_handle<> awaiter = nullptr; + + /** + * @brief Check if the result is empty, throws otherwise. + * + * @throw dpp::logic_exception if the result isn't empty. + */ + void throw_if_not_empty() { + if (value.index() != 0) [[unlikely]] { + throw dpp::logic_exception("cannot set a value on a promise that already has one"); + } + } + + /** + * @brief Unlinks this promise from its currently linked awaiter and returns it. + * + * At the time of writing this is only used in the case of a serious internal error in dpp::task. + * Avoid using this as this will crash if the promise is used after this. + */ + std_coroutine::coroutine_handle<> release_awaiter() { + return std::exchange(awaiter, nullptr); + } + + /** + * @brief Construct a new promise, with empty result. + */ + promise_base() = default; + + /** + * @brief Copy construction is disabled. + */ + promise_base(const promise_base&) = delete; + + /** + * @brief Move construction is disabled. + * + * awaitable hold a pointer to this object so moving is not possible. + */ + promise_base(promise_base&& rhs) = delete; + +public: + /** + * @brief Copy assignment is disabled. + */ + promise_base &operator=(const promise_base&) = delete; + + /** + * @brief Move assignment is disabled. + */ + promise_base &operator=(promise_base&& rhs) = delete; + + /** + * @brief Set this promise to an exception and resume any awaiter. + * + * @tparam Notify Whether to resume any awaiter or not. + * @throws dpp::logic_exception if the promise is not empty. + * @throws ? Any exception thrown by the coroutine if resumed will propagate + */ + template + void set_exception(std::exception_ptr ptr) { + throw_if_not_empty(); + value.template emplace<2>(std::move(ptr)); + [[maybe_unused]] auto previous_value = this->state.fetch_or(sf_ready, std::memory_order::acq_rel); + if constexpr (Notify) { + if ((previous_value & sf_awaited) != 0) { + this->awaiter.resume(); + } + } + } + + /** + * @brief Notify a currently awaiting coroutine that the result is ready. + * + * @note This may resume the coroutine on the current thread. + * @throws ? Any exception thrown by the coroutine if resumed will propagate + */ + void notify_awaiter() { + if ((state.load(std::memory_order::acquire) & sf_awaited) != 0) { + awaiter.resume(); + } + } + + /** + * @brief Get an awaitable object for this promise. + * + * @throws dpp::logic_exception if get_awaitable has already been called on this object. + * @return awaitable An object that can be co_await-ed to retrieve the value of this promise. + */ + awaitable get_awaitable() { + uint8_t previous_flags = state.fetch_or(sf_has_awaitable, std::memory_order::relaxed); + if (previous_flags & sf_has_awaitable) [[unlikely]] { + throw dpp::logic_exception{"an awaitable was already created from this promise"}; + } + return {this}; + } +}; + +} + +/** + * @brief Generic promise class, represents the owning potion of an asynchronous value. + * + * This class is roughly equivalent to std::promise, with the crucial distinction that the promise *IS* the shared state. + * As such, the promise needs to be kept alive for the entire time a value can be retrieved. + * + * @tparam T Type of the asynchronous value + * @see awaitable + */ +template +class basic_promise : public detail::promise::promise_base { +public: + using detail::promise::promise_base::promise_base; + using detail::promise::promise_base::operator=; + + /** + * @brief Construct the result in place by forwarding the arguments, and by default resume any awaiter. + * + * @tparam Notify Whether to resume any awaiter or not. + * @throws dpp::logic_exception if the promise is not empty. + */ + template + requires (std::constructible_from) + void emplace_value(Args&&... args) { + this->throw_if_not_empty(); + try { + this->value.template emplace<1>(std::forward(args)...); + } catch (...) { + this->value.template emplace<2>(std::current_exception()); + } + [[maybe_unused]] auto previous_value = this->state.fetch_or(detail::promise::sf_ready, std::memory_order::acq_rel); + if constexpr (Notify) { + if (previous_value & detail::promise::sf_awaited) { + this->awaiter.resume(); + } + } + } + + /** + * @brief Construct the result by forwarding reference, and resume any awaiter. + * + * @tparam Notify Whether to resume any awaiter or not. + * @throws dpp::logic_exception if the promise is not empty. + */ + template + requires (std::convertible_to) + void set_value(U&& v) { + emplace_value(std::forward(v)); + } + + /** + * @brief Construct a void result, and resume any awaiter. + * + * @tparam Notify Whether to resume any awaiter or not. + * @throws dpp::logic_exception if the promise is not empty. + */ + template + requires (std::is_void_v) + void set_value() { + this->throw_if_not_empty(); + this->value.template emplace<1>(); + [[maybe_unused]] auto previous_value = this->state.fetch_or(detail::promise::sf_ready, std::memory_order::acq_rel); + if constexpr (Notify) { + if (previous_value & detail::promise::sf_awaited) { + this->awaiter.resume(); + } + } + } +}; + +/** + * @brief Generic promise class, represents the owning potion of an asynchronous value. + * + * This class is roughly equivalent to std::promise, with the crucial distinction that the promise *IS* the shared state. + * As such, the promise needs to be kept alive for the entire time a value can be retrieved. + * + * The difference between basic_promise and this object is that this one is moveable as it wraps an underlying basic_promise in a std::unique_ptr. + * + * @see awaitable + */ +template +class moveable_promise { + /** + * @brief Shared state, wrapped in a unique_ptr to allow move without disturbing an awaitable's promise pointer. + */ + std::unique_ptr> shared_state = std::make_unique>(); + +public: + /** + * @copydoc basic_promise::emplace_value + */ + template + requires (std::constructible_from) + void emplace_value(Args&&... args) { + shared_state->template emplace_value(std::forward(args)...); + } + + /** + * @copydoc basic_promise::set_value(U&&) + */ + template + void set_value(U&& v) requires (std::convertible_to) { + shared_state->template set_value(std::forward(v)); + } + + /** + * @copydoc basic_promise::set_value() + */ + template + void set_value() requires (std::is_void_v) { + shared_state->template set_value(); + } + + /** + * @copydoc basic_promise::set_value(T&&) + */ + template + void set_exception(std::exception_ptr ptr) { + shared_state->template set_exception(std::move(ptr)); + } + + /** + * @copydoc basic_promise::notify_awaiter + */ + void notify_awaiter() { + shared_state->notify_awaiter(); + } + + /** + * @copydoc basic_promise::get_awaitable + */ + awaitable get_awaitable() { + return shared_state->get_awaitable(); + } +}; + +template +using promise = moveable_promise; + +template +auto awaitable::abandon() -> uint8_t { + uint8_t previous_state = state_flags::sf_broken; + if (state_ptr) { + previous_state = state_ptr->state.fetch_or(state_flags::sf_broken, std::memory_order::acq_rel); + state_ptr = nullptr; + } + return previous_state; +} + +template +awaitable::~awaitable() { + if_this_causes_an_invalid_read_your_promise_was_destroyed_before_your_awaitable____check_your_promise_lifetime(); +} + +template +bool awaitable::valid() const noexcept { + return state_ptr != nullptr; +} + +template +bool awaitable::await_ready() const { + if (!this->valid()) { + throw dpp::logic_exception("cannot co_await an empty awaitable"); + } + uint8_t state = this->state_ptr->state.load(std::memory_order::relaxed); + return state & detail::promise::sf_ready; +} + +template +template +bool awaitable::awaiter::await_suspend(detail::std_coroutine::coroutine_handle<> handle) { + auto &promise = *awaitable_obj.state_ptr; + + promise.awaiter = handle; + auto previous_flags = promise.state.fetch_or(detail::promise::sf_awaited, std::memory_order::relaxed); + if (previous_flags & detail::promise::sf_awaited) { + throw dpp::logic_exception("awaitable is already being awaited"); + } + return !(previous_flags & detail::promise::sf_ready); +} + +template +template +T awaitable::awaiter::await_resume() { + auto &promise = *awaitable_obj.state_ptr; + + promise.state.fetch_and(~detail::promise::sf_awaited, std::memory_order::acq_rel); + if (std::holds_alternative(promise.value)) { + std::rethrow_exception(std::get<2>(promise.value)); + } + if constexpr (!std::is_void_v) { + return std::get<1>(std::move(promise.value)); + } else { + return; + } +} + + + +template +template +bool awaitable::awaiter::await_ready() const { + return static_cast(awaitable_obj).await_ready(); +} + +} + +#include + +namespace dpp { + +namespace detail::promise { + +template +void spawn_sync_wait_job(auto* awaitable, std::condition_variable &cv, auto&& result) { + [](auto* awaitable_, std::condition_variable &cv_, auto&& result_) -> dpp::job { + try { + if constexpr (std::is_void_v) { + co_await *awaitable_; + result_.template emplace<1>(); + } else { + result_.template emplace<1>(co_await *awaitable_); + } + } catch (...) { + result_.template emplace<2>(std::current_exception()); + } + cv_.notify_all(); + }(awaitable, cv, std::forward(result)); +} + +} + +} + +#endif /* DPP_CORO */ diff --git a/3rdParty/dpp/coro/coro.h b/3rdParty/dpp/coro/coro.h new file mode 100644 index 0000000000..d920cab93e --- /dev/null +++ b/3rdParty/dpp/coro/coro.h @@ -0,0 +1,202 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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 +#ifdef DPP_CORO + +#if (defined(_LIBCPP_VERSION) and !defined(__cpp_impl_coroutine)) // if libc++ experimental implementation (LLVM < 14) +# define STDCORO_EXPERIMENTAL_HEADER +# define STDCORO_EXPERIMENTAL_NAMESPACE +#endif + +#ifdef STDCORO_GLIBCXX_COMPAT +# define __cpp_impl_coroutine 1 +namespace std { + namespace experimental { + using namespace std; + } +} +#endif + +#ifdef STDCORO_EXPERIMENTAL_HEADER +# include +#else +# include +#endif + +namespace dpp { + +/** + * @brief Implementation details for internal use only. + * + * @attention This is only meant to be used by D++ internally. Support will not be given regarding the facilities in this namespace. + */ +namespace detail { +#ifdef _DOXYGEN_ +/** + * @brief Alias for either std or std::experimental depending on compiler and library. Used by coroutine implementation. + * + * @todo Remove and use std when all supported libraries have coroutines in it + */ +namespace std_coroutine {} +#else +# ifdef STDCORO_EXPERIMENTAL_NAMESPACE +namespace std_coroutine = std::experimental; +# else +namespace std_coroutine = std; +# endif +#endif + +#ifndef _DOXYGEN_ +/** + * @brief Concept to check if a type has a useable `operator co_await()` member + */ +template +concept has_co_await_member = requires (T expr) { expr.operator co_await(); }; + +/** + * @brief Concept to check if a type has a useable overload of the free function `operator co_await(expr)` + */ +template +concept has_free_co_await = requires (T expr) { operator co_await(expr); }; + +/** + * @brief Concept to check if a type has useable `await_ready()`, `await_suspend()` and `await_resume()` member functions. + */ +template +concept has_await_members = requires (T expr) { expr.await_ready(); expr.await_suspend(); expr.await_resume(); }; + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + */ +template +requires (has_co_await_member) +decltype(auto) co_await_resolve(T&& expr) noexcept(noexcept(expr.operator co_await())) { + decltype(auto) awaiter = expr.operator co_await(); + return awaiter; +} + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + */ +template +requires (!has_co_await_member && has_free_co_await) +decltype(auto) co_await_resolve(T&& expr) noexcept(noexcept(operator co_await(expr))) { + decltype(auto) awaiter = operator co_await(static_cast(expr)); + return awaiter; +} + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + */ +template +requires (!has_co_await_member && !has_free_co_await) +decltype(auto) co_await_resolve(T&& expr) noexcept { + return static_cast(expr); +} + +#else + +/** + * @brief Concept to check if a type has a useable `operator co_await()` member + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +inline constexpr bool has_co_await_member; + +/** + * @brief Concept to check if a type has a useable overload of the free function `operator co_await(expr)` + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +inline constexpr bool has_free_co_await; + +/** + * @brief Concept to check if a type has useable `await_ready()`, `await_suspend()` and `await_resume()` member functions. + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +inline constexpr bool has_await_members; + +/** + * @brief Concept to check if a type can be used with co_await + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +inline constexpr bool awaitable_type; + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + * + * This function is conditionally noexcept, if the returned expression also is. + */ +decltype(auto) co_await_resolve(auto&& expr) {} + +#endif + +/** + * @brief Convenience alias for the result of a certain awaitable's await_resume. + */ +template +using awaitable_result = decltype(co_await_resolve(std::declval()).await_resume()); + +} // namespace detail + +/** + * @brief Concept to check if a type can be used with co_await + */ +template +concept awaitable_type = requires (T expr) { detail::co_await_resolve(expr).await_ready(); }; + +struct confirmation_callback_t; + +template +class async; + +template +#ifndef _DOXYGEN_ +requires (!std::is_reference_v) +#endif +class task; + +template +class coroutine; + +struct job; + +#ifdef DPP_CORO_TEST +/** + * @brief Allocation count of a certain type, for testing purposes. + * + * @todo Remove when coro is stable + */ +template +inline int coro_alloc_count = 0; +#endif + +} // namespace dpp + +#endif /* DPP_CORO */ + diff --git a/3rdParty/dpp/coro/coroutine.h b/3rdParty/dpp/coro/coroutine.h new file mode 100644 index 0000000000..89b41427be --- /dev/null +++ b/3rdParty/dpp/coro/coroutine.h @@ -0,0 +1,406 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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 { + +struct coroutine_dummy { + int *handle_dummy = nullptr; +}; + +} + +#ifdef DPP_CORO + +#include +#include + +#include +#include +#include +#include +#include + +namespace dpp { + +namespace detail { + +namespace coroutine { + +template +struct promise_t; + +template +/** + * @brief Alias for the handle_t of a coroutine. + */ +using handle_t = std_coroutine::coroutine_handle>; + +} // namespace coroutine + +} // namespace detail + +/** + * @class coroutine coroutine.h coro/coroutine.h + * @brief Base type for a coroutine, starts on co_await. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. + * Please report any to GitHub Issues or to our Discord Server. + * @warning - Using co_await on this object more than once is undefined behavior. + * @tparam R Return type of the coroutine. Can be void, or a complete object that supports move construction and move assignment. + */ +template +class [[nodiscard("dpp::coroutine only starts when it is awaited, it will do nothing if discarded")]] coroutine : public basic_awaitable> { + /** + * @brief Promise has friend access for the constructor + */ + friend struct detail::coroutine::promise_t; + + /** + * @brief Coroutine handle. + */ + detail::coroutine::handle_t handle{nullptr}; + + /** + * @brief Construct from a handle. Internal use only. + */ + coroutine(detail::coroutine::handle_t h) : handle{h} {} + + struct awaiter { + /** + * @brief Reference to the coroutine object being awaited. + */ + coroutine &coro; + + /** + * @brief First function called by the standard library when the coroutine is co_await-ed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throws invalid_operation_exception if the coroutine is empty or finished. + * @return bool Whether the coroutine is done + */ + [[nodiscard]] bool await_ready() const { + if (!coro.handle) { + throw dpp::logic_exception("cannot co_await an empty coroutine"); + } + return coro.handle.done(); + } + + /** + * @brief Second function called by the standard library when the coroutine is co_await-ed. + * + * Stores the calling coroutine in the promise to resume when this coroutine suspends. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @param caller The calling coroutine, now suspended + */ + template + [[nodiscard]] detail::coroutine::handle_t await_suspend(detail::std_coroutine::coroutine_handle caller) noexcept { + coro.handle.promise().parent = caller; + return coro.handle; + } + + /** + * @brief Final function called by the standard library when the coroutine is co_await-ed. + * + * Pops the coroutine's result and returns it. + * @remark Do not call this manually, use the co_await keyword instead. + */ + R await_resume() { + detail::coroutine::promise_t &promise = coro.handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + if constexpr (!std::is_void_v) { + return *std::exchange(promise.result, std::nullopt); + } else { + return; // unnecessary but makes lsp happy + } + } + }; + +public: + /** + * @brief The type of the result produced by this coroutine. + */ + using result_type = R; + + /** + * @brief Default constructor, creates an empty coroutine. + */ + coroutine() = default; + + /** + * @brief Copy constructor is disabled + */ + coroutine(const coroutine &) = delete; + + /** + * @brief Move constructor, grabs another coroutine's handle + * + * @param other Coroutine to move the handle from + */ + coroutine(coroutine &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + + /** + * @brief Destructor, destroys the handle. + */ + ~coroutine() { + if (handle) { + handle.destroy(); + } + } + + /** + * @brief Copy assignment is disabled + */ + coroutine &operator=(const coroutine &) = delete; + + /** + * @brief Move assignment, grabs another coroutine's handle + * + * @param other Coroutine to move the handle from + */ + coroutine &operator=(coroutine &&other) noexcept { + handle = std::exchange(other.handle, nullptr); + return *this; + } + + [[nodiscard]] auto operator co_await() { + return awaiter{*this}; + } +}; + +namespace detail::coroutine { + template + struct final_awaiter; + +#ifdef DPP_CORO_TEST + struct promise_t_base{}; +#endif + + /** + * @brief Promise type for coroutine. + */ + template + struct promise_t { + /** + * @brief Handle of the coroutine co_await-ing this coroutine. + */ + std_coroutine::coroutine_handle<> parent{nullptr}; + + /** + * @brief Return value of the coroutine + */ + std::optional result{}; + + /** + * @brief Pointer to an uncaught exception thrown by the coroutine + */ + std::exception_ptr exception{nullptr}; + +#ifdef DPP_CORO_TEST + promise_t() { + ++coro_alloc_count; + } + + ~promise_t() { + --coro_alloc_count; + } +#endif + + /** + * @brief Function called by the standard library when reaching the end of a coroutine + * + * @return final_awaiter Resumes any coroutine co_await-ing on this + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept; + + /** + * @brief Function called by the standard library when the coroutine start + * + * @return @return std::suspend_always Always suspend at the start, for a lazy start + */ + [[nodiscard]] std_coroutine::suspend_always initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called when an exception escapes the coroutine + * + * Stores the exception to throw to the co_await-er + */ + void unhandled_exception() noexcept { + exception = std::current_exception(); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(R&& expr) noexcept(std::is_nothrow_move_constructible_v) requires std::move_constructible { + result = static_cast(expr); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(const R &expr) noexcept(std::is_nothrow_copy_constructible_v) requires std::copy_constructible { + result = expr; + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + template + requires (!std::is_same_v> && std::convertible_to) + void return_value(T&& expr) noexcept (std::is_nothrow_convertible_v) { + result = std::forward(expr); + } + + /** + * @brief Function called to get the coroutine object + */ + dpp::coroutine get_return_object() { + return dpp::coroutine{handle_t::from_promise(*this)}; + } + }; + + /** + * @brief Struct returned by a coroutine's final_suspend, resumes the continuation + */ + template + struct final_awaiter { + /** + * @brief First function called by the standard library when reaching the end of a coroutine + * + * @return false Always return false, we need to suspend to resume the parent + */ + [[nodiscard]] bool await_ready() const noexcept { + return false; + } + + /** + * @brief Second function called by the standard library when reaching the end of a coroutine. + * + * @return std::handle_t<> Coroutine handle to resume, this is either the parent if present or std::noop_coroutine() + */ + [[nodiscard]] std_coroutine::coroutine_handle<> await_suspend(std_coroutine::coroutine_handle> handle) const noexcept { + auto parent = handle.promise().parent; + + return parent ? parent : std_coroutine::noop_coroutine(); + } + + /** + * @brief Function called by the standard library when this object is resumed + */ + void await_resume() const noexcept {} + }; + + template + final_awaiter promise_t::final_suspend() const noexcept { + return {}; + } + + /** + * @brief Struct returned by a coroutine's final_suspend, resumes the continuation + */ + template <> + struct promise_t { + /** + * @brief Handle of the coroutine co_await-ing this coroutine. + */ + std_coroutine::coroutine_handle<> parent{nullptr}; + + /** + * @brief Pointer to an uncaught exception thrown by the coroutine + */ + std::exception_ptr exception{nullptr}; + + /** + * @brief Function called by the standard library when reaching the end of a coroutine + * + * @return final_awaiter Resumes any coroutine co_await-ing on this + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called by the standard library when the coroutine start + * + * @return @return std::suspend_always Always suspend at the start, for a lazy start + */ + [[nodiscard]] std_coroutine::suspend_always initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called when an exception escapes the coroutine + * + * Stores the exception to throw to the co_await-er + */ + void unhandled_exception() noexcept { + exception = std::current_exception(); + } + + /** + * @brief Function called when co_return is used + */ + void return_void() const noexcept {} + + /** + * @brief Function called to get the coroutine object + */ + [[nodiscard]] dpp::coroutine get_return_object() { + return dpp::coroutine{handle_t::from_promise(*this)}; + } + }; + +} // namespace detail + +DPP_CHECK_ABI_COMPAT(coroutine, coroutine_dummy) +DPP_CHECK_ABI_COMPAT(coroutine, coroutine_dummy) + +} + +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits, Args...> { + using promise_type = dpp::detail::coroutine::promise_t; +}; + +#endif /* DPP_CORO */ diff --git a/3rdParty/dpp/coro/job.h b/3rdParty/dpp/coro/job.h new file mode 100644 index 0000000000..6bed5f312e --- /dev/null +++ b/3rdParty/dpp/coro/job.h @@ -0,0 +1,145 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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 { + +struct job_dummy { +}; + +} + +#ifdef DPP_CORO + +#include "coro.h" + +#include +#include + +namespace dpp { + +/** + * @class job job.h coro/job.h + * @brief Extremely light coroutine object designed to send off a coroutine to execute on its own. + * Can be used in conjunction with coroutine events via @ref dpp::event_router_t::operator()(F&&) "event routers", or on its own. + * + * This object stores no state and is the recommended way to use coroutines if you do not need to co_await the result. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. + * Please report any to GitHub Issues or to our Discord Server. + * @warning - It cannot be co_awaited, which means the second it co_awaits something, the program jumps back to the calling function, which continues executing. + * At this point, if the function returns, every object declared in the function including its parameters are destroyed, which causes @ref lambdas-and-locals "dangling references". + * For this reason, `co_await` will error if any parameters are passed by reference. + * If you must pass a reference, pass it as a pointer or with std::ref, but you must fully understand the reason behind this warning, and what to avoid. + * If you prefer a safer type, use `coroutine` for synchronous execution, or `task` for parallel tasks, and co_await them. + */ +struct job {}; + +namespace detail { + +namespace job { + +#ifdef DPP_CORO_TEST + struct promise{}; +#endif + +/** + * @brief Coroutine promise type for a job + */ +template +struct promise { + +#ifdef DPP_CORO_TEST + promise() { + ++coro_alloc_count; + } + + ~promise() { + --coro_alloc_count; + } +#endif + + /** + * @brief Function called when the job is done. + * + * @return std::suspend_never Do not suspend at the end, destroying the handle immediately + */ + std_coroutine::suspend_never final_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called when the job is started. + * + * @return std::suspend_never Do not suspend at the start, starting the job immediately + */ + std_coroutine::suspend_never initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called to get the job object + * + * @return job + */ + dpp::job get_return_object() const noexcept { + return {}; + } + + /** + * @brief Function called when an exception is thrown and not caught. + * + * @throw Immediately rethrows the exception to the caller / resumer + */ + void unhandled_exception() const { + throw; + } + + /** + * @brief Function called when the job returns. Does nothing. + */ + void return_void() const noexcept {} +}; + +} // namespace job + +} // namespace detail + +DPP_CHECK_ABI_COMPAT(job, job_dummy) +} // namespace dpp + +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits { + /** + * @brief Promise type for this coroutine signature. + * + * When the coroutine is created from a lambda, that lambda is passed as a first parameter. + * Not ideal but we'll allow any callable that takes the rest of the arguments passed + */ + using promise_type = dpp::detail::job::promise; +}; + +#endif /* DPP_CORO */ diff --git a/3rdParty/dpp/coro/task.h b/3rdParty/dpp/coro/task.h new file mode 100644 index 0000000000..24038e534e --- /dev/null +++ b/3rdParty/dpp/coro/task.h @@ -0,0 +1,446 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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 + +namespace dpp { + +struct task_dummy : awaitable_dummy { + int* handle_dummy = nullptr; +}; + +} + +#ifdef DPP_CORO + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include // std::cerr in final_suspend + +namespace dpp { + +namespace detail { + +/* Internal cogwheels for dpp::task */ +namespace task { + +/** + * @brief A @ref dpp::task "task"'s promise_t type, with special logic for handling nested tasks. + * + * @tparam R Return type of the task + */ +template +struct promise_t; + +/** + * @brief The object automatically co_await-ed at the end of a @ref dpp::task "task". Ensures nested coroutine chains are resolved, and the promise_t cleans up if it needs to. + * + * @tparam R Return type of the task + */ +template +struct final_awaiter; + +/** + * @brief Alias for for a @ref dpp::task "task"'s @ref promise_t. + * + * @tparam R Return type of the task + */ +template +using handle_t = std_coroutine::coroutine_handle>; + +} // namespace task + +} // namespace detail + +/** + * @class task task.h coro/task.h + * @brief A coroutine task. It starts immediately on construction and can be co_await-ed, making it perfect for parallel coroutines returning a value. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. + * Please report any to GitHub Issues or to our Discord Server. + * @tparam R Return type of the task. Cannot be a reference but can be void. + */ +template +#ifndef _DOXYGEN_ +requires (!std::is_reference_v) +#endif +class [[nodiscard("dpp::task cancels itself on destruction. use co_await on it, or its sync_wait method")]] task : public awaitable { + friend struct detail::task::promise_t; + + using handle_t = detail::task::handle_t; + using state_flags = detail::promise::state_flags; + + handle_t handle{}; + +protected: + /** + * @brief Construct from a coroutine handle. Internal use only + */ + explicit task(handle_t handle_) : awaitable(&handle_.promise()), handle(handle_) {} + + /** + * @brief Clean up our handle, cancelling any running task + */ + void cleanup() { + if (handle && this->valid()) { + if (this->abandon() & state_flags::sf_done) { + handle.destroy(); + } else { + cancel(); + } + handle = nullptr; + } + } + +public: + /** + * @brief Default constructor, creates a task not bound to a coroutine. + */ + task() = default; + + /** + * @brief Copy constructor is disabled + */ + task(const task &) = delete; + + /** + * @brief Move constructor, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task(task &&other) noexcept : awaitable(std::move(other)), handle(std::exchange(other.handle, nullptr)) {} + + /** + * @brief Copy assignment is disabled + */ + task &operator=(const task &) = delete; + + /** + * @brief Move assignment, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task &operator=(task &&other) noexcept { + cleanup(); + handle = std::exchange(other.handle, nullptr); + awaitable::operator=(std::move(other)); + return *this; + } + + /** + * @brief Destructor. + * + * Destroys the handle. If the task is still running, it will be cancelled. + */ + ~task() { + cleanup(); + } + + /** + * @brief Function to check if the task has finished its execution entirely + * + * @return bool Whether the task is finished. + */ + [[nodiscard]] bool done() const noexcept { + return handle && (!this->valid() || handle.promise().state.load(std::memory_order_relaxed) == state_flags::sf_done); + } + + /** + * @brief Cancel the task, it will stop the next time it uses co_await. On co_await-ing this task, throws dpp::task_cancelled_exception. + * + * @return *this + */ + task& cancel() & noexcept { + handle.promise().cancelled.exchange(true, std::memory_order_relaxed); + return *this; + } + + /** + * @brief Cancel the task, it will stop the next time it uses co_await. On co_await-ing this task, throws dpp::task_cancelled_exception. + * + * @return *this + */ + task&& cancel() && noexcept { + handle.promise().cancelled.exchange(true, std::memory_order_relaxed); + return *this; + } +}; + +namespace detail::task { +/** + * @brief Awaitable returned from task::promise_t's final_suspend. Resumes the parent and cleans up its handle if needed + */ +template +struct final_awaiter { + /** + * @brief Always suspend at the end of the task. This allows us to clean up and resume the parent + */ + [[nodiscard]] bool await_ready() const noexcept { + return (false); + } + + /** + * @brief The suspension logic of the coroutine when it finishes. Always suspend the caller, meaning cleaning up the handle is on us + * + * @param handle The handle of this coroutine + * @return std::coroutine_handle<> Handle to resume, which is either the parent if present or std::noop_coroutine() otherwise + */ + [[nodiscard]] std_coroutine::coroutine_handle<> await_suspend(handle_t handle) const noexcept; + + /** + * @brief Function called when this object is co_awaited by the standard library at the end of final_suspend. Do nothing, return nothing + */ + void await_resume() const noexcept {} +}; + +/** + * @brief Base implementation of task::promise_t, without the logic that would depend on the return type. Meant to be inherited from + */ +template +struct promise_base : basic_promise { + /** + * @brief Whether the task is cancelled or not. + */ + std::atomic cancelled = false; + +#ifdef DPP_CORO_TEST + promise_base() { + ++coro_alloc_count; + } + + ~promise_base() { + --coro_alloc_count; + } +#endif + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return std::suspend_never Don't suspend, the coroutine starts immediately. + */ + [[nodiscard]] std_coroutine::suspend_never initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called by the standard library when an exception is thrown and not caught in the coroutine. + * + * Stores the exception pointer to rethrow on co_await. If the task object is destroyed and was not cancelled, throw instead + */ + void unhandled_exception() { + if ((this->state.load() & promise::state_flags::sf_broken) && !cancelled) { + throw; + } + this->template set_exception(std::current_exception()); + } + + /** + * @brief Proxy awaitable that wraps any co_await inside the task and checks for cancellation on resumption + * + * @see await_transform + */ + template + struct proxy_awaiter { + /** @brief The promise_t object bound to this proxy */ + const promise_base &promise; + + /** @brief The inner awaitable being awaited */ + A awaitable; + + /** @brief Wrapper for the awaitable's await_ready */ + [[nodiscard]] bool await_ready() noexcept(noexcept(awaitable.await_ready())) { + return awaitable.await_ready(); + } + + /** @brief Wrapper for the awaitable's await_suspend */ + template + [[nodiscard]] decltype(auto) await_suspend(T&& handle) noexcept(noexcept(awaitable.await_suspend(std::forward(handle)))) { + return awaitable.await_suspend(std::forward(handle)); + } + + /** + * @brief Wrapper for the awaitable's await_resume, throws if the task is cancelled + * + * @throw dpp::task_cancelled_exception If the task was cancelled + */ + decltype(auto) await_resume() { + if (promise.cancelled.load()) { + throw dpp::task_cancelled_exception{"task was cancelled"}; + } + return awaitable.await_resume(); + } + }; + + /** + * @brief Function called whenever co_await is used inside of the task + * + * @throw dpp::task_cancelled_exception On resumption if the task was cancelled + * + * @return @ref proxy_awaiter Returns a proxy awaiter that will check for cancellation on resumption + */ + template + [[nodiscard]] auto await_transform(T&& expr) const noexcept(noexcept(co_await_resolve(std::forward(expr)))) { + using awaitable_t = decltype(co_await_resolve(std::forward(expr))); + return proxy_awaiter{*this, co_await_resolve(std::forward(expr))}; + } +}; + +/** + * @brief Implementation of task::promise_t for non-void return type + */ +template +struct promise_t : promise_base { + friend struct final_awaiter; + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(R&& expr) noexcept(std::is_nothrow_move_constructible_v) requires std::move_constructible { + this->template set_value(std::move(expr)); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(const R &expr) noexcept(std::is_nothrow_copy_constructible_v) requires std::copy_constructible { + this->template set_value(expr); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + template + requires (!std::is_same_v> && std::convertible_to) + void return_value(T&& expr) noexcept (std::is_nothrow_convertible_v) { + this->template emplace_value(std::forward(expr)); + } + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return dpp::task The coroutine object + */ + [[nodiscard]] dpp::task get_return_object() noexcept { + return dpp::task{handle_t::from_promise(*this)}; + } + + /** + * @brief Function called by the standard library when the coroutine reaches its last suspension point + * + * @return final_awaiter Special object containing the chain resolution and clean-up logic. + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept { + return {}; + } +}; + +/** + * @brief Implementation of task::promise_t for void return type + */ +template <> +struct promise_t : promise_base { + friend struct final_awaiter; + + /** + * @brief Function called by the standard library when the coroutine co_returns + * + * Sets the promise state to finished. + */ + void return_void() noexcept { + set_value(); + } + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return task The coroutine object + */ + [[nodiscard]] dpp::task get_return_object() noexcept { + return dpp::task{handle_t::from_promise(*this)}; + } + + /** + * @brief Function called by the standard library when the coroutine reaches its last suspension point + * + * @return final_awaiter Special object containing the chain resolution and clean-up logic. + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept { + return {}; + } +}; + +template +std_coroutine::coroutine_handle<> final_awaiter::await_suspend(handle_t handle) const noexcept { + using state_flags = promise::state_flags; + promise_t &promise = handle.promise(); + uint8_t previous_state = promise.state.fetch_or(state_flags::sf_done); + + if ((previous_state & state_flags::sf_awaited) != 0) { // co_await-ed, resume parent + if ((previous_state & state_flags::sf_broken) != 0) { // major bug, these should never be set together + // we don't have a cluster so just log it on cerr + std::cerr << "dpp: task promise ended in both an awaited and dangling state. this is a bug and a memory leak, please report it to us!" << std::endl; + } + return promise.release_awaiter(); + } + if ((previous_state & state_flags::sf_broken) != 0) { // task object is gone, free the handle + handle.destroy(); + } + return std_coroutine::noop_coroutine(); +} + +} // namespace detail::task + +DPP_CHECK_ABI_COMPAT(task, task_dummy) +DPP_CHECK_ABI_COMPAT(task, task_dummy) + +} + +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise_t type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits, Args...> { + using promise_type = dpp::detail::task::promise_t; +}; + +#endif /* DPP_CORO */ diff --git a/3rdParty/dpp/coro/when_any.h b/3rdParty/dpp/coro/when_any.h new file mode 100644 index 0000000000..4d31ba246b --- /dev/null +++ b/3rdParty/dpp/coro/when_any.h @@ -0,0 +1,532 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 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. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro.h" +#include "job.h" + +#include +#include +#include +#include +#include + +namespace dpp { + +template +class event_router_t; + +namespace detail { + +namespace event_router { + +template +class awaitable; + +} + +/** + * @brief Internal cogwheels for dpp::when_any + */ +namespace when_any { + +/** + * @brief Current state of a when_any object + */ +enum class await_state { + /** + * @brief Object was started but not awaited + */ + started, + /** + * @brief Object is being awaited + */ + waiting, + /** + * @brief Object was resumed + */ + done, + /** + * @brief Object was destroyed + */ + dangling +}; + +/** + * @brief Type trait helper to obtain the actual type that will be used by a when_any when a type is passed as a parameter. + * May specialize for certain types for specific behavior, e.g. for an event_router, store the awaitable directly + */ +template +struct arg_helper_s { + /** Raw type of the awaitable */ + using type = T; + + /** Helper static method to get the awaitable from a variable */ + static decltype(auto) get(auto&& v) { + return static_cast(v); + } +}; + +template +struct arg_helper_s> { + using type = event_router::awaitable; + + template +#ifndef _DOXYGEN + requires (std::same_as, dpp::event_router_t>) +#endif + static event_router::awaitable get(U&& v) { + return static_cast(v).operator co_await(); + } +}; + +/** + * @brief Alias for the actual type that an awaitable will be stored as in a when_any. + * For example if given an event_router, store the awaitable, not the event_router. + */ +template +using awaitable_type = typename arg_helper_s::type; + +/** + * @brief Helper struct with a method to convert an awaitable parameter to the actual value it will be stored as. + * For example if given an event_router, store the awaitable, not the event_router. + */ +template +using arg_helper = arg_helper_s>; + +/** + * @brief Empty result from void-returning awaitable + */ +struct empty{}; + +/** + * @brief Actual type a result will be stores as in when_any + */ +template +using storage_type = std::conditional_t, empty, T>; + +/** + * @brief Concept satisfied if a stored result is void + */ +template +concept void_result = std::same_as; + +} + +} // namespace detail + +/** + * @class when_any when_any.h coro/when_any.h + * @brief Experimental class to co_await on a bunch of awaitable objects, resuming when the first one completes. + * On completion, returns a @ref result object that contains the index of the awaitable that finished first. + * A user can call @ref result::index() and @ref result::get() on the result object to get the result, similar to std::variant. + * + * @see when_any::result + * @tparam Args... Type of each awaitable to await on + */ +template +#ifndef _DOXYGEN_ +requires (sizeof...(Args) >= 1) +#endif +class when_any { + /** + * @brief Alias for the type of the result variant + */ + using variant_type = std::variant>>...>; + + /** + * @brief Alias for the result type of the Nth arg. + * + * @tparam N index of the argument to fetch + */ + template + using result_t = std::variant_alternative_t; + + /** + * @brief State shared between all the jobs to spawn + */ + struct state_t { + /** + * @brief Constructor for the internal state. Its arguments are used to construct each awaitable + */ + template + state_t(Args_&&... args) : awaitables{std::forward(args)...} {} + + /** + * @brief Awaitable objects to handle. + */ + std::tuple awaitables; + + /** + * @brief Result or exception, as a variant. This will contain the result of the first awaitable to finish + */ + variant_type result{}; + + /** + * @brief Coroutine handle to resume after finishing an awaitable + */ + detail::std_coroutine::coroutine_handle<> handle{}; + + /** + * @brief Index of the awaitable that finished. Initialized to the maximum value of std::size_t. + */ + size_t index_finished = std::numeric_limits::max(); + + /** + * @brief State of the when_any object. + * + * @see detail::when_any::await_state + */ + std::atomic owner_state{detail::when_any::await_state::started}; + }; + + /** + * @brief Shared pointer to the state shared between the jobs spawned. Contains the awaitable objects and the result. + */ + std::shared_ptr my_state{nullptr}; + + /** + * @brief Spawn a dpp::job handling the Nth argument. + * + * @tparam N Index of the argument to handle + * @return dpp::job Job handling the Nth argument + */ + template + static dpp::job make_job(std::shared_ptr shared_state) { + /** + * Any exceptions from the awaitable's await_suspend should be thrown to the caller (the coroutine creating the when_any object) + * If the co_await passes, and it is the first one to complete, try construct the result, catch any exceptions to rethrow at resumption, and resume. + */ + if constexpr (!std::same_as, detail::when_any::empty>) { + decltype(auto) result = co_await std::get(shared_state->awaitables); + + if (auto s = shared_state->owner_state.load(std::memory_order_relaxed); s == detail::when_any::await_state::dangling || s == detail::when_any::await_state::done) { + co_return; + } + + using result_t = decltype(result); + + /* Try construct, prefer move if possible, store any exception to rethrow */ + try { + if constexpr (std::is_lvalue_reference_v && !std::is_const_v && std::is_move_constructible_v>) { + shared_state->result.template emplace(std::move(result)); + } else { + shared_state->result.template emplace(result); + } + } catch (...) { + shared_state->result.template emplace<0>(std::current_exception()); + } + } else { + co_await std::get(shared_state->awaitables); + + if (auto s = shared_state->owner_state.load(std::memory_order_relaxed); s == detail::when_any::await_state::dangling || s == detail::when_any::await_state::done) { + co_return; + } + + shared_state->result.template emplace(); + } + + if (shared_state->owner_state.exchange(detail::when_any::await_state::done) != detail::when_any::await_state::waiting) { + co_return; + } + + if (auto handle = shared_state->handle; handle) { + shared_state->index_finished = N; + shared_state->handle.resume(); + } + } + + /** + * @brief Spawn a dpp::job to handle each awaitable. + * Each of them will co_await the awaitable and set the result if they are the first to finish + */ + void make_jobs() { + [](when_any *self, std::index_sequence) { + (make_job(self->my_state), ...); + }(this, std::index_sequence_for{}); + } + +public: + /** + * @brief Object returned by \ref operator co_await() on resumption. Can be moved but not copied. + */ + class result { + friend class when_any; + + /** + * @brief Reference to the shared state to pull the data from + */ + std::shared_ptr shared_state; + + /** + * @brief Default construction is deleted + */ + result() = delete; + + /** + * @brief Internal constructor taking the shared state + */ + result(std::shared_ptr state) : shared_state{state} {} + + public: + /** + * @brief Move constructor + */ + result(result&&) = default; + + /** + * @brief This object is not copyable. + */ + result(const result &) = delete; + + /** + * @brief Move assignment operator + */ + result &operator=(result&&) = default; + + /** + * @brief This object is not copyable. + */ + result &operator=(const result&) = delete; + + /** + * @brief Retrieve the index of the awaitable that finished first. + * + * @return size_t Index of the awaitable that finished first, relative to the template arguments of when_any + */ + size_t index() const noexcept { + return shared_state->index_finished; + } + + /** + * @brief Retrieve the non-void result of an awaitable. + * + * @tparam N Index of the result to retrieve. Must correspond to index(). + * @throw ??? Throws any exception triggered at construction, or std::bad_variant_access if N does not correspond to index() + * @return Result of the awaitable as a reference. + */ + template +#ifndef _DOXYGEN_ + requires (!detail::when_any::void_result>) +#endif + result_t& get() & { + if (is_exception()) { + std::rethrow_exception(std::get<0>(shared_state->result)); + } + return std::get(shared_state->result); + } + + /** + * @brief Retrieve the non-void result of an awaitable. + * + * @tparam N Index of the result to retrieve. Must correspond to index(). + * @throw ??? Throws any exception triggered at construction, or std::bad_variant_access if N does not correspond to index() + * @return Result of the awaitable as a cpnst reference. + */ + template +#ifndef _DOXYGEN_ + requires (!detail::when_any::void_result>) +#endif + const result_t& get() const& { + if (is_exception()) { + std::rethrow_exception(std::get<0>(shared_state->result)); + } + return std::get(shared_state->result); + } + + /** + * @brief Retrieve the non-void result of an awaitable. + * + * @tparam N Index of the result to retrieve. Must correspond to index(). + * @throw ??? Throws any exception triggered at construction, or std::bad_variant_access if N does not correspond to index() + * @return Result of the awaitable as an rvalue reference. + */ + template +#ifndef _DOXYGEN_ + requires (!detail::when_any::void_result>) +#endif + result_t&& get() && { + if (is_exception()) { + std::rethrow_exception(std::get<0>(shared_state->result)); + } + return std::get(shared_state->result); + } + + /** + * @brief Cannot retrieve a void result. + */ + template +#ifndef _DOXYGEN + requires (detail::when_any::void_result>) +#endif + [[deprecated("cannot retrieve a void result")]] void get() = delete; + + /** + * @brief Checks whether the return of the first awaitable triggered an exception, that is, a call to get() will rethrow. + * + * @return Whether or not the result is an exception + */ + [[nodiscard]] bool is_exception() const noexcept { + return shared_state->result.index() == 0; + } + }; + + /** + * @brief Object returned by \ref operator co_await(). Meant to be used by the standard library, not by a user. + * + * @see result + */ + struct awaiter { + /** + * @brief Pointer to the when_any object + */ + when_any *self; + + /** + * @brief First function called by the standard library when using co_await. + * + * @return bool Whether the result is ready + */ + [[nodiscard]] bool await_ready() const noexcept { + return self->await_ready(); + } + + /** + * @brief Second function called by the standard library when using co_await. + * + * @return bool Returns false if we want to resume immediately. + */ + bool await_suspend(detail::std_coroutine::coroutine_handle<> caller) noexcept { + auto sent = detail::when_any::await_state::started; + self->my_state->handle = caller; + return self->my_state->owner_state.compare_exchange_strong(sent, detail::when_any::await_state::waiting); // true (suspend) if `started` was replaced with `waiting` -- false (resume) if the value was not `started` (`done` is the only other option) + } + + /** + * @brief Third and final function called by the standard library when using co_await. Returns the result object. + * + * @see result + */ + result await_resume() const noexcept { + return {self->my_state}; + } + }; + + /** + * @brief Default constructor. + * A when_any object created this way holds no state + */ + when_any() = default; + + /** + * @brief Constructor from awaitable objects. Each awaitable is executed immediately and the when_any object can then be co_await-ed later. + * + * @throw ??? Any exception thrown by the start of each awaitable will propagate to the caller. + * @param args Arguments to construct each awaitable from. The when_any object will construct an awaitable for each, it is recommended to pass rvalues or std::move. + */ + template +#ifndef _DOXYGEN_ + requires (sizeof...(Args_) == sizeof...(Args)) +#endif /* _DOXYGEN_ */ + when_any(Args_&&... args) : my_state{std::make_shared(detail::when_any::arg_helper::get(std::forward(args))...)} { + make_jobs(); + } + + /** + * @brief This object is not copyable. + */ + when_any(const when_any &) = delete; + + /** + * @brief Move constructor. + */ + when_any(when_any &&) noexcept = default; + + /** + * @brief On destruction the when_any will try to call @ref dpp::task::cancel() cancel() on each of its awaitable if they have such a method. + * + * @note If you are looking to use a custom type with when_any and want it to cancel on its destruction, + * make sure it has a cancel() method, which will trigger an await_resume() throwing a dpp::task_cancelled_exception. + * This object will swallow the exception and return cleanly. Any other exception will be thrown back to the resumer. + */ + ~when_any() { + if (!my_state) + return; + + my_state->owner_state = detail::when_any::await_state::dangling; + + [](when_any *self, std::index_sequence) constexpr { + constexpr auto cancel = [](when_any *self) constexpr { + if constexpr (requires { std::get(self->my_state->awaitables).cancel(); }) { + try { + std::get(self->my_state->awaitables).cancel(); + } catch (...) { + // swallow any exception. no choice here, we're in a destructor + } + } + }; + (cancel.template operator()(self), ...); + }(this, std::index_sequence_for()); + } + + /** + * @brief This object is not copyable. + */ + when_any &operator=(const when_any &) = delete; + + /** + * @brief Move assignment operator. + */ + when_any &operator=(when_any &&) noexcept = default; + + /** + * @brief Check whether a call to co_await would suspend. + * + * @note This can change from false to true at any point, but not the other way around. + * @return bool Whether co_await would suspend + */ + [[nodiscard]] bool await_ready() const noexcept { + return my_state->owner_state == detail::when_any::await_state::done; + } + + /** + * @brief Suspend the caller until any of the awaitables completes. + * + * @see result + * @throw ??? On resumption, throws any exception caused by the construction of the result. + * @return result On resumption, this object returns an object that allows to retrieve the index and result of the awaitable. + */ + [[nodiscard]] awaiter operator co_await() noexcept { + return {this}; + } +}; + +template +#ifndef _DOXYGEN_ +requires (sizeof...(Args) >= 1) +#endif /* _DOXYGEN_ */ +when_any(Args...) -> when_any...>; + +} /* namespace dpp */ + +#endif \ No newline at end of file diff --git a/3rdParty/dpp/discordclient.h b/3rdParty/dpp/discordclient.h index 745b705848..1cd7ef66ed 100644 --- a/3rdParty/dpp/discordclient.h +++ b/3rdParty/dpp/discordclient.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -24,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,7 +35,7 @@ #include #include -using json = nlohmann::json; + #define DISCORD_API_VERSION "10" #define API_PATH "/api/v" DISCORD_API_VERSION @@ -86,6 +87,12 @@ class DPP_EXPORT voiceconn { */ class discord_voice_client* voiceclient; + /** + * @brief True to enable DAVE E2EE + * @warning This is an EXPERIMENTAL feature! + */ + bool dave; + /** * @brief Construct a new voiceconn object */ @@ -96,8 +103,10 @@ class DPP_EXPORT voiceconn { * * @param o owner * @param _channel_id voice channel id + * @param enable_dave True to enable DAVE E2EE + * @warn DAVE is an EXPERIMENTAL feature! */ - voiceconn(class discord_client* o, snowflake _channel_id); + voiceconn(class discord_client* o, snowflake _channel_id, bool enable_dave); /** * @brief Destroy the voiceconn object @@ -263,6 +272,10 @@ class DPP_EXPORT discord_client : public websocket_client */ void set_resume_hostname(); + /** + * @brief Clean up resources + */ + void cleanup(); public: /** * @brief Owning cluster @@ -354,7 +367,7 @@ class DPP_EXPORT discord_client : public websocket_client /** * @brief List of voice channels we are connecting to keyed by guild id */ - std::unordered_map connecting_voice_channels; + std::unordered_map> connecting_voice_channels; /** * @brief The gateway address we reconnect to when we resume a session @@ -449,6 +462,8 @@ class DPP_EXPORT discord_client : public websocket_client * @param intents Privileged intents to use, a bitmask of values from dpp::intents * @param compressed True if the received data will be gzip compressed * @param ws_protocol Websocket protocol to use for the connection, JSON or ETF + * + * @throws std::bad_alloc Passed up to the caller if any internal objects fail to allocate, after cleanup has completed */ discord_client(dpp::cluster* _cluster, uint32_t _shard_id, uint32_t _max_shards, const std::string &_token, uint32_t intents = 0, bool compressed = true, websocket_protocol_t ws_protocol = ws_json); @@ -466,9 +481,10 @@ class DPP_EXPORT discord_client : public websocket_client /** * @brief Handle JSON from the websocket. * @param buffer The entire buffer content from the websocket client + * @param opcode The type of frame, e.g. text or binary * @returns True if a frame has been handled */ - virtual bool handle_frame(const std::string &buffer); + virtual bool handle_frame(const std::string &buffer, ws_opcode opcode); /** * @brief Handle a websocket error. @@ -490,12 +506,13 @@ class DPP_EXPORT discord_client : public websocket_client * @param channel_id Channel ID of the voice channel * @param self_mute True if the bot should mute itself * @param self_deaf True if the bot should deafen itself + * @param enable_dave True to enable DAVE E2EE - EXPERIMENTAL * @return reference to self * @note This is NOT a synchronous blocking call! The bot isn't instantly ready to send or listen for audio, * as we have to wait for the connection to the voice server to be established! * e.g. wait for dpp::cluster::on_voice_ready event, and then send the audio within that event. */ - discord_client& connect_voice(snowflake guild_id, snowflake channel_id, bool self_mute = false, bool self_deaf = false); + discord_client& connect_voice(snowflake guild_id, snowflake channel_id, bool self_mute = false, bool self_deaf = false, bool enable_dave = false); /** * @brief Disconnect from the connected voice channel on a guild @@ -516,4 +533,4 @@ class DPP_EXPORT discord_client : public websocket_client voiceconn* get_voice(snowflake guild_id); }; -}; +} diff --git a/3rdParty/dpp/discordevents.h b/3rdParty/dpp/discordevents.h index 62f88c33ee..8b05d1001b 100644 --- a/3rdParty/dpp/discordevents.h +++ b/3rdParty/dpp/discordevents.h @@ -21,53 +21,102 @@ #pragma once #include -#include +#include +#include +#include +#include +#include namespace dpp { -/** @brief Returns a snowflake id from a json field value, if defined, else returns 0 +/** + * @brief Returns a snowflake id from a json field value, if defined, else returns 0 * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ uint64_t DPP_EXPORT snowflake_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets a snowflake id from a json field value, if defined, else does nothing +/** + * @brief Sets a snowflake id from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_snowflake_not_null(const nlohmann::json* j, const char *keyname, uint64_t &v); -/** @brief Returns a string from a json field value, if defined, else returns an empty string. +/** + * @brief Sets an array of snowflakes from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for the values + * @param v Value to change + */ +void DPP_EXPORT set_snowflake_array_not_null(const nlohmann::json* j, const char *keyname, std::vector &v); + +/** + * @brief Applies a function to each element of a json array. + * @param parent nlohmann::json instance to retrieve value from + * @param key key name to check for the values + * @param fn function to apply to each element + */ +void DPP_EXPORT for_each_json(nlohmann::json* parent, std::string_view key, const std::function &fn); + +/** + * @brief Sets an array of objects from a json field value, if defined, else does nothing + * @tparam T The class of which the array consists of. Must be derived from dpp::json_interface + * @param j nlohmann::json instance to retrieve value from + * @param key key name to check for the values + * @param v Value to change + */ +template void set_object_array_not_null(nlohmann::json* j, std::string_view key, std::vector& v) { + v.clear(); + for_each_json(j, key, [&v](nlohmann::json* elem) { + v.push_back(T{}.fill_from_json(elem)); + }); +} + +/** + * @brief Returns a string from a json field value, if defined, else returns an empty string. * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ std::string DPP_EXPORT string_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets a string from a json field value, if defined, else does nothing +/** + * @brief Sets a string from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_string_not_null(const nlohmann::json* j, const char *keyname, std::string &v); -/** @brief Returns a double from a json field value, if defined, else returns 0. +/** + * @brief This is a repeat of set_string_not_null, but takes in a iconhash. + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_iconhash_not_null(const nlohmann::json* j, const char *keyname, utility::iconhash &v); + +/** + * @brief Returns a double from a json field value, if defined, else returns 0. * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ double DPP_EXPORT double_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets a double from a json field value, if defined, else does nothing +/** + * @brief Sets a double from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_double_not_null(const nlohmann::json* j, const char *keyname, double &v); -/** @brief Returns a 64 bit unsigned integer from a json field value, if defined, else returns 0. +/** + * @brief Returns a 64 bit unsigned integer from a json field value, if defined, else returns 0. * DO NOT use this for snowflakes, as usually snowflakes are wrapped in a string! * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value @@ -75,70 +124,80 @@ void DPP_EXPORT set_double_not_null(const nlohmann::json* j, const char *keyname */ uint64_t DPP_EXPORT int64_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets an unsigned 64 bit integer from a json field value, if defined, else does nothing +/** + * @brief Sets an unsigned 64 bit integer from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_int64_not_null(const nlohmann::json* j, const char *keyname, uint64_t &v); -/** @brief Returns a 32 bit unsigned integer from a json field value, if defined, else returns 0 +/** + * @brief Returns a 32 bit unsigned integer from a json field value, if defined, else returns 0 * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ uint32_t DPP_EXPORT int32_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets an unsigned 32 bit integer from a json field value, if defined, else does nothing +/** + * @brief Sets an unsigned 32 bit integer from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_int32_not_null(const nlohmann::json* j, const char *keyname, uint32_t &v); -/** @brief Returns a 16 bit unsigned integer from a json field value, if defined, else returns 0 +/** + * @brief Returns a 16 bit unsigned integer from a json field value, if defined, else returns 0 * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ uint16_t DPP_EXPORT int16_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets an unsigned 16 bit integer from a json field value, if defined, else does nothing +/** + * @brief Sets an unsigned 16 bit integer from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_int16_not_null(const nlohmann::json* j, const char *keyname, uint16_t &v); -/** @brief Returns an 8 bit unsigned integer from a json field value, if defined, else returns 0 +/** + * @brief Returns an 8 bit unsigned integer from a json field value, if defined, else returns 0 * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ uint8_t DPP_EXPORT int8_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets an unsigned 8 bit integer from a json field value, if defined, else does nothing +/** + * @brief Sets an unsigned 8 bit integer from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_int8_not_null(const nlohmann::json* j, const char *keyname, uint8_t &v); -/** @brief Returns a boolean value from a json field value, if defined, else returns false +/** + * @brief Returns a boolean value from a json field value, if defined, else returns false * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @return found value */ bool DPP_EXPORT bool_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets a boolean from a json field value, if defined, else does nothing +/** + * @brief Sets a boolean from a json field value, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_bool_not_null(const nlohmann::json* j, const char *keyname, bool &v); -/** @brief Returns a time_t from an ISO8601 timestamp field in a json value, if defined, else returns +/** + * @brief Returns a time_t from an ISO8601 timestamp field in a json value, if defined, else returns * epoch value of 0. * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value @@ -146,14 +205,16 @@ void DPP_EXPORT set_bool_not_null(const nlohmann::json* j, const char *keyname, */ time_t DPP_EXPORT ts_not_null(const nlohmann::json* j, const char *keyname); -/** @brief Sets an timestamp from a json field value containing an ISO8601 string, if defined, else does nothing +/** + * @brief Sets an timestamp from a json field value containing an ISO8601 string, if defined, else does nothing * @param j nlohmann::json instance to retrieve value from * @param keyname key name to check for a value * @param v Value to change */ void DPP_EXPORT set_ts_not_null(const nlohmann::json* j, const char *keyname, time_t &v); -/** @brief Base64 encode data into a string. +/** + * @brief Base64 encode data into a string. * @param buf Raw binary buffer * @param buffer_length Buffer length to encode * @return The base64 encoded string @@ -168,4 +229,4 @@ std::string DPP_EXPORT base64_encode(unsigned char const* buf, unsigned int buff */ std::string DPP_EXPORT ts_to_string(time_t ts); -}; +} diff --git a/3rdParty/dpp/discordvoiceclient.h b/3rdParty/dpp/discordvoiceclient.h index 9ecd3840d5..a090df9c9d 100644 --- a/3rdParty/dpp/discordvoiceclient.h +++ b/3rdParty/dpp/discordvoiceclient.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -32,7 +33,7 @@ #include #include #include -#include +#include #include #include #include @@ -47,8 +48,7 @@ #include #include #include - -using json = nlohmann::json; +#include struct OpusDecoder; struct OpusEncoder; @@ -56,6 +56,56 @@ struct OpusRepacketizer; namespace dpp { +/** + * @brief Sample rate for OPUS (48khz) + */ +[[maybe_unused]] inline constexpr int32_t opus_sample_rate_hz = 48000; + +/** + * @brief Channel count for OPUS (stereo) + */ +[[maybe_unused]] inline constexpr int32_t opus_channel_count = 2; + +/** + * @brief Discord voice protocol version + */ +[[maybe_unused]] inline constexpr uint8_t voice_protocol_version = 8; + + +class audio_mixer; + +namespace dave::mls { + class session; +} + +// !TODO: change these to constexpr and rename every occurrence across the codebase +#define AUDIO_TRACK_MARKER (uint16_t)0xFFFF + +#define AUDIO_OVERLAP_SLEEP_SAMPLES 30 + +inline constexpr size_t send_audio_raw_max_length = 11520; + +inline constexpr size_t secret_key_size = 32; + +struct dave_state; + +/* +* @brief For holding a moving average of the number of current voice users, for applying a smooth gain ramp. +*/ +struct DPP_EXPORT moving_averager { + moving_averager() = default; + + moving_averager(uint64_t collection_count_new); + + moving_averager operator+=(int64_t value); + + operator float(); + +protected: + std::deque values{}; + uint64_t collectionCount{}; +}; + // Forward declaration class cluster; @@ -68,21 +118,133 @@ struct DPP_EXPORT voice_out_packet { * Generally these will be RTP. */ std::string packet; + /** * @brief Duration of packet */ uint64_t duration; }; -#define AUDIO_TRACK_MARKER (uint16_t)0xFFFF +/** + * @brief Supported DAVE (Discord Audio Visual Encryption) protocol versions + */ +enum dave_version_t : uint8_t { + /** + * @brief DAVE disabled (default for now) + */ + dave_version_none = 0, + /** + * @brief DAVE enabled, version 1 (E2EE encryption on top of openssl) + */ + dave_version_1 = 1, +}; -#define AUDIO_OVERLAP_SLEEP_SAMPLES 30 +/** + * @brief Discord voice websocket opcode types + */ +enum voice_websocket_opcode_t : uint8_t { + voice_opcode_connection_identify = 0, + voice_opcode_connection_select_protocol = 1, + voice_opcode_connection_ready = 2, + voice_opcode_connection_heartbeat = 3, + voice_opcode_connection_description = 4, + voice_opcode_client_speaking = 5, + voice_opcode_connection_heartbeat_ack = 6, + voice_opcode_connection_resume = 7, + voice_opcode_connection_hello = 8, + voice_opcode_connection_resumed = 9, + voice_opcode_multiple_clients_connect = 11, + voice_opcode_client_disconnect = 13, + voice_opcode_media_sink = 15, + voice_client_flags = 18, + voice_client_platform = 20, + voice_client_dave_prepare_transition = 21, + voice_client_dave_execute_transition = 22, + voice_client_dave_transition_ready = 23, + voice_client_dave_prepare_epoch = 24, + voice_client_dave_mls_external_sender = 25, + voice_client_dave_mls_key_package = 26, + voice_client_dave_mls_proposals = 27, + voice_client_dave_mls_commit_message = 28, + voice_client_dave_announce_commit_transition = 29, + voice_client_dave_mls_welcome = 30, + voice_client_dave_mls_invalid_commit_welcome = 31, +}; + +/** + * @brief DAVE E2EE Binary frame header + */ +struct dave_binary_header_t { + /** + * @brief Sequence number + */ + uint16_t seq; + + /** + * @brief Opcode type + */ + uint8_t opcode; + + /** + * @brief Data package, an opaque structure passed to the + * Discord libdave functions. + */ + std::vector package; + + /** + * @brief Fill binary header from inbound buffer + * @param buffer inbound websocket buffer + */ + dave_binary_header_t(const std::string& buffer); + + /** + * Get the data package from the packed binary frame, as a vector of uint8_t + * for use in the libdave functions + + * @return data blob + */ + [[nodiscard]] std::vector get_data() const; + + /** + * Get transition ID for process_commit and process_welcome + * + * @return Transition ID + */ + [[nodiscard]] uint16_t get_transition_id() const; + +private: + /** + * @brief Transition id, only valid when the opcode is + * commit and welcome state. Use get_transition_id() to obtain value. + */ + uint16_t transition_id; +}; + +/** + * @brief A callback for obtaining a user's privacy code. + * The privacy code is returned as the parameter to the function. + * + * This is a callback function because DAVE requires use of a very resource + * intensive SCRYPT call, which uses lots of ram and cpu and take significant + * time. + */ +using privacy_code_callback_t = std::function; /** @brief Implements a discord voice connection. * Each discord_voice_client connects to one voice channel and derives from a websocket client. */ class DPP_EXPORT discord_voice_client : public websocket_client { + /** + * @brief Clean up resources + */ + void cleanup(); + + /** + * @brief A frame of silence packet + */ + static constexpr uint8_t silence_packet[3] = { 0xf8, 0xff, 0xfe }; + /** * @brief Mutex for outbound packet stream */ @@ -93,6 +255,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ std::shared_mutex queue_mutex; + /** + * @brief Our public IP address + * + * Use discord_voice_client::discover_ip() to access this value + */ + std::string external_ip; + /** * @brief Queue of outbound messages */ @@ -113,6 +282,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ time_t connect_time; + /* + * @brief For mixing outgoing voice data. + */ + std::unique_ptr mixer; + /** * @brief IP of UDP/RTP endpoint */ @@ -158,6 +332,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * voice payload. */ rtp_seq_t seq; + /** * @brief The timestamp of the RTP packet that generated this voice * payload. @@ -166,6 +341,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * number wraps around. */ rtp_timestamp_t timestamp; + /** * @brief The event payload that voice handlers receive. */ @@ -195,6 +371,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client rtp_seq_t min_seq, max_seq; rtp_timestamp_t min_timestamp, max_timestamp; } range; + /** * @brief The queue of parked voice payloads. * @@ -203,10 +380,12 @@ class DPP_EXPORT discord_voice_client : public websocket_client * are parked and sorted in this queue. */ std::priority_queue parked_payloads; + /** * @brief The decoder ctls to be set on the decoder. */ std::vector> pending_decoder_ctls; + /** * @brief libopus decoder * @@ -220,6 +399,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @brief Thread used to deliver incoming voice data to handlers. */ std::thread voice_courier; + /** * @brief Shared state between this voice client and the courier thread. */ @@ -228,16 +408,19 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @brief Protects all following members. */ std::mutex mtx; + /** * @brief Signaled when there is a new payload to deliver or terminating state has changed. */ std::condition_variable signal_iteration; + /** * @brief Voice buffers to be reported to handler, grouped by speaker. * * Buffers are parked here and flushed every 500ms. */ std::map parked_voice_payloads; + /** * @brief Used to signal termination. * @@ -245,6 +428,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ bool terminating = false; } voice_courier_shared_state; + /** * @brief The run loop of the voice courier thread. */ @@ -255,6 +439,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ bool paused; + /** + * @brief Whether has sent 5 frame of silence before stopping on pause. + * + * This is to avoid unintended Opus interpolation with subsequent transmissions. + */ + bool sent_stop_frames; + #ifdef HAVE_VOICE /** * @brief libopus encoder @@ -266,6 +457,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client * (merges frames into one packet) */ OpusRepacketizer* repacketizer; + + /** + * @brief This holds the state information for DAVE E2EE. + * it is only allocated if E2EE is active on the voice channel. + */ + std::unique_ptr mls_state; + #else /** * @brief libopus encoder @@ -277,8 +475,26 @@ class DPP_EXPORT discord_voice_client : public websocket_client * (merges frames into one packet) */ void* repacketizer; + + /** + * @brief This holds the state information for DAVE E2EE. + * it is only allocated if E2EE is active on the voice channel. + */ + std::unique_ptr mls_state{}; #endif + /** + * @brief The list of users that have E2EE potentially enabled for + * DAVE protocol. + */ + std::set dave_mls_user_list; + + /** + * @brief The list of users that have left the voice channel but + * not yet removed from MLS group. + */ + std::set dave_mls_pending_remove_list; + /** * @brief File descriptor for UDP connection */ @@ -286,10 +502,15 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Secret key for encrypting voice. - * If it has been sent, this is non-null and points to a - * sequence of exactly 32 bytes. + * If it has been sent, this contains a sequence of exactly 32 bytes + * (secret_key_size) and has_secret_key is set to true. */ - uint8_t* secret_key; + std::array secret_key; + + /** + * @brief True if the voice client has a secret key + */ + bool has_secret_key{false}; /** * @brief Sequence number of outbound audio. This is incremented @@ -297,6 +518,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ uint16_t sequence; + /** + * @brief Last received sequence from gateway. + * + * Needed for heartbeat and resume payload. + */ + int32_t receive_sequence; + /** * @brief Timestamp value used in outbound audio. Each packet * has the timestamp value which is incremented to match @@ -304,6 +532,17 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ uint32_t timestamp; + /** + * @brief Each packet should have a nonce, a 32-bit incremental + * integer value appended to payload. + * + * We should keep track of this value and increment it for each + * packet sent. + * + * Current initial value is hardcoded to 1. + */ + uint32_t packet_nonce; + /** * @brief Last sent packet high-resolution timestamp */ @@ -349,6 +588,21 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ uint8_t encode_buffer[65536]; + /** + * @brief DAVE - Discord Audio Visual Encryption + * Used for E2EE encryption. dave_protocol_none is + * the default right now. + * @warning DAVE E2EE is an EXPERIMENTAL feature! + */ + dave_version_t dave_version; + + /** + * @brief Destination address for where packets go + * on the UDP socket + */ + address_t destination{}; + + /** * @brief Send data to UDP socket immediately. * @@ -398,6 +652,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @brief Called by ssl_client when there is data to be * read. At this point we insert that data into the * input queue. + * @throw dpp::voice_exception if voice support is not compiled into D++ */ void read_ready(); @@ -407,8 +662,10 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @param packet packet data * @param len length of packet * @param duration duration of opus packet + * @param send_now send this packet right away without buffering. + * Do NOT set send_now to true outside write_ready. */ - void send(const char* packet, size_t len, uint64_t duration); + void send(const char* packet, size_t len, uint64_t duration, bool send_now = false); /** * @brief Queue a message to be sent via the websocket @@ -447,6 +704,12 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ size_t encode(uint8_t *input, size_t inDataSize, uint8_t *output, size_t &outDataSize); + /** + * Updates DAVE MLS ratchets for users in the VC + * @param force True to force updating of ratchets regardless of state + */ + void update_ratchets(bool force = false); + public: /** @@ -455,15 +718,24 @@ class DPP_EXPORT discord_voice_client : public websocket_client class dpp::cluster* creator; /** - * @brief This needs to be static, we only initialise libsodium once per program start, - * so initialising it on first use in a voice connection is best. + * @brief True when the thread is shutting down */ - static bool sodium_initialised; + bool terminating; /** - * @brief True when the thread is shutting down + * @brief The gain value for the end of the current voice iteration. */ - bool terminating; + float end_gain; + + /** + * @brief The gain value for the current voice iteration. + */ + float current_gain; + + /** + * @brief The amount to increment each successive sample for, for the current voice iteration. + */ + float increment; /** * @brief Heartbeat interval for sending heartbeat keepalive @@ -495,13 +767,21 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ snowflake server_id; + /** + * @brief Moving averager. + */ + moving_averager moving_average; + /** * @brief Channel ID */ snowflake channel_id; /** - * @brief The audio type to be sent. The default type is recorded audio. + * @brief The audio type to be sent. + * + * @note On Windows, the default type is overlap audio. + * On all other platforms, it 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. @@ -531,10 +811,15 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ enum send_audio_type_t { - satype_recorded_audio, - satype_live_audio, + satype_recorded_audio, + satype_live_audio, satype_overlap_audio - } send_audio_type = satype_recorded_audio; + } send_audio_type = +#ifdef _WIN32 + satype_overlap_audio; +#else + satype_recorded_audio; +#endif /** * @brief Sets the gain for the specified user. @@ -591,6 +876,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ dpp::utility::uptime get_uptime(); + /** + * @brief The time (in milliseconds) between each interval when parsing audio. + * + * @warning You should only change this if you know what you're doing. It is set to 500ms by default. + */ + uint16_t iteration_interval{500}; + /** Constructor takes shard id, max shards and token. * @param _cluster The cluster which owns this voice connection, for related logging, REST requests etc * @param _channel_id The channel id to identify the voice connection as @@ -598,9 +890,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @param _token The voice session token to use for identifying to the websocket * @param _session_id The voice session id to identify with * @param _host The voice server hostname to connect to (hostname:port format) - * @throw dpp::voice_exception Sodium or Opus failed to initialise, or D++ is not compiled with voice support + * @param enable_dave Enable DAVE E2EE + * @throw dpp::voice_exception Opus failed to initialise, or D++ is not compiled with voice support + * @warning DAVE E2EE is an EXPERIMENTAL feature! */ - discord_voice_client(dpp::cluster* _cluster, snowflake _channel_id, snowflake _server_id, const std::string &_token, const std::string &_session_id, const std::string &_host); + discord_voice_client(dpp::cluster* _cluster, snowflake _channel_id, snowflake _server_id, const std::string &_token, const std::string &_session_id, const std::string &_host, bool enable_dave = false); /** * @brief Destroy the discord voice client object @@ -610,10 +904,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Handle JSON from the websocket. * @param buffer The entire buffer content from the websocket client + * @param opcode Frame type, e.g. OP_TEXT, OP_BINARY * @return bool True if a frame has been handled * @throw dpp::exception If there was an error processing the frame, or connection to UDP socket failed */ - virtual bool handle_frame(const std::string &buffer); + virtual bool handle_frame(const std::string &buffer, ws_opcode opcode); /** * @brief Handle a websocket error. @@ -629,27 +924,35 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Send raw audio to the voice channel. * - * You should send an audio packet of 11520 bytes. + * You should send an audio packet of `send_audio_raw_max_length` (11520) bytes. * Note that this function can be costly as it has to opus encode - * the PCM audio on the fly, and also encrypt it with libsodium. + * the PCM audio on the fly, and also encrypt it with openssl. * * @note Because this function encrypts and encodes packets before * pushing them onto the output queue, if you have a complete stream * ready to send and know its length it is advisable to call this * method multiple times to enqueue the entire stream audio so that * it is all encoded at once (unless you have set use_opus to false). - * Constantly calling this from the dpp::on_voice_buffer_send callback - * can and will eat a TON of cpu! + * **Constantly calling this from dpp::cluster::on_voice_buffer_send + * can, and will, eat a TON of cpu!** * * @param audio_data Raw PCM audio data. Channels are interleaved, * with each channel's amplitude being a 16 bit value. * - * The audio data should be 48000Hz signed 16 bit audio. + * @warning **The audio data needs to be 48000Hz signed 16 bit audio, otherwise, the audio will come through incorrectly!** * * @param length The length of the audio data. The length should * be a multiple of 4 (2x 16 bit stereo channels) with a maximum - * length of 11520, which is a complete opus frame at highest - * quality. + * length of `send_audio_raw_max_length`, which is a complete opus + * frame at highest quality. + * + * Generally when you're streaming and you know there will be + * more packet to come you should always provide packet data with + * length of `send_audio_raw_max_length`. + * Silence packet will be appended if length is less than + * `send_audio_raw_max_length` as discord expects to receive such + * specific packet size. This can cause gaps in your stream resulting + * in distorted audio if you have more packet to send later on. * * @return discord_voice_client& Reference to self * @@ -663,7 +966,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * Some containers such as .ogg may contain OPUS * encoded data already. In this case, we don't need to encode the * frames using opus here. We can bypass the codec, only applying - * libsodium to the stream. + * openssl to the stream. * * @param opus_packet Opus packets. Discord expects opus frames * to be encoded at 48000Hz @@ -673,6 +976,10 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @param duration Generally duration is 2.5, 5, 10, 20, 40 or 60 * if the timescale is 1000000 (1ms) * + * @param send_now Send this packet right away without buffering, + * this will skip duration calculation for the packet being sent + * and only safe to be set to true in write_ready. + * * @return discord_voice_client& Reference to self * * @note It is your responsibility to ensure that packets of data @@ -683,7 +990,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ */ - discord_voice_client& send_audio_opus(uint8_t* opus_packet, const size_t length, uint64_t duration); + discord_voice_client& send_audio_opus(const uint8_t* opus_packet, const size_t length, uint64_t duration, bool send_now = false); /** * @brief Send opus packets to the voice channel @@ -691,7 +998,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * Some containers such as .ogg may contain OPUS * encoded data already. In this case, we don't need to encode the * frames using opus here. We can bypass the codec, only applying - * libsodium to the stream. + * opens to the stream. * * Duration is calculated internally * @@ -710,7 +1017,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ */ - discord_voice_client& send_audio_opus(uint8_t* opus_packet, const size_t length); + discord_voice_client& send_audio_opus(const uint8_t* opus_packet, const size_t length); /** * @brief Send silence to the voice channel @@ -723,6 +1030,19 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ discord_voice_client& send_silence(const uint64_t duration); + /** + * @brief Send stop frames to the voice channel. + * + * @param send_now send this packet right away without buffering. + * Do NOT set send_now to true outside write_ready. + * Also make sure you're not locking stream_mutex if you + * don't set send_now to true. + * + * @return discord_voice_client& Reference to self + * @throw dpp::voice_exception if voice support is not compiled into D++ + */ + discord_voice_client& send_stop_frames(bool send_now = false); + /** * @brief Sets the audio type that will be sent with send_audio_* methods. * @@ -773,6 +1093,22 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ discord_voice_client& stop_audio(); + /** + * @brief Change the iteration interval time. + * + * @param interval The time (in milliseconds) between each interval when parsing audio. + * + * @return Reference to self. + */ + discord_voice_client& set_iteration_interval(uint16_t interval); + + /** + * @brief Get the iteration interval time (in milliseconds). + * + * @return iteration_interval + */ + uint16_t get_iteration_interval(); + /** * @brief Returns true if we are playing audio * @@ -862,7 +1198,78 @@ class DPP_EXPORT discord_voice_client : public websocket_client * for a single packet from Discord's voice servers. */ std::string discover_ip(); -}; + /** + * @brief Returns true if end-to-end encryption is enabled + * for the active voice call (Discord Audio Visual + * Encryption, a.k.a. DAVE). + * + * @return True if end-to-end encrypted + */ + bool is_end_to_end_encrypted() const; + + /** + * @brief Returns the privacy code for the end to end encryption + * scheme ("DAVE"). if end-to-end encryption is not active, + * or is not yet established, this will return an empty + * string. + * + * @return A sequence of six five-digit integers which + * can be matched against the Discord client, in the + * privacy tab for the properties of the voice call. + */ + std::string get_privacy_code() const; + + /** + * @brief Returns the privacy code for a given user by id, + * if they are in the voice call, and enc-to-end encryption + * is enabled. + * + * @param user User ID to fetch the privacy code for + * @param callback Callback to call with the privacy code when + * the creation of the code is complete. + * @warning This call spawns a thread, as getting a user's + * privacy code is a CPU-intensive and memory-intensive operation + * which internally uses scrypt. + */ + void get_user_privacy_code(const dpp::snowflake user, privacy_code_callback_t callback) const; + + /** + * @brief Notify gateway ready for a DAVE transition. + * + * Fires Voice Ready event when appropriate. + * + * https://daveprotocol.com/#commit-handling + * + * @param data Websocket frame data + */ + void ready_for_transition(const std::string &data); + + /** + * @brief Reset dave session, send voice_client_dave_mls_invalid_commit_welcome + * payload with current transition Id and our new key package to gateway. + * + * https://daveprotocol.com/#recovery-from-invalid-commit-or-welcome + */ + void recover_from_invalid_commit_welcome(); + + /** + * @brief Execute pending protocol upgrade/downgrade to/from dave. + * @return true if did an upgrade/downgrade + */ + bool execute_pending_upgrade_downgrade(); + + /** + * @brief Reset dave session and prepare initial session group. + */ + void reinit_dave_mls_group(); + + /** + * @brief Process roster map from commit/welcome. + * @param rmap Roster map + */ + void process_mls_group_rosters(const std::map>& rmap); }; +} + diff --git a/3rdParty/dpp/dispatcher.h b/3rdParty/dpp/dispatcher.h index 7f595dcd38..af374ddaf6 100644 --- a/3rdParty/dpp/dispatcher.h +++ b/3rdParty/dpp/dispatcher.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -40,21 +42,29 @@ #include #include #include +#include +#include #include #include #include #include #include +#ifdef DPP_CORO +#include +#endif /* DPP_CORO */ + namespace dpp { /* Forward declaration */ struct confirmation_callback_t; +class discord_client; +class discord_voice_client; /** * @brief A function used as a callback for any REST based command */ -typedef std::function command_completion_event_t; +using command_completion_event_t = std::function; /** @brief Base event parameter struct. * Each event you receive from the library will have its parameter derived from this class. @@ -63,35 +73,101 @@ typedef std::function command_completion_e * not get notified of the current event if you call it. */ struct DPP_EXPORT event_dispatch_t { +protected: + +public: /** * @brief Raw event data. * If you are using json on your websocket, this will contain json, and if you are using * ETF as your websocket protocol, it will contain raw ETF data. */ - const std::string raw_event; + std::string raw_event = {}; /** * @brief Shard the event came from. * Note that for some events, notably voice events, this may be nullptr. */ - class discord_client* from; + discord_client* from = nullptr; + + /** + * @brief Whether the event was cancelled using cancel_event(). + */ + mutable bool cancelled = false; /** * @brief Construct a new event_dispatch_t object - * - * @param client The shard the event originated on. May be a nullptr, e.g. for voice events + */ + event_dispatch_t() = default; + + /** + * @brief Construct a new event_dispatch_t object + * + * @param rhs event_dispatch_t object to copy from + */ + event_dispatch_t(const event_dispatch_t& rhs) = default; + + /** + * @brief Construct a new event_dispatch_t object + * + * @param rhs event_dispatch_t object to move from + */ + event_dispatch_t(event_dispatch_t&& rhs) = default; + + /** + * @brief Construct a new event_dispatch_t object + * + * @param client The shard the event originated on. May be a nullptr, e.g. for voice events + * @param raw Raw event data as JSON or ETF + */ + event_dispatch_t(discord_client* client, const std::string& raw); + + /** + * @brief Construct a new event_dispatch_t object + * + * @param client The shard the event originated on. May be a nullptr, e.g. for voice events * @param raw Raw event data as JSON or ETF */ - event_dispatch_t(class discord_client* client, const std::string& raw); + event_dispatch_t(discord_client* client, std::string&& raw); + + /** + * @brief Copy another event_dispatch_t object + * + * @param rhs The event to copy from + */ + event_dispatch_t &operator=(const event_dispatch_t& rhs) = default; + + /** + * @brief Move from another event_dispatch_t object + * + * @param rhs The event to move from + */ + event_dispatch_t &operator=(event_dispatch_t&& rhs) = default; + + /** + * @brief Destroy an event_dispatch_t object + * Protected because this object is to be derived from + */ + virtual ~event_dispatch_t() = default; + // ^^^^ THIS MUST BE VIRTUAL. It used to be interaction_create_t's destructor was virtual, + // however before gcc 8.4 a bug prevents inheriting constructors with a user-declared destructors. + // since we need to support gcc 8.3... this is the fix. see https://godbolt.org/z/4xrsPhjzv foo is event_dispatch_t, bar is interaction_create_t /** * @brief Cancels the event in progress. Any other attached lambdas for this event after this one are not called. - * Note that event cancellation is a thread local state, and not stored in the object (because object which can - * be cancelled is `const` during the event, and cannot itself contain the changeable state). + * + * @warning This will modify the event object in a non-thread-safe manner. * @return const event_dispatch_t& reference to self for chaining */ const event_dispatch_t& cancel_event() const; + /** + * @brief Cancels the event in progress. Any other attached lambdas for this event after this one are not called. + * + * @warning This will modify the event object in a non-thread-safe manner. + * @return const event_dispatch_t& reference to self for chaining + */ + event_dispatch_t &cancel_event(); + /** * @brief Returns true if the event is cancelled. * Note that event cancellation is a thread local state, and not stored in the object (because object which can @@ -101,20 +177,22 @@ struct DPP_EXPORT event_dispatch_t { bool is_cancelled() const; }; -/** @brief Log messages */ +/** + * @brief Log messages + */ struct DPP_EXPORT log_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 + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Severity. */ - log_t(class discord_client* client, const std::string& raw); - /** Severity */ - loglevel severity; - /** Log Message */ - std::string message; + loglevel severity = ll_info; - log_t(const log_t&) = default; + /** + * @brief Log Message + */ + std::string message = {}; }; namespace utility { @@ -135,233 +213,254 @@ namespace utility { * * @return A lambda for attaching to an API callback */ - std::function DPP_EXPORT log_error(); -}; + command_completion_event_t DPP_EXPORT log_error(); +} // namespace utility -/** @brief Add user to scheduled event */ +/** + * @brief Add user to scheduled event + */ struct DPP_EXPORT guild_scheduled_event_user_add_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 - */ - guild_scheduled_event_user_add_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief event user added to */ - snowflake event_id; + snowflake event_id = {}; /** * @brief User being added - * */ - snowflake user_id; + snowflake user_id = {}; /** * @brief Guild being added to - * */ - snowflake guild_id; + snowflake guild_id = {}; }; -/** @brief Delete user from scheduled event */ +/** + * @brief Delete user from scheduled event + */ struct DPP_EXPORT guild_scheduled_event_user_remove_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 - */ - guild_scheduled_event_user_remove_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief event user removed from */ - snowflake event_id; + snowflake event_id = {}; /** * @brief User being removed - * */ - snowflake user_id; + snowflake user_id = {}; /** * @brief Guild being removed from - * */ - snowflake guild_id; + snowflake guild_id = {}; }; -/** @brief Create scheduled event */ +/** + * @brief Create scheduled event + */ struct DPP_EXPORT guild_scheduled_event_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 - */ - guild_scheduled_event_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief created event */ - scheduled_event created; + scheduled_event created = {}; }; -/** @brief Create scheduled event */ +/** + * @brief Create scheduled event + */ struct DPP_EXPORT guild_scheduled_event_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 - */ - guild_scheduled_event_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated event */ - scheduled_event updated; + scheduled_event updated = {}; }; -/** @brief Delete scheduled event */ +/** + * @brief Delete scheduled event + */ struct DPP_EXPORT guild_scheduled_event_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 - */ - guild_scheduled_event_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief deleted event */ - scheduled_event deleted; + scheduled_event deleted = {}; }; -/** @brief Create automod rule */ +/** + * @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); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated event */ - automod_rule created; + automod_rule created = {}; }; -/** @brief Update automod rule */ +/** + * @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); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated event */ - automod_rule updated; + automod_rule updated = {}; }; -/** @brief Delete automod rule */ +/** + * @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); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated event */ - automod_rule deleted; + automod_rule deleted = {}; }; -/** @brief Execute/trigger automod rule */ +/** + * @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 + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief The id of the guild in which action was executed. */ - automod_rule_execute_t(class discord_client* client, const std::string& raw); + snowflake guild_id = {}; - 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) -}; + /** + * @brief The action which was executed. + */ + automod_action action = {}; + /** + * @brief The id of the rule which action belongs to. + */ + snowflake rule_id = {}; + /** + * @brief The trigger type of rule which was triggered. + */ + automod_trigger_type rule_trigger_type = {}; -/** @brief Create stage instance */ -struct DPP_EXPORT stage_instance_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 + /** + * @brief The id of the user which generated the content which triggered the rule. + */ + snowflake user_id = {}; + + /** + * @brief Optional: the id of the channel in which user content was posted. + */ + snowflake channel_id = {}; + + /** + * @brief Optional: the id of any user message which content belongs to. + */ + snowflake message_id = {}; + + /** + * @brief Optional: the id of any system auto moderation messages posted as a result of this action. + */ + snowflake alert_system_message_id = {}; + + /** + * @brief The user generated text content. */ - stage_instance_create_t(class discord_client* client, const std::string& raw); + std::string content = {}; + + /** + * @brief The word or phrase configured in the rule that triggered the rule (may be empty). + */ + std::string matched_keyword = {}; + + /** + * @brief The substring in content that triggered the rule (may be empty). + */ + std::string matched_content = {}; +}; + +/** + * @brief Create stage instance + */ +struct DPP_EXPORT stage_instance_create_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief created stage instance */ - stage_instance created; + stage_instance created = {}; }; -/** @brief Update stage instance */ +/** + * @brief Update stage instance + */ struct DPP_EXPORT stage_instance_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 - */ - stage_instance_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated stage instance */ - stage_instance updated; + stage_instance updated = {}; }; -/** @brief Delete stage instance */ +/** + * @brief Delete stage instance + */ struct DPP_EXPORT stage_instance_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 - */ - stage_instance_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief deleted stage instance */ - stage_instance deleted; + stage_instance deleted = {}; }; -/** @brief Voice state update */ +/** + * @brief Voice state update + */ struct DPP_EXPORT voice_state_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Voice state */ - voice_state_update_t(class discord_client* client, const std::string& raw); - /** Voice state */ - voicestate state; + voicestate state = {}; }; /** * @brief Create interaction */ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { - - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - interaction_create_t(class discord_client* client, const std::string& raw); - + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; /** * @brief Acknowledge interaction without displaying a message to the user, @@ -374,47 +473,47 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { /** * @brief Send a reply for this interaction - * + * * @param t Type of reply to send * @param m Message object to send. Not all fields are supported by Discord. * @param callback User function to execute 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 reply(interaction_response_type t, const message & m, command_completion_event_t callback = utility::log_error()) const; + void reply(interaction_response_type t, const message& m, command_completion_event_t callback = utility::log_error()) const; /** * @brief Send a reply for this interaction - * + * * @param t Type of reply to send * @param mt The string value to send, for simple text only messages * @param callback User function to execute 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 reply(interaction_response_type t, const std::string & mt, command_completion_event_t callback = utility::log_error()) const; + void reply(interaction_response_type t, const std::string& mt, command_completion_event_t callback = utility::log_error()) const; /** * @brief Send a reply for this interaction. * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. - * + * * @param m Message object to send. Not all fields are supported by Discord. * @param callback User function to execute 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 reply(const message & m, command_completion_event_t callback = utility::log_error()) const; + void reply(const message& m, command_completion_event_t callback = utility::log_error()) const; /** * @brief Send a reply for this interaction. * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. - * + * * @param mt The string value to send, for simple text only messages * @param callback User function to execute 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 reply(const std::string & mt, command_completion_event_t callback = utility::log_error()) const; + void reply(const std::string& mt, command_completion_event_t callback = utility::log_error()) const; /** * @brief Reply to interaction with a dialog box - * + * * @param mr Dialog box response to send * @param callback User function to execute 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(). @@ -428,7 +527,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @param callback User function to execute 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 edit_response(const message & m, command_completion_event_t callback = utility::log_error()) const; + void edit_response(const message& m, command_completion_event_t callback = utility::log_error()) const; /** * @brief Edit the response for this interaction @@ -437,10 +536,10 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @param callback User function to execute 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 edit_response(const std::string & mt, command_completion_event_t callback = utility::log_error()) const; + void edit_response(const std::string& mt, command_completion_event_t callback = utility::log_error()) const; /** - * @brief Set the bot to 'thinking' state + * @brief Set the bot to 'thinking' state where you have up to 15 minutes to respond * * @param ephemeral True if the thinking state should be ephemeral * @param callback User function to execute when the api call completes. @@ -463,7 +562,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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_original_response(const message & m, command_completion_event_t callback = utility::log_error()) const; + void edit_original_response(const message& m, command_completion_event_t callback = utility::log_error()) const; /** * @brief Delete original response message for this interaction. This cannot be used on an ephemeral interaction response. @@ -473,123 +572,197 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { */ void delete_original_response(command_completion_event_t callback = utility::log_error()) const; +#ifdef DPP_CORO /** - * @brief Get a command line parameter - * - * @param name The command line parameter to retrieve - * @return const command_value& If the command line parameter does not - * exist, an empty variant is returned. + * @brief Acknowledge interaction without displaying a message to the user, + * for use with button and select menu components. + * + * On success the result 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(). + */ + dpp::async co_reply() const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result 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(). + */ + dpp::async co_reply(interaction_response_type t, const message& m) const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param mt The string value to send, for simple text only messages + * On success the result 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(). + */ + dpp::async co_reply(interaction_response_type t, const std::string& mt) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result 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(). + */ + dpp::async co_reply(const message& m) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param mt The string value to send, for simple text only messages + * On success the result 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(). + */ + dpp::async co_reply(const std::string& mt) const; + + /** + * @brief Reply to interaction with a dialog box + * + * @param mr Dialog box response to send + * On success the result 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(). + */ + dpp::async co_dialog(const interaction_modal_response& mr) const; + + /** + * @brief Edit the response for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result 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(). + */ + dpp::async co_edit_response(const message& m) const; + + /** + * @brief Edit the response for this interaction + * + * @param mt The string value to send, for simple text only messages + * On success the result 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(). + */ + dpp::async co_edit_response(const std::string& mt) const; + + /** + * @brief Set the bot to 'thinking' state where you have up to 15 minutes to respond + * + * @param ephemeral True if the thinking state should be ephemeral + * On success the result 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(). + */ + dpp::async co_thinking(bool ephemeral = false) const; + + /** + * @brief Get original response message for this interaction + * + * On success the result 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(). */ - const virtual command_value& get_parameter(const std::string& name) const; + dpp::async co_get_original_response() const; + + /** + * @brief Edit original response message for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result 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(). + */ + dpp::async co_edit_original_response(const message& m) const; + + /** + * @brief Delete original response message for this interaction. This cannot be used on an ephemeral interaction response. + * + * On success the result 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(). + */ + dpp::async co_delete_original_response() const; +#endif /* DPP_CORO */ /** * @brief command interaction */ - interaction command; + interaction command = {}; /** - * @brief Destroy this object + * @brief Get a slashcommand parameter + * + * @param name The name of the command line parameter to retrieve the value for + * @return command_value Returns the value of the first option that matches the given name. + * If no matches are found, an empty variant is returned. + * + * @throw dpp::logic_exception if the interaction is not for a command */ - virtual ~interaction_create_t() = default; + virtual command_value get_parameter(const std::string& name) const; }; /** * @brief User has issued a slash command */ struct DPP_EXPORT slashcommand_t : public interaction_create_t { -public: - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - slashcommand_t(class discord_client* client, const std::string& raw); + using interaction_create_t::interaction_create_t; + using interaction_create_t::operator=; }; /** * @brief Click on button */ struct DPP_EXPORT button_click_t : public interaction_create_t { - - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - button_click_t(class discord_client* client, const std::string& raw); +private: + using interaction_create_t::get_parameter; +public: + using interaction_create_t::interaction_create_t; + using interaction_create_t::operator=; - /** - * @brief Get a command line parameter - * - * @param name The command line parameter to retrieve - * @return Always returns an empty parameter as buttons dont have parameters! - */ - const virtual command_value& get_parameter(const std::string& name) const; /** * @brief button custom id */ - std::string custom_id; + std::string custom_id = {}; + /** * @brief component type */ - uint8_t component_type; + uint8_t component_type = {}; }; +/** + * @brief On form submitted. + */ struct DPP_EXPORT form_submit_t : public interaction_create_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - form_submit_t(class discord_client* client, const std::string& raw); +private: + using interaction_create_t::get_parameter; +public: + using interaction_create_t::interaction_create_t; + using interaction_create_t::operator=; - /** - * @brief Get a command line parameter - * - * @param name The command line parameter to retrieve - * @return Always returns an empty parameter as buttons dont have parameters! - */ - const virtual command_value& get_parameter(const std::string& name) const; /** * @brief button custom id */ - std::string custom_id; + std::string custom_id = {}; + /** * @brief Message components for form reply */ - std::vector components; + std::vector components = {}; }; /** * @brief Discord requests that we fill a list of auto completion choices for a command option */ struct DPP_EXPORT autocomplete_t : public interaction_create_t { - - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - autocomplete_t(class discord_client* client, const std::string& raw); - - /** - * @brief Get a command line parameter - * - * @param name The command line parameter to retrieve - * @return Always returns an empty parameter as auto complete requests dont have parameters! - */ - const virtual command_value& get_parameter(const std::string& name) const; +private: + using interaction_create_t::get_parameter; +public: + using interaction_create_t::interaction_create_t; + using interaction_create_t::operator=; /** * @brief Command ID */ - dpp::snowflake id; + dpp::snowflake id = {}; /** * @brief Command name */ - std::string name; + std::string name = {}; /** * @brief auto completion options */ - std::vector options; + std::vector options = {}; }; /** @@ -597,40 +770,36 @@ struct DPP_EXPORT autocomplete_t : public interaction_create_t { * user or message. */ struct DPP_EXPORT context_menu_t : public interaction_create_t { +private: + using interaction_create_t::get_parameter; public: - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - context_menu_t(class discord_client* client, const std::string& raw); + using interaction_create_t::interaction_create_t; + using interaction_create_t::operator=; }; /** * @brief Event parameter for context menu interactions for messages */ struct DPP_EXPORT message_context_menu_t : public context_menu_t { +public: + using context_menu_t::context_menu_t; + using context_menu_t::operator=; /** * @brief Related message */ - message ctx_message; -public: - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_context_menu_t(class discord_client* client, const std::string& raw); + message ctx_message = {}; /** * @brief Get the message which was right-clicked on - * + * * @return message right-clicked on */ - message get_message() const; + const message& get_message() const; /** * @brief Set the message object for this event - * + * * @param m message to set * @return message_context_menu_t& reference to self for fluent chaining */ @@ -641,28 +810,25 @@ struct DPP_EXPORT message_context_menu_t : public context_menu_t { * @brief Event parameter for context menu interactions for users */ struct DPP_EXPORT user_context_menu_t : public context_menu_t { +public: + using context_menu_t::context_menu_t; + using context_menu_t::operator=; /** * @brief Related user */ - user ctx_user; -public: - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - user_context_menu_t(class discord_client* client, const std::string& raw); + user ctx_user = {}; /** * @brief Get the user which was right-clicked on - * + * * @return user right clicked on */ - user get_user() const; + const user& get_user() const; /** * @brief Set the user object for this event - * + * * @param u user to set * @return user_context_menu_t& reference to self for fluent chaining */ @@ -674,539 +840,647 @@ struct DPP_EXPORT user_context_menu_t : public context_menu_t { * @brief Click on select */ struct DPP_EXPORT select_click_t : public interaction_create_t { - - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - select_click_t(class discord_client* client, const std::string& raw); - - /** - * @brief Get a command line parameter - * - * @param name The command line parameter to retrieve - * @return Always returns an empty parameter as buttons dont have parameters! - */ - const virtual command_value& get_parameter(const std::string& name) const; +private: + using interaction_create_t::get_parameter; +public: + using interaction_create_t::interaction_create_t; + using interaction_create_t::operator=; /** * @brief select menu custom id */ - std::string custom_id; + std::string custom_id = {}; + /** * @brief select menu values */ - std::vector values; + std::vector values = {}; + /** * @brief select menu component type (dpp::component_type) */ - uint8_t component_type; + uint8_t component_type = {}; }; -/** @brief Delete guild */ +/** + * @brief Delete guild + */ struct DPP_EXPORT guild_delete_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Deleted guild + * */ - guild_delete_t(class discord_client* client, const std::string& raw); - /** Deleted guild */ - guild* deleted; + guild deleted; + + /** + * @brief Guild ID deleted + * + */ + snowflake guild_id{0}; }; -/** @brief Update guild stickers */ +/** + * @brief Update guild stickers + */ struct DPP_EXPORT guild_stickers_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Updating guild */ - guild_stickers_update_t(class discord_client* client, const std::string& raw); - /** Updating guild */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief stickers being updated */ - std::vector stickers; + std::vector stickers = {}; }; -/** @brief Guild join request delete (user declined membership screening) */ +/** + * @brief Guild join request delete (user declined membership screening) + */ struct DPP_EXPORT guild_join_request_delete_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Deleted guild */ - guild_join_request_delete_t(class discord_client* client, const std::string& raw); - /** Deleted guild */ - snowflake guild_id; + snowflake guild_id = {}; + /** * @brief user id */ - snowflake user_id; + snowflake user_id = {}; }; -/** @brief Delete channel */ +/** + * @brief Delete channel + */ struct DPP_EXPORT channel_delete_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - channel_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild channel is being deleted from */ - guild* deleting_guild; + guild* deleting_guild = nullptr; + /** * @brief channel being deleted */ - channel* deleted; + channel deleted; }; -/** @brief Update channel */ +/** + * @brief Update channel + */ struct DPP_EXPORT channel_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - channel_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild channel is being updated on */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief channel being updated */ - channel* updated; + channel* updated = nullptr; }; -/** @brief Session ready */ +/** + * @brief Session ready + */ struct DPP_EXPORT ready_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - ready_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief websocket session id */ - std::string session_id; + std::string session_id = {}; + /** * @brief shard id */ - uint32_t shard_id; + uint32_t shard_id = {}; + + /** + * @brief Array of guild IDs the bot is in, at the time of this event. + */ + std::vector guilds{}; + + /** + * @brief The number of guilds the bot is in, at the time of this event. + */ + uint32_t guild_count{0}; }; -/** @brief Message Deleted */ +/** + * @brief Message Deleted + */ struct DPP_EXPORT message_delete_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief message being deleted */ - message* deleted; + snowflake id{0}; + + /** + * @brief Channel the message was deleted on + * + */ + snowflake channel_id{0}; + + /** + * @brief Guild the message was deleted on + */ + snowflake guild_id{0}; + }; -/** @brief Guild member remove */ +/** + * @brief Guild member remove + */ struct DPP_EXPORT guild_member_remove_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_member_remove_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild user is being removed from */ - guild* removing_guild; + guild* removing_guild = nullptr; + + /** + * @brief Guild ID removed from + */ + snowflake guild_id{0}; + /** * @brief user being removed */ - user* removed; + user removed; }; -/** @brief Session resumed */ +/** + * @brief Session resumed + */ struct DPP_EXPORT resumed_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - resumed_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief websocket session id */ - std::string session_id; + std::string session_id = {}; + /** * @brief shard id */ - uint32_t shard_id; + uint32_t shard_id = 0; }; -/** @brief Guild role create */ +/** + * @brief Guild role create + */ struct DPP_EXPORT guild_role_create_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_role_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild role is being created on */ - guild* creating_guild; + guild* creating_guild = nullptr; + /** * @brief role being created */ - role* created; + role* created = nullptr; }; -/** @brief Typing start */ +/** + * @brief Typing start + */ struct DPP_EXPORT typing_start_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - typing_start_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild user is typing on */ - guild* typing_guild; + guild* typing_guild = nullptr; + /** * @brief channel user is typing on */ - channel* typing_channel; + channel* typing_channel = nullptr; + /** * @brief user who is typing. * Can be nullptr if user is not cached */ - user* typing_user; + user* typing_user = nullptr; + /** * @brief User id of user typing. * Always set regardless of caching */ - snowflake user_id; + snowflake user_id = {}; + /** * @brief Time of typing event */ - time_t timestamp; + time_t timestamp = 0; }; -/** @brief Voice state update */ +/** + * @brief Voice state update + */ struct DPP_EXPORT voice_track_marker_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on. - * Will always be null. - * @param raw Raw event text as JSON. - * Will always be empty. + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Voice client */ - voice_track_marker_t(class discord_client* client, const std::string& raw); - /** Voice client */ - class discord_voice_client* voice_client; - /** Track metadata */ - std::string track_meta; + discord_voice_client* voice_client = nullptr; + + /** + * @brief Track metadata + */ + std::string track_meta = {}; }; -/** @brief Message reaction add */ +/** + * @brief Message reaction add + */ struct DPP_EXPORT message_reaction_add_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_reaction_add_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief Guild reaction occurred on */ - guild* reacting_guild; + guild* reacting_guild = nullptr; + /** * @brief User who reacted */ - user reacting_user; + user reacting_user = {}; + /** * @brief member data of user who reacted */ - guild_member reacting_member; + guild_member reacting_member = {}; + + /** + * @brief Channel ID the reaction happened on + */ + snowflake channel_id = {}; + /** - * @brief channel the reaction happened on + * @brief channel the reaction happened on (Optional) + * @note only filled when the channel is cached */ - channel* reacting_channel; + channel* reacting_channel = nullptr; + /** * @brief emoji of reaction */ - emoji reacting_emoji; + emoji reacting_emoji = {}; + /** * @brief message id of the message reacted upon */ - snowflake message_id; + snowflake message_id = {}; + + /** + * @brief ID of the user who authored the message which was reacted to (Optional) + */ + snowflake message_author_id = {}; }; -/** @brief Guild members chunk */ +/** + * @brief Guild members chunk + */ struct DPP_EXPORT guild_members_chunk_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_members_chunk_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild the members chunk is for */ - guild* adding; + guild* adding = nullptr; + /** * @brief list of members in the chunk */ - guild_member_map* members; + guild_member_map* members = nullptr; }; -/** @brief Message reaction remove */ +/** + * @brief Message reaction remove + */ struct DPP_EXPORT message_reaction_remove_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_reaction_remove_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief Guild reaction occurred on */ - guild* reacting_guild; + guild* reacting_guild = nullptr; + /** * @brief User who reacted */ - dpp::snowflake reacting_user_id; + dpp::snowflake reacting_user_id = {}; + + /** + * @brief Channel ID the reaction was removed in + */ + snowflake channel_id = {}; + /** - * @brief channel the reaction happened on + * @brief channel the reaction happened on (optional) + * @note only filled when the channel is cached */ - channel* reacting_channel; + channel* reacting_channel = nullptr; + /** * @brief emoji of reaction */ - emoji reacting_emoji; + emoji reacting_emoji = {}; + /** * @brief message id of the message reacted upon */ - snowflake message_id; + snowflake message_id = {}; }; -/** @brief Create guild */ +/** + * @brief Create guild + */ struct DPP_EXPORT guild_create_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild that was created */ - guild* created; + guild* created = nullptr; + /** * @brief List of presences of all users on the guild. - * + * * This is only filled if you have the GUILD_PRESENCES * privileged intent. */ - presence_map presences; + presence_map presences = {}; + /** * @brief List of scheduled events in the guild */ - scheduled_event_map scheduled_events; + scheduled_event_map scheduled_events = {}; + /** * @brief List of stage instances in the guild */ - stage_instance_map stage_instances; + stage_instance_map stage_instances = {}; + /** * @brief List of threads in the guild */ - thread_map threads; + thread_map threads = {}; + /** * @brief List of stickers in the guild */ - sticker_map stickers; + sticker_map stickers = {}; }; -/** @brief Create channel */ +/** + * @brief Create channel + */ struct DPP_EXPORT channel_create_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - channel_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild channel was created on */ - guild* creating_guild; + guild* creating_guild = nullptr; + /** * @brief channel that was created */ - channel* created; + channel* created = nullptr; }; -/** @brief Message remove emoji */ +/** + * @brief Message remove emoji + */ struct DPP_EXPORT message_reaction_remove_emoji_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_reaction_remove_emoji_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief Guild reaction occurred on */ - guild* reacting_guild; + guild* reacting_guild = nullptr; + /** - * @brief channel the reaction happened on + * @brief Channel ID the reactions was removed in */ - channel* reacting_channel; + snowflake channel_id = {}; + + /** + * @brief channel the reaction happened on (optional) + * @note only filled when the channel is cached + */ + channel* reacting_channel = nullptr; + /** * @brief emoji of reaction */ - emoji reacting_emoji; + emoji reacting_emoji = {}; + /** * @brief message id of the message reacted upon */ - snowflake message_id; + snowflake message_id = {}; }; -/** @brief Message delete bulk */ +/** + * @brief Message delete bulk + */ struct DPP_EXPORT message_delete_bulk_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_delete_bulk_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild messages are being deleted upon */ - guild* deleting_guild; + guild* deleting_guild = nullptr; + /** * @brief user who is deleting the messages */ - user* deleting_user; + user* deleting_user = nullptr; + /** * @brief channel messages are being deleted from */ - channel* deleting_channel; + channel* deleting_channel = nullptr; + /** * @brief list of message ids of deleted messages */ - std::vector deleted; + std::vector deleted = {}; }; -/** @brief Guild role update */ +/** + * @brief Guild role update + */ struct DPP_EXPORT guild_role_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_role_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where roles are being updated */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief the role being updated */ - role* updated; + role* updated = nullptr; }; -/** @brief Guild role delete */ +/** + * @brief Guild role delete + */ struct DPP_EXPORT guild_role_delete_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_role_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where role is being deleted */ - guild* deleting_guild; + guild* deleting_guild = nullptr; + /** * @brief role being deleted */ - role* deleted; + role* deleted = nullptr; + /** * @brief ID of the deleted role */ - snowflake role_id; + snowflake role_id = {}; }; -/** @brief Channel pins update */ +/** + * @brief Channel pins update + */ struct DPP_EXPORT channel_pins_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - channel_pins_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where message is being pinned */ - guild* pin_guild; + guild* pin_guild = nullptr; + /** * @brief channel where message is being pinned */ - channel* pin_channel; + channel* pin_channel = nullptr; + /** * @brief timestamp of pin */ - time_t timestamp; + time_t timestamp = 0; }; -/** @brief Message remove all reactions */ +/** + * @brief Message remove all reactions + */ struct DPP_EXPORT message_reaction_remove_all_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_reaction_remove_all_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief Guild reaction occurred on */ - guild* reacting_guild; + guild* reacting_guild = nullptr; + + /** + * @brief Channel ID the reactions was removed in + */ + snowflake channel_id = {}; + /** - * @brief channel the reaction happened on + * @brief channel the reaction happened on (optional) + * @note only filled when the channel is cached */ - channel* reacting_channel; + channel* reacting_channel = nullptr; + /** * @brief message id of the message reacted upon */ - snowflake message_id; + snowflake message_id = {}; }; -/** @brief Voice server update */ +/** + * @brief Voice server update + */ struct DPP_EXPORT voice_server_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - voice_server_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild id where voice server updated */ - snowflake guild_id; + snowflake guild_id = {}; + /** * @brief voice server token, used to connect to vc */ - std::string token; + std::string token = {}; + /** * @brief voice server endpoint wss:// address - * */ - std::string endpoint; + std::string endpoint = {}; }; -/** @brief Guild emojis update */ +/** + * @brief Guild emojis update + */ struct DPP_EXPORT guild_emojis_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_emojis_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief snowflake ids of list of emojis */ - std::vector emojis; + std::vector emojis = {}; + /** * @brief guild where emojis are being updated */ - guild* updating_guild; + guild* updating_guild = nullptr; }; /** @@ -1214,157 +1488,158 @@ struct DPP_EXPORT guild_emojis_update_t : public event_dispatch_t { * */ struct DPP_EXPORT presence_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - presence_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief rich presence being updated */ - presence rich_presence; + presence rich_presence = {}; }; -/** @brief Webhooks update */ +/** + * @brief Webhooks update + */ struct DPP_EXPORT webhooks_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - webhooks_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where webhooks are being updated */ - guild* webhook_guild; + guild* webhook_guild = nullptr; + /** * @brief channel where webhooks are being updated */ - channel* webhook_channel; + channel* webhook_channel = nullptr; }; -/** @brief Guild member add */ +/** + * @brief Guild member add + */ struct DPP_EXPORT guild_member_add_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_member_add_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild which gained new member */ - guild* adding_guild; + guild* adding_guild = nullptr; + /** * @brief member which was added */ - guild_member added; + guild_member added = {}; }; -/** @brief Invite delete */ +/** + * @brief Invite delete + */ struct DPP_EXPORT invite_delete_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - invite_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief the deleted invite */ - invite deleted_invite; + invite deleted_invite = {}; }; -/** @brief Guild update */ +/** + * @brief Guild update + */ struct DPP_EXPORT guild_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild being updated */ - guild* updated; + guild* updated = nullptr; }; -/** @brief Guild integrations update */ +/** + * @brief Guild integrations update + */ struct DPP_EXPORT guild_integrations_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_integrations_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where integrations are being updated */ - guild* updating_guild; + guild* updating_guild = nullptr; }; -/** @brief Guild member update */ +/** + * @brief Guild member update + */ struct DPP_EXPORT guild_member_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_member_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where member is being updated */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief member being updated */ - guild_member updated; + guild_member updated = {}; }; -/** @brief Invite create */ +/** + * @brief Invite create + */ struct DPP_EXPORT invite_create_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - invite_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief created invite */ - invite created_invite; + invite created_invite = {}; }; -/** @brief Message update */ +/** + * @brief Message update + */ struct DPP_EXPORT message_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief message being updated */ - message msg; + message msg = {}; }; -/** @brief User update */ +/** + * @brief User update + */ struct DPP_EXPORT user_update_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - user_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief user being updated */ - user updated; + user updated = {}; }; -/** @brief Create message */ +/** + * @brief Create message + */ struct DPP_EXPORT message_create_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - message_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief message that was created (sent). */ - message msg; + message msg = {}; /** * @brief Send a text to the same channel as the channel_id in received event. * @param m Text to send @@ -1378,7 +1653,7 @@ struct DPP_EXPORT message_create_t : public event_dispatch_t { * @param callback User function to execute once the API call completes. * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. */ - void send(message& msg, command_completion_event_t callback = utility::log_error()) const; + void send(const message& msg, command_completion_event_t callback = utility::log_error()) const; /** * @brief Send a message to the same channel as the channel_id in received event. * @param msg Message to send @@ -1401,7 +1676,7 @@ struct DPP_EXPORT message_create_t : public event_dispatch_t { * @param callback User function to execute once the API call completes. * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. */ - void reply(message& msg, bool mention_replied_user = false, command_completion_event_t callback = utility::log_error()) const; + void reply(const message& msg, bool mention_replied_user = false, command_completion_event_t callback = utility::log_error()) const; /** * @brief Reply to the message received in the event. * @param msg Message to send as a reply. @@ -1412,283 +1687,350 @@ struct DPP_EXPORT message_create_t : public event_dispatch_t { void reply(message&& msg, bool mention_replied_user = false, command_completion_event_t callback = utility::log_error()) const; }; -/** @brief Guild ban add */ -struct DPP_EXPORT guild_ban_add_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON +/** + * @brief Message poll vote add + */ +struct DPP_EXPORT message_poll_vote_add_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief ID of the user who added the vote + */ + snowflake user_id; + + /** + * @brief ID of the channel containing the vote + */ + snowflake channel_id; + + /** + * @brief ID of the message containing the vote + */ + snowflake message_id; + + /** + * @brief ID of the guild containing the vote or 0 for DMs + */ + snowflake guild_id; + + /** + * @brief ID of the answer in the message poll object + */ + uint32_t answer_id; +}; + +/** + * @brief Message poll vote remove + */ +struct DPP_EXPORT message_poll_vote_remove_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief ID of the user who added the vote + */ + snowflake user_id; + + /** + * @brief ID of the channel containing the vote + */ + snowflake channel_id; + + /** + * @brief ID of the message containing the vote + */ + snowflake message_id; + + /** + * @brief ID of the guild containing the vote or 0 for DMs + */ + snowflake guild_id; + + /** + * @brief ID of the answer in the message poll object */ - guild_ban_add_t(class discord_client* client, const std::string& raw); + uint32_t answer_id; +}; + +/** + * @brief Guild audit log entry create + */ +struct DPP_EXPORT guild_audit_log_entry_create_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief created audit log entry + */ + audit_entry entry = {}; +}; + +/** + * @brief Guild ban add + */ +struct DPP_EXPORT guild_ban_add_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where ban was added */ - guild* banning_guild; + guild* banning_guild = nullptr; + /** * @brief user being banned */ - user banned; + user banned = {}; }; -/** @brief Guild ban remove */ +/** + * @brief Guild ban remove + */ struct DPP_EXPORT guild_ban_remove_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - guild_ban_remove_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where ban is being removed */ - guild* unbanning_guild; + guild* unbanning_guild = nullptr; + /** * @brief user being unbanned */ - user unbanned; + user unbanned = {}; }; -/** @brief Integration create */ +/** + * @brief Integration create + */ struct DPP_EXPORT integration_create_t : public event_dispatch_t { - /** Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - integration_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief created integration */ - integration created_integration; + integration created_integration = {}; }; -/** @brief Integration update */ +/** + * @brief Integration update + */ struct DPP_EXPORT integration_update_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - integration_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated integration */ - integration updated_integration; + integration updated_integration = {}; }; -/** @brief Integration delete */ +/** + * @brief Integration delete + */ struct DPP_EXPORT integration_delete_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - integration_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief deleted integration */ - integration deleted_integration; + integration deleted_integration = {}; }; -/** @brief Thread Create*/ +/** + * @brief Thread Create + */ struct DPP_EXPORT thread_create_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - thread_create_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where thread was created */ - guild* creating_guild; + guild* creating_guild = nullptr; + /** * @brief thread created */ - thread created; + thread created = {}; }; -/** @brief Thread Update -*/ +/** + * @brief Thread Update + */ struct DPP_EXPORT thread_update_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - thread_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where thread was updated */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief thread updated */ - thread updated; + thread updated = {}; }; -/** @brief Thread Delete +/** + * @brief Thread Delete */ struct DPP_EXPORT thread_delete_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - thread_delete_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where thread was deleted */ - guild* deleting_guild; + guild* deleting_guild = nullptr; + /** * @brief thread deleted */ - thread deleted; + thread deleted = {}; }; -/** @brief Thread List Sync +/** + * @brief Thread List Sync */ struct DPP_EXPORT thread_list_sync_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - thread_list_sync_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief guild where thread list was synchronised */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief list of threads (channels) synchronised */ - std::vector threads; + std::vector threads = {}; + /** * @brief list of thread members for the channels (threads) */ - std::vector members; + std::vector members = {}; }; -/** @brief Thread Member Update +/** + * @brief Thread Member Update */ struct DPP_EXPORT thread_member_update_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - thread_member_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief updated thread member */ - thread_member updated; + thread_member updated = {}; }; -/** @brief Thread Members Update +/** + * @brief Thread Members Update */ struct DPP_EXPORT thread_members_update_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * @param raw Raw event text as JSON - */ - thread_members_update_t(class discord_client* client, const std::string& raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief thread (channel) id */ - snowflake thread_id; + snowflake thread_id = {}; + /** * @brief guild thread members updated on */ - guild* updating_guild; + guild* updating_guild = nullptr; + /** * @brief new approximate member count */ - uint8_t member_count; + uint8_t member_count = 0; + /** * @brief added members */ - std::vector added; + std::vector added = {}; + /** * @brief ids only of removed members */ - std::vector removed_ids; + std::vector removed_ids = {}; }; -/** @brief voice buffer send +/** + * @brief voice buffer send + * @warning The shard in `from` will ALWAYS be null. */ struct DPP_EXPORT voice_buffer_send_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * WILL ALWAYS be NULL. - * @param raw Raw event text as JSON - */ - voice_buffer_send_t(class discord_client* client, const std::string &raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief voice client where buffer was sent */ - class discord_voice_client* voice_client; + class discord_voice_client* voice_client = nullptr; + /** * @brief encoded size of sent buffer */ - int buffer_size; -}; + uint64_t buffer_size = 0; -/** @brief voice user talking */ -struct DPP_EXPORT voice_user_talking_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * WILL ALWAYS be NULL. - * @param raw Raw event text as JSON - */ - voice_user_talking_t(class discord_client* client, const std::string &raw); - /** - * @brief voice client where user is talking - */ - class discord_voice_client* voice_client; - /** - * @brief talking user id - */ - snowflake user_id; /** - * @brief flags for talking user + * @brief number of packet waiting to be sent in the queue */ - uint8_t talking_flags; + size_t packets_left = 0; }; -/** @brief voice user talking */ +/** + * @brief voice ready + */ struct DPP_EXPORT voice_ready_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on - * WILL ALWAYS be NULL. - * @param raw Raw event text as JSON - */ - voice_ready_t(class discord_client* client, const std::string &raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief voice client which is ready */ - class discord_voice_client* voice_client; + discord_voice_client* voice_client = nullptr; + /** * @brief id of voice channel */ - snowflake voice_channel_id; + snowflake voice_channel_id = {}; }; -/** @brief voice receive packet */ +/** + * @brief voice receive packet + */ struct DPP_EXPORT voice_receive_t : public event_dispatch_t { + friend class discord_voice_client; -friend class discord_voice_client; + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; - /** - * @brief Constructor + /** + * @brief Construct a new voice receive t object + * * @param client The shard the event originated on. * WILL ALWAYS be NULL. * @param raw Raw event text as UDP packet. + * @param vc owning voice client pointer + * @param _user_id user id who is speaking, 0 for a mix of all user audio + * @param pcm user audio to set + * @param length length of user audio in bytes */ - voice_receive_t(class discord_client* client, const std::string &raw); + voice_receive_t(discord_client* client, const std::string& raw, class discord_voice_client* vc, snowflake _user_id, const uint8_t* pcm, size_t length); + /** * @brief Construct a new voice receive t object - * + * * @param client The shard the event originated on. * WILL ALWAYS be NULL. * @param raw Raw event text as UDP packet. @@ -1697,83 +2039,164 @@ friend class discord_voice_client; * @param pcm user audio to set * @param length length of user audio in bytes */ - voice_receive_t(class discord_client* client, const std::string &raw, class discord_voice_client* vc, snowflake _user_id, uint8_t* pcm, size_t length); + voice_receive_t(discord_client* client, std::string&& raw, class discord_voice_client* vc, snowflake _user_id, const uint8_t* pcm, size_t length); + /** * @brief Voice client */ - class discord_voice_client* voice_client; + discord_voice_client* voice_client = nullptr; + /** * @brief Audio data, encoded as 48kHz stereo PCM or Opus, * @deprecated Please switch to using audio_data. */ uint8_t* audio = nullptr; + /** * @brief Size of audio buffer * @deprecated Please switch to using audio_data. */ size_t audio_size = 0; + /** * @brief Audio data, encoded as 48kHz stereo PCM or Opus, */ - std::basic_string audio_data; + std::vector audio_data = {}; + /** * @brief User ID of speaker (zero if unknown) */ - snowflake user_id; + snowflake user_id = {}; + protected: /** * @brief Reassign values outside of the constructor for use within discord_voice_client - * + * * @param vc owning voice client pointer * @param _user_id user id who is speaking, 0 for a mix of all user audio * @param pcm user audio to set * @param length length of user audio in bytes */ - void reassign(class discord_voice_client* vc, snowflake _user_id, uint8_t* pcm, size_t length); + void reassign(discord_voice_client* vc, snowflake _user_id, const uint8_t* pcm, size_t length); }; -/** @brief voice client speaking event */ +/** + * @brief voice client speaking event + */ struct DPP_EXPORT voice_client_speaking_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on. - * WILL ALWAYS be NULL. - * @param raw Raw event text as JSON - */ - voice_client_speaking_t(class discord_client* client, const std::string &raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief voice client where user is speaking */ - class discord_voice_client* voice_client; + discord_voice_client* voice_client = nullptr; + /** * @brief speaking user id - * */ - snowflake user_id; + snowflake user_id = {}; + /** * @brief ssrc value of speaking user */ - uint32_t ssrc; + uint32_t ssrc = 0; }; -/** @brief voice client disconnect event */ +/** + * @brief voice client disconnect event + */ struct DPP_EXPORT voice_client_disconnect_t : public event_dispatch_t { - /** - * @brief Constructor - * @param client The shard the event originated on. - * WILL ALWAYS be NULL. - * @param raw Raw event text as JSON - */ - voice_client_disconnect_t(class discord_client* client, const std::string &raw); + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + /** * @brief voice client where user disconnected */ - class discord_voice_client* voice_client; + discord_voice_client* voice_client = nullptr; + /** * @brief user id of user who left vc */ - snowflake user_id; + snowflake user_id = {}; +}; + +/** + * @brief Discord voice platform types + */ +enum client_platform_t : uint8_t { + /** + * @brief Web, Desktop + */ + client_platform_desktop = 0, + /** + * @brief Mobile device + */ + client_platform_mobile = 1, +}; + +/** + * @brief voice client platform type notification event + */ +struct DPP_EXPORT voice_client_platform_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief voice client where user is + */ + discord_voice_client* voice_client = nullptr; + + /** + * @brief user id of user who left vc + */ + snowflake user_id = {}; + + /** + * @brief Client platform for the voice user + * Either desktop, or mobile + */ + client_platform_t platform = client_platform_desktop; +}; + +/** + * @brief Delete stage instance + */ +struct DPP_EXPORT entitlement_create_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief The created entitlement. + */ + entitlement created = {}; +}; + +/** + * @brief Delete stage instance + */ +struct DPP_EXPORT entitlement_update_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief The entitlement that was updated. + */ + entitlement updating_entitlement = {}; }; +/** + * @brief Delete stage instance + */ +struct DPP_EXPORT entitlement_delete_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief The deleted entitlement. + */ + entitlement deleted = {}; }; +} + diff --git a/3rdParty/dpp/dns.h b/3rdParty/dpp/dns.h index 33265e979c..e9fcd25356 100644 --- a/3rdParty/dpp/dns.h +++ b/3rdParty/dpp/dns.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include namespace dpp { @@ -40,23 +42,42 @@ namespace dpp { */ struct dns_cache_entry { /** - * @brief Resolved address information + * @brief Resolved address metadata */ 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. + * @brief Resolved address as string. + * The metadata is needed to know what type of address it is. + * Do not do silly stuff like just looking to see if '.' is in it! */ - sockaddr_storage ai_addr; + std::string resolved_addr; /** * @brief Time at which this cache entry is invalidated */ time_t expire_timestamp; + + /** + * @brief Get address length + * @return address length + */ + [[nodiscard]] int size() const; + + /** + * @brief Get the address_t that corresponds to this cache entry + * for use when connecting with ::connect() + * @param port Port number to connect to + * @return address_t prefilled with the IP and port number + */ + [[nodiscard]] const address_t get_connecting_address(uint16_t port) const; + + /** + * @brief Allocate a socket file descriptor for the given dns address + * @return File descriptor ready for calling connect(), or INVALID_SOCKET + * on failure. + */ + [[nodiscard]] socket make_connecting_socket() const; }; /** @@ -73,4 +94,4 @@ namespace dpp { * @throw dpp::connection_exception On failure to resolve hostname */ const dns_cache_entry* resolve_hostname(const std::string& hostname, const std::string& port); -}; +} diff --git a/3rdParty/dpp/dpp.h b/3rdParty/dpp/dpp.h index 297c94c272..0acd82896c 100644 --- a/3rdParty/dpp/dpp.h +++ b/3rdParty/dpp/dpp.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -72,3 +74,4 @@ #include #include #include +#include diff --git a/3rdParty/dpp/dtemplate.h b/3rdParty/dpp/dtemplate.h index 7569c5f786..3de77a8f1c 100644 --- a/3rdParty/dpp/dtemplate.h +++ b/3rdParty/dpp/dtemplate.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -21,7 +22,7 @@ #pragma once #include #include -#include +#include #include #include @@ -30,41 +31,66 @@ namespace dpp { /** * @brief Represents a guild template */ -class DPP_EXPORT dtemplate : public json_interface { -public: +class DPP_EXPORT dtemplate : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + dtemplate& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build the JSON for this object + * + * @param with_id Add ID to output + * @return json JSON content + */ + json to_json_impl(bool with_id = false) const; + +public: /** * @brief Template code */ std::string code; + /** * @brief Template name */ std::string name; + /** * @brief Template description */ std::string description; + /** * @brief Usage counter */ uint32_t usage_count; + /** * @brief User ID of creator */ snowflake creator_id; + /** * @brief Creation date/time * */ time_t created_at; + /** * @brief Last update date/time */ time_t updated_at; + /** * @brief Guild id the template is created from */ snowflake source_guild_id; + /** * @brief True if needs synchronising */ @@ -79,25 +105,11 @@ class DPP_EXPORT dtemplate : public json_interface { * @brief Destroy the dtemplate object */ virtual ~dtemplate() = default; - - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - dtemplate& fill_from_json(nlohmann::json* j); - - /** - * @brief Build the JSON for this object - * - * @param with_id Add ID to output - * @return std::string JSON content - */ - std::string build_json(bool with_id = false) const; - }; -/** A container of invites */ +/** + * @brief A container of invites + */ typedef std::unordered_map dtemplate_map; - -}; +} diff --git a/3rdParty/dpp/emoji.h b/3rdParty/dpp/emoji.h index 7c47fe5eba..b45f8e6d2a 100644 --- a/3rdParty/dpp/emoji.h +++ b/3rdParty/dpp/emoji.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -24,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -36,56 +37,122 @@ namespace dpp { * @brief Flags for dpp::emoji */ enum emoji_flags : uint8_t { - /// Emoji requires colons + /** + * @brief Emoji requires colons. + */ e_require_colons = 0b00000001, - /// Managed (introduced by application) - e_managed = 0b00000010, - /// Animated - e_animated = 0b00000100, - /// Available (false if the guild doesn't meet boosting criteria, etc) - e_available = 0b00001000, + + /** + * @brief Managed (introduced by application) + */ + e_managed = 0b00000010, + + /** + * @brief Animated emoji. + */ + e_animated = 0b00000100, + + /** + * @brief Available (false if the guild doesn't meet boosting criteria, etc) + */ + e_available = 0b00001000, }; /** * @brief Represents an emoji for a dpp::guild */ -class DPP_EXPORT emoji : public managed, public json_interface { +class DPP_EXPORT emoji : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + emoji& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + json to_json_impl(bool with_id = false) const; + public: /** - * @brief Emoji name + * @brief Emoji name. + */ + std::string name{}; + + /** + * @brief Roles allowed to use this emoji. */ - std::string name; + std::vector roles; + /** - * @brief User id who uploaded the emoji + * @brief The id of the user that created this emoji. */ snowflake user_id; + /** - * @brief Flags for the emoji from dpp::emoji_flags + * @brief Image data for the emoji, if uploading. */ - uint8_t flags; + utility::image_data image_data; + /** - * @brief Image data for the emoji if uploading + * @brief Flags for the emoji from dpp::emoji_flags. */ - std::string* image_data; - + uint8_t flags{0}; + /** * @brief Construct a new emoji object */ - emoji(); + emoji() = default; /** * @brief Construct a new emoji object with name, ID and flags - * - * @param n The emoji's name - * @param i ID, if it has one (unicode does not) - * @param f Emoji flags (emoji_flags) + * + * @param name The emoji's name + * @param id ID, if it has one (unicode does not) + * @param flags Emoji flags (emoji_flags) */ - emoji(const std::string n, const snowflake i = 0, const uint8_t f = 0); + emoji(const std::string_view name, const snowflake id = 0, const uint8_t flags = 0); + + /** + * @brief Copy constructor, copies another emoji's data + * + * @param rhs Emoji to copy + */ + emoji(const emoji &rhs) = default; + + /** + * @brief Move constructor, moves another emoji's data to this + * + * @param rhs Emoji to move from + */ + emoji(emoji &&rhs) noexcept = default; /** * @brief Destroy the emoji object */ - virtual ~emoji(); + ~emoji() override = default; + + /** + * @brief Copy assignment operator, copies another emoji's data + * + * @param rhs Emoji to copy + */ + emoji &operator=(const emoji &rhs) = default; + + /** + * @brief Move constructor, moves another emoji's data to this + * + * @param rhs Emoji to move from + */ + emoji &operator=(emoji &&rhs) noexcept = default; /** * @brief Create a mentionable emoji @@ -94,27 +161,11 @@ class DPP_EXPORT emoji : public managed, public json_interface { * @param is_animated is emoji animated. * @return std::string The formatted mention of the emoji. */ - static std::string get_mention(const std::string& name, const snowflake& id, bool is_animated = false); - - /** - * @brief Read class values from json object - * - * @param j A json object to read from - * @return A reference to self - */ - emoji& fill_from_json(nlohmann::json* j); - - /** - * @brief Build the json for this object - * - * @param with_id include the id in the JSON - * @return std::string json data - */ - std::string build_json(bool with_id = false) const; + static std::string get_mention(std::string_view name, snowflake id, bool is_animated = false); /** * @brief Emoji requires colons - * + * * @return true Requires colons * @return false Does not require colons */ @@ -122,7 +173,7 @@ class DPP_EXPORT emoji : public managed, public json_interface { /** * @brief Emoji is managed - * + * * @return true Is managed * @return false Is not managed */ @@ -138,25 +189,36 @@ class DPP_EXPORT emoji : public managed, public json_interface { /** * @brief Is available - * + * * @return true Is available * @return false Is unavailable */ bool is_available() const; /** - * @brief Load an image into the object as base64 - * + * @brief Load an image into the object + * * @param image_blob Image binary data - * @param type Type of image + * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. * @return emoji& Reference to self - * @throw dpp::exception Image content exceeds discord maximum of 256 kilobytes + * @throw dpp::length_exception Image content exceeds discord maximum of 256 kilobytes */ - emoji& load_image(const std::string &image_blob, const image_type type); + emoji& load_image(std::string_view image_blob, const image_type type); + + /** + * @brief Load an image into the object + * + * @param data Image binary data + * @param size Size of the image. + * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @return emoji& Reference to self + * @throw dpp::length_exception Image content exceeds discord maximum of 256 kilobytes + */ + emoji& load_image(const std::byte* data, uint32_t size, const image_type type); /** * @brief Format to name if unicode, name:id if has id or a:name:id if animated - * + * * @return Formatted name for reactions */ std::string format() const; @@ -167,6 +229,19 @@ class DPP_EXPORT emoji : public managed, public json_interface { * @return std::string mention */ std::string get_mention() const; + + /** + * @brief Get the custom emoji url + * + * @param size The size of the emoji in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized emoji is returned. + * @param format The format to use for the emoji. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated emojis. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the emoji is available as animated. + * @return std::string emoji url or an empty string, if the id is not set + */ + std::string get_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; }; /** @@ -174,4 +249,4 @@ class DPP_EXPORT emoji : public managed, public json_interface { */ typedef std::unordered_map emoji_map; -}; +} diff --git a/3rdParty/dpp/entitlement.h b/3rdParty/dpp/entitlement.h new file mode 100644 index 0000000000..099b314b41 --- /dev/null +++ b/3rdParty/dpp/entitlement.h @@ -0,0 +1,246 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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 { + +/** + * @brief The type of entitlement. + * */ +enum entitlement_type : uint8_t { + /** + * @brief Entitlement was purchased by user + */ + PURCHASE = 1, + + /** + * @brief Entitlement for Discord Nitro subscription + */ + PREMIUM_SUBSCRIPTION = 2, + + /** + * @brief Entitlement was gifted by developer + */ + DEVELOPER_GIFT = 3, + + /** + * @brief Entitlement was purchased by a dev in application test mode + */ + TEST_MODE_PURCHASE = 4, + + /** + * @brief Entitlement was granted when the SKU was free + */ + FREE_PURCHASE = 5, + + /** + * @brief Entitlement was gifted by another user + */ + USER_GIFT = 6, + + /** + * @brief Entitlement was claimed by user for free as a Nitro Subscriber + */ + PREMIUM_PURCHASE = 7, + + /** + * @brief Entitlement was purchased as an app subscription + */ + APPLICATION_SUBSCRIPTION = 8, +}; + +/** + * @brief Entitlement flags. + */ +enum entitlement_flags : uint8_t { + /** + * @brief Entitlement was deleted + * + * @note Only discord staff can delete an entitlement via + * their internal tooling. It should rarely happen except in cases + * of fraud or chargeback. + */ + ent_deleted = 0b0000001, + + /** + * @brief Entitlement was consumed. + * + * @note A consumed entitlement is a used-up one-off purchase. + */ + ent_consumed = 0b0000010, +}; + +/** + * @brief A definition of a discord entitlement. + * + * An entitlement is a user's connection to an SKU, basically a subscription + * or a one-off purchase. + */ +class DPP_EXPORT entitlement : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + entitlement& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build json for this entitlement object + * + * @param with_id include the ID in the json + * @return json JSON object + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief ID of the entitlement event + * + * Not sure if this remains constant, it does not relate to the SKU, + * user, guild or subscription. Do not use it for anything except state + * tracking. + */ + snowflake sku_id{0}; + + /** + * @brief ID of the parent application + */ + snowflake application_id{0}; + + /** + * @brief Subscription ID + * + * This is a unique identifier of the user or guilds subscription to the SKU. + * It won't ever change. + */ + snowflake subscription_id{0}; + + /** + * @brief Promotion id + * + * These are undocumented but given in examples in the docs. + */ + snowflake promotion_id{0}; + + /** + * @brief Gift Code Flags (undocumented) + * + * Undocumented, but given in examples in the docs. + */ + uint8_t gift_code_flags{0}; + + /** + * @brief Optional: ID of the user that is granted access to the entitlement's SKU + */ + snowflake user_id{0}; + + /** + * @brief Optional: ID of the user that is granted access to the entitlement's SKU + * + * If a guild is provided, according to the examples the user who triggered the + * purchase will also be passed in the user ID. The presence of a non-zero guild + * id snowflake is indication it is a guild subscription. + */ + snowflake guild_id{0}; + + /** + * @brief The type of entitlement. + */ + entitlement_type type = entitlement_type::APPLICATION_SUBSCRIPTION; + + /** + * @brief Optional: Start date at which the entitlement is valid. + * + * @note Not present when using test entitlements. + */ + time_t starts_at{0}; + + /** + * @brief Optional: Date at which the entitlement is no longer valid. + * + * @note Not present when using test entitlements. + */ + time_t ends_at{0}; + + /** + * @brief Flags bitmap from dpp::entitlement_flags + */ + uint16_t flags{0}; + + /** + * @brief Construct a new entitlement object + */ + entitlement() = default; + + /** + * @brief Construct a new entitlement object with sku_id, ID, application_id, type, and flags. + * + * @param sku_id The ID of the SKU. + * @param id The ID of the entitlement. + * @param application_id The ID of the parent application. + * @param type The type of entitlement (Should only ever be APPLICATION_SUBSCRIPTION unless you going to use this object as a parameter for dpp::cluster::entitlement_test_create). + * @param flags The flags for the SKU from dpp::entitlement_flags. + */ + entitlement(const snowflake sku_id, const snowflake id = 0, const snowflake application_id = 0, const entitlement_type type = dpp::entitlement_type::APPLICATION_SUBSCRIPTION, const uint8_t flags = 0); + + /** + * @brief Get the type of entitlement. + * + * @return entitlement_type Entitlement type + */ + [[nodiscard]] entitlement_type get_type() const; + + /** + * @brief Was the entitlement consumed? + * + * A consumed entitlement is a one off purchase which + * has been claimed as used by the application. for example + * in-app purchases. + * + * @return true if the entitlement was consumed. + */ + [[nodiscard]] bool is_consumed() const; + + /** + * @brief Was the entitlement deleted? + * + * @return true if the entitlement was deleted. + */ + [[nodiscard]] bool is_deleted() const; + +}; + +/** + * @brief Group of entitlements. + */ +typedef std::unordered_map entitlement_map; + +} diff --git a/3rdParty/dpp/etf.h b/3rdParty/dpp/etf.h index 5b07b6c961..b0bfb24ef4 100644 --- a/3rdParty/dpp/etf.h +++ b/3rdParty/dpp/etf.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -34,11 +35,13 @@ #pragma once #include #include -#include +#include namespace dpp { -/** Current ETF format version in use */ +/** + * @brief Current ETF format version in use + */ const uint8_t FORMAT_VERSION = 131; /** @@ -46,76 +49,142 @@ const uint8_t FORMAT_VERSION = 131; * in the ETF binary structure. */ enum etf_token_type : uint8_t { - /// 68 [Distribution header] - ett_distribution = 'D', - /// 70 [Float64:IEEE float] - ett_new_float = 'F', - /// 77 [UInt32:Len, UInt8:Bits, Len:Data] - ett_bit_binary = 'M', - /// 80 [UInt4:UncompressedSize, N:ZlibCompressedData] - ett_compressed = 'P', - /// 97 [UInt8:Int] - ett_smallint = 'a', - /// 98 [Int32:Int] - ett_integer = 'b', - /// 99 [31:Float String] Float in string format (formatted "%.20e", sscanf "%lf"). Superseded by ett_new_float - ett_float = 'c', - /// 100 [UInt16:Len, Len:AtomName] max Len is 255 - ett_atom = 'd', - /// 101 [atom:Node, UInt32:ID, UInt8:Creation] - ett_reference = 'e', - /// 102 [atom:Node, UInt32:ID, UInt8:Creation] - ett_port = 'f', - /// 103 [atom:Node, UInt32:ID, UInt32:Serial, UInt8:Creation] - ett_pid = 'g', - /// 104 [UInt8:Arity, N:Elements] - ett_small_tuple = 'h', - /// 105 [UInt32:Arity, N:Elements] - ett_large_tuple = 'i', - /// 106 empty list - ett_nil = 'j', - /// 107 [UInt16:Len, Len:Characters] - ett_string = 'k', - /// 108 [UInt32:Len, Elements, Tail] - ett_list = 'l', - /// 109 [UInt32:Len, Len:Data] - ett_binary = 'm', - /// 110 [UInt8:n, UInt8:Sign, n:nums] - ett_bigint_small = 'n', - /// 111 [UInt32:n, UInt8:Sign, n:nums] - ett_bigint_large = 'o', - /// 112 [UInt32:Size, UInt8:Arity, 16*Uint6-MD5:Uniq, UInt32:Index, UInt32:NumFree, atom:Module, int:OldIndex, int:OldUniq, pid:Pid, NunFree*ext:FreeVars] - ett_new_function = 'p', - /// 113 [atom:Module, atom:Function, smallint:Arity] - ett_export = 'q', - /// 114 [UInt16:Len, atom:Node, UInt8:Creation, Len*UInt32:ID] - ett_new_reference = 'r', - /// 115 [UInt8:Len, Len:AtomName] - ett_atom_small = 's', - /// 116 [UInt32:Airty, N:Pairs] - ett_map = 't', - /// 117 [UInt4:NumFree, pid:Pid, atom:Module, int:Index, int:Uniq, NumFree*ext:FreeVars] - ett_function = 'u', - /// 118 [UInt16:Len, Len:AtomName] max Len is 255 characters (up to 4 bytes per) - ett_atom_utf8 = 'v', - /// 119 [UInt8:Len, Len:AtomName] - ett_atom_utf8_small = 'w' -}; + /** + * @brief 68 [Distribution header] + */ + ett_distribution = 'D', + + /** + * @brief 70 [Float64:IEEE float] + */ + ett_new_float = 'F', + + /** + * @brief 77 [UInt32:Len, UInt8:Bits, Len:Data] + */ + ett_bit_binary = 'M', + + /** + * @brief 80 [UInt4:UncompressedSize, N:ZlibCompressedData] + */ + ett_compressed = 'P', + + /** + * @brief 97 [UInt8:Int] + */ + ett_smallint = 'a', + + /** + * @brief 98 [Int32:Int] + */ + ett_integer = 'b', + + /** + * @brief 99 [31:Float String] Float in string format (formatted "%.20e", sscanf "%lf"). + * + * @note Superseded by ett_new_float. + */ + ett_float = 'c', + + /** + * @brief 100 [UInt16:Len, Len:AtomName] max Len is 255 + */ + ett_atom = 'd', + + /** + * @brief 101 [atom:Node, UInt32:ID, UInt8:Creation] + */ + ett_reference = 'e', + + /** + * @brief 102 [atom:Node, UInt32:ID, UInt8:Creation] + */ + ett_port = 'f', + + /** + * @brief 103 [atom:Node, UInt32:ID, UInt32:Serial, UInt8:Creation] + */ + ett_pid = 'g', + + /** + * @brief 104 [UInt8:Arity, N:Elements] + */ + ett_small_tuple = 'h', + + /** + * @brief 105 [UInt32:Arity, N:Elements] + */ + ett_large_tuple = 'i', + + /** + * @brief 106 empty list + */ + ett_nil = 'j', + + /** + * @brief 107 [UInt16:Len, Len:Characters] + */ + ett_string = 'k', + + /** + * @brief 108 [UInt32:Len, Elements, Tail] + */ + ett_list = 'l', + + /** + * @brief 109 [UInt32:Len, Len:Data] + */ + ett_binary = 'm', + + /** + * @brief 110 [UInt8:n, UInt8:Sign, n:nums] + */ + ett_bigint_small = 'n', + + /** + * @brief 111 [UInt32:n, UInt8:Sign, n:nums] + */ + ett_bigint_large = 'o', + + /** + * @brief 112 [UInt32:Size, UInt8:Arity, 16*Uint6-MD5:Uniq, UInt32:Index, UInt32:NumFree, atom:Module, int:OldIndex, int:OldUniq, pid:Pid, NunFree*ext:FreeVars] + */ + ett_new_function = 'p', + + /** + * @brief 113 [atom:Module, atom:Function, smallint:Arity] + */ + ett_export = 'q', -/** - * @brief A horrible structure used within the ETF parser to convert uint64_t to double and back. - * This is horrible, but it is the official way erlang term format does this, so we can't really - * mess with it much. - */ -union type_punner { /** - * @brief binary integer value + * @brief 114 [UInt16:Len, atom:Node, UInt8:Creation, Len*UInt32:ID] */ - uint64_t ui64; + ett_new_reference = 'r', + /** - * @brief double floating point value + * @brief 115 [UInt8:Len, Len:AtomName] */ - double df; + ett_atom_small = 's', + + /** + * @brief 116 [UInt32:Airty, N:Pairs] + */ + ett_map = 't', + + /** + * @brief 117 [UInt4:NumFree, pid:Pid, atom:Module, int:Index, int:Uniq, NumFree*ext:FreeVars] + */ + ett_function = 'u', + + /** + * @brief 118 [UInt16:Len, Len:AtomName] max Len is 255 characters (up to 4 bytes per) + */ + ett_atom_utf8 = 'v', + + /** + * @brief 119 [UInt8:Len, Len:AtomName] + */ + ett_atom_utf8_small = 'w' }; /** @@ -126,6 +195,7 @@ struct DPP_EXPORT etf_buffer { * @brief Raw buffer */ std::vector buf; + /** * @brief Current used length of buffer * (this is different from buf.size() as it is pre-allocated @@ -152,7 +222,6 @@ struct DPP_EXPORT etf_buffer { * have to be any different for handling ETF. */ class DPP_EXPORT etf_parser { - /** * @brief Current size of binary data */ @@ -639,4 +708,4 @@ class DPP_EXPORT etf_parser { std::string build(const nlohmann::json& j); }; -}; +} diff --git a/3rdParty/dpp/event.h b/3rdParty/dpp/event.h index 8f50f1d0b1..2ef2aee8ca 100644 --- a/3rdParty/dpp/event.h +++ b/3rdParty/dpp/event.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -21,27 +22,24 @@ #pragma once #include #include -#include +#include #define event_decl(x,wstype) /** @brief Internal event handler for wstype websocket events. Called for each websocket message of this type. @internal */ \ - class x : public event { public: virtual void handle(dpp::discord_client* client, nlohmann::json &j, const std::string &raw); }; - -namespace dpp { - -class discord_client; + class x : public event { public: virtual void handle(class dpp::discord_client* client, nlohmann::json &j, const std::string &raw); }; /** * @brief The events namespace holds the internal event handlers for each websocket event. * These are handled internally and also dispatched to the user code if the event is hooked. */ -namespace events { +namespace dpp::events { /** * @brief An event object represents an event handled internally, passed from the websocket e.g. MESSAGE_CREATE. */ class DPP_EXPORT event { public: - /** Pure virtual method for event handler code + /** + * @brief Pure virtual method for event handler code * @param client The creating shard * @param j The json data of the event * @param raw The raw event json @@ -102,6 +100,8 @@ event_decl(message_create,MESSAGE_CREATE); event_decl(message_update,MESSAGE_UPDATE); event_decl(message_delete,MESSAGE_DELETE); event_decl(message_delete_bulk,MESSAGE_DELETE_BULK); +event_decl(message_poll_vote_add,MESSAGE_POLL_VOTE_ADD); +event_decl(message_poll_vote_remove,MESSAGE_POLL_VOTE_REMOVE); /* Presence/typing */ event_decl(presence_update,PRESENCE_UPDATE); @@ -148,4 +148,12 @@ 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); -}}; +/* Audit log */ +event_decl(guild_audit_log_entry_create, GUILD_AUDIT_LOG_ENTRY_CREATE); + +/* Entitlements */ +event_decl(entitlement_create, ENTITLEMENT_CREATE); +event_decl(entitlement_update, ENTITLEMENT_UPDATE); +event_decl(entitlement_delete, ENTITLEMENT_DELETE); + +} diff --git a/3rdParty/dpp/event_router.h b/3rdParty/dpp/event_router.h index 6532ed8823..4d01075bf9 100644 --- a/3rdParty/dpp/event_router.h +++ b/3rdParty/dpp/event_router.h @@ -27,17 +27,132 @@ #include #include #include -#include +#include #include #include #include #include -#include - -using json = nlohmann::json; +#include +#include +#include +#include namespace dpp { +#ifdef DPP_CORO + +template +class event_router_t; + +namespace detail { + +/** @brief Internal cogwheels for dpp::event_router_t */ +namespace event_router { + +/** @brief State of an owner of an event_router::awaitable */ +enum class awaiter_state { + /** @brief Awaitable is not being awaited */ + none, + /** @brief Awaitable is being awaited */ + waiting, + /** @brief Awaitable will be resumed imminently */ + resuming, + /** @brief Awaitable will be cancelled imminently */ + cancelling +}; + +/** + * @brief Awaitable object representing an event. + * A user can co_await on this object to resume the next time the event is fired, + * optionally with a condition. + */ +template +class awaitable { + friend class event_router_t; + + /** @brief Resume the coroutine waiting on this object */ + void resume() { + std_coroutine::coroutine_handle<>::from_address(handle).resume(); + } + + /** @brief Event router that will manage this object */ + event_router_t *self; + + /** @brief Predicate on the event, or nullptr for always match */ + std::function predicate = nullptr; + + /** @brief Event that triggered a resumption, to give to the resumer */ + const T *event = nullptr; + + /** @brief Coroutine handle, type-erased */ + void* handle = nullptr; + + /** @brief The state of the awaiting coroutine */ + std::atomic state = awaiter_state::none; + + /** Default constructor is accessible only to event_router_t */ + awaitable() = default; + + /** Normal constructor is accessible only to event_router_t */ + template + awaitable(event_router_t *router, F&& fun) : self{router}, predicate{std::forward(fun)} {} + +public: + /** This object is not copyable. */ + awaitable(const awaitable &) = delete; + + /** Move constructor. */ + awaitable(awaitable &&rhs) noexcept : self{rhs.self}, predicate{std::move(rhs.predicate)}, event{rhs.event}, handle{std::exchange(rhs.handle, nullptr)}, state{rhs.state.load(std::memory_order_relaxed)} {} + + /** This object is not copyable. */ + awaitable& operator=(const awaitable &) = delete; + + /** Move assignment operator. */ + awaitable& operator=(awaitable&& rhs) noexcept { + self = rhs.self; + predicate = std::move(rhs.predicate); + event = rhs.event; + handle = std::exchange(rhs.handle, nullptr); + state = rhs.state.load(std::memory_order_relaxed); + return *this; + } + + /** + * @brief Request cancellation. This will detach this object from the event router and resume the awaiter, which will be thrown dpp::task_cancelled::exception. + * + * @throw ??? As this resumes the coroutine, it may throw any exceptions at the caller. + */ + void cancel(); + + /** + * @brief First function called by the standard library when awaiting this object. Returns true if we need to suspend. + * + * @retval false always. + */ + [[nodiscard]] constexpr bool await_ready() const noexcept; + + /** + * @brief Second function called by the standard library when awaiting this object, after suspension. + * This will attach the object to its event router, to be resumed on the next event that satisfies the predicate. + * + * @return void never resume on call. + */ + void await_suspend(detail::std_coroutine::coroutine_handle<> caller); + + /** + * @brief Third and final function called by the standard library, called when resuming the coroutine. + * + * @throw @ref task_cancelled_exception if cancel() has been called + * @return const T& __Reference__ to the event that matched + */ + [[maybe_unused]] const T& await_resume(); +}; + +} + +} +#endif + /** * @brief A returned event handle for an event which was attached */ @@ -45,25 +160,24 @@ typedef size_t event_handle; /** * @brief Handles routing of an event to multiple listeners. + * Multiple listeners may attach to the event_router_t by means of @ref operator()(F&&) "operator()". Passing a + * lambda into @ref operator()(F&&) "operator()" attaches to the event. * - * Multiple listeners may attach to the event_router_t by means of operator(). Passing a - * lambda into operator() attaches to the event. - * - * Dispatchers of the event may call the event_router_t::call() method to cause all listeners + * @details Dispatchers of the event may call the @ref call() method to cause all listeners * to receive the event. * - * The event_router_t::empty() method will return true if there are no listeners attached + * The @ref empty() method will return true if there are no listeners attached * to the event_router_t (this can be used to save time by not constructing objects that * nobody will ever see). * - * The event_router_t::detach() method removes an existing listener from the event, - * using the event_handle ID returned by operator(). + * The @ref detach() method removes an existing listener from the event, + * using the event_handle ID returned by @ref operator()(F&&) "operator()". * * This class is used by the library to route all websocket events to listening code. * * Example: * - * ```cpp + * @code{cpp} * // Declare an event that takes log_t as its parameter * event_router_t my_event; * @@ -79,7 +193,7 @@ typedef size_t event_handle; * * // Detach from an event using the handle returned by operator() * my_event.detach(id); - * ``` + * @endcode * * @tparam T type of single parameter passed to event lambda derived from event_dispatch_t */ @@ -87,33 +201,73 @@ template class event_router_t { private: friend class cluster; + /** + * @brief Non-coro event handler type + */ + using regular_handler_t = std::function; + + /** + * @brief Type that event handlers will be stored as with DPP_CORO off. + * This is the ABI DPP_CORO has to match. + */ + using event_handler_abi_t = std::variant>; + +#ifdef DPP_CORO + friend class detail::event_router::awaitable; + + /** @brief dpp::task coro event handler */ + using task_handler_t = std::function(const T&)>; + + /** @brief Type that event handlers are stored as */ + using event_handler_t = std::variant; + + DPP_CHECK_ABI_COMPAT(event_handler_t, event_handler_abi_t) +#else + /** + * @brief Type that event handlers are stored as + */ + using event_handler_t = event_handler_abi_t; +#endif + + /** + * @brief Identifier for the next event handler, will be given to the user on attaching a handler + */ event_handle next_handle = 1; /** * @brief Thread safety mutex */ - mutable std::shared_mutex lock; + mutable std::shared_mutex mutex; + /** * @brief Container of event listeners keyed by handle, * as handles are handed out sequentially they will always * be called in they order they are bound to the event * as std::map is an ordered container. */ - std::map> dispatch_container; - + std::map dispatch_container; #ifdef DPP_CORO /** - * @brief Container for event listeners (coroutines only) + * @brief Mutex for messing with coro_awaiters. + */ + mutable std::shared_mutex coro_mutex; + + /** + * @brief Vector containing the awaitables currently being awaited on for this event router. */ - std::map> coroutine_container; + mutable std::vector *> coro_awaiters; #else - /** - * @brief Dummy container to keep the struct size same - */ - std::map> dummy_container; -#endif + /** + * @brief Dummy for ABI compatibility between DPP_CORO and not + */ + utility::dummy definitely_not_a_mutex; + /** + * @brief Dummy for ABI compatibility between DPP_CORO and not + */ + utility::dummy> definitely_not_a_vector; +#endif /** * @brief A function to be called whenever the method is called, to check @@ -137,110 +291,455 @@ template class event_router_t { warning = warning_function; } + /** + * @brief Handle an event. This function should only be used without coro enabled, otherwise use handle_coro. + */ + void handle(const T& event) const { + if (warning) { + warning(event); + } + + std::shared_lock l(mutex); + for (const auto& [_, listener] : dispatch_container) { + if (!event.is_cancelled()) { + if (std::holds_alternative(listener)) { + std::get(listener)(event); + } else { + throw dpp::logic_exception("cannot handle a coroutine event handler with a library built without DPP_CORO"); + } + } + }; + } + +#ifdef DPP_CORO + /** + * @brief Handle an event as a coroutine, ensuring the lifetime of the event object. + */ + dpp::job handle_coro(T event) const { + if (warning) { + warning(event); + } + + resume_awaiters(event); + + std::vector> tasks; + { + std::shared_lock l(mutex); + + for (const auto& [_, listener] : dispatch_container) { + if (!event.is_cancelled()) { + if (std::holds_alternative(listener)) { + tasks.push_back(std::get(listener)(event)); + } else if (std::holds_alternative(listener)) { + std::get(listener)(event); + } + } + }; + } + + for (dpp::task& t : tasks) { + co_await t; // keep the event object alive until all tasks finished + } + } + + /** + * @brief Attach a suspended coroutine to this event router via detail::event_router::awaitable. + * It will be resumed and detached when an event satisfying its condition completes, or it is cancelled. + * + * This is for internal usage only, the user way to do this is to co_await it (which will call this when suspending) + * This guarantees that the coroutine is indeed suspended and thus can be resumed at any time + * + * @param awaiter Awaiter to attach + */ + void attach_awaiter(detail::event_router::awaitable *awaiter) { + std::unique_lock lock{coro_mutex}; + + coro_awaiters.emplace_back(awaiter); + } + + /** + * @brief Detach an awaiting coroutine handle from this event router. + * This is mostly called when a detail::event_router::awaitable is cancelled. + * + * @param handle Coroutine handle to find in the attached coroutines + */ + void detach_coro(void *handle) { + std::unique_lock lock{coro_mutex}; + + coro_awaiters.erase(std::remove_if(coro_awaiters.begin(), coro_awaiters.end(), [handle](detail::event_router::awaitable const *awaiter) { return awaiter->handle == handle; }), coro_awaiters.end()); + } + + /** + * @brief Resume any awaiter whose predicate matches this event, or is null. + * + * @param event Event to compare and pass to accepting awaiters + */ + void resume_awaiters(const T& event) const { + std::vector*> to_resume; + std::unique_lock lock{coro_mutex}; + + for (auto it = coro_awaiters.begin(); it != coro_awaiters.end();) { + detail::event_router::awaitable* awaiter = *it; + + if (awaiter->predicate && !awaiter->predicate(event)) { + ++it; + } else { + using state_t = detail::event_router::awaiter_state; + + /** + * If state == none (was never awaited), do nothing + * If state == waiting, prevent resumption, resume on our end + * If state == resuming || cancelling, ignore + * + * Technically only cancelling || waiting should be possible here + * We do this by trying to exchange "waiting" with "resuming". If that returns false, this is presumed to be "cancelling" + */ + + state_t s = state_t::waiting; + if (awaiter->state.compare_exchange_strong(s, state_t::resuming)) { + to_resume.emplace_back(awaiter); + awaiter->event = &event; + + it = coro_awaiters.erase(it); + } else { + ++it; + } + } + } + lock.unlock(); + for (detail::event_router::awaitable* awaiter : to_resume) + awaiter->resume(); + } +#endif + public: /** * @brief Construct a new event_router_t object. */ event_router_t() = default; + /** + * @brief Destructor. Will cancel any coroutine awaiting on events. + * + * @throw ! Cancelling a coroutine will throw a dpp::task_cancelled_exception to it. + * This will be caught in this destructor, however, make sure no other exceptions are thrown in the coroutine after that or it will terminate. + */ + ~event_router_t() { +#ifdef DPP_CORO + while (!coro_awaiters.empty()) { + // cancel all awaiters. here we cannot do the usual loop as we'd need to lock coro_mutex, and cancel() locks and modifies coro_awaiters + try { + coro_awaiters.back()->cancel(); + /* + * will resume coroutines and may throw ANY exception, including dpp::task_cancelled_exception cancel() throws at them. + * we catch that one. for the rest, good luck :) + * realistically the only way any other exception would pop up here is if someone catches dpp::task_cancelled_exception THEN throws another exception. + */ + } catch (const dpp::task_cancelled_exception &) { + // ok. likely we threw this one + } + } +#endif + } + /** * @brief Call all attached listeners. * Listeners may cancel, by calling the event.cancel method. - * + * * @param event Class to pass as parameter to all listeners. */ void call(const T& event) const { - if (warning) { - warning(event); - } - std::shared_lock l(lock); - std::for_each(dispatch_container.begin(), dispatch_container.end(), [&](auto &ev) { - if (!event.is_cancelled()) { - ev.second(event); - } - }); #ifdef DPP_CORO - std::for_each(coroutine_container.begin(), coroutine_container.end(), [&](auto &ev) { - if (!event.is_cancelled()) { - ev.second(event); - } - }); + handle_coro(event); +#else + handle(event); #endif }; + /** + * @brief Call all attached listeners. + * Listeners may cancel, by calling the event.cancel method. + * + * @param event Class to pass as parameter to all listeners. + */ + void call(T&& event) const { +#ifdef DPP_CORO + handle_coro(std::move(event)); +#else + handle(std::move(event)); +#endif + }; + +#ifdef DPP_CORO + /** + * @brief Obtain an awaitable object that refers to an event with a certain condition. + * It can be co_await-ed to wait for the next event that satisfies this condition. + * On resumption the awaiter will be given __a reference__ to the event, + * saving it in a variable is recommended to avoid variable lifetime issues. + * + * @details Example: @code{cpp} + * dpp::task<> my_handler(const dpp::slashcommand_t& event) { + * co_await event.co_reply(dpp::message().add_component(dpp::component().add_component().set_label("click me!").set_id("test"))); + * + * dpp::button_click_t b = co_await c->on_button_click.with([](const dpp::button_click_t &event){ return event.custom_id == "test"; }); + * + * // do something on button click + * } + * @endcode + * + * This can be combined with dpp::when_any and other awaitables, for example dpp::cluster::co_sleep to create @ref expiring-buttons "expiring buttons". + * + * @warning On resumption the awaiter will be given a reference to the event. + * This means that variable may become dangling at the next co_await, be careful and save it in a variable + * if you need to. + * @param pred Predicate to check the event against. This should be a callable of the form `bool(const T&)` + * where T is the event type, returning true if the event is to match. + * @return awaitable An awaitable object that can be co_await-ed to await an event matching the condition. + */ + template +#ifndef _DOXYGEN_ + requires utility::callable_returns +#endif + auto when(Predicate&& pred) +#ifndef _DOXYGEN_ + noexcept(noexcept(std::function{std::declval()})) +#endif + { + return detail::event_router::awaitable{this, std::forward(pred)}; + } + + /** + * @brief Obtain an awaitable object that refers to any event. + * It can be co_await-ed to wait for the next event. + * + * Example: + * @details Example: @code{cpp} + * dpp::task<> my_handler(const dpp::slashcommand_t& event) { + * co_await event.co_reply(dpp::message().add_component(dpp::component().add_component().set_label("click me!").set_id("test"))); + * + * dpp::button_click_t b = co_await c->on_message_create; + * + * // do something on button click + * } + * @endcode + * + * This can be combined with dpp::when_any and other awaitables, for example dpp::cluster::co_sleep to create expiring buttons. + * + * @warning On resumption the awaiter will be given a reference to the event. + * This means that variable may become dangling at the next co_await, be careful and save it in a variable + * if you need to. + * @return awaitable An awaitable object that can be co_await-ed to await an event matching the condition. + */ + [[nodiscard]] auto operator co_await() noexcept { + return detail::event_router::awaitable{this, nullptr}; + } +#endif + /** * @brief Returns true if the container of listeners is empty, * i.e. there is nothing listening for this event right now. * - * @return true if there are no listeners - * @return false if there are some listeners + * @retval true if there are no listeners + * @retval false if there are some listeners */ - bool empty() const { - std::shared_lock l(lock); + [[nodiscard]] bool empty() const { +#ifdef DPP_CORO + std::shared_lock lock{mutex}; + std::shared_lock coro_lock{coro_mutex}; + + return dispatch_container.empty() && coro_awaiters.empty(); +#else + std::shared_lock lock{mutex}; + return dispatch_container.empty(); +#endif } /** * @brief Returns true if any listeners are attached. * * This is the boolean opposite of event_router_t::empty(). - * @return true if listeners are attached - * @return false if no listeners are attached + * @retval true if listeners are attached + * @retval false if no listeners are attached */ operator bool() const { return !empty(); } +#ifdef _DOXYGEN_ /** - * @brief Attach a lambda to the event, adding a listener. - * The lambda should follow the signature specified when declaring - * the event object and should take exactly one parameter derived - * from event_dispatch_t. - * - * @param func Function lambda to attach to event + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T&)` or + * `dpp::task(const T&)` (the latter requires DPP_CORO to be defined), + * where T is the event type for this event router. + * + * This has the exact same behavior as using \ref attach(F&&) "attach". + * + * @see attach + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] event_handle operator()(F&& fun); + + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T&)` or + * `dpp::task(const T&)` (the latter requires DPP_CORO to be defined), + * where T is the event type for this event router. + * + * @param fun Callable to attach to event * @return event_handle An event handle unique to this event, used to * detach the listener from the event later if necessary. */ - event_handle operator()(std::function func) { - return this->attach(func); + template + [[maybe_unused]] event_handle attach(F&& fun); +#else /* not _DOXYGEN_ */ +# ifdef DPP_CORO + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T&)` or + * `dpp::task(const T&)`, where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + requires (utility::callable_returns, const T&> || utility::callable_returns) + [[maybe_unused]] event_handle operator()(F&& fun) { + return this->attach(std::forward(fun)); } /** - * @brief Attach a lambda to the event, adding a listener. - * The lambda should follow the signature specified when declaring - * the event object and should take exactly one parameter derived - * from event_dispatch_t. - * - * @param func Function lambda to attach to event + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T&)` or + * `dpp::task(const T&)`, where T is the event type for this event router. + * + * @param fun Callable to attach to event * @return event_handle An event handle unique to this event, used to * detach the listener from the event later if necessary. */ - event_handle attach(std::function func) { - std::unique_lock l(lock); + template + requires (utility::callable_returns) + [[maybe_unused]] event_handle attach(F&& fun) { + std::unique_lock l(mutex); event_handle h = next_handle++; - dispatch_container.emplace(h, func); - return h; + dispatch_container.emplace(std::piecewise_construct, std::forward_as_tuple(h), std::forward_as_tuple(std::in_place_type_t{}, std::forward(fun))); + return h; } -#ifdef DPP_CORO - event_handle co_attach(std::function func) { - std::unique_lock l(lock); + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T&)` or + * `dpp::task(const T&)`, where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + requires (utility::callable_returns, const T&>) + [[maybe_unused]] event_handle attach(F&& fun) { + assert(dpp::utility::is_coro_enabled()); + + std::unique_lock l(mutex); event_handle h = next_handle++; - coroutine_container.emplace(h, func); - return h; + dispatch_container.emplace(std::piecewise_construct, std::forward_as_tuple(h), std::forward_as_tuple(std::in_place_type_t{}, std::forward(fun))); + return h; } -#endif +# else + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should be of the form `void(const T&)` + * where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] std::enable_if_t, event_handle> operator()(F&& fun) { + return this->attach(std::forward(fun)); + } + + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should be of the form `void(const T&)` + * where T is the event type for this event router. + *f + * @warning You cannot call this within an event handler. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] std::enable_if_t, event_handle> attach(F&& fun) { + std::unique_lock l(mutex); + event_handle h = next_handle++; + dispatch_container.emplace(h, std::forward(fun)); + return h; + } +# endif /* DPP_CORO */ +#endif /* _DOXYGEN_ */ /** * @brief Detach a listener from the event using a previously obtained ID. - * - * @param handle An ID obtained from event_router_t::operator() - * @return true The event was successfully detached - * @return false The ID is invalid (possibly already detached, or does not exist) + * + * @warning You cannot call this within an event handler. + * + * @param handle An ID obtained from @ref operator(F&&) "operator()" + * @retval true The event was successfully detached + * @retval false The ID is invalid (possibly already detached, or does not exist) */ - bool detach(const event_handle& handle) { - std::unique_lock l(lock); + [[maybe_unused]] bool detach(const event_handle& handle) { + std::unique_lock l(mutex); return this->dispatch_container.erase(handle); } }; -}; +#ifdef DPP_CORO + +namespace detail::event_router { + +template +void awaitable::cancel() { + awaiter_state s = awaiter_state::waiting; + /** + * If state == none (was never awaited), do nothing + * If state == waiting, prevent resumption, resume on our end + * If state == resuming || cancelling, ignore + */ + if (state.compare_exchange_strong(s, awaiter_state::cancelling)) { + self->detach_coro(handle); + resume(); + } +} + +template +constexpr bool awaitable::await_ready() const noexcept { + return false; +} + +template +void awaitable::await_suspend(detail::std_coroutine::coroutine_handle<> caller) { + state.store(awaiter_state::waiting); + handle = caller.address(); + self->attach_awaiter(this); +} + +template +const T &awaitable::await_resume() { + handle = nullptr; + predicate = nullptr; + if (state.exchange(awaiter_state::none, std::memory_order_relaxed) == awaiter_state::cancelling) { + throw dpp::task_cancelled_exception{"event_router::awaitable was cancelled"}; + } + return *std::exchange(event, nullptr); +} + +} +#endif + +} diff --git a/3rdParty/dpp/exception.h b/3rdParty/dpp/exception.h index ffdc1bdb83..5d986f4761 100644 --- a/3rdParty/dpp/exception.h +++ b/3rdParty/dpp/exception.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -26,6 +27,358 @@ namespace dpp { +/** + * @brief Exception error codes possible for dpp::exception::code() + * + * This list is a combined list of Discord's error codes, HTTP error codes, + * zlib, opus and C library codes (e.g. DNS, socket etc). You may + * use these to easily identify a type of exception without having to resort + * to string comparison against dpp::exception::what() + * + * For detailed descriptions of each error code, see the text description + * returned in `what()`. + * + * @note Some exceptions MAY have error codes which are NOT in this list + * in the event a C library is updated and adds new codes we did not document + * here. In this case, or where the code is not specific, refer to `what()`. + */ +enum exception_error_code { + err_no_code_specified = 0, + err_zlib_see_errno = -1, + err_zlib_init_stream = -2, + err_zlib_init_data = -3, + err_zlib_init_mem = -4, + err_zlib_init_buffer = -5, + err_zlib_init_version = -6, + err_opus_bad_arg = -11, + err_opus_buffer_too_small = -12, + err_opus_internal_error = -13, + err_opus_invalid_packet = -14, + err_opus_unimplemented = -15, + err_opus_invalid_state = -16, + err_opus_alloc_fail = -17, + err_dns_bad_flags = -21, + err_name_or_service_unknown = -22, + err_dns_again = -23, + err_dns_fail = -24, + err_dns_family = -26, + err_dns_socket_type = -27, + err_dns_service = -28, + err_dns_memory = -30, + err_dns_system_error = -31, + err_dns_overflow = -32, + err_ssl_new = 1, + err_ssl_connect = 2, + err_write = 3, + err_ssl_write = 4, + err_no_sessions_left = 5, + err_auto_shard = 6, + err_reconnection = 7, + err_bind_failure = 8, + err_nonblocking_failure = 9, + err_voice_terminating = 10, + err_connect_failure = 11, + err_ssl_context = 12, + err_ssl_version = 13, + err_invalid_socket = 14, + err_socket_error = 15, + err_websocket_proto_already_set = 16, + err_command_handler_not_ready = 17, + err_no_owning_message = 18, + err_cancelled_event = 19, + err_event_status = 20, + err_event_start_time = 21, + err_event_end_time = 22, + err_command_has_caps = 23, + err_choice_autocomplete = 24, + err_interaction = 25, + err_too_many_component_rows = 26, + err_invalid_webhook = 27, + err_voice_state_timestamp = 28, + err_no_voice_support = 29, + err_invalid_voice_packet_length = 30, + err_opus = 31, + err_etf = 33, + err_cache = 34, + err_icon_size = 35, + err_massive_audio = 36, + err_unknown = 37, + err_bad_request = 400, + err_unauthorized = 401, + err_payment_required = 402, + err_forbidden = 403, + err_not_found = 404, + err_method_not_allowed = 405, + err_not_acceptable = 406, + err_proxy_auth_required = 407, + err_request_timeout = 408, + err_conflict = 409, + err_gone = 410, + err_length_required = 411, + err_precondition_failed = 412, + err_payload_too_large = 413, + err_uri_too_long = 414, + err_unsupported_media_type = 415, + err_range_not_satisfiable = 416, + err_expectation_failed = 417, + err_im_a_teapot = 418, + err_page_expired = 419, + err_twitter_rate_limited = 420, + err_misdirected_request = 421, + err_unprocessable_content = 422, + err_webdav_locked = 423, + err_webdav_failed_dependency = 424, + err_too_early = 425, + err_upgrade_required = 426, + err_precondition_required = 428, + err_rate_limited = 429, + err_request_headers_too_large = 431, + err_page_blocked = 450, + err_unavailable_for_legal_reasons = 451, + err_http_request_on_https_port = 497, + err_internal_server_error = 500, + err_not_implemented = 501, + err_bad_gateway = 502, + err_service_unavailable = 503, + err_gateway_timeout = 504, + err_http_version_not_supported = 505, + err_variant_also_negotiates = 506, + err_webdav_insufficient_storage = 507, + err_webdav_loop_detected = 508, + err_bandwidth_limit_exceeded = 509, + err_not_extended = 510, + err_network_auth_required = 511, + err_web_server_down = 521, + err_connection_timed_out = 522, + err_origin_unreachable = 523, + err_timeout = 524, + err_ssl_handshake_failed = 525, + err_invalid_ssl_certificate = 526, + err_railgun = 527, + err_cloudflare = 530, + err_websocket_unknown = 4000, + err_websocket_bad_opcode= 4001, + err_websocket_decode = 4002, + err_websocket_not_authenticated = 4003, + err_websocket_authentication_failed = 4004, + err_websocket_already_authenticated = 4005, + err_websocket_invalid_seq_number = 4007, + err_websocket_rate_limited = 4008, + err_websocket_session_timeout = 4009, + err_websocket_invalid_shard = 4010, + err_websocket_sharding_required = 4011, + err_websocket_invalid_api_version = 4012, + err_websocket_invalid_intents = 4013, + err_websocket_disallowed_intents = 4014, + err_websocket_voice_disconnected = 4014, + err_websocket_voice_server_crashed = 4015, + err_websocket_voice_unknown_encryption = 4016, + err_compression_stream = 6000, + err_compression_data = 6001, + err_compression_memory = 6002, + err_unknown_account = 10001, + err_unknown_application = 10002, + err_unknown_channel = 10003, + err_unknown_guild = 10004, + err_unknown_integration = 10005, + err_unknown_invite = 10006, + err_unknown_member = 10007, + err_unknown_message = 10008, + err_unknown_permission_overwrite = 10009, + err_unknown_provider = 10010, + err_unknown_role = 10011, + err_unknown_token = 10012, + err_unknown_user = 10013, + err_unknown_emoji = 10014, + err_unknown_webhook = 10015, + err_unknown_webhook_service = 10016, + err_unknown_session = 10020, + err_unknown_ban = 10026, + err_unknown_sku = 10027, + err_unknown_store_listing = 10028, + err_unknown_entitlement = 10029, + err_unknown_build = 10030, + err_unknown_lobby = 10031, + err_unknown_branch = 10032, + err_unknown_store_directory_layout = 10033, + err_unknown_redistributable = 10036, + err_unknown_gift_code = 10038, + err_unknown_stream = 10049, + err_unknown_premium_server_subscribe_cooldown = 10050, + err_unknown_guild_template = 10057, + err_unknown_discoverable_server_category = 10059, + err_unknown_sticker = 10060, + err_unknown_interaction = 10062, + err_unknown_application_command = 10063, + err_unknown_voice_state = 10065, + err_unknown_application_command_permissions = 10066, + err_unknown_stage_instance = 10067, + err_unknown_guild_member_verification_form = 10068, + err_unknown_guild_welcome_screen = 10069, + err_unknown_guild_scheduled_event = 10070, + err_unknown_guild_scheduled_event_user = 10071, + err_unknown_tag = 10087, + err_bots_cannot_use_this_endpoint = 20001, + err_only_bots_can_use_this_endpoint = 20002, + err_explicit_content = 20009, + err_unauthorized_for_application = 20012, + err_slowmode_rate_limit = 20016, + err_owner_only = 20018, + err_announcement_rate_limit = 20022, + err_under_minimum_age = 20024, + err_write_rate_limit = 20029, + err_stage_banned_words = 20031, + err_guild_premium_subscription_level_too_low = 20035, + err_guilds = 30001, + err_friends = 30002, + err_pins_for_the_channel = 30003, + err_recipients = 30004, + err_guild_roles = 30005, + err_webhooks = 30007, + err_emojis = 30008, + err_reactions = 30010, + err_group_dms = 30011, + err_guild_channels = 30013, + err_attachments_in_a_message = 30015, + err_invites = 30016, + err_animated_emojis = 30018, + err_server_members = 30019, + err_server_categories = 30030, + err_guild_already_has_a_template = 30031, + err_application_commands = 30032, + err_thread_participants = 30033, + err_daily_application_command_creates = 30034, + err_bans_for_non_guild_members_have_been_exceeded = 30035, + err_bans_fetches = 30037, + err_uncompleted_guild_scheduled_events = 30038, + err_stickers = 30039, + err_prune_requests = 30040, + err_guild_widget_settings_updates = 30042, + err_edits_to_messages_older_than_1_hour = 30046, + err_pinned_threads_in_a_forum_channel = 30047, + err_tags_in_a_forum_channel = 30048, + err_bitrate_is_too_high_for_channel_of_this_type = 30052, + err_premium_emojis = 30056, + err_webhooks_per_guild = 30058, + err_channel_permission_overwrites = 30060, + err_the_channels_for_this_guild_are_too_large = 30061, + err_unauthorized_invalid_token = 40001, + err_verify_your_account = 40002, + err_you_are_opening_direct_messages_too_fast = 40003, + err_send_messages_has_been_temporarily_disabled = 40004, + err_request_entity_too_large = 40005, + err_this_feature_has_been_temporarily_disabled_server_side = 40006, + err_the_user_is_banned_from_this_guild = 40007, + err_connection_has_been_revoked = 40012, + err_target_user_is_not_connected_to_voice = 40032, + err_this_message_has_already_been_crossposted = 40033, + err_an_application_command_with_that_name_already_exists = 40041, + err_application_interaction_failed_to_send = 40043, + err_cannot_send_a_message_in_a_forum_channel = 40058, + err_interaction_has_already_been_acknowledged = 40060, + err_tag_names_must_be_unique = 40061, + err_service_resource_is_being_rate_limited = 40062, + err_no_tags_available = 40066, + err_tag_required = 40067, + err_entitlement_already_granted = 40074, + err_missing_access = 50001, + err_invalid_account_type = 50002, + err_cannot_execute_action_on_a_dm_channel = 50003, + err_guild_widget_disabled = 50004, + err_cannot_edit_a_message_by_other_user = 50005, + err_cannot_send_empty_message = 50006, + err_cannot_send_messages_to_this_user = 50007, + err_cannot_send_messages_in_a_non_text_channel = 50008, + err_channel_verification_level_too_high = 50009, + err_oauth2_application_does_not_have_a_bot = 50010, + err_oauth2_application_limit = 50011, + err_invalid_oauth2_state = 50012, + err_permissions = 50013, + err_invalid_authentication_token = 50014, + err_note_was_too_long = 50015, + err_too_few_or_too_many_messages = 50016, + err_invalid_mfa_level = 50017, + err_invalid_pin = 50019, + err_invite_code_invalid = 50020, + err_system_message = 50021, + err_channel_type = 50024, + err_invalid_oauth2_access_token = 50025, + err_missing_required_oauth2_scope = 50026, + err_invalid_webhook_token = 50027, + err_invalid_role = 50028, + err_invalid_recipients = 50033, + err_too_old_to_bulk_delete = 50034, + err_invalid_form_body = 50035, + err_invite_error = 50036, + err_invalid_activity_action = 50039, + err_invalid_api_version_provided = 50041, + err_file_uploaded_exceeds_the_maximum_size = 50045, + err_invalid_file_uploaded = 50046, + err_cannot_self_redeem_this_gift = 50054, + err_invalid_guild = 50055, + err_invalid_sku = 50057, + err_invalid_request_origin = 50067, + err_invalid_message_type = 50068, + err_payment_source_required = 50070, + err_cannot_modify_a_system_webhook = 50073, + err_cannot_delete_a_channel_required_for_community_guilds = 50074, + err_cannot_edit_stickers_within_a_message = 50080, + err_invalid_sticker_sent = 50081, + err_tried_to_perform_an_operation_on_an_archived_thread = 50083, + err_invalid_thread_notification_settings = 50084, + err_before_value_is_earlier_than_the_thread_creation_date = 50085, + err_community_server_channels_must_be_text_channels = 50086, + err_bad_event_entity_type = 50091, + err_this_server_is_not_available_in_your_location = 50095, + err_monetization_enabled_in_order_to_perform_this_action = 50097, + err_more_boosts_to_perform_this_action = 50101, + err_the_request_body_contains_invalid_json = 50109, + err_owner_cannot_be_pending_member = 50131, + err_ownership_cannot_be_transferred_to_a_bot_user = 50132, + err_failed_to_resize_asset_below_the_maximum_size = 50138, + err_cannot_mix_subscription_and_non_subscription_roles_for_an_emoji = 50144, + err_cannot_convert_between_premium_emoji_and_normal_emoji = 50145, + err_uploaded_file_not_found = 50146, + err_voice_messages_do_not_support_additional_content = 50159, + err_voice_messages_must_have_a_single_audio_attachment = 50160, + err_voice_messages_must_have_supporting_metadata = 50161, + err_voice_messages_cannot_be_edited = 50162, + err_cannot_delete_guild_subscription_integration = 50163, + err_you_cannot_send_voice_messages_in_this_channel = 50173, + err_the_user_account_must_first_be_verified = 50178, + err_you_do_not_have_permission_to_send_this_sticker = 50600, + err_two_factor_is_required_for_this_operation = 60003, + err_no_users_with_discordtag_exist = 80004, + err_reaction_was_blocked = 90001, + err_user_cannot_use_burst_reactions = 90002, + err_application_not_yet_available = 110001, + err_api_resource_is_currently_overloaded = 130000, + err_the_stage_is_already_open = 150006, + err_cannot_reply_without_permission_to_read_message_history = 160002, + err_a_thread_has_already_been_created_for_this_message = 160004, + err_thread_is_locked = 160005, + err_active_threads = 160006, + err_active_announcement_threads = 160007, + err_invalid_json_for_uploaded_lottie_file = 170001, + err_uploaded_lotties_cannot_contain_rasterized_images = 170002, + err_sticker_maximum_framerate = 170003, + err_sticker_frame_count = 170004, + err_lottie_animation_dimensions = 170005, + err_sticker_frame_rate = 170006, + err_sticker_animation_duration = 170007, + err_cannot_update_a_finished_event = 180000, + err_failed_to_create_stage_needed_for_stage_event = 180002, + err_message_was_blocked_by_automatic_moderation = 200000, + err_title_was_blocked_by_automatic_moderation = 200001, + err_webhooks_posted_to_forum_channels_must_have_a_thread_name_or_thread_id = 220001, + err_webhooks_posted_to_forum_channels_cannot_have_both_a_thread_name_and_thread_id = 220002, + err_webhooks_can_only_create_threads_in_forum_channels = 220003, + err_webhook_services_cannot_be_used_in_forum_channels = 220004, + err_message_blocked_links = 240000, + err_cannot_enable_onboarding_requirements_are_not_met = 350000, + err_cannot_update_onboarding_below_requirements = 350001, +}; + /** * @brief The dpp::exception class derives from std::exception and supports some other * ways of passing in error details such as via std::string. @@ -38,6 +391,11 @@ class exception : public std::exception */ std::string msg; + /** + * @brief Exception error code + */ + exception_error_code error_code; + public: using std::exception::exception; @@ -52,7 +410,15 @@ class exception : public std::exception * * @param what reason message */ - explicit exception(const char* what) : msg(what) { } + explicit exception(const char* what) : msg(what), error_code(err_no_code_specified) { } + + /** + * @brief Construct a new exception object + * + * @param what reason message + * @param code Exception code + */ + explicit exception(exception_error_code code, const char* what) : msg(what), error_code(code) { } /** * @brief Construct a new exception object @@ -60,14 +426,22 @@ class exception : public std::exception * @param what reason message * @param len length of reason message */ - exception(const char* what, size_t len) : msg(what, len) { } + exception(const char* what, size_t len) : msg(what, len), error_code(err_no_code_specified) { } /** * @brief Construct a new exception object * * @param what reason message */ - explicit exception(const std::string& what) : msg(what) { } + explicit exception(const std::string& what) : msg(what), error_code(err_no_code_specified) { } + + /** + * @brief Construct a new exception object + * + * @param what reason message + * @param code Exception code + */ + explicit exception(exception_error_code code, const std::string& what) : msg(what), error_code(code) { } /** * @brief Construct a new exception object @@ -76,6 +450,14 @@ class exception : public std::exception */ explicit exception(std::string&& what) : msg(std::move(what)) { } + /** + * @brief Construct a new exception object + * + * @param what reason message + * @param code Exception code + */ + explicit exception(exception_error_code code, std::string&& what) : msg(std::move(what)), error_code(code) { } + /** * @brief Construct a new exception object (copy constructor) */ @@ -112,6 +494,13 @@ class exception : public std::exception */ [[nodiscard]] const char* what() const noexcept override { return msg.c_str(); }; + /** + * @brief Get exception code + * + * @return exception_error_code error code + */ + [[nodiscard]] exception_error_code code() const noexcept { return error_code; }; + }; #ifndef _DOXYGEN_ @@ -120,15 +509,19 @@ class exception : public std::exception using dpp::ancestor::ancestor; \ name() = default; \ explicit name(const char* what) : ancestor(what) { } \ + explicit name(exception_error_code code, const char* what) : ancestor(code, what) { } \ name(const char* what, size_t len) : ancestor(what, len) { } \ explicit name(const std::string& what) : ancestor(what) { } \ + explicit name(exception_error_code code, const std::string& what) : ancestor(code, what) { } \ explicit name(std::string&& what) : ancestor(what) { } \ + explicit name(exception_error_code code, std::string&& what) : ancestor(code, what) { } \ name(const name&) = default; \ name(name&&) = default; \ ~name() override = default; \ name & operator = (const name &) = default; \ name & operator = (name&&) = default; \ [[nodiscard]] const char* what() const noexcept override { return msg.c_str(); }; \ + [[nodiscard]] exception_error_code code() const noexcept { return error_code; }; \ }; #endif @@ -181,21 +574,33 @@ class exception : public std::exception */ class cache_exception : public dpp::exception { }; /** - * @brief Represents an attempt to construct a cluster with an invalid bot token. + * @brief Represents an attempt to construct a cluster with an invalid bot token. * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. */ class invalid_token_exception : public dpp::rest_exception { }; +#ifdef DPP_CORO + /** + * @brief Represents the cancellation of a task. Will be thrown to the awaiter of a cancelled task. + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class task_cancelled_exception : public dpp::exception { }; +#endif /* DPP_CORO */ #else derived_exception(logic_exception, exception); derived_exception(file_exception, exception); derived_exception(connection_exception, exception); derived_exception(voice_exception, exception); + derived_exception(encryption_exception, voice_exception); + derived_exception(decryption_exception, voice_exception); derived_exception(rest_exception, exception); derived_exception(invalid_token_exception, rest_exception); derived_exception(length_exception, exception); derived_exception(parse_exception, exception); derived_exception(cache_exception, exception); +# ifdef DPP_CORO + derived_exception(task_cancelled_exception, exception); +# endif /* DPP_CORO */ #endif -}; +} diff --git a/3rdParty/dpp/export.h b/3rdParty/dpp/export.h index 5fc9ac465b..a83fd06752 100644 --- a/3rdParty/dpp/export.h +++ b/3rdParty/dpp/export.h @@ -59,8 +59,79 @@ #define DPP_EXPORT #endif -#ifndef _WIN32 - #define SOCKET int -#else +namespace dpp { + +/** + * @brief Represents a build configuration. On some platforms (e.g. Windows) release isn't compatible with debug, so we use this enum to detect it. + */ +enum class build_type { + /** + * @brief Universal build, works with both debug and release + */ + universal, + + /** + * @brief Debug build + */ + debug, + + /** + * @brief Release build + */ + release +}; + +template +extern bool DPP_EXPORT validate_configuration(); + +#if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_GAME) || defined(UE_EDITOR) || defined(UE_BUILD_SHIPPING_WITH_EDITOR) || defined(UE_BUILD_DOCS) + /* + * We need to tell DPP to NOT do the version checker if something from Unreal Engine is defined. + * We have to do this because UE is causing some weirdness where the version checker is broken and always errors. + * This is really only for DPP-UE. There is no reason to not do the version checker unless you are in Unreal Engine. + */ + #define DPP_BYPASS_VERSION_CHECKING +#endif /* UE */ + +#ifndef DPP_BUILD /* when including dpp */ + /** + * Version checking, making sure the program is in a configuration compatible with DPP's. + * + * Do NOT make these variables constexpr. + * We want them to initialize at runtime so the function can be pulled from the shared library object. + */ + #ifndef DPP_BYPASS_VERSION_CHECKING + #if defined(_WIN32) + #ifdef _DEBUG + inline const bool is_valid_config = validate_configuration(); + #else + inline const bool is_valid_config = validate_configuration(); + #endif /* _DEBUG */ + #else + inline const bool is_valid_config = validate_configuration(); + #endif /* _WIN32 */ + #endif /* !DPP_BYPASS_VERSION_CHECKING */ +#endif /* !DPP_BUILD */ + +} + +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include -#endif \ No newline at end of file +#endif + +#ifdef _DOXYGEN_ + /** @brief Macro that expands to [[deprecated(reason)]] when including the library, nothing when building the library */ + #define DPP_DEPRECATED(reason) +#else /* !_DOXYGEN_ */ + #if defined(DPP_BUILD) || defined(DPP_NO_DEPRECATED) + /** @brief Macro that expands to [[deprecated(reason)]] when including the library, nothing when building the library */ + #define DPP_DEPRECATED(reason) + #else + /** @brief Macro that expands to [[deprecated(reason)]] when including the library, nothing when building the library */ + #define DPP_DEPRECATED(reason) [[deprecated(reason)]] + #endif +#endif /* _DOXYGEN_ */ diff --git a/3rdParty/dpp/guild.h b/3rdParty/dpp/guild.h index 48f7e3fbd6..f131925e77 100644 --- a/3rdParty/dpp/guild.h +++ b/3rdParty/dpp/guild.h @@ -21,6 +21,7 @@ #pragma once #include #include +#include #include #include #include @@ -34,164 +35,444 @@ namespace dpp { class channel; +/* Note from Archie: I'd like to move this soon (dpp::guild::region) and allow users to use a region enum. + * This would make it easier for people to be able to alter a channel region without having to get the text right. + */ + /** * @brief Represents voice regions for guilds and channels. * @deprecated Deprecated in favour of per-channel regions. - * Please use channel::rtc_region instead. + * Please use dpp::channel::rtc_region instead. */ enum region : uint8_t { - r_brazil, //!< Brazil - r_central_europe, //!< Central Europe - r_hong_kong, //!< Hong Kong - r_india, //!< India - r_japan, //!< Japan - r_russia, //!< Russia - r_singapore, //!< Singapore - r_south_africa, //!< South Africa - r_sydney, //!< Sydney - r_us_central, //!< US Central - r_us_east, //!< US East Coast - r_us_south, //!< US South - r_us_west, //!< US West Coast - r_western_europe //!< Western Europe + /** + * @brief Brazil + */ + r_brazil, + + /** + * @brief Central Europe + */ + r_central_europe, + + /** + * @brief Hong Kong + */ + r_hong_kong, + + /** + * @brief India + */ + r_india, + + /** + * @brief Japan + */ + r_japan, + + /** + * @brief Russia + */ + r_russia, + + /** + * @brief Singapore + */ + r_singapore, + + /** + * @brief South Africa + */ + r_south_africa, + + /** + * @brief Sydney + */ + r_sydney, + + /** + * @brief US Central + */ + r_us_central, + + /** + * @brief US East Coast + */ + r_us_east, + + /** + * @brief US South + */ + r_us_south, + + /** + * @brief US West Coast + */ + r_us_west, + + /** + * @brief Western Europe + */ + r_western_europe }; /** * @brief The various flags that represent the status of a dpp::guild object */ enum guild_flags : uint32_t { - /** Large guild */ - g_large = 0b00000000000000000000000000000001, - /** Unavailable guild (inaccessible due to an outage) */ - g_unavailable = 0b00000000000000000000000000000010, - /** Guild has widget enabled */ - g_widget_enabled = 0b00000000000000000000000000000100, - /** Guild can have an invite splash image */ - g_invite_splash = 0b00000000000000000000000000001000, - /** Guild can have VIP regions */ - g_vip_regions = 0b00000000000000000000000000010000, - /** Guild can have a vanity url */ - g_vanity_url = 0b00000000000000000000000000100000, - /** Guild is verified */ - g_verified = 0b00000000000000000000000001000000, - /** Guild is partnered */ - g_partnered = 0b00000000000000000000000010000000, - /** Community features enabled */ - g_community = 0b00000000000000000000000100000000, - /** Guild has commerce features enabled + /** + * @brief Large guild + */ + g_large = 0b00000000000000000000000000000001, + + /** + * @brief Unavailable guild (inaccessible due to an outage) + */ + g_unavailable = 0b00000000000000000000000000000010, + + /** + * @brief Guild has widget enabled + */ + g_widget_enabled = 0b00000000000000000000000000000100, + + /** + * @brief Guild can have an invite splash image + */ + g_invite_splash = 0b00000000000000000000000000001000, + + /** + * @brief Guild can have VIP regions + */ + g_vip_regions = 0b00000000000000000000000000010000, + + /** + * @brief Guild can have a vanity url + */ + g_vanity_url = 0b00000000000000000000000000100000, + + /** + * @brief Guild is verified + */ + g_verified = 0b00000000000000000000000001000000, + + /** + * @brief Guild is partnered + */ + g_partnered = 0b00000000000000000000000010000000, + + /** + * @brief Community features enabled + */ + g_community = 0b00000000000000000000000100000000, + + /** + * @brief Guild has enabled role subscriptions + */ + g_role_subscription_enabled = 0b00000000000000000000001000000000, + + /** + * @brief Guild has access to create announcement channels + */ + g_news = 0b00000000000000000000010000000000, + + /** + * @brief Guild is discoverable in discovery + */ + g_discoverable = 0b00000000000000000000100000000000, + + /** + * @brief Guild is featureable + */ + g_featureable = 0b00000000000000000001000000000000, + + /** + * @brief Guild can have an animated icon (doesn't mean it actually has one though) + */ + g_animated_icon = 0b00000000000000000010000000000000, + + /** + * @brief Guild can have a banner image + */ + g_banner = 0b00000000000000000100000000000000, + + /** + * @brief Guild has a welcome screen + */ + g_welcome_screen_enabled = 0b00000000000000001000000000000000, + + /** + * @brief Guild has a member verification gate + */ + g_member_verification_gate = 0b00000000000000010000000000000000, + + /** + * @brief Guild has a preview + */ + g_preview_enabled = 0b00000000000000100000000000000000, + + /** + * @brief Guild join notifications are off + */ + g_no_join_notifications = 0b00000000000001000000000000000000, + + /** + * @brief Guild boost notifications are off + */ + g_no_boost_notifications = 0b00000000000010000000000000000000, + + /** + * @brief Guild has an actual animated icon (set by the icon hash starting with 'a_') + */ + g_has_animated_icon = 0b00000000000100000000000000000000, + + /** + * @brief Guild has an actual animated banner (set by the icon hash starting with 'a_') + */ + g_has_animated_banner = 0b00000000001000000000000000000000, + + /** + * @brief Guild setup tips are off + */ + g_no_setup_tips = 0b00000000010000000000000000000000, + + /** + * @brief "Wave to say hi" sticker prompt buttons are off + */ + g_no_sticker_greeting = 0b00000000100000000000000000000000, + + /** + * @brief Guild has enabled monetization + */ + g_monetization_enabled = 0b00000001000000000000000000000000, + + /** + * @brief Guild has increased custom sticker slots + */ + g_more_stickers = 0b00000010000000000000000000000000, + + /** + * @brief Guild has enabled the role subscription promo page + */ + g_creator_store_page_enabled = 0b00000100000000000000000000000000, + + /** + * @brief Guild is able to set role icons + */ + g_role_icons = 0b00001000000000000000000000000000, + + /** + * @brief Guild has access to the seven day archive time for threads + * @deprecated Removed by Discord + */ + g_seven_day_thread_archive = 0b00010000000000000000000000000000, + + /** + * @brief Guild has access to the three day archive time for threads * @deprecated Removed by Discord */ - g_commerce = 0b00000000000000000000001000000000, - /** Guild has access to create announcement channels */ - g_news = 0b00000000000000000000010000000000, - /** Guild is discoverable in discovery */ - g_discoverable = 0b00000000000000000000100000000000, - /** Guild is featureable */ - g_featureable = 0b00000000000000000001000000000000, - /** Guild can have an animated icon (doesn't mean it actually has one though) */ - g_animated_icon = 0b00000000000000000010000000000000, - /** Guild can have a banner image */ - g_banner = 0b00000000000000000100000000000000, - /** Guild has a welcome screen */ - g_welcome_screen_enabled = 0b00000000000000001000000000000000, - /** Guild has a member verification gate */ - g_member_verification_gate = 0b00000000000000010000000000000000, - /** Guild has a preview */ - g_preview_enabled = 0b00000000000000100000000000000000, - /** Guild join notifications are off */ - g_no_join_notifications = 0b00000000000001000000000000000000, - /** Guild boost notifications are off */ - g_no_boost_notifications = 0b00000000000010000000000000000000, - /** Guild has an actual animated icon (set by the icon hash starting with 'a_') */ - g_has_animated_icon = 0b00000000000100000000000000000000, - /** Guild has an actual animated banner (set by the icon hash starting with 'a_') */ - g_has_animated_banner = 0b00000000001000000000000000000000, - /** Guild setup tips are off */ - g_no_setup_tips = 0b00000000010000000000000000000000, - /** "Wave to say hi" sticker prompt buttons are off */ - g_no_sticker_greeting = 0b00000000100000000000000000000000, - /** guild has enabled monetization */ - g_monetization_enabled = 0b00000001000000000000000000000000, - /** guild has increased custom sticker slots */ - g_more_stickers = 0b00000010000000000000000000000000, - /** guild has access to create private threads + g_three_day_thread_archive = 0b00100000000000000000000000000000, + + /** + * @brief Guild has enabled ticketed events + */ + g_ticketed_events = 0b01000000000000000000000000000000, + + /** + * @brief Guild can have channel banners * @deprecated Removed by Discord - * */ - g_private_threads = 0b00000100000000000000000000000000, - /** guild is able to set role icons */ - g_role_icons = 0b00001000000000000000000000000000, - /** guild has access to the seven day archive time for threads */ - g_seven_day_thread_archive = 0b00010000000000000000000000000000, - /** guild has access to the three day archive time for threads */ - g_three_day_thread_archive = 0b00100000000000000000000000000000, - /** guild has enabled ticketed events */ - g_ticketed_events = 0b01000000000000000000000000000000, - /** guild can have channel banners */ - g_channel_banners = 0b10000000000000000000000000000000, + */ + g_channel_banners = 0b10000000000000000000000000000000, }; /** * @brief Additional boolean flag values for guild, as guild_flags is full */ -enum guild_flags_extra : uint8_t { - /** Guild has premium progress bar enabled */ - g_premium_progress_bar_enabled = 0b00000001, - /** Guild can have an animated banner (doesn't mean it actually has one though) */ - g_animated_banner = 0b00000010, - /** Guild has auto moderation */ - g_auto_moderation = 0b00000100, - /** Guild has paused invites, preventing new users from joining */ - g_invites_disabled = 0b00001000, - /** Guild has been set as support server of an app in the App Directory */ - g_developer_support_server = 0b00010000, +enum guild_flags_extra : uint16_t { + /** + * @brief Guild has premium progress bar enabled + */ + g_premium_progress_bar_enabled = 0b0000000000000001, + + /** + * @brief Guild can have an animated banner (doesn't mean it actually has one though) + */ + g_animated_banner = 0b0000000000000010, + + /** + * @brief Guild has auto moderation + */ + g_auto_moderation = 0b0000000000000100, + + /** + * @brief Guild has paused invites, preventing new users from joining + */ + g_invites_disabled = 0b0000000000001000, + + /** + * @brief Guild has been set as support server of an app in the App Directory + */ + g_developer_support_server = 0b0000000000010000, + + /** + * @brief Guild role subscription purchase and renewal notifications are off + */ + g_no_role_subscription_notifications = 0b0000000000100000, + + /** + * @brief Guild role subscription sticker reply buttons are off + */ + g_no_role_subscription_notification_replies = 0b0000000001000000, + + /** + * @brief Guild has role subscriptions that can be purchased + */ + g_role_subscriptions_available_for_purchase = 0b0000000010000000, + + /** + * @brief Guild has disabled alerts for join raids in the configured safety alerts channel + */ + g_raid_alerts_disabled = 0b0000000100000000, }; /** * @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. + * @note Use the setter functions in dpp::guild_member and do not toggle the bits yourself. */ -enum guild_member_flags : uint8_t { - /** Member deafened in voice channels */ - gm_deaf = 0b00000001, - /** Member muted in voice channels */ - gm_mute = 0b00000010, - /** Member pending verification by membership screening */ - gm_pending = 0b00000100, - /** Member has animated guild-specific avatar */ - gm_animated_avatar = 0b00001000, - /** gm_deaf or gm_mute has been toggled */ - gm_voice_action = 0b00010000, +enum guild_member_flags : uint16_t { + /** + * @brief Member deafened in voice channel + */ + gm_deaf = 0b0000000000000001, + + /** + * @brief Member muted in voice channels + */ + gm_mute = 0b0000000000000010, + + /** + * @brief Member pending verification by membership screening + */ + gm_pending = 0b0000000000000100, + + /** + * @brief Member has animated guild-specific avatar + */ + gm_animated_avatar = 0b0000000000001000, + + /** + * @brief gm_deaf or gm_mute has been toggled + */ + gm_voice_action = 0b0000000000010000, + + /** + * @brief Member has left and rejoined the guild + */ + gm_did_rejoin = 0b0000000000100000, + + /** + * @brief Member has completed onboarding + */ + gm_completed_onboarding = 0b0000000001000000, + + /** + * @brief Member is exempt from guild verification requirements + */ + gm_bypasses_verification = 0b0000000010000000, + + /** + * @brief Member has started onboarding + */ + gm_started_onboarding = 0b0000000100000000, + + /** + * @brief Should change roles on next call to edit guild_member? + * @warning This is set by dpp::guild_member::add_role, dpp::guild_member::remove_role, and dpp::guild_member::set_roles. + * You should only ever use this value if you know what you're doing, otherwise stick to the three functions listed. + */ + gm_roles_action = 0b0000001000000000, + + /** + * @brief Should nicknames on next call to edit guild_member? + * @warning This is set by dpp::guild_member::set_nickname. + * You should only ever use this value if you know what you're doing, otherwise stick to the function listed. + */ + gm_nickname_action = 0b0000010000000000, }; /** * @brief Represents dpp::user membership upon a dpp::guild. * This contains the user's nickname, guild roles, and any other guild-specific flags. */ -class DPP_EXPORT guild_member { -public: - /** Nickname, or empty string if they don't have a nickname on this guild */ +class DPP_EXPORT guild_member : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Build json for the member object + * + * @param with_id Add ID to output + * @return json JSON object + */ + json to_json_impl(bool with_id = false) const; + +protected: + /** + * @brief Nickname. + * + * @note Empty string if they don't have a nickname on this guild + */ std::string nickname; - /** List of roles this user has on this guild */ + + /** + * @brief List of roles this user has on this guild + */ std::vector roles; - /** Guild id */ + + /** + * @brief A set of flags built from the bitmask defined by dpp::guild_member_flags + */ + uint16_t flags; + + friend void from_json(const nlohmann::json& j, guild_member& gm); + +public: + /** + * @brief Guild id + */ snowflake guild_id; - /** User id */ + + /** + * @brief User id + */ snowflake user_id; - /** User avatar (per-server avatar is a nitro only feature) */ + + /** + * @brief User avatar. + * + * @note Per-server avatar is a nitro only feature. + */ utility::iconhash avatar; - /** timestamp of when the time out will be removed; until then, they cannot interact with the guild */ + + /** + * @brief Timestamp of when the time out will be removed; until then, they cannot interact with the guild + */ time_t communication_disabled_until; - /** Date and time the user joined the guild */ + + /** + * @brief Date and time the user joined the guild. + */ time_t joined_at; - /** Boosting since */ + + /** + * @brief The Date and time the user started boosting. + */ time_t premium_since; - /** A set of flags built from the bitmask defined by dpp::guild_member_flags */ - uint8_t flags; /** Default constructor */ guild_member(); - /** Fill this object from a json object. + /** + * @brief Fill this object from a json object. * @param j The json object to get data from * @param g_id The guild id to associate the member with * @param u_id The user id to associate the member with @@ -199,14 +480,6 @@ class DPP_EXPORT guild_member { */ guild_member& fill_from_json(nlohmann::json* j, snowflake g_id, snowflake u_id); - /** - * @brief Build json string for the member object - * - * @param with_id Add ID to output - * @return std::string json string - */ - std::string build_json(bool with_id = false) const; - /** * @brief Returns true if the user is in time-out (communication disabled) * @@ -239,6 +512,46 @@ class DPP_EXPORT guild_member { */ bool is_pending() const; + /** + * @brief Returns true if the user has left and rejoined the guild + * + * @return true user has left and rejoined the guild + * @return false user has not rejoined + */ + bool has_rejoined() const; + + /** + * @brief Is this user also the guild member? + * @return true if the user is the guild owner. + * @return false if the user is not the guild owner or the guild is not in the cache. + * @note If the guild cache is disabled, this function will always return false. + */ + bool is_guild_owner() const; + + /** + * @brief Returns true if the user has completed onboarding + * + * @return true user has completed onboarding + * @return false user has not completed onboarding + */ + bool has_completed_onboarding() const; + + /** + * @brief Returns true if the user has started onboarding + * + * @return true user has started onboarding + * @return false user has not started onboarding yet + */ + bool has_started_onboarding() const; + + /** + * @brief Returns true if the user is exempt from guild verification requirements + * + * @return true user bypasses verification + * @return false user doesn't bypass verification + */ + bool has_bypasses_verification() const; + /** * @brief Returns true if the user's per-guild custom avatar is animated * @@ -248,14 +561,19 @@ class DPP_EXPORT guild_member { bool has_animated_guild_avatar() const; /** - * @brief Returns the members per guild avatar if they have one, otherwise returns an empty string + * @brief Returns the member's per guild avatar url if they have one, otherwise returns an empty string. * - * @note per-server avatar is a nitro only feature so it might be not set. If you need the real user avatar, use user::get_avatar_url. + * @note per-server avatar is a nitro only feature so it might be not set. If you need the user avatar, use user::get_avatar_url. * - * @param size The size of the avatar in pixels. It can be any power of two between 16 and 4096. If not specified, the default sized avatar is returned. - * @return std::string avatar url or empty string + * @param size The size of the avatar in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized avatar is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string avatar url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_avatar_url(uint16_t size = 0) const; + std::string get_avatar_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; /** * @brief Set the nickname @@ -267,12 +585,41 @@ class DPP_EXPORT guild_member { guild_member& set_nickname(const std::string& nick); /** - * @brief Get the dpp::user object for this member - * @return dpp::user user object. If not in cache, it returns nullptr - * + * @brief Get the nickname + * + * @return std::string nickname + */ + std::string get_nickname() const; + + /** + * @brief Get the roles * + * @return std::vector roles + */ + const std::vector& get_roles() const; + + /** + * @brief Find the dpp::user object for this member. This is an alias for dpp::find_user + * @return dpp::user* Pointer to the user object. If not in cache, it returns nullptr */ - dpp::user* get_user() const; + user* get_user() const; + + /** + * @brief Check if this member is equal to another member object. + * @param other_member other member object to compare + * @return true if their user ids are equal, false otherwise. + */ + + bool operator == (guild_member const& other_member) const; + + /** + * @brief Set whether the user is exempt from guild verification requirements + * + * @param is_bypassing_verification value to set + * + * @return guild_member& reference to self + */ + guild_member& set_bypasses_verification(const bool is_bypassing_verification); /** * @brief Set whether the user is muted in voice channels @@ -307,31 +654,167 @@ class DPP_EXPORT guild_member { * @return std::string mention */ std::string get_mention() const; + + /** + * @brief Add a role to this member + * @note This call sets the role change bit, which causes the new role + * list to be sent if this is passed to dpp::cluster::guild_edit_member + * or dpp::cluster::guild_add_member + * + * @param role_id Role ID to add + * @return guild_member& Reference to self + */ + guild_member& add_role(dpp::snowflake role_id); + + /** + * @brief Remove a role from this member + * @note This call sets the role change bit, which causes the new role + * list to be sent if this is passed to dpp::cluster::guild_edit_member + * or dpp::cluster::guild_add_member + * + * @param role_id Role ID to remove + * @return guild_member& Reference to self + */ + guild_member& remove_role(dpp::snowflake role_id); + + /** + * @brief Set a new role list for this member + * @note This call sets the role change bit, which causes the new role + * list to be sent if this is passed to dpp::cluster::guild_edit_member + * or dpp::cluster::guild_add_member + * + * @param role_ids Roles to set + * @return guild_member& Reference to self + */ + guild_member& set_roles(const std::vector &role_ids); + }; /** * @brief Defines a channel on a server's welcome screen */ -struct welcome_channel_t { - /// the description shown for the channel +struct DPP_EXPORT welcome_channel : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + welcome_channel& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return json JSON data + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief The description shown for the channel. + */ std::string description; - /// the emoji name if custom, the unicode character if standard, or null if no emoji is set + + /** + * @brief The emoji name. + * + * @note name if custom, the unicode character if standard, or null if no emoji is set. + */ std::string emoji_name; - /// the channel's id - snowflake channel_id = 0; - /// the emoji id, if the emoji is custom - snowflake emoji_id = 0; + + /** + * @brief The channel's id. + */ + snowflake channel_id; + + /** + * @brief The emoji id, if the emoji is custom. + */ + snowflake emoji_id; + + /** + * @brief Construct a new welcome channel object + */ + welcome_channel(); + + /** + * @brief Destroy the welcome channel object + */ + virtual ~welcome_channel() = default; + + /** + * @brief Set the channel ID of this welcome channel object + * + * @param _channel_id The channel ID to set + * @return Reference to self, so these method calls may be chained + */ + welcome_channel& set_channel_id(const snowflake _channel_id); + + /** + * @brief Set the description of this welcome channel object + * + * @param _description The description to set + * @return Reference to self, so these method calls may be chained + */ + welcome_channel& set_description(const std::string& _description); }; /** * @brief Defines a server's welcome screen */ -struct welcome_screen_t { - /// the server description shown in the welcome screen +struct DPP_EXPORT welcome_screen : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + welcome_screen& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief The server description shown in the welcome screen + */ std::string description; - /// the channels shown in the welcome screen, up to 5 - std::vector welcome_channels; + + /** + * @brief The channels shown in the welcome screen (max 5) + */ + std::vector welcome_channels; + + /** + * @brief Construct a new welcome screen object + */ + welcome_screen() = default; + + /** + * @brief Destroy the welcome screen object + */ + virtual ~welcome_screen() = default; + + /** + * @brief Set the server description for this welcome screen object shown in the welcome screen + * + * @param s string The server description + * @return Reference to self, so these method calls may be chained + */ + welcome_screen& set_description(const std::string& s); }; /** @@ -340,14 +823,25 @@ struct welcome_screen_t { * @note This is set by Discord, and cannot be set by any bot or user on the guild. */ enum guild_nsfw_level_t : uint8_t { - /// Default setting, not configured - nsfw_default = 0, - /// Explicit content may be in this guild - nsfw_explicit = 1, - /// Safe for work content only - nsfw_safe = 2, - /// Age restricted, 18+ - nsfw_age_restricted = 3 + /** + * @brief Default setting, not configured + */ + nsfw_default = 0, + + /** + * @brief Explicit content may be in this guild + */ + nsfw_explicit = 1, + + /** + * @brief Safe for work content only + */ + nsfw_safe = 2, + + /** + * @brief Age restricted, 18+ + */ + nsfw_age_restricted = 3 }; /** @@ -356,22 +850,35 @@ enum guild_nsfw_level_t : uint8_t { * partnered, official etc. */ enum guild_explicit_content_t : uint8_t { - /// media content will not be scanned - expl_disabled = 0, - /// media content sent by members without roles will be scanned - expl_members_without_roles = 1, - /// media content sent by all members will be scanned - expl_all_members = 2 + /** + * @brief Media content will not be scanned. + */ + expl_disabled = 0, + + /** + * @brief Media content sent by members without roles will be scanned. + */ + expl_members_without_roles = 1, + + /** + * @brief Media content sent by all members will be scanned. + */ + expl_all_members = 2 }; /** - * @brief MFA level for server. If set to elevated all moderators need MFA to perform specific - * actions such as kick or ban. + * @brief MFA level for server. + * If set to elevated all moderators need MFA to perform specific actions such as kick or ban. */ enum mfa_level_t : uint8_t { - /// MFA not elevated + /** + * @brief MFA not elevated + */ mfa_none = 0, - /// MFA elevated + + /** + * @brief MFA elevated. + */ mfa_elevated = 1 }; @@ -379,73 +886,142 @@ enum mfa_level_t : uint8_t { * @brief Guild verification level */ enum verification_level_t : uint8_t { - /// unrestricted - ver_none = 0, - /// must have verified email on account - ver_low = 1, - /// must be registered on Discord for longer than 5 minutes - ver_medium = 2, - /// must be a member of the server for longer than 10 minutes - ver_high = 3, - /// must have a verified phone number + /** + * @brief Unrestricted. + */ + ver_none = 0, + + /** + * @brief Must have verified email on account. + */ + ver_low = 1, + + /** + * @brief Must be registered on Discord for longer than 5 minutes. + */ + ver_medium = 2, + + /** + * @brief Must be a member of the server for longer than 10 minutes. + */ + ver_high = 3, + + /** + * @brief Must have a verified phone number. + */ ver_very_high = 4, }; /** * @brief Default message notification level */ -enum default_message_notification_t: uint8_t { - /// members will receive notifications for all messages by default +enum default_message_notification_t : uint8_t { + /** + * @brief Members will receive notifications for all messages by default. + */ dmn_all = 0, - /// members will receive notifications only for messages that \@mention them by default + + /** + * @brief Members will receive notifications only for messages that \@mention them by default. + */ dmn_only_mentions = 1, }; /** - * @brief Premium tier + * @brief The guild's premium tier. */ -enum guild_premium_tier_t: uint8_t { - /// guild has not unlocked any Server Boost perks +enum guild_premium_tier_t : uint8_t { + /** + * @brief Guild has not unlocked any Server Boost perks. + */ tier_none = 0, - /// guild has unlocked Server Boost level 1 perks + + /** + * @brief Guild has unlocked Server Boost level 1 perks. + */ tier_1 = 1, - /// guild has unlocked Server Boost level 2 perks + + /** + * @brief Guild has unlocked Server Boost level 2 perks. + */ tier_2 = 2, - /// guild has unlocked Server Boost level 3 perks + + /** + * @brief Guild has unlocked Server Boost level 3 perks. + */ tier_3 = 3, }; /** * @brief Voice AFK timeout values for guild::afk_timeout */ -enum guild_afk_timeout_t: uint8_t { - /// AFK timeout disabled +enum guild_afk_timeout_t : uint8_t { + /** + * @brief AFK timeout disabled + */ afk_off, - /// AFK timeout of 1 Minute + + /** + * @brief AFK timeout of 1 Minute + */ afk_60, - /// AFK timeout of 5 Minutes + + /** + * @brief AFK timeout of 5 Minutes + */ afk_300, - /// AFK timeout of 15 Minutes + + /** + * @brief AFK timeout of 15 Minutes + */ afk_900, - /// AFK timeout of 30 Minutes + + /** + * @brief AFK timeout of 30 Minutes + */ afk_1800, - /// AFK timeout of 1 Hour + + /** + * @brief AFK timeout of 1 Hour + */ afk_3600, }; -/** @brief Guild members container +/** + * @brief Guild members container */ typedef std::unordered_map members_container; /** * @brief Represents a guild on Discord (AKA a server) */ -class DPP_EXPORT guild : public managed, public json_interface { +class DPP_EXPORT guild : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + guild& fill_from_json_impl(nlohmann::json* j); + + /** Build a JSON from this object. + * @param with_id True if an ID is to be included in the JSON + * @return JSON + */ + json to_json_impl(bool with_id = false) const; + public: - /** Guild name */ + using json_interface::fill_from_json; + + /** + * @brief Guild name + */ std::string name; - /** Server description */ + /** + * @brief Server description + */ std::string description; /** @@ -456,71 +1032,114 @@ class DPP_EXPORT guild : public managed, public json_interface { */ std::string vanity_url_code; - /** Roles defined on this server */ + /** + * @brief Roles defined on this server + */ std::vector roles; - /** List of channels on this server */ + /** + * @brief List of channels on this server + */ std::vector channels; - /** List of threads on this server */ + /** + * @brief List of threads on this server + */ std::vector threads; - /** List of emojis - */ + /** + * @brief List of emojis + */ std::vector emojis; - /** List of members in voice channels in the guild. + /** + * @brief List of members in voice channels in the guild. */ std::map voice_members; - /** List of guild members. Note that when you first receive the - * guild create event, this may be empty or near empty. - * This depends upon your dpp::intents and the size of your bot. + /** + * @brief List of guild members. + * + * @note When you first receive the guild create event, + * this may be empty or near empty. This depends upon your + * dpp::intents and the size of your bot. * It will be filled by guild member chunk requests. */ members_container members; - /** Welcome screen + /** + * @brief Welcome screen */ - welcome_screen_t welcome_screen; + dpp::welcome_screen welcome_screen; - /** Guild icon hash */ - utility::iconhash icon; + /** + * @brief Guild icon + */ + utility::icon icon; - /** Guild splash hash */ - utility::iconhash splash; + /** + * @brief Guild splash + */ + utility::icon splash; - /** Guild discovery splash hash */ - utility::iconhash discovery_splash; + /** + * @brief Guild discovery splash + */ + utility::icon discovery_splash; - /** Server banner hash */ - utility::iconhash banner; + /** + * @brief Server banner + */ + utility::icon banner; - /** Snowflake id of guild owner */ + /** + * @brief Snowflake id of guild owner + */ snowflake owner_id; - /** Snowflake ID of AFK voice channel or 0 */ + /** + * @brief Snowflake ID of AFK voice channel or 0 + */ snowflake afk_channel_id; - /** ID of creating application, if any, or 0 */ + /** + * @brief ID of creating application, if any, or 0 + */ snowflake application_id; - /** ID of system channel where discord update messages are sent */ + /** + * @brief ID of system channel where discord update messages are sent + */ snowflake system_channel_id; - /** ID of rules channel for communities */ + /** + * @brief ID of rules channel for communities + */ snowflake rules_channel_id; - /** Public updates channel id or 0 */ + /** + * @brief Public updates channel id or 0 + */ snowflake public_updates_channel_id; - /** Snowflake ID of widget channel, or 0 */ + /** + * @brief Snowflake ID of widget channel, or 0 + */ snowflake widget_channel_id; - /** Approximate member count. May be sent as zero */ + /** + * @brief The id of the channel where admins and moderators of Community guilds receive safety alerts from Discord + */ + snowflake safety_alerts_channel_id; + + /** + * @brief Approximate member count. May be sent as zero + */ uint32_t member_count; - /** Flags bitmask as defined by values within dpp::guild_flags */ + /** + * @brief Flags bitmask as defined by values within dpp::guild_flags + */ uint32_t flags; /** @@ -534,31 +1153,54 @@ class DPP_EXPORT guild : public managed, public json_interface { */ uint32_t max_members; - /** Shard ID of the guild */ + /** + * @brief Additional flags (values from dpp::guild_flags_extra) + */ + uint16_t flags_extra; + + /** + * @brief Shard ID of the guild + */ uint16_t shard_id; - /** Number of boosters */ + /** + * @brief Number of boosters + */ uint16_t premium_subscription_count; - /** Voice AFK timeout before moving users to AFK channel */ + /** + * @brief Voice AFK timeout before moving users to AFK channel + */ guild_afk_timeout_t afk_timeout; - /** Maximum users in a video channel, or 0 */ + /** + * @brief Maximum users in a video channel, or 0 + */ uint8_t max_video_channel_users; - /** Setting for how notifications are to be delivered to users */ + /** + * @brief Setting for how notifications are to be delivered to users + */ default_message_notification_t default_message_notifications; - /** Boost level */ + /** + * @brief Boost level + */ guild_premium_tier_t premium_tier; - /** Verification level of server */ + /** + * @brief Verification level of server + */ verification_level_t verification_level; - /** Whether or not explicit content filtering is enable and what setting it is */ + /** + * @brief Whether or not explicit content filtering is enable and what setting it is + */ guild_explicit_content_t explicit_content_filter; - /** If multi factor authentication is required for moderators or not */ + /** + * @brief If multi factor authentication is required for moderators or not + */ mfa_level_t mfa_level; /** @@ -566,11 +1208,6 @@ class DPP_EXPORT guild : public managed, public json_interface { */ guild_nsfw_level_t nsfw_level; - /** - * @brief Additional flags - */ - uint8_t flags_extra; - /** Default constructor, zeroes all values */ guild(); @@ -579,12 +1216,6 @@ class DPP_EXPORT guild : public managed, public json_interface { */ virtual ~guild() = default; - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - guild& fill_from_json(nlohmann::json* j); - /** Read class values from json object * @param shard originating shard * @param j A json object to read from @@ -592,12 +1223,6 @@ class DPP_EXPORT guild : public managed, public json_interface { */ guild& fill_from_json(class discord_client* shard, nlohmann::json* j); - /** Build a JSON string from this object. - * @param with_id True if an ID is to be included in the JSON - * @return JSON string - */ - std::string build_json(bool with_id = false) const; - /** * @brief Compute the base permissions for a member on this guild, * before channel overwrites are applied. @@ -606,7 +1231,7 @@ class DPP_EXPORT guild : public managed, public json_interface { * - Guild roles including \@everyone * * @param user User to get permissions for - * @return permission permissions bitmask + * @return permission permissions bitmask. If the member has administrator privileges, the bitmask returns with all flags set * @note Requires role cache to be enabled (it's enabled by default). * * @warning The method will search for the guild member in the cache by the users id. @@ -622,7 +1247,7 @@ class DPP_EXPORT guild : public managed, public json_interface { * - Guild roles including \@everyone * * @param member member to get permissions for - * @return permission permissions bitmask + * @return permission permissions bitmask. If the member has administrator privileges, the bitmask returns with all flags set * @note Requires role cache to be enabled (it's enabled by default). */ permission base_permissions(const guild_member &member) const; @@ -663,44 +1288,60 @@ class DPP_EXPORT guild : public managed, public json_interface { * @param user_id User id to join * @param self_mute True if the bot should mute itself * @param self_deaf True if the bot should deafen itself + * @param dave True to enable DAVE E2EE + * @warning DAVE is EXPERIMENTAL and subject to change. * @return True if the user specified is in a vc, false if they aren't * @note This is NOT a synchronous blocking call! The bot isn't instantly ready to send or listen for audio, * as we have to wait for the connection to the voice server to be established! * e.g. wait for dpp::cluster::on_voice_ready event, and then send the audio within that event. */ - bool connect_member_voice(snowflake user_id, bool self_mute = false, bool self_deaf = false); + bool connect_member_voice(snowflake user_id, bool self_mute = false, bool self_deaf = false, bool dave = false); /** * @brief Get the banner url of the guild if it have one, otherwise returns an empty string * - * @param size The size of the banner in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized banner is returned. - * @return std::string banner url or empty string + * @param size The size of the banner in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized banner is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * Passing `i_gif` might result in an invalid url for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string banner url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_banner_url(uint16_t size = 0) const; + std::string get_banner_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; /** * @brief Get the discovery splash url of the guild if it have one, otherwise returns an empty string * - * @param size The size of the discovery splash in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized discovery splash is returned. - * @return std::string discovery splash url or empty string + * @param size The size of the discovery splash in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized discovery splash is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string discovery splash url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_discovery_splash_url(uint16_t size = 0) const; + std::string get_discovery_splash_url(uint16_t size = 0, const image_type format = i_png) const; /** * @brief Get the icon url of the guild if it have one, otherwise returns an empty string * - * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized icon is returned. - * @return std::string icon url or empty string + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_icon_url(uint16_t size = 0) const; + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; /** * @brief Get the splash url of the guild if it have one, otherwise returns an empty string * - * @param size The size of the splash in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized splash is returned. - * @return std::string splash url or empty string + * @param size The size of the splash in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized splash is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string splash url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_splash_url(uint16_t size = 0) const; + std::string get_splash_url(uint16_t size = 0, const image_type format = i_png) const; /** * @brief Set the name of the guild in the object @@ -711,6 +1352,108 @@ class DPP_EXPORT guild : public managed, public json_interface { */ guild& set_name(const std::string& n); + /** + * @brief Remove the guild banner. + * @return guild& Reference to self for chaining + */ + guild& remove_banner(); + + /** + * @brief Set the guild banner image. Server needs banner feature. + * Must be 16:9, and depending on nitro level, must be png or jpeg. + * Animated gif needs the animated banner server feature. + * @param format Image format. + * @param data Image data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_banner(image_type format, std::string_view data); + + /** + * @brief Set the guild banner image. Server needs banner feature. + * Must be 16:9, and depending on nitro level, must be png or jpeg. + * Animated gif needs the animated banner server feature. + * @param format Image format. + * @param data Image data in bytes + * @param size Size of the data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_banner(image_type format, const std::byte* data, uint32_t size); + + /** + * @brief Remove the guild discovery splash. + * @return guild& Reference to self for chaining + */ + guild& remove_discovery_splash(); + + /** + * @brief Set the guild discovery splash image. Server needs discoverable feature. + * Must be 16:9 and png or jpeg. + * @param format Image format. + * @param data Image data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_discovery_splash(image_type format, std::string_view data); + + /** + * @brief Set the guild discovery splash image. Server needs discoverable feature. + * Must be 16:9 and png or jpeg. + * @param format Image format. + * @param data Image data in bytes + * @param size Size of the data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_discovery_splash(image_type format, const std::byte* data, uint32_t size); + + /** + * @brief Remove the guild invite splash. + * @return guild& Reference to self for chaining + */ + guild& remove_splash(); + + /** + * @brief Set the guild invite splash image. Server needs invite splash feature. + * Must be 16:9 and png or jpeg. + * @param format Image format. + * @param data Image data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_splash(image_type format, std::string_view data); + + /** + * @brief Set the guild invite splash image. Server needs invite splash feature. + * Must be 16:9 and png or jpeg. + * @param format Image format. + * @param data Image data in bytes + * @param size Size of the data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_splash(image_type format, const std::byte* data, uint32_t size); + + /** + * @brief Remove the guild icon. + * @return guild& Reference to self for chaining + */ + guild& remove_icon(); + + /** + * @brief Set the guild icon image. + * Must be 1024x1024 and png or jpeg. Gif allowed only if the server has animated icon. + * @param format Image format. + * @param data Image data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_icon(image_type format, std::string_view data); + + /** + * @brief Set the 1024x1024 guild icon image. + * Must be png or jpeg. Gif allowed only if the server has animated icon. + * @param format Image format. + * @param data Image data in bytes + * @param size Size of the data in bytes + * @return guild& Reference to self for chaining + */ + guild& set_icon(image_type format, const std::byte* data, uint32_t size); + /** * @brief Is a large server (>250 users) * @return bool is a large guild @@ -766,11 +1509,10 @@ class DPP_EXPORT guild : public managed, public json_interface { bool is_community() const; /** - * @brief Guild has access to use commerce features - * @return bool has commerce features enabled - * @deprecated Removed by Discord + * @brief Has enabled role subscriptions + * @return bool has enabled role subscriptions */ - bool has_commerce() const; + bool has_role_subscriptions() const; /** * @brief Guild has access to create announcement channels @@ -808,6 +1550,18 @@ class DPP_EXPORT guild : public managed, public json_interface { */ bool has_support_server() const; + /** + * @brief Guild has role subscriptions that can be purchased + * @return bool has role subscriptions that can be purchased + */ + bool has_role_subscriptions_available_for_purchase() const; + + /** + * @brief Guild has disabled alerts for join raids in the configured safety alerts channel + * @return bool dpp::g_raid_alerts_disabled flag is set + */ + bool has_raid_alerts_disabled() const; + /** * @brief Guild has access to set an animated guild icon * @return bool can have animated icon @@ -864,11 +1618,10 @@ class DPP_EXPORT guild : public managed, public json_interface { bool has_more_stickers() const; /** - * @brief guild has access to create private threads - * @return bool has private threads - * @deprecated Removed by Discord + * @brief guild has enabled the role subscription promo page + * @return bool has role subscription promo page enabled */ - bool has_private_threads() const; + bool has_creator_store_page() const; /** * @brief guild is able to set role icons @@ -879,12 +1632,14 @@ class DPP_EXPORT guild : public managed, public json_interface { /** * @brief guild has access to the seven day archive time for threads * @return bool has seven day thread archive + * @deprecated Removed by Discord */ bool has_seven_day_thread_archive() const; /** * @brief guild has access to the three day archive time for threads * @return bool has three day thread archive + * @deprecated Removed by Discord */ bool has_three_day_thread_archive() const; @@ -897,6 +1652,7 @@ class DPP_EXPORT guild : public managed, public json_interface { /** * @brief guild has access to channel banners feature * @return bool has channel banners + * @deprecated Removed by Discord */ bool has_channel_banners() const; @@ -913,13 +1669,34 @@ class DPP_EXPORT guild : public managed, public json_interface { bool has_invites_disabled() const; }; -/** A container of guilds */ +/** + * @brief A container of guilds + */ typedef std::unordered_map guild_map; /** * @brief Represents a guild widget, simple web widget of member list */ -class DPP_EXPORT guild_widget { +class DPP_EXPORT guild_widget : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Build a guild widget from json + * + * @param j json to build from + * @return guild_widget& reference to self + */ + guild_widget& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build json for a guild widget + * + * @param with_id Add ID to output + * @return std::string guild widget stringified json + */ + json to_json_impl(bool with_id = false) const; + public: /** * @brief Channel widget points to @@ -935,22 +1712,314 @@ class DPP_EXPORT guild_widget { * @brief Construct a new guild widget object */ guild_widget(); +}; +/** + * @brief The onboarding mode for the dpp::onboarding object. Defines the criteria used to satisfy Onboarding constraints that are required for enabling. + */ +enum onboarding_mode : uint8_t { /** - * @brief Build a guild widget from json + * @brief Counts only Default Channels towards constraints. + */ + gom_default = 0, + + /** + * @brief Counts Default Channels and Questions towards constraints. + */ + gom_advanced = 1, +}; + +/** + * @brief The various types of dpp::onboarding_prompt + */ +enum onboarding_prompt_type : uint8_t { + /** + * @brief Multiple choice. + */ + opt_multiple_choice = 0, + + /** + * @brief Dropdown. + */ + opt_dropdown = 1, +}; + +/** + * @brief Various flags for dpp::onboarding_prompt + */ +enum onboarding_prompt_flags : uint8_t { + /** + * @brief Indicates whether users are limited to selecting one option for the prompt. + */ + opf_single_select = 1 << 0, + + /** + * @brief Indicates whether the prompt is required before a user completes the onboarding flow. + */ + opf_required = 1 << 1, + + /** + * @brief Indicates whether the prompt is present in the onboarding flow. + * If set, the prompt will only appear in the Channels & Roles tab + */ + opf_in_onboarding = 1 << 2, +}; + +/** + * @brief Represents an onboarding prompt option + */ +struct DPP_EXPORT onboarding_prompt_option : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read class values from json object * - * @param j json to build from - * @return guild_widget& reference to self + * @param j A json object to read from + * @return A reference to self */ - guild_widget& fill_from_json(nlohmann::json* j); + onboarding_prompt_option& fill_from_json_impl(nlohmann::json* j); /** - * @brief Build json for a guild widget + * @brief Build the json for this object * - * @param with_id Add ID to output - * @return std::string guild widget stringified json + * @param with_id include the id in the JSON + * @return json data + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief IDs for channels a member is added to when the option is selected. + */ + std::vector channel_ids; + + /** + * @brief IDs for roles assigned to a member when the option is selected + */ + std::vector role_ids; + + /** + * @brief Emoji of the option + */ + dpp::emoji emoji; + + /** + * @brief Title of the option + */ + std::string title; + + /** + * @brief Description of the option + */ + std::string description; + + /** + * @brief Construct a new onboarding prompt option object + */ + onboarding_prompt_option(); + + /** + * @brief Destroy the onboarding prompt option object + */ + virtual ~onboarding_prompt_option() = default; + + /** + * @brief Set the emoji of this onboarding prompt option object + * + * @param _emoji The emoji to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_emoji(const dpp::emoji& _emoji); + + /** + * @brief Set the title of this onboarding prompt option object + * + * @param _title The title to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_title(const std::string& _title); + + /** + * @brief Set the description of this onboarding prompt option object + * + * @param _description The description to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_description(const std::string& _description); +}; + +/** + * @brief Represents an onboarding prompt + */ +struct DPP_EXPORT onboarding_prompt : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding_prompt& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return json data + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief Type of prompt (defaults to dpp::opt_multiple_choice). + */ + onboarding_prompt_type type; + + /** + * @brief Options available within the prompt + */ + std::vector options; + + /** + * @brief Title of the prompt. + */ + std::string title; + + /** + * @brief A set of flags built from the bitmask defined by dpp::onboarding_prompt_flags + */ + uint8_t flags; + + /** + * @brief Construct a new onboarding prompt object + */ + onboarding_prompt(); + + /** + * @brief Destroy the onboarding prompt object + */ + virtual ~onboarding_prompt() = default; + + /** + * @brief Set the type of this onboarding prompt object + * + * @param _type The prompt type to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt& set_type(const onboarding_prompt_type _type); + + /** + * @brief Set the title of this onboarding prompt object + * + * @param _title The title to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt& set_title(const std::string& _title); + + /** + * @brief Indicates whether users are limited to selecting one option for the prompt + * @return bool True if the users are limited to selecting one option for the prompt + */ + bool is_single_select() const; + + /** + * @brief Indicates whether the prompt is required before a user completes the onboarding flow + * @return bool True if the prompt is required before a user completes the onboarding flow + */ + bool is_required() const; + + /** + * @brief Indicates whether the prompt is present in the onboarding flow + * @return bool True if the prompt is present in the onboarding flow. False if the prompt will only appear in the Channels & Roles tab + */ + bool is_in_onboarding() const; +}; + +/** + * @brief Represents a guild's onboarding flow + */ +struct DPP_EXPORT onboarding : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return json data + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief ID of the guild this onboarding is part of. + */ + snowflake guild_id; + + /** + * @brief Prompts shown during onboarding and in customize community. + */ + std::vector prompts; + + /** + * @brief Channel IDs that members get opted into automatically + */ + std::vector default_channel_ids; + + /** + * @brief Current mode of onboarding (defaults to dpp::gom_default) + */ + onboarding_mode mode; + + /** + * @brief Whether onboarding is enabled in the guild + */ + bool enabled; + + /** + * @brief Construct a new onboarding object + */ + onboarding(); + + /** + * @brief Destroy the onboarding object + */ + virtual ~onboarding() = default; + + /** + * @brief Set guild_id of this onboarding object + * + * @param id Guild ID to set + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_guild_id(const snowflake id); + + /** + * @brief Set the mode of this onboarding object + * + * @param m onboarding_mode Mode to set + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_mode(const onboarding_mode m); + + /** + * @brief Set the enabling of this onboarding object + * + * @param is_enabled bool Whether onboarding is enabled in the guild + * @return Reference to self, so these method calls may be chained */ - std::string build_json(bool with_id = false) const; + onboarding& set_enabled(const bool is_enabled); }; /** @@ -963,7 +2032,9 @@ class DPP_EXPORT guild_widget { */ void from_json(const nlohmann::json& j, guild_member& gm); -/** A container of guild members */ +/** + * @brief A container of guild members + */ typedef std::unordered_map guild_member_map; /** @@ -977,4 +2048,4 @@ typedef std::unordered_map guild_member_map; */ guild_member DPP_EXPORT find_guild_member(const snowflake guild_id, const snowflake user_id); -}; +} diff --git a/3rdParty/dpp/httpsclient.h b/3rdParty/dpp/httpsclient.h index d0a51edf2c..e0f3a7171a 100644 --- a/3rdParty/dpp/httpsclient.h +++ b/3rdParty/dpp/httpsclient.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,12 +23,21 @@ #include #include #include +#include #include #include #include +#include +#include namespace dpp { +static inline const std::string http_version = "DiscordBot (https://github.com/brainboxdotcc/DPP, " + + to_hex(DPP_VERSION_MAJOR, false) + "." + + to_hex(DPP_VERSION_MINOR, false) + "." + + to_hex(DPP_VERSION_PATCH, false) + ")"; + +static inline constexpr const char* DISCORD_HOST = "https://discord.com"; /** * @brief HTTP connection status @@ -86,6 +96,7 @@ struct multipart_content { * @brief Multipart body */ std::string body; + /** * @brief MIME type */ @@ -101,14 +112,17 @@ struct http_connect_info { * @brief True if the connection should be SSL */ bool is_ssl; + /** * @brief The request scheme, e.g. 'https' or 'http' */ std::string scheme; + /** * @brief The request hostname part, e.g. 'discord.com' */ std::string hostname; + /** * @brief The port number, either determined from the scheme, * or from the part of the hostname after a colon ":" character @@ -120,8 +134,7 @@ struct http_connect_info { * @brief Implements a HTTPS socket client based on the SSL client. * @note plaintext HTTP without SSL is also supported via a "downgrade" setting */ -class DPP_EXPORT https_client : public ssl_client -{ +class DPP_EXPORT https_client : public ssl_client { /** * @brief Current connection state */ @@ -165,6 +178,11 @@ class DPP_EXPORT https_client : public ssl_client */ uint16_t status; + /** + * @brief The HTTP protocol to use + */ + std::string http_protocol; + /** * @brief Time at which the request should be abandoned */ @@ -189,7 +207,7 @@ class DPP_EXPORT https_client : public ssl_client * @brief Headers from the server's response, e.g. RateLimit * headers, cookies, etc. */ - std::map response_headers; + std::multimap response_headers; /** * @brief Handle input buffer @@ -200,7 +218,6 @@ class DPP_EXPORT https_client : public ssl_client bool do_buffer(std::string& buffer); protected: - /** * @brief Start the connection */ @@ -213,7 +230,11 @@ class DPP_EXPORT https_client : public ssl_client http_state get_state(); public: - + /** + * @brief If true the response timed out while waiting + */ + bool timed_out; + /** * @brief Connect to a specific HTTP(S) server and complete a request. * @@ -233,13 +254,14 @@ class DPP_EXPORT https_client : public ssl_client * @param extra_headers Additional request headers, e.g. user-agent, authorization, etc * @param plaintext_connection Set to true to make the connection plaintext (turns off SSL) * @param request_timeout How many seconds before the connection is considered failed if not finished + * @param protocol Request HTTP protocol (default: 1.1) */ - https_client(const std::string &hostname, uint16_t port = 443, const std::string &urlpath = "/", const std::string &verb = "GET", const std::string &req_body = "", const http_headers& extra_headers = {}, bool plaintext_connection = false, uint16_t request_timeout = 5); + https_client(const std::string &hostname, uint16_t port = 443, const std::string &urlpath = "/", const std::string &verb = "GET", const std::string &req_body = "", const http_headers& extra_headers = {}, bool plaintext_connection = false, uint16_t request_timeout = 5, const std::string &protocol = "1.1"); /** * @brief Destroy the https client object */ - virtual ~https_client(); + virtual ~https_client() = default; /** * @brief Build a multipart content from a set of files and some json @@ -247,9 +269,10 @@ class DPP_EXPORT https_client : public ssl_client * @param json The json content * @param filenames File names of files to send * @param contents Contents of each of the files to send + * @param mimetypes MIME types of each of the files to send * @return multipart mime content and headers */ - static multipart_content build_multipart(const std::string &json, const std::vector& filenames = {}, const std::vector& contents = {}); + static multipart_content build_multipart(const std::string &json, const std::vector& filenames = {}, const std::vector& contents = {}, const std::vector& mimetypes = {}); /** * @brief Processes incoming data from the SSL socket input buffer. @@ -272,16 +295,36 @@ class DPP_EXPORT https_client : public ssl_client * @brief Get a HTTP response header * * @param header_name Header name to find, case insensitive - * @return Header content or empty string if not found + * @return Header content or empty string if not found. + * If multiple values have the same header_name, this will return one of them. + * @see get_header_count to determine if multiple are present + * @see get_header_list to retrieve all entries of the same header_name */ const std::string get_header(std::string header_name) const; + /** + * @brief Get the number of headers with the same header name + * + * @param header_name + * @return the number of headers with this count + */ + size_t get_header_count(std::string header_name) const; + + + /** + * @brief Get a set of HTTP response headers with a common name + * + * @param header_name + * @return A list of headers with the same name, or an empty list if not found + */ + const std::list get_header_list(std::string header_name) const; + /** * @brief Get all HTTP response headers * * @return headers as a map */ - const std::map get_headers() const; + const std::multimap get_headers() const; /** * @brief Get the response content @@ -313,4 +356,4 @@ class DPP_EXPORT https_client : public ssl_client }; -}; \ No newline at end of file +} diff --git a/3rdParty/dpp/integration.h b/3rdParty/dpp/integration.h index 6205b36635..c6bda7ed94 100644 --- a/3rdParty/dpp/integration.h +++ b/3rdParty/dpp/integration.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,37 +23,83 @@ #include #include #include -#include +#include #include #include +#include namespace dpp { +/** + * @brief Where an app can be installed, also called its supported installation contexts. + */ +enum application_integration_types { + /** + * @brief Installable to servers + */ + ait_guild_install = 0, + /** + * @brief Installable to users + */ + ait_user_install = 1, +}; + /** * @brief Integration types */ enum integration_type { - /// Twitch integration + /** + * @brief Twitch integration + */ i_twitch, - /// YouTube integration + + /** + * @brief YouTube integration + */ i_youtube, - /// Discord integration - i_discord + + /** + * @brief Discord integration + */ + i_discord, + + /** + * @brief Subscription + */ + i_guild_subscription, }; /** * @brief Integration flags */ enum integration_flags { - /// Integration enabled - if_enabled = 0b00000001, - /// Integration syncing - if_syncing = 0b00000010, - /// Emoji integration - if_emoticons = 0b00000100, - /// Integration revoked - if_revoked = 0b00001000, - /// Kick users when their subscription expires + /** + * @brief Is this integration enabled? + */ + if_enabled = 0b00000001, + + /** + * @brief Is this integration syncing? + * @warning This is not provided for discord bot integrations. + */ + if_syncing = 0b00000010, + + /** + * @brief Whether emoticons should be synced for this integration (twitch only currently). + * @warning This is not provided for discord bot integrations. + */ + if_emoticons = 0b00000100, + + /** + * @brief Has this integration been revoked? + * @warning This is not provided for discord bot integrations. + */ + if_revoked = 0b00001000, + + /** + * @brief Kick user when their subscription expires, otherwise only remove the role that is specified by `role_id`. + * @warning This is not provided for discord bot integrations. + */ if_expire_kick = 0b00010000, }; @@ -60,112 +107,247 @@ enum integration_flags { * @brief An application that has been integrated */ struct DPP_EXPORT integration_app { - /// Integration id + /** + * @brief The id of the app. + */ snowflake id; - /// Name + + /** + * @brief The name of the app. + */ std::string name; - /// Icon - std::string icon; - /// Description + + /** + * @brief The icon hash of the app. + */ + utility::iconhash icon; + + /** + * @brief The description of the app + */ std::string description; - /// Integration summary @deprecated Removed by Discord - std::string summary; - /// Pointer to bot user + + /** + * @brief The bot associated with this application. + */ class user* bot; }; +/** + * @brief The account information for an integration. + */ +struct DPP_EXPORT integration_account { + /** + * @brief ID of the account + */ + snowflake id; + + /** + * @brief Name of the account. + */ + std::string name; +}; + /** * @brief Represents an integration on a guild, e.g. a connection to twitch. */ class DPP_EXPORT integration : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + integration& fill_from_json_impl(nlohmann::json* j); + + /** Build a json from this object. + * @param with_id Add ID to output + * @return JSON of the object + */ + virtual json to_json_impl(bool with_id = false) const; + public: - /** Integration name */ + /** + * @brief Integration name. + */ std::string name; - /** Integration type */ + + /** + * @brief Integration type (twitch, youtube, discord, or guild_subscription). + */ integration_type type; - /** Integration flags from dpp::integration_flags */ + + /** + * @brief Integration flags from dpp::integration_flags + */ uint8_t flags; - /** Role id */ + + /** + * @brief ID that this integration uses for "subscribers". + * + * @warning This is not provided for discord bot integrations. + */ snowflake role_id; - /** User id */ - snowflake user_id; - /** The grace period (in days) before expiring subscribers */ + + /** + * @brief The grace period (in days) before expiring subscribers. + * + * @warning This is not provided for discord bot integrations. + */ uint32_t expire_grace_period; - /** Sync time */ + + /** + * @brief User for this integration + */ + user user_obj; + + /** + * @brief Integration account information + */ + integration_account account; + + /** + * @brief When this integration was last synced. + * + * @warning This is not provided for discord bot integrations. + */ time_t synced_at; - /** Subscriber count */ + + /** + * @brief How many subscribers this integration has. + * + * @warning This is not provided for discord bot integrations. + */ uint32_t subscriber_count; - /** Account id */ - std::string account_id; - /** Account name */ - std::string account_name; - /** The bot/OAuth2 application for discord integrations */ + + /** + * @brief The bot/OAuth2 application for discord integrations. + */ integration_app app; + /** + * @brief The scopes the application has been authorized for. + */ + std::vector scopes; + /** Default constructor */ integration(); /** Default destructor */ - ~integration(); + ~integration() = default; - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self + /** + * Are emoticons enabled for this integration? + * @warning This is not provided for discord bot integrations. */ - integration& fill_from_json(nlohmann::json* j); + bool emoticons_enabled() const; - /** Build a json string from this object. - * @param with_id Add ID to output - * @return JSON string of the object + /** + * Is the integration enabled? + * @warning This is not provided for discord bot integrations. */ - virtual std::string build_json(bool with_id = false) const; - - /** True if emoticons are enabled */ - bool emoticons_enabled() const; - /** True if integration is enabled */ bool is_enabled() const; - /** True if is syncing */ + + /** + * Is the integration syncing? + * @warning This is not provided for discord bot integrations. + */ bool is_syncing() const; - /** True if has been revoked */ + + /** + * Has this integration been revoked? + * @warning This is not provided for discord bot integrations. + */ bool is_revoked() const; - /** True if expiring kicks the user */ + + /** + * Will the user be kicked if their subscription runs out to the integration? + * If false, the integration will simply remove the role that is specified by `role_id`. + * @warning This is not provided for discord bot integrations. + */ bool expiry_kicks_user() const; }; /** * @brief The connection object that the user has attached. */ -class DPP_EXPORT connection { +class DPP_EXPORT connection : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + connection& fill_from_json_impl(nlohmann::json* j); + public: - std::string id; //!< id of the connection account - std::string name; //!< the username of the connection account - std::string type; //!< the service of the connection (twitch, youtube) - bool revoked; //!< Optional: whether the connection is revoked - std::vector integrations; //!< Optional: an array of partial server integrations - bool verified; //!< whether the connection is verified - bool friend_sync; //!< whether friend sync is enabled for this connection - bool show_activity; //!< whether activities related to this connection will be shown in presence updates - bool two_way_link; //!< Whether this connection has a corresponding third party OAuth2 token - bool visible; //!< visibility of this connection + /** + * @brief ID of the connection account. + */ + std::string id; /** - * @brief Construct a new connection object + * @brief the username of the connection account. */ - connection(); + std::string name; - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self + /** + * @brief the service of the connection (twitch, youtube, discord, or guild_subscription). + */ + std::string type; + + /** + * @brief Optional: whether the connection is revoked. + */ + bool revoked; + + /** + * @brief Optional: an array of partial server integrations. + */ + std::vector integrations; + + /** + * @brief Whether the connection is verified. + */ + bool verified; + + /** + * @brief Whether friend sync is enabled for this connection. + */ + bool friend_sync; + + /** + * @brief Whether activities related to this connection will be shown in presence updates. + */ + bool show_activity; + + /** + * @brief Whether this connection has a corresponding third party OAuth2 token. + */ + bool two_way_link; + + /** + * @brief Visibility of this connection. */ - connection& fill_from_json(nlohmann::json* j); + bool visible; + /** + * @brief Construct a new connection object + */ + connection(); }; -/** A group of integrations */ +/** + * @brief A group of integrations + */ typedef std::unordered_map integration_map; -/** A group of connections */ +/** + * @brief A group of connections + */ typedef std::unordered_map connection_map; -}; +} diff --git a/3rdParty/dpp/intents.h b/3rdParty/dpp/intents.h index 3cb84fb693..45ba9542f7 100644 --- a/3rdParty/dpp/intents.h +++ b/3rdParty/dpp/intents.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -30,57 +31,125 @@ namespace dpp { * your real life ID. */ enum intents { - /// Intent for receipt of guild information + /** + * @brief Intent for receipt of guild information. + */ i_guilds = (1 << 0), - /// Intent for receipt of guild members + + /** + * @brief Intent for receipt of guild members. + */ i_guild_members = (1 << 1), - /// Intent for receipt of guild bans + + /** + * @brief Intent for receipt of guild bans. + */ i_guild_bans = (1 << 2), - /// Intent for receipt of guild emojis + + /** + * @brief Intent for receipt of guild emojis. + */ i_guild_emojis = (1 << 3), - /// Intent for receipt of guild integrations + + /** + * @brief Intent for receipt of guild integrations. + */ i_guild_integrations = (1 << 4), - /// Intent for receipt of guild webhooks + + /** + * @brief Intent for receipt of guild webhooks. + */ i_guild_webhooks = (1 << 5), - /// Intent for receipt of guild invites + + /** + * @brief Intent for receipt of guild invites. + */ i_guild_invites = (1 << 6), - /// Intent for receipt of guild voice states + + /** + * @brief Intent for receipt of guild voice states. + */ i_guild_voice_states = (1 << 7), - /// Intent for receipt of guild presences + + /** + * @brief Intent for receipt of guild presences. + */ i_guild_presences = (1 << 8), - /// Intent for receipt of guild messages + + /** + * @brief Intent for receipt of guild messages. + */ i_guild_messages = (1 << 9), - /// Intent for receipt of guild message reactions + + /** + * @brief Intent for receipt of guild message reactions. + */ i_guild_message_reactions = (1 << 10), - /// Intent for receipt of guild message typing notifications + + /** + * @brief Intent for receipt of guild message typing notifications. + */ i_guild_message_typing = (1 << 11), - /// Intent for receipt of direct messages (DMs) + + /** + * @brief Intent for receipt of direct messages (DMs). + */ i_direct_messages = (1 << 12), - /// Intent for receipt of direct message reactions + + /** + * @brief Intent for receipt of direct message reactions. + */ i_direct_message_reactions = (1 << 13), - /// Intent for receipt of direct message typing notifications + + /** + * @brief Intent for receipt of direct message typing notifications. + */ i_direct_message_typing = (1 << 14), - /// Intent for receipt of message content + + /** + * @brief Intent for receipt of message content. + */ i_message_content = (1 << 15), - /// Scheduled events + + /** + * @brief Scheduled events. + */ i_guild_scheduled_events = (1 << 16), - /// Auto moderation configuration + + /** + * @brief Auto moderation configuration. + */ i_auto_moderation_configuration = (1 << 20), - /// Auto moderation configuration + + /** + * @brief Auto moderation configuration. + */ i_auto_moderation_execution = (1 << 21), - /// Default D++ intents (all non-privileged intents) + + /** + * @brief 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 | - dpp::i_auto_moderation_execution, - /// Privileged intents requiring ID + dpp::i_auto_moderation_execution, + + /** + * @brief Privileged intents requiring ID. + */ i_privileged_intents = dpp::i_guild_members | dpp::i_guild_presences | dpp::i_message_content, - /// Every single intent + + /** + * @brief Every single intent (dpp::i_default_intents and dpp::i_privileged_intents). + */ i_all_intents = dpp::i_default_intents | dpp::i_privileged_intents, - /// Unverified bots default intents + + /** + * @brief Unverified bots default intents. + */ i_unverified_default_intents = dpp::i_default_intents | dpp::i_message_content }; -}; +} diff --git a/3rdParty/dpp/invite.h b/3rdParty/dpp/invite.h index 26ac50ddc1..ec275a6301 100644 --- a/3rdParty/dpp/invite.h +++ b/3rdParty/dpp/invite.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -21,90 +22,224 @@ #pragma once #include #include -#include +#include #include #include #include +#include +#include +#include namespace dpp { +/** + * @brief Invite target types for dpp::invite + */ +enum invite_target_t : uint8_t { + /** + * @brief Undefined invite target type. + */ + itt_none = 0, + + /** + * @brief Stream target type. + */ + itt_stream = 1, + + /** + * @brief Embedded Application target type. + */ + itt_embedded_application = 2, +}; + /** * @brief Represents an invite to a discord guild or channel */ class DPP_EXPORT invite : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + invite& fill_from_json_impl(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_id Include ID in JSON + * @return The JSON of the invite + */ + virtual json to_json_impl(bool with_id = false) const; + public: - /** Invite code + /** + * @brief Invite code. */ std::string code; - /** Readonly expiration timestamp of this invite or 0 if the invite doesn't expire + + /** + * @brief Readonly expiration timestamp of this invite or 0 if the invite doesn't expire. + * @note Only returned from cluster::invite_get */ time_t expires_at; - /** Guild for the invite + + /** + * @brief Guild ID this invite is for. */ snowflake guild_id; - /** Channel id for invite + + /** + * @brief The partial guild this invite is for. + * @note Only filled in retrieved invites. + */ + guild destination_guild; + + /** + * @brief Channel ID this invite is for. */ snowflake channel_id; - /** User ID of invite creator + + /** + * @brief The partial channel this invite is for. + * @note Only filled in retrieved invites. + */ + channel destination_channel; + + /** + * @brief User ID who created this invite. + * @deprecated Use the `inviter` field instead */ snowflake inviter_id; - /** Target user ID of invite, for invites sent via DM + + /** + * @brief User who created this invite. + */ + user inviter; + + /** + * @brief The user ID whose stream to display for this voice channel stream invite. */ snowflake target_user_id; - /** Target user type (generally this is always 1, "stream") + + /** + * @brief Target type for this voice channel invite. */ - uint8_t target_user_type; - /** Approximate number of online users + invite_target_t target_type; + + /** + * @brief Approximate number of online users. + * @note Only returned from cluster::invite_get */ uint32_t approximate_presence_count; - /** Approximate total users online and offline + + /** + * @brief Approximate number of total users online and offline. + * @note Only returned from cluster::invite_get. */ uint32_t approximate_member_count; - /** Maximum age (in seconds) of invite + + /** + * @brief Duration (in seconds) after which the invite expires, or 0 for no expiration. Defaults to 86400 (1 day). + * + * @note Must be between 0 and 604800 (7 days). */ uint32_t max_age; - /** Maximum number of uses + + /** + * @brief Maximum number of uses, or 0 for unlimited. Defaults to 0. + * + * @note Must be between 0 and 100. */ - uint32_t max_uses; - /** True if a temporary invite which grants access for a limited time + uint8_t max_uses; + + /** + * @brief Whether this invite only grants temporary membership. */ bool temporary; - /** True if this invite should not replace or "attach to" similar invites + + /** + * @brief True if this invite should not replace or "attach to" similar invites. */ bool unique; - /** How many times this invite has been used - * - * @note Only set when using cluster::channel_invites_get + + /** + * @brief How many times this invite has been used. */ uint32_t uses; - /** The stage instance data if there is a public stage instance in the stage channel this invite is for + + /** + * @note The stage instance data if there is a public stage instance in the stage channel this invite is for. * @deprecated Deprecated */ stage_instance stage; - /** Constructor + /** + * @brief Timestamp at which the invite was created. + */ + time_t created_at; + + /** + * @brief Constructor. */ invite(); - /** Destructor + /** + * @brief Destructor. */ virtual ~invite() = default; - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self + /** + * @brief Set the max age after which the invite expires + * + * @param max_age_ The duration in seconds, or 0 for no expiration. Must be between 0 and 604800 (7 days) + * @return invite& reference to self for chaining of calls */ - invite& fill_from_json(nlohmann::json* j); + invite& set_max_age(const uint32_t max_age_); - /** Build JSON from this object. - * @param with_id Include ID in JSON - * @return The JSON text of the invite + /** + * @brief Set the maximum number of uses for this invite + * + * @param max_uses_ Maximum number of uses, or 0 for unlimited. Must be between 0 and 100 + * @return invite& reference to self for chaining of calls + */ + invite& set_max_uses(const uint8_t max_uses_); + + /** + * @brief Set the target user id + * + * @param user_id The user ID whose stream to display for this voice channel stream invite + * @return invite& reference to self for chaining of calls + */ + invite& set_target_user_id(const snowflake user_id); + + /** + * @brief Set the target type for this voice channel invite + * + * @param type invite_target_t Target type + * @return invite& reference to self for chaining of calls */ - virtual std::string build_json(bool with_id = false) const; + invite& set_target_type(const invite_target_t type); + /** + * @brief Set temporary property of this invite object + * + * @param is_temporary Whether this invite only grants temporary membership + * @return invite& reference to self for chaining of calls + */ + invite& set_temporary(const bool is_temporary); + + /** + * @brief Set unique property of this invite object + * + * @param is_unique True if this invite should not replace or "attach to" similar invites + * @return invite& reference to self for chaining of calls + */ + invite& set_unique(const bool is_unique); }; -/** A container of invites */ +/** + * @brief A container of invites + */ typedef std::unordered_map invite_map; -}; +} diff --git a/3rdParty/dpp/isa/avx.h b/3rdParty/dpp/isa/avx.h new file mode 100644 index 0000000000..d771ed7a36 --- /dev/null +++ b/3rdParty/dpp/isa/avx.h @@ -0,0 +1,112 @@ +/************************************************************************************ + * + * 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 + +#if defined _MSC_VER || defined __GNUC__ || defined __clang__ + +#include +#include +#include +#include + +namespace dpp { + + using avx_float = __m128; + + /** + * @brief A class for audio mixing operations using AVX instructions. + */ + class audio_mixer { + public: + + /** + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 4 }; + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses AVX instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + avx_float current_samples_new{ _mm_mul_ps(gather_values(data_in), + _mm_add_ps(_mm_set1_ps(current_gain), _mm_mul_ps(_mm_set1_ps(increment), _mm_set_ps(0.0f, 1.0f, 2.0f, 3.0f)))) }; + + current_samples_new = _mm_blendv_ps(_mm_max_ps(current_samples_new, _mm_set1_ps(static_cast(std::numeric_limits::min()))), + _mm_min_ps(current_samples_new, _mm_set1_ps(static_cast(std::numeric_limits::max()))), + _mm_cmp_ps(current_samples_new, _mm_set1_ps(0.0f), _CMP_GE_OQ)); + + store_values(current_samples_new, data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses AVX instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + */ + inline void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + auto newValues{ _mm_add_ps(gather_values(up_sampled_vector), gather_values(decoded_data)) }; + store_values(newValues, up_sampled_vector); + } + + protected: + /** + * @brief Array for storing the values to be loaded/stored. + */ + alignas(16) float values[byte_blocks_per_register]{}; + + /** + * @brief Stores values from a 128-bit AVX vector to a storage location. + * @tparam value_type The target value type for storage. + * @param values_to_store The 128-bit AVX vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline void store_values(const avx_float& values_to_store, value_type* storage_location) { + _mm_store_ps(values, values_to_store); + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(values[x]); + } + } + + /** + * @brief Specialization for gathering non-float values into an AVX register. + * @tparam value_type The type of values being gathered. + * @tparam Indices Parameter pack of indices for gathering values. + * @return An AVX register containing gathered values. + */ + template inline avx_float gather_values(value_type* values_new) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + values[x] = static_cast(values_new[x]); + } + return _mm_load_ps(values); + } + }; + +} + +#endif \ No newline at end of file diff --git a/3rdParty/dpp/isa/avx2.h b/3rdParty/dpp/isa/avx2.h new file mode 100644 index 0000000000..23fe51bb9c --- /dev/null +++ b/3rdParty/dpp/isa/avx2.h @@ -0,0 +1,115 @@ +/************************************************************************************ + * + * 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 + +#if defined _MSC_VER || defined __GNUC__ || defined __clang__ + +#include +#include +#include +#include + +namespace dpp { + + using avx_2_float = __m256; + + /** + * @brief A class for audio mixing operations using AVX2 instructions. + */ + class audio_mixer { + public: + + /** + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 8 }; + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses AVX2 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + avx_2_float current_samples_new{ _mm256_mul_ps(gather_values(data_in), + _mm256_add_ps(_mm256_set1_ps(current_gain), + _mm256_mul_ps(_mm256_set1_ps(increment), _mm256_set_ps(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f)))) }; + + current_samples_new = + _mm256_blendv_ps(_mm256_max_ps(current_samples_new, _mm256_set1_ps(static_cast(std::numeric_limits::min()))), + _mm256_min_ps(current_samples_new, _mm256_set1_ps(static_cast(std::numeric_limits::max()))), + _mm256_cmp_ps(current_samples_new, _mm256_set1_ps(0.0f), _CMP_GE_OQ)); + + store_values(current_samples_new, data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses AVX2 instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + * @param x Index to select a specific set of elements to combine. + */ + inline void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + auto newValues{ _mm256_add_ps(gather_values(up_sampled_vector), gather_values(decoded_data)) }; + store_values(newValues, up_sampled_vector); + } + + protected: + /** + * @brief Array for storing the values to be loaded/stored. + */ + alignas(32) float values[byte_blocks_per_register]{}; + + /** + * @brief Stores values from a 256-bit AVX2 vector to a storage location. + * @tparam value_type The target value type for storage. + * @param values_to_store The 256-bit AVX2 vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline void store_values(const avx_2_float& values_to_store, value_type* storage_location) { + _mm256_store_ps(values, values_to_store); + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(values[x]); + } + } + + /** + * @brief Specialization for gathering non-float values into an AVX2 register. + * @tparam value_type The type of values being gathered. + * @tparam Indices Parameter pack of indices for gathering values. + * @return An AVX2 register containing gathered values. + */ + template inline avx_2_float gather_values(value_type* values_new) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + values[x] = static_cast(values_new[x]); + } + return _mm256_load_ps(values); + } + }; + +} + +#endif \ No newline at end of file diff --git a/3rdParty/dpp/isa/avx512.h b/3rdParty/dpp/isa/avx512.h new file mode 100644 index 0000000000..2d1ea1dcd6 --- /dev/null +++ b/3rdParty/dpp/isa/avx512.h @@ -0,0 +1,118 @@ +/************************************************************************************ + * + * 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 + +#if defined _MSC_VER || defined __GNUC__ || defined __clang__ + +#include +#include +#include +#include + +namespace dpp { + + using avx_512_float = __m512; + + /** + * @brief A class for audio mixing operations using AVX512 instructions. + */ + class audio_mixer { + public: + + /** + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 16 }; + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses AVX512 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + avx_512_float current_samples_new{ _mm512_mul_ps(gather_values(data_in), + _mm512_add_ps(_mm512_set1_ps(current_gain), + _mm512_mul_ps(_mm512_set1_ps(increment), + _mm512_set_ps(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f)))) }; + + __m512 lower_limit = _mm512_set1_ps(static_cast(std::numeric_limits::min())); + __m512 upper_limit = _mm512_set1_ps(static_cast(std::numeric_limits::max())); + + __mmask16 mask_ge = _mm512_cmp_ps_mask(current_samples_new, _mm512_set1_ps(0.0f), _CMP_GE_OQ); + + current_samples_new = _mm512_mask_max_ps(current_samples_new, mask_ge, current_samples_new, lower_limit); + current_samples_new = _mm512_mask_min_ps(current_samples_new, ~mask_ge, current_samples_new, upper_limit); + + store_values(current_samples_new, data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses AVX512 instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + */ + inline void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + auto newValues{ _mm512_add_ps(gather_values(up_sampled_vector), gather_values(decoded_data)) }; + store_values(newValues, up_sampled_vector); + } + + protected: + /** + * @brief Array for storing the values to be loaded/stored. + */ + alignas(64) float values[byte_blocks_per_register]{}; + + /** + * @brief Stores values from a 512-bit AVX512 vector to a storage location. + * @tparam value_type The target value type for storage. + * @param values_to_store The 512-bit AVX512 vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline static void store_values(const avx_512_float& values_to_store, value_type* storage_location) { + _mm256_store_ps(values, values_to_store); + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(values[x]); + } + } + + /** + * @brief Specialization for gathering non-float values into an AVX512 register. + * @tparam value_type The type of values being gathered. + * @tparam Indices Parameter pack of indices for gathering values. + * @return An AVX512 register containing gathered values. + */ + template inline avx_512_float gather_values(value_type* values_new) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + values[x] = static_cast(values_new[x]); + } + return _mm512_load_ps(values); + } + }; + +} + +#endif \ No newline at end of file diff --git a/3rdParty/dpp/isa/fallback.h b/3rdParty/dpp/isa/fallback.h new file mode 100644 index 0000000000..9ac63140f1 --- /dev/null +++ b/3rdParty/dpp/isa/fallback.h @@ -0,0 +1,79 @@ +/************************************************************************************ + * + * 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 + +namespace dpp { + + /** + * @brief A class for audio mixing operations using x64 instructions. + */ + class audio_mixer { + public: + /* + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 2 }; + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses x64 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline static void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + auto increment_new = increment * x; + auto current_gain_new = current_gain + increment_new; + auto current_sample_new = data_in[x] * current_gain_new; + if (current_sample_new >= std::numeric_limits::max()) { + current_sample_new = std::numeric_limits::max(); + } + else if (current_sample_new <= std::numeric_limits::min()) { + current_sample_new = std::numeric_limits::min(); + } + data_out[x] = static_cast(current_sample_new); + } + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses x64 instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + */ + inline static void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + up_sampled_vector[x] += static_cast(decoded_data[x]); + } + + } + }; + +} diff --git a/3rdParty/dpp/isa/neon.h b/3rdParty/dpp/isa/neon.h new file mode 100644 index 0000000000..655b388a88 --- /dev/null +++ b/3rdParty/dpp/isa/neon.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 + +#if defined _MSC_VER || defined __GNUC__ || defined __clang__ + +#include +#include +#include +#include + +namespace dpp { + + using neon_float = float32x4_t; + + /** + * @brief A class for audio mixing operations using ARM NEON instructions. + */ + class audio_mixer { + public: + + /** + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 4 }; + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses ARM NEON instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + neon_float gathered_values = gather_values(data_in); + neon_float gain_vector = vdupq_n_f32(current_gain); + static constexpr float data[4] = { 0.0f, 1.0f, 2.0f, 3.0f }; + neon_float floats = vld1q_f32(data); + neon_float increment_vector = vmulq_f32(vdupq_n_f32(increment), floats); + neon_float current_samples_new = vmulq_f32(gathered_values, vaddq_f32(gain_vector, increment_vector)); + + // Clamping the values between int16_t min and max + neon_float min_val = vdupq_n_f32(static_cast(std::numeric_limits::min())); + neon_float max_val = vdupq_n_f32(static_cast(std::numeric_limits::max())); + + current_samples_new = vmaxq_f32(current_samples_new, min_val); + current_samples_new = vminq_f32(current_samples_new, max_val); + + store_values(current_samples_new, data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses ARM NEON instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + */ + inline void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + neon_float up_sampled = gather_values(up_sampled_vector); + neon_float decoded = gather_values(decoded_data); + neon_float newValues = vaddq_f32(up_sampled, decoded); + store_values(newValues, up_sampled_vector); + } + + protected: + /** + * @brief Array for storing the values to be loaded/stored. + */ + alignas(16) float values[byte_blocks_per_register]{}; + + /** + * @brief Stores values from a 128-bit NEON vector to a storage location. + * @tparam value_type The target value type for storage. + * @param values_to_store The 128-bit NEON vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline void store_values(const neon_float& values_to_store, value_type* storage_location) { + vst1q_f32(values, values_to_store); + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(values[x]); + } + } + + /** + * @brief Specialization for gathering non-float values into a NEON register. + * @tparam value_type The type of values being gathered. + * @return A NEON register containing gathered values. + */ + template inline neon_float gather_values(value_type* values_new) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + values[x] = static_cast(values_new[x]); + } + return vld1q_f32(values); + } + }; + +} // namespace dpp + +#endif \ No newline at end of file diff --git a/3rdParty/dpp/isa_detection.h b/3rdParty/dpp/isa_detection.h new file mode 100644 index 0000000000..a3cecf365c --- /dev/null +++ b/3rdParty/dpp/isa_detection.h @@ -0,0 +1,33 @@ +/************************************************************************************ + * + * 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 + +#if AVX_TYPE == 1024 + #include "isa/neon.h" +#elif AVX_TYPE == 512 + #include "isa/avx512.h" +#elif AVX_TYPE == 2 + #include "isa/avx2.h" +#elif AVX_TYPE == 1 + #include "isa/avx.h" +#else + #include "isa/fallback.h" +#endif diff --git a/3rdParty/dpp/json.h b/3rdParty/dpp/json.h new file mode 100644 index 0000000000..3fa22d95e2 --- /dev/null +++ b/3rdParty/dpp/json.h @@ -0,0 +1,32 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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. + * + ************************************************************************************/ +#ifdef DPP_USE_EXTERNAL_JSON + #include +#else + #include +#endif + +namespace dpp { + +using json = nlohmann::json; + +} diff --git a/3rdParty/dpp/json_fwd.h b/3rdParty/dpp/json_fwd.h new file mode 100644 index 0000000000..9c1c89f8e9 --- /dev/null +++ b/3rdParty/dpp/json_fwd.h @@ -0,0 +1,32 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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. + * + ************************************************************************************/ +#ifdef DPP_USE_EXTERNAL_JSON + #include +#else + #include +#endif + +namespace dpp { + +using json = nlohmann::json; + +} diff --git a/3rdParty/dpp/json_interface.h b/3rdParty/dpp/json_interface.h index 5097b9b333..fa97b01806 100644 --- a/3rdParty/dpp/json_interface.h +++ b/3rdParty/dpp/json_interface.h @@ -21,41 +21,53 @@ #pragma once #include -#include -#include +#include namespace dpp { + +/** + * @brief Represents an interface for an object that can optionally implement functions + * for converting to and from nlohmann::json. The methods are only present if the actual object + * also has those methods. + * + * @tparam T Type of class that implements the interface + */ +template +struct json_interface { /** - * @brief Represents an interface for an object that can optionally implement functions - * for converting to and from nlohmann::json. In the event either parse_from_json() or - * build_json() are not implemented and are called, they will throw at runtime. - * - * @tparam T Type of class that implements the interface + * @brief Convert object from nlohmann::json + * + * @param j nlohmann::json object + * @return T& Reference to self for fluent calling */ - template struct DPP_EXPORT json_interface { - protected: - /* Must not destruct through pointer to json_interface. */ - ~json_interface() = default; + template ().fill_from_json_impl(std::declval()))> + T& fill_from_json(nlohmann::json* j) { + return static_cast(this)->fill_from_json_impl(j); + } - public: - /** - * @brief Convert object from nlohmann::json - * - * @param j nlohmann::json object - * @return T& Reference to self for fluent calling - */ - T& fill_from_json(nlohmann::json* j) { - throw dpp::logic_exception("JSON interface doesn't implement parse_from_json"); - } + /** + * @brief Convert object to nlohmann::json + * + * @param with_id Whether to include the ID or not + * @note Some fields are conditionally filled, do not rely on all fields being present + * @return json Json built from the structure + */ + template ().to_json_impl(bool{}))> + auto to_json(bool with_id = false) const { + return static_cast(this)->to_json_impl(with_id); + } - /** - * @brief Build JSON string from the object - * - * @param with_id Include the ID in the JSON - * @return std::string JSON string version of object - */ - virtual std::string build_json(bool with_id = false) const { - throw dpp::logic_exception("JSON interface doesn't implement build_json"); - } - }; + /** + * @brief Convert object to json string + * + * @param with_id Whether to include the ID or not + * @note Some fields are conditionally filled, do not rely on all fields being present + * @return std::string Json built from the structure + */ + template ().to_json_impl(bool{}))> + std::string build_json(bool with_id = false) const { + return to_json(with_id).dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); + } }; + +} diff --git a/3rdParty/dpp/managed.h b/3rdParty/dpp/managed.h index 4d4cb0fc3b..1e829ad39e 100644 --- a/3rdParty/dpp/managed.h +++ b/3rdParty/dpp/managed.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -25,52 +26,91 @@ namespace dpp { - /** @brief The managed class is the base class for various types that can - * be stored in a cache that are identified by a dpp::snowflake id. +/** @brief The managed class is the base class for various types that can + * be stored in a cache that are identified by a dpp::snowflake id. + */ +class DPP_EXPORT managed { +public: + /** + * @brief Unique ID of object set by Discord. + * This value contains a timestamp, worker ID, internal server ID, and an incrementing value. + * Only the timestamp is relevant to us as useful metadata. */ - class DPP_EXPORT managed { - public: - /** - * @brief Unique ID of object set by Discord. - * This value contains a timestamp, worker ID, internal server ID, and an incrementing value. - * Only the timestamp is relevant to us as useful metadata. - */ - snowflake id; - /** - * @brief Constructor, initialises ID - * @param nid ID to set - */ - managed(const snowflake nid = 0); - /** - * @brief Destroy the managed object - */ - virtual ~managed() = default; + snowflake id = {}; - /** - * @brief Get the creation time of this object according to Discord. - * - * @return double creation time inferred from the snowflake ID. - * The minimum possible value is the first second of 2015. - */ - double get_creation_time() const; + /** + * @brief Constructor, initialises id to 0. + */ + managed() = default; - /** - * @brief Comparison operator for comparing two managed objects by id - * - * @param other Other object to compare against - * @return true objects are the same id - * @return false objects are not the same id - */ - bool operator==(const managed& other) const noexcept; + /** + * @brief Constructor, initialises ID + * @param nid ID to set + */ + managed(const snowflake nid) : id{nid} {} - /** - * @brief Comparison operator for comparing two managed objects by id - * - * @param other Other object to compare against - * @return true objects are not the same id - * @return false objects are the same id - */ - bool operator!=(const managed& other) const noexcept; + /** + * @brief Copy constructor + * @param rhs Object to copy + */ + managed(const managed &rhs) = default; + + /** + * @brief Move constructor + * + * Effectively equivalent to copy constructor + * @param rhs Object to move from + */ + managed(managed &&rhs) = default; + + /** + * @brief Destroy the managed object + */ + virtual ~managed() = default; + + /** + * @brief Copy assignment operator + * @param rhs Object to copy + */ + managed &operator=(const managed& rhs) = default; + + /** + * @brief Move assignment operator + * @param rhs Object to move from + */ + managed &operator=(managed&& rhs) = default; + + /** + * @brief Get the creation time of this object according to Discord. + * + * @return double creation time inferred from the snowflake ID. + * The minimum possible value is the first second of 2015. + */ + constexpr double get_creation_time() const noexcept { + return id.get_creation_time(); }; + /** + * @brief Comparison operator for comparing two managed objects by id + * + * @param other Other object to compare against + * @return true objects are the same id + * @return false objects are not the same id + */ + constexpr bool operator==(const managed& other) const noexcept { + return id == other.id; + } + + /** + * @brief Comparison operator for comparing two managed objects by id + * + * @param other Other object to compare against + * @return true objects are not the same id + * @return false objects are the same id + */ + constexpr bool operator!=(const managed& other) const noexcept { + return id != other.id; + } }; + +} diff --git a/3rdParty/dpp/message.h b/3rdParty/dpp/message.h index 61dd046a3b..54241a65e6 100644 --- a/3rdParty/dpp/message.h +++ b/3rdParty/dpp/message.h @@ -24,10 +24,11 @@ #include #include #include +#include #include #include #include -#include +#include #include namespace dpp { @@ -36,31 +37,127 @@ namespace dpp { * @brief Represents the type of a component */ enum component_type : uint8_t { - /// Action row, a container for other components + /** + * @brief Action row, a container for other components. + */ cot_action_row = 1, - /// Clickable button + + /** + * @brief Clickable button. + */ cot_button = 2, - /// Select menu for picking from defined text options + + /** + * @brief Select menu for picking from defined text options. + */ cot_selectmenu = 3, - /// Text input + + /** + * @brief Text input. + */ cot_text = 4, - /// Select menu for users + + /** + * @brief Select menu for users. + */ cot_user_selectmenu = 5, - /// Select menu for roles + + /** + * @brief Select menu for roles. + */ cot_role_selectmenu = 6, - /// Select menu for mentionables (users and roles) + + /** + * @brief Select menu for mentionables (users and roles). + */ cot_mentionable_selectmenu = 7, - /// Select menu for channels + + /** + * @brief Select menu for channels. + */ cot_channel_selectmenu = 8, }; +/** + * @brief An emoji reference for a component (select menus included) or a poll. + * + * To set an emoji on your button or poll answer, you must set one of either the name or id fields. + * The easiest way for buttons is to use the dpp::component::set_emoji method. + * + * @note This is a **very** scaled down version of dpp::emoji, we advise that you refrain from using this. + */ +struct partial_emoji { + /** + * @brief The name of the emoji. + * + * For built in unicode emojis, set this to the + * actual unicode value of the emoji e.g. "😄" + * and not for example ":smile:" + */ + std::string name{}; + + /** + * @brief The emoji ID value for emojis that are custom + * ones belonging to a guild. + * + * The same rules apply as with other emojis, + * that the bot must be on the guild where the emoji resides + * and it must be available for use + * (e.g. not disabled due to lack of boosts, etc) + */ + dpp::snowflake id{0}; + + /** + * @brief Is the emoji animated? + * + * @note Only applies to custom emojis. + */ + bool animated{false}; +}; + +/** + * @brief An emoji for a component. Alias to partial_emoji, for backwards compatibility. + * + * @see partial_emoji + */ +using component_emoji = partial_emoji; + +/** + * @brief The data for a file attached to a message. + * + * @todo Change the naming of this and make stickers (and potentially anything else that has data like this) use this. + */ +struct message_file_data { + /** + * @brief Name of file to upload (for use server-side in discord's url). + */ + std::string name{}; + + /** + * @brief File content to upload (raw binary) + */ + std::string content{}; + + /** + * @brief Mime type of files to upload. + * + * @todo Look at turning this into an enum? This would allow people to easily compare mimetypes if they happen to change. + */ + std::string mimetype{}; +}; + /** * @brief Types of text input */ enum text_style_type : uint8_t { - /// Intended for short single-line text. + /** + * @brief Intended for short single-line text. + */ text_short = 1, - /// Intended for much longer inputs. + + /** + * @brief Intended for much longer inputs. + */ text_paragraph = 2, }; @@ -68,67 +165,103 @@ enum text_style_type : uint8_t { * @brief Represents the style of a button */ enum component_style : uint8_t { - /// Blurple + /** + * @brief Blurple; Primary + */ cos_primary = 1, - /// Grey + + /** + * @brief Grey; Secondary + */ cos_secondary, - /// Green + + /** + * @brief Green; Success + */ cos_success, - /// Red + + /** + * @brief Red; danger + */ cos_danger, - /// An external hyperlink to a website - cos_link + + /** + * @brief An external hyperlink to a website, requires url to be set + * @note Will not work unless url is set + */ + cos_link, + + /** + * @brief Premium upsell button, requires sku_id to be set + * @note Will not work unless sku is set + */ + cos_premium, +}; + +/** + * Represents the type of a dpp::component_default_value + * + * @note They're different to discord's value types + */ +enum component_default_value_type: uint8_t { + cdt_user = 0, + cdt_role = 1, + cdt_channel = 2, +}; + +/** + * @brief A Default value structure for components + */ +struct DPP_EXPORT component_default_value { + /** + * @brief The type this default value represents + */ + component_default_value_type type; + + /** + * @brief Default value. ID of a user, role, or channel + */ + dpp::snowflake id; }; /** * @brief An option for a select component */ struct DPP_EXPORT select_option : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + select_option& fill_from_json_impl(nlohmann::json* j); + +public: /** - * @brief Label for option + * @brief User-facing name of the option */ std::string label; + /** - * @brief Value for option + * @brief Dev-defined value of the option */ std::string value; + /** - * @brief Description of option + * @brief Additional description of the option */ std::string description; + /** * @brief True if option is the default option */ bool is_default; + /** - * @brief Emoji definition. To set an emoji on your button - * you must set one of either the name or id fields. - * The easiest way is to use the component::set_emoji - * method. + * @brief The emoji for the select option. */ - struct inner_select_emoji { - /** - * @brief Set the name field to the name of the emoji. - * For built in unicode emojis, set this to the - * actual unicode value of the emoji e.g. "😄" - * and not for example ":smile:" - */ - std::string name; - /** - * @brief The emoji ID value for emojis that are custom - * ones belonging to a guild. The same rules apply - * as with other emojis, that the bot must be on - * the guild where the emoji resides and it must - * be available for use (e.g. not disabled due to - * lack of boosts etc) - */ - dpp::snowflake id = 0; - /** - * @brief True if the emoji is animated. Only applies to - * custom emojis. - */ - bool animated = false; - } emoji; + partial_emoji emoji; /** * @brief Construct a new select option object @@ -147,7 +280,7 @@ struct DPP_EXPORT select_option : public json_interface { * @param value Value of option * @param description Description of option */ - select_option(const std::string &label, const std::string &value, const std::string &description = ""); + select_option(std::string_view label, std::string_view value, std::string_view description = ""); /** * @brief Set the label @@ -155,7 +288,7 @@ struct DPP_EXPORT select_option : public json_interface { * @param l the user-facing name of the option. It will be truncated to the maximum length of 100 UTF-8 characters. * @return select_option& reference to self for chaining */ - select_option& set_label(const std::string &l); + select_option& set_label(std::string_view l); /** * @brief Set the value @@ -163,7 +296,7 @@ struct DPP_EXPORT select_option : public json_interface { * @param v value to set. It will be truncated to the maximum length of 100 UTF-8 characters. * @return select_option& reference to self for chaining */ - select_option& set_value(const std::string &v); + select_option& set_value(std::string_view v); /** * @brief Set the description @@ -171,7 +304,7 @@ struct DPP_EXPORT select_option : public json_interface { * @param d description to set. It will be truncated to the maximum length of 100 UTF-8 characters. * @return select_option& reference to self for chaining */ - select_option& set_description(const std::string &d); + select_option& set_description(std::string_view d); /** * @brief Set the emoji @@ -181,7 +314,7 @@ struct DPP_EXPORT select_option : public json_interface { * @param animated true if animated emoji * @return select_option& reference to self for chaining */ - select_option& set_emoji(const std::string &n, dpp::snowflake id = 0, bool animated = false); + select_option& set_emoji(std::string_view n, dpp::snowflake id = 0, bool animated = false); /** * @brief Set the option as default @@ -198,12 +331,6 @@ struct DPP_EXPORT select_option : public json_interface { * @return select_option& reference to self for chaining */ select_option& set_animated(bool anim); - - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - select_option& fill_from_json(nlohmann::json* j); }; /** @@ -219,113 +346,137 @@ struct DPP_EXPORT select_option : public json_interface { * object is an action row and the child objects are buttons. */ class DPP_EXPORT component : public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + component& fill_from_json_impl(nlohmann::json* j); + public: - /** Component type, either a button or action row + /** + * @brief Component type, either a button or action row */ component_type type; - /** Sub components, buttons on an action row + /** + * @brief Sub components, buttons on an action row */ std::vector components; - /** Component label (for buttons, text inputs). + /** + * @brief Component label (for buttons, text inputs). * Maximum of 80 characters. */ std::string label; - /** Component style (for buttons) + /** + * @brief Component style (for buttons). */ component_style style; /** - * @brief Text style (for text inputs) + * @brief Text style (for text inputs). */ text_style_type text_style; - /** Component id (for buttons, menus, text inputs). + /** + * @brief Component id (for buttons, menus, text inputs). * Maximum of 100 characters. */ std::string custom_id; - /** URL for link types (dpp::cos_link). + /** + * @brief URL for link types (dpp::cos_link). * Maximum of 512 characters. */ std::string url; - /** Placeholder text for select menus and text inputs (max 150 characters) + /** + * @brief The SKU ID for premium upsell buttons + */ + dpp::snowflake sku_id; + + /** + * @brief Placeholder text for select menus and text inputs (max 150 characters) */ std::string placeholder; - /** Minimum number of items that must be chosen for a select menu. - * Default is -1 to not set this + /** + * @brief Minimum number of items that must be chosen for a select menu (0-25). + * + * @note Use -1 to not set this. This is done by default. */ int32_t min_values; - /** Maximum number of items that can be chosen for a select menu. - * Default is -1 to not set this + /** + * @brief Maximum number of items that can be chosen for a select menu (0-25). + * + * @note Use -1 to not set this. This is done by default. */ int32_t max_values; - /** Minimum length for text input (0-4000) + /** + * @brief Minimum length for text input (0-4000) */ int32_t min_length; - /** Maximum length for text input (1-4000) + /** + * @brief Maximum length for text input (1-4000) */ int32_t max_length; - /** Select options for select menus. Only required and available for select menus of type dpp::cot_selectmenu + /** + * @brief Select options for select menus. + * + * @warning Only required and available for select menus of type dpp::cot_selectmenu */ std::vector options; - /** List of channel types (dpp::channel_type) to include in the channel select component (dpp::cot_channel_selectmenu) + /** + * @brief List of channel types (dpp::channel_type) to include in the channel select component (dpp::cot_channel_selectmenu) */ std::vector channel_types; - /** Disabled flag (for buttons) + /** + * @brief List of default values for auto-populated select menu components. + * + * @note The amount of default values must be in the range defined by dpp::component::min_values and dpp::component::max_values. + * + * @warning Only available for auto-populated select menu components, which include dpp::cot_user_selectmenu, dpp::cot_role_selectmenu, dpp::cot_mentionable_selectmenu, and dpp::cot_channel_selectmenu components. + */ + std::vector default_values; + + /** + * @brief Disabled flag (for buttons) */ bool disabled; - /** Whether the text input is required to be filled + /** + * @brief Whether the text input is required to be filled */ bool required; - /** Value of the modal (filled or valid when populated from an - * on_form_submit event, or from the set_value function) + /** + * @brief Value of the modal. + * Filled or valid when populated from an on_form_submit event, or from the set_value function. */ std::variant value; - /** Emoji definition. To set an emoji on your button - * you must set one of either the name or id fields. - * The easiest way is to use the component::set_emoji - * method. + /** + * @brief The emoji for this component. */ - struct inner_emoji { - /** Set the name field to the name of the emoji. - * For built in unicode emojis, set this to the - * actual unicode value of the emoji e.g. "😄" - * and not for example ":smile:" - */ - std::string name; - /** The emoji ID value for emojis that are custom - * ones belonging to a guild. The same rules apply - * as with other emojis, that the bot must be on - * the guild where the emoji resides and it must - * be available for use (e.g. not disabled due to - * lack of boosts etc) - */ - dpp::snowflake id; - /** True if the emoji is animated. Only applies to - * custom emojis. - */ - bool animated; - } emoji; + partial_emoji emoji; - /** Constructor + /** + * @brief Constructor */ component(); - /** Destructor + /** + * @brief Destructor */ virtual ~component() = default; @@ -350,6 +501,17 @@ class DPP_EXPORT component : public json_interface { */ component& set_type(component_type ct); + /** + * @brief Set the SKU ID for a premium upsell button + * This is only valid for premium upsell buttons of type + * cos_premium. It indicates which premium package to + * link to when the button is clicked. + * + * @param sku The SKU ID + * @return component& reference to self + */ + component& set_sku_id(dpp::snowflake sku); + /** * @brief Set the text style of a text component * @note Sets type to `cot_text` @@ -367,7 +529,7 @@ class DPP_EXPORT component : public json_interface { * @param label Label text to set. It will be truncated to the maximum length of 80 UTF-8 characters. * @return component& Reference to self */ - component& set_label(const std::string &label); + component& set_label(std::string_view label); /** * @brief Set the default value of the text input component. @@ -377,7 +539,7 @@ class DPP_EXPORT component : public json_interface { * @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); + component& set_default_value(std::string_view val); /** * @brief Set the url for dpp::cos_link types. @@ -387,7 +549,7 @@ class DPP_EXPORT component : public json_interface { * @param url URL to set. It will be truncated to the maximum length of 512 UTF-8 characters. * @return component& reference to self. */ - component& set_url(const std::string &url); + component& set_url(std::string_view url); /** * @brief Set the style of the component, e.g. button colour. @@ -410,13 +572,13 @@ class DPP_EXPORT component : public json_interface { * If your Custom ID is longer than this, it will be truncated. * @return component& Reference to self */ - component& set_id(const std::string &id); + component& set_id(std::string_view id); /** * @brief Set the component to disabled. * Defaults to false on all created components. * - * @param disable True to disable, false to disable. + * @param disable True to disable the component, False to enable the component. * @return component& Reference to self */ component& set_disabled(bool disable); @@ -438,36 +600,36 @@ class DPP_EXPORT component : public json_interface { * characters for modals. * @return component& Reference to self */ - component& set_placeholder(const std::string &placeholder); + component& set_placeholder(std::string_view placeholder); /** - * @brief Set the min value + * @brief Set the minimum number of items that must be chosen for a select menu * - * @param min_values min value to set + * @param min_values min value to set (0-25) * @return component& Reference to self */ component& set_min_values(uint32_t min_values); /** - * @brief Set the max value + * @brief Set the maximum number of items that can be chosen for a select menu * - * @param max_values max value to set (0 - 25) + * @param max_values max value to set (0-25) * @return component& Reference to self */ component& set_max_values(uint32_t max_values); /** - * @brief Set the min length of text input + * @brief Set the minimum input length for a text input * - * @param min_l min value to set (0 - 25) + * @param min_l min length to set (0-4000) * @return component& Reference to self */ component& set_min_length(uint32_t min_l); /** - * @brief Set the max length of text input + * @brief Set the maximum input length for a text input * - * @param max_l max value to set + * @param max_l max length to set (1-4000) * @return component& Reference to self */ component& set_max_length(uint32_t max_l); @@ -490,11 +652,22 @@ class DPP_EXPORT component : public json_interface { */ component& add_component(const component& c); + /** + * @brief Add a default value. + * + * @param id Default value. ID of a user, role, or channel + * @param type The type this default value represents + */ + component& add_default_value(const snowflake id, const component_default_value_type type); + /** * @brief Set the emoji of the current sub-component. - * Only valid for buttons. Adding an emoji to a component - * will automatically set this components type to - * dpp::cot_button. One or both of name and id must be set. + * + * @warning Only valid for buttons. + * + * @note Adding an emoji to a component will + * automatically set this components type to + * dpp::cot_button. **One or both of name and id must be set**. * For a built in unicode emoji, you only need set name, * and should set it to a unicode character e.g. "😄". * For custom emojis, set the name to the name of the emoji @@ -507,57 +680,74 @@ class DPP_EXPORT component : public json_interface { * @param animated True if the custom emoji is animated. * @return component& Reference to self */ - component& set_emoji(const std::string& name, dpp::snowflake id = 0, bool animated = false); - - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self - */ - component& fill_from_json(nlohmann::json* j); - + component& set_emoji(std::string_view name, dpp::snowflake id = 0, bool animated = false); }; /** * @brief A footer in a dpp::embed */ struct DPP_EXPORT embed_footer { - /** Footer text */ + /** + * @brief Footer text + */ std::string text; - /** Footer icon url */ + + /** + * @brief Footer icon url. + * + * @warning Only supports http(s) and attachments. + */ std::string icon_url; - /** Proxied icon url */ + + /** + * @brief Proxied icon url. + */ std::string proxy_url; - /** Set footer's text. Returns footer itself so these methods may be "chained" + /** + * @brief Set footer's text. * @param t string to set as footer text. It will be truncated to the maximum length of 2048 UTF-8 characters. - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed_footer& set_text(const std::string& t); + embed_footer& set_text(std::string_view t); - /** Set footer's icon url. Returns footer itself so these methods may be "chained" + /** + * @brief Set footer's icon url. * @param i url to set as footer icon url - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed_footer& set_icon(const std::string& i); + embed_footer& set_icon(std::string_view i); - /** Set footer's proxied icon url. Returns footer itself so these methods may be "chained" + /** + * @brief Set footer's proxied icon url. * @param p url to set as footer proxied icon url - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed_footer& set_proxy(const std::string& p); + embed_footer& set_proxy(std::string_view p); }; /** * @brief An video, image or thumbnail in a dpp::embed */ struct DPP_EXPORT embed_image { - /** URL to image or video */ + /** + * @brief URL to image or video. + */ std::string url; - /** Proxied image url */ + + /** + * @brief Proxied image url. + */ std::string proxy_url; - /** Height (calculated by discord) */ + + /** + * @brief Height (calculated by discord). + */ std::string height; - /** Width (calculated by discord) */ + + /** + * @brief Width (calculated by discord). + */ std::string width; }; @@ -565,9 +755,14 @@ struct DPP_EXPORT embed_image { * @brief Embed provider in a dpp::embed. Received from discord but cannot be sent */ struct DPP_EXPORT embed_provider { - /** Provider name */ + /** + * @brief Provider name. + */ std::string name; - /** Provider URL */ + + /** + * @brief Provider URL. + */ std::string url; }; @@ -575,174 +770,300 @@ struct DPP_EXPORT embed_provider { * @brief Author within a dpp::embed object */ struct DPP_EXPORT embed_author { - /** Author name */ + /** + * @brief Author name. + */ std::string name; - /** Author url */ + + /** + * @brief Author url. + * + * @warning Only supports http(s). + */ std::string url; - /** Author icon url */ + + /** + * @brief Author icon url. + * + * @warning Only supports http(s) and attachments. + */ std::string icon_url; - /** Proxied icon url */ + + /** + * @brief Proxied icon url. + */ std::string proxy_icon_url; }; /** - * @brief A dpp::embed may contain zero or more fields + * @brief A dpp::embed may contain zero or more fields. */ struct DPP_EXPORT embed_field { - /** Name of field */ + /** + * @brief Name of field (max length 256). + */ std::string name; - /** Value of field (max length 1000) */ + + /** + * @brief Value of field (max length 1024). + */ std::string value; - /** True if the field is to be displayed inline */ + + /** + * @brief True if the field is to be displayed inline. + */ bool is_inline; }; /** - * @brief A rich embed for display within a dpp::message + * @brief A rich embed for display within a dpp::message. */ struct DPP_EXPORT embed { - /** Optional: title of embed */ - std::string title; - /** Optional: type of embed (always "rich" for webhook embeds) */ - std::string type; - /** Optional: description of embed */ - std::string description; - /** Optional: url of embed */ - std::string url; - /** Optional: timestamp of embed content */ - time_t timestamp; - /** Optional: color code of the embed */ - uint32_t color; - /** Optional: footer information */ - std::optional footer; - /** Optional: image information */ - std::optional image; - /** Optional: thumbnail information */ - std::optional thumbnail; - /** Optional: video information (can't send these) */ - std::optional video; - /** Optional: provider information (can't send these) */ - std::optional provider; - /** Optional: author information */ - std::optional author; - /** Optional: fields information */ - std::vector fields; - - /** Constructor */ - embed(); - - /** Constructor to build embed from json object - * @param j JSON to read content from + /** + * @brief Optional: Title of embed. */ - embed(nlohmann::json* j); - - /** Destructor */ - ~embed(); + std::string title; - /** Set embed title. Returns the embed itself so these method calls may be "chained" - * @param text The text of the title. It will be truncated to the maximum length of 256 UTF-8 characters. - * @return A reference to self + /** + * @brief Optional: Type of embed. + * + * @note Always "rich" for webhook embeds. */ - embed& set_title(const std::string &text); + std::string type; - /** Set embed description. Returns the embed itself so these method calls may be "chained" - * @param text The text of the title. It will be truncated to the maximum length of 4096 UTF-8 characters. - * @return A reference to self + /** + * @brief Optional: Description of embed. */ - embed& set_description(const std::string &text); + std::string description; - /** Set the footer of the embed. Returns the embed itself so these method calls may be "chained" - * @param f the footer to set - * @return A reference to self + /** + * @brief Optional: URL of embed. */ - embed& set_footer(const embed_footer& f); + std::string url; - /** Set the footer of the embed. Returns the embed itself so these method calls may be "chained" - * @param text string to set as footer text. It will be truncated to the maximum length of 2048 UTF-8 characters. - * @param icon_url an url to set as footer icon url - * @return A reference to self - */ - embed& set_footer(const std::string& text, const std::string& icon_url); + /** + * @brief Optional: Timestamp of embed content. + */ + time_t timestamp; - /** Set embed colour. Returns the embed itself so these method calls may be "chained" - * @param col The colour of the embed - * @return A reference to self + /** + * @brief Optional: Color code of the embed. */ - embed& set_color(uint32_t col); + std::optional color; - /** Set embed timestamp. Returns the embed itself so these method calls may be "chained" - * @param tstamp The timestamp to show in the footer, should be in UTC - * @return A reference to self + /** + * @brief Optional: Footer information. */ - embed& set_timestamp(time_t tstamp); + std::optional footer; - /** Set embed url. Returns the embed itself so these method calls may be "chained" + /** + * @brief Optional: Image information. + */ + std::optional image; + + /** + * @brief Optional: Thumbnail information. + */ + std::optional thumbnail; + + /** + * @brief Optional: Video information + * + * @warning Can't send this. + */ + std::optional video; + + /** + * @brief Optional: Provider information. + * + * @warning Can't send this. + */ + std::optional provider; + + /** + * @brief Optional: Author information. + */ + std::optional author; + + /** + * @brief Optional: Fields information. + */ + std::vector fields; + + /** + * @brief Constructor + */ + embed(); + + /** + * @brief Constructor to build embed from json object + * @param j JSON to read content from + */ + embed(nlohmann::json* j); + + /** + * @brief Destructor + */ + ~embed(); + + /** + * @brief Set embed title. + * @param text The text of the title. It will be truncated to the maximum length of 256 UTF-8 characters. + * @return A reference to self so this method may be "chained". + */ + embed& set_title(std::string_view text); + + /** + * @brief Set embed description. + * @param text The text of the title. It will be truncated to the maximum length of 4096 UTF-8 characters. + * @return A reference to self so this method may be "chained". + */ + embed& set_description(std::string_view text); + + /** + * @brief Set the footer of the embed. + * @param f the footer to set + * @return A reference to self so this method may be "chained". + */ + embed& set_footer(const embed_footer& f); + + /** + * @brief Set the footer of the embed. + * @param text string to set as footer text. It will be truncated to the maximum length of 2048 UTF-8 characters. + * @param icon_url an url to set as footer icon url (only supports http(s) and attachments) + * @return A reference to self so this method may be "chained". + */ + embed& set_footer(std::string_view text, std::string_view icon_url); + + /** + * @brief Set embed colour. + * @param col The colour of the embed + * @return A reference to self so this method may be "chained". + */ + embed& set_color(uint32_t col); + + /** + * @brief Set embed colour. + * @param col The colour of the embed + * @return A reference to self so this method may be "chained". + */ + embed& set_colour(uint32_t col); + + /** + * @brief Set embed timestamp. + * @param tstamp The timestamp to show in the footer, should be in UTC + * @return A reference to self so this method may be "chained". + */ + embed& set_timestamp(time_t tstamp); + + /** + * @brief Set embed url. * @param url the url of the embed - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed& set_url(const std::string &url); + embed& set_url(std::string_view url); - /** Add an embed field. Returns the embed itself so these method calls may be "chained" + /** + * @brief Add an embed field. * @param name The name of the field. It will be truncated to the maximum length of 256 UTF-8 characters. * @param value The value of the field. It will be truncated to the maximum length of 1024 UTF-8 characters. * @param is_inline Whether or not to display the field 'inline' or on its own line - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed& add_field(const std::string& name, const std::string &value, bool is_inline = false); + embed& add_field(std::string_view name, std::string_view value, bool is_inline = false); - /** Set embed author. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed author. * @param a The author to set - * @return A reference to self + * @return A reference to self so this method may be "chained". */ embed& set_author(const dpp::embed_author& a); - /** Set embed author. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed author. * @param name The name of the author. It will be truncated to the maximum length of 256 UTF-8 characters. - * @param url The url of the author - * @param icon_url The icon URL of the author - * @return A reference to self + * @param url The url of the author (only supports http(s)) + * @param icon_url The icon URL of the author (only supports http(s) and attachments) + * @return A reference to self so this method may be "chained". */ - embed& set_author(const std::string& name, const std::string& url, const std::string& icon_url); + embed& set_author(std::string_view name, std::string_view url, std::string_view icon_url); - /** Set embed provider. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed provider. * @param name The provider name. It will be truncated to the maximum length of 256 UTF-8 characters. * @param url The provider url - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed& set_provider(const std::string& name, const std::string& url); + embed& set_provider(std::string_view name, std::string_view url); - /** Set embed image. Returns the embed itself so these method calls may be "chained" - * @param url The embed image URL - * @return A reference to self + /** + * @brief Set embed image. + * @param url The embed image URL (only supports http(s) and attachments) + * @return A reference to self so this method may be "chained". */ - embed& set_image(const std::string& url); + embed& set_image(std::string_view url); - /** Set embed video. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed video. * @param url The embed video url - * @return A reference to self + * @return A reference to self so this method may be "chained". */ - embed& set_video(const std::string& url); + embed& set_video(std::string_view url); - /** Set embed thumbnail. Returns the embed itself so these method calls may be "chained" - * @param url The embed thumbnail url - * @return A reference to self + /** + * @brief Set embed thumbnail. + * @param url The embed thumbnail url (only supports http(s) and attachments) + * @return A reference to self so this method may be "chained". */ - embed& set_thumbnail(const std::string& url); + embed& set_thumbnail(std::string_view url); }; /** - * @brief Represents a reaction to a dpp::message + * @brief Represents a reaction to a dpp::message. */ struct DPP_EXPORT reaction { - /** Number of times this reaction has been added */ + /** + * @brief Total number of times this emoji has been used to react (including super reacts) + */ uint32_t count; - /** Reaction was from the bot's id */ - bool me; - /** ID of emoji for reaction */ + + /** + * @brief Count of super reactions + */ + uint32_t count_burst; + + /** + * @brief Count of normal reactions + */ + uint32_t count_normal; + + /** + * @brief ID of emoji for reaction + */ snowflake emoji_id; - /** Name of emoji, if applicable */ + + /** + * @brief Name of emoji, if applicable + */ std::string emoji_name; + /** + * @brief Whether your bot reacted using this emoji + */ + bool me; + + /** + * @brief Whether your bot super-reacted using this emoji + */ + bool me_burst; + + /** + * @brief HEX colors used for super reaction. + * + * @note Stored as integers. + */ + std::vector burst_colors; + /** * @brief Constructs a new reaction object. */ @@ -760,31 +1081,93 @@ struct DPP_EXPORT reaction { ~reaction() = default; }; +/** + * @brief Bitmask flags for a dpp::attachment + */ +enum attachment_flags : uint8_t { + /** + * @brief This attachment has been edited using the remix feature on mobile. + */ + a_is_remix = 1 << 2, +}; + /** * @brief Represents an attachment in a dpp::message */ struct DPP_EXPORT attachment { - /** ID of attachment */ + /** + * @brief ID of attachment. + */ snowflake id; - /** Size of the attachment in bytes */ + + /** + * @brief Size of the attachment in bytes. + */ uint32_t size; - /** File name of the attachment */ + + /** + * @brief File name of the attachment. + */ std::string filename; - /** Optional: Description of the attachment (max 1024 characters) */ + + /** + * @brief Optional: Description of the attachment. + * Max 1024 characters. + */ std::string description; - /** URL which points to the attachment */ + + /** + * @brief URL which points to the attachment. + */ std::string url; - /** Proxied URL which points to the attachment */ + + /** + * @brief Proxied URL which points to the attachment. + */ std::string proxy_url; - /** Width of the attachment, if applicable */ + + /** + * @brief Width of the attachment, if applicable. + */ uint32_t width; - /** Height of the attachment, if applicable */ + + /** + * @brief Height of the attachment, if applicable. + */ uint32_t height; - /** MIME type of the attachment, if applicable */ + + /** + * @brief MIME type of the attachment, if applicable. + */ std::string content_type; - /** Whether this attachment is ephemeral, if applicable */ + + /** + * @brief Whether this attachment is ephemeral, if applicable. + */ bool ephemeral; - /** Owning message */ + + /** + * @brief The duration of the audio file. + * + * @note Currently for voice messages. + */ + double duration_secs; + + /** + * @brief Base64 encoded bytearray representing a sampled waveform. + * + * @note Currently for voice messages. + */ + std::string waveform; + + /** + * @brief Flags made from dpp::attachment_flags. + */ + uint8_t flags; + + /** + * @brief Owning message + */ struct message* owner; /** @@ -807,6 +1190,7 @@ struct DPP_EXPORT attachment { /** * @brief Download this attachment + * * @param callback A callback which is called when the download completes. * @note The content of the file will be in the http_info.body parameter of the * callback parameter. @@ -814,15 +1198,41 @@ struct DPP_EXPORT attachment { * itself has an owning cluster, this method will throw a dpp::logic_exception when called. */ void download(http_completion_event callback) const; + + /** + * @brief Returns true if remixed + * + * @return true if remixed + */ + bool is_remix() const; + + /** + * @brief Returns expiration timestamp for attachment's URL + * + * @return timestamp of URL expiration + */ + time_t get_expire_time() const; + + /** + * @brief Returns creation timestamp for attachment's URL + * + * @return timestamp of URL creation + */ + time_t get_issued_time() const; }; /** * @brief Represents the type of a sticker */ enum sticker_type : uint8_t { - /// Nitro pack sticker + /** + * @brief An official sticker in a pack. + */ st_standard = 1, - /// Guild sticker + + /** + * @brief Guild sticker. + */ st_guild = 2 }; @@ -832,74 +1242,120 @@ enum sticker_type : uint8_t { enum sticker_format : uint8_t { sf_png = 1, sf_apng = 2, - sf_lottie = 3 + sf_lottie = 3, + sf_gif = 4, }; /** * @brief Represents stickers received in messages */ struct DPP_EXPORT sticker : public managed, public json_interface { - /** Optional: for standard stickers, id of the pack the sticker is from +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + sticker& fill_from_json_impl(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_id True if the ID is to be set in the JSON structure + * @return The JSON of the invite + */ + virtual json to_json_impl(bool with_id = true) const; + +public: + /** + * @brief Optional: for standard stickers, id of the pack the sticker is from + */ + snowflake pack_id; + + /** + * @brief The name of the sticker. + */ + std::string name; + + /** + * @brief Description of the sticker + * + * @note This may be empty. */ - snowflake pack_id; - /** The name of the sticker */ - std::string name; - /// description of the sticker (may be empty) - std::string description; - /** for guild stickers, the Discord name of a unicode emoji representing the sticker's expression. - * for standard stickers, a comma-separated list of related expressions. + std::string description; + + /** + * @brief The sticker's (or related) expressions. + * + * @note If it's a guild sticker, this will be the Discord name of + * a unicode emoji representing the sticker's expression. + * Otherwise, this will be a comma-separated list of related expressions. */ - std::string tags; + std::string tags; + /** * @brief Asset ID + * * @deprecated now an empty string but still sent by discord. - * While discord still send this empty string value we will still have a field - * here in the library. - */ - std::string asset; - /** The type of sticker */ - sticker_type type; - /// type of sticker format - sticker_format format_type; - /// Optional: whether this guild sticker can be used, may be false due to loss of Server Boosts - bool available; - /// Optional: id of the guild that owns this sticker - snowflake guild_id; - /// Optional: the user that uploaded the guild sticker - user sticker_user; - /// Optional: the standard sticker's sort order within its pack - uint8_t sort_value; - /** Name of file to upload (when adding or editing a sticker) */ - std::string filename; - /** File content to upload (raw binary) */ - std::string filecontent; + * While discord still send this empty string value, + * we will still have this field here in the library. + */ + std::string asset; /** - * @brief Construct a new sticker object + * @brief The type of sticker. */ - sticker(); + sticker_type type; - virtual ~sticker() = default; + /** + * @brief type of sticker format. + */ + sticker_format format_type; - /** Read class values from json object - * @param j A json object to read from - * @return A reference to self + /** + * @brief Optional: Whether this guild sticker can be used. + * + * @note May be false due to loss of Server Boosts. */ - sticker& fill_from_json(nlohmann::json* j); + bool available; - /** Build JSON from this object. - * @param with_id True if the ID is to be set in the JSON structure - * @return The JSON text of the invite + /** + * @brief Optional: ID of the guild that owns this sticker. + */ + snowflake guild_id; + + /** + * @brief Optional: The user that uploaded the guild sticker. + */ + user sticker_user; + + /** + * @brief Optional: The standard sticker's sort order within its pack. */ - virtual std::string build_json(bool with_id = true) const; + uint8_t sort_value; /** - * @brief Get the sticker url + * @brief Name of file to upload (when adding or editing a sticker). + */ + std::string filename; + + /** + * @brief File content to upload (raw binary). + */ + std::string filecontent; + + /** + * @brief Construct a new sticker object + */ + sticker(); + + virtual ~sticker() = default; + + /** + * @brief Get the sticker url. * - * @param accept_lottie Whether to allow that [lottie](https://airbnb.io/lottie/#/) (json format) can be returned or not - * @return std::string The sticker url or an empty string when its a lottie and accept_lottie is false + * @return std::string The sticker url or an empty string, if the id is empty */ - std::string get_url(bool accept_lottie = true) const; + std::string get_url() const; /** * @brief Set the filename @@ -907,15 +1363,15 @@ struct DPP_EXPORT sticker : public managed, public json_interface { * @param fn filename * @return message& reference to self */ - sticker& set_filename(const std::string &fn); + sticker& set_filename(std::string_view fn); /** * @brief Set the file content - * - * @param fc raw file content contained in std::string + * + * @param fc raw file content * @return message& reference to self */ - sticker& set_file_content(const std::string &fc); + sticker& set_file_content(std::string_view fc); }; @@ -923,152 +1379,551 @@ struct DPP_EXPORT sticker : public managed, public json_interface { * @brief Represents a sticker pack (the built in groups of stickers that all nitro users get to use) */ struct DPP_EXPORT sticker_pack : public managed, public json_interface { - /// the stickers in the pack - std::map stickers; - /// name of the sticker pack - std::string name; - /// id of the pack's SKU - snowflake sku_id; - /// Optional: id of a sticker in the pack which is shown as the pack's icon - snowflake cover_sticker_id; - /// description of the sticker pack - std::string description; - /// id of the sticker pack's banner image - snowflake banner_asset_id; - - /** - * @brief Construct a new sticker pack object - */ - sticker_pack(); - - virtual ~sticker_pack() = default; +protected: + friend struct json_interface; /** Read class values from json object * @param j A json object to read from * @return A reference to self */ - sticker_pack& fill_from_json(nlohmann::json* j); + sticker_pack& fill_from_json_impl(nlohmann::json* j); /** Build JSON from this object. * @param with_id True if the ID is to be set in the JSON structure - * @return The JSON text of the invite + * @return The JSON of the invite + */ + virtual json to_json_impl(bool with_id = true) const; + +public: + /** + * @brief The stickers in the pack. + */ + std::map stickers{}; + + /** + * @brief Name of the sticker pack. + */ + std::string name{}; + + /** + * @brief ID of the pack's SKU. + */ + snowflake sku_id{0}; + + /** + * @brief Optional: ID of a sticker in the pack which is shown as the pack's icon. + */ + snowflake cover_sticker_id{0}; + + /** + * @brief Description of the sticker pack. + */ + std::string description{}; + + /** + * @brief ID of the sticker pack's banner image. + */ + snowflake banner_asset_id{}; +}; + +/** + * @brief Poll layout types + * + * @note At the time of writing Discord only has 1, "The, uhm, default layout type." + * @see https://discord.com/developers/docs/resources/poll#layout-type + */ +enum poll_layout_type { + /** + * @brief According to Discord, quote, "The, uhm, default layout type." + */ + pl_default = 1 +}; + +/** + * @brief Structure representing a poll media, for example the poll question or a possible poll answer. + * + * @see https://discord.com/developers/docs/resources/poll#poll-media-object-poll-media-object-structure + */ +struct poll_media { + /** + * @brief Text of the media + */ + std::string text{}; + + /** + * @brief Emoji of the media. + */ + partial_emoji emoji{}; +}; + +/** + * @brief Represents an answer in a poll. + * + * @see https://discord.com/developers/docs/resources/poll#poll-answer-object-poll-answer-object-structure + */ +struct poll_answer { + /** + * @brief ID of the answer. Only sent by the Discord API, this is a dead field when creating a poll. + * + * @warn At the time of writing the Discord API warns users not to rely on anything regarding sequence or "first value" of this field. + */ + uint32_t id{0}; + + /** + * @brief Data of the answer. + * + * @see poll_media + */ + poll_media media{}; +}; + +/** + * @brief Represents the results of a poll + * + * @see https://discord.com/developers/docs/resources/poll#poll-results-object-poll-results-object-structure + */ +struct poll_results { + /** + * @brief Represents a reference to an answer and its count of votes + * + * @see https://discord.com/developers/docs/resources/poll#poll-results-object-poll-answer-count-object-structure + */ + struct answer_count { + /** + * @brief ID of the answer. Relates to an answer in the answers field + * + * @see poll_answer::answer_id + */ + uint32_t answer_id{0}; + + /** + * @brief Number of votes for this answer + */ + uint32_t count{0}; + + /** + * @brief Whether the current user voted + */ + bool me_voted{false}; + }; + + /** + * @brief Whether the poll has finalized, and the answers are precisely counted + * + * @note Discord states that due to the way they count and cache answers, + * while a poll is running the count of answers might not be accurate. + */ + bool is_finalized{false}; + + /** + * @brief Count of votes for each answer. If an answer is not present in this list, + * then its vote count is 0 + */ + std::map answer_counts; +}; + +/** + * @brief Represents a poll. + * + * @see https://discord.com/developers/docs/resources/poll + */ +struct DPP_EXPORT poll { + /** + * @brief Poll question. At the time of writing only the text field is supported by Discord + * + * @see media + */ + poll_media question{}; + + /** + * @brief List of answers of the poll. + * + * @note At the time of writing this can contain up to 10 answers + * @see answer + */ + std::map answers{}; + + /** + * @brief When retriving a poll from the API, this is the timestamp at which the poll will expire. + * When creating a poll, this is the number of hours the poll should be up for, up to 7 days (168 hours), and this field will be rounded. + */ + double expiry{24.0}; + + /** + * @brief Whether a user can select multiple answers + */ + bool allow_multiselect{false}; + + /** + * @brief Layout type of the poll. Defaults to, well, pl_default + * + * @see poll_layout_type */ - virtual std::string build_json(bool with_id = true) const; + poll_layout_type layout_type{pl_default}; + /** + * @brief The (optional) results of the poll. This field may or may not be present, and its absence means "unknown results", not "no results". + * + * @note Quote from Discord: "The results field may be not present in certain responses where, as an implementation detail, + * we do not fetch the poll results in our backend. This should be treated as "unknown results", + * as opposed to "no results". You can keep using the results if you have previously received them through other means." + * + * @see https://discord.com/developers/docs/resources/poll#poll-results-object + */ + std::optional results{std::nullopt}; + + /** + * @brief Set the question for this poll + * + * @param text Text for the question + * @return self for method chaining + */ + poll& set_question(std::string_view text); + + /** + * @brief Set the duration of the poll in hours + * + * @param hours Duration of the poll in hours, max 7 days (168 hours) at the time of writing + * @return self for method chaining + */ + poll& set_duration(uint32_t hours) noexcept; + + /** + * @brief Set if the poll should allow multi-selecting + * + * @param allow Should allow multi-select? + * @return self for method chaining + */ + poll& set_allow_multiselect(bool allow) noexcept; + + /** + * @brief Add an answer to this poll + * + * @note At the time of writing this, a poll can have up to 10 answers + * @param media Data of the answer + * @return self for method chaining + */ + poll& add_answer(const poll_media& media); + + /** + * @brief Add an answer to this poll + * + * @note At the time of writing this, a poll can have up to 10 answers + * @param text Text for the answer + * @param emoji_id Optional emoji + * @param is_animated Whether the emoji is animated + * @return self for method chaining + */ + poll& add_answer(std::string_view text, snowflake emoji_id = 0, bool is_animated = false); + + /** + * @brief Add an answer to this poll + * + * @note At the time of writing this, a poll can have up to 10 answers + * @param text Text for the answer + * @param emoji Optional emoji + * @return self for method chaining + */ + poll& add_answer(std::string_view text, std::string_view emoji); + + /** + * @brief Add an answer to this poll + * + * @note At the time of writing this, a poll can have up to 10 answers + * @param text Text for the answer + * @param e Optional emoji + * @return self for method chaining + */ + poll& add_answer(std::string_view text, const emoji& e); + + /** + * @brief Helper to get the question text + * + * @return question.text + */ + [[nodiscard]] const std::string& get_question_text() const noexcept; + + /** + * @brief Helper to find an answer by ID + * + * @param id ID to find + * @return Pointer to the answer with the matching ID, or nullptr if not found + */ + [[nodiscard]] const poll_media* find_answer(uint32_t id) const noexcept; + + /** + * @brief Helper to find the vote count in the results + * + * @param answer_id ID of the answer to find + * @return std::optional Optional count of votes. An empty optional means Discord did not send the results, it does not mean 0. It can also mean the poll does not have an answer with this ID + * @see https://discord.com/developers/docs/resources/poll#poll-results-object + */ + [[nodiscard]] std::optional get_vote_count(uint32_t answer_id) const noexcept; }; /** * @brief Bitmask flags for a dpp::message */ enum message_flags : uint16_t { - /// this message has been published to subscribed channels (via Channel Following) + /** + * @brief This message has been published to subscribed channels (via Channel Following). + */ m_crossposted = 1 << 0, - /// this message originated from a message in another channel (via Channel Following) + + /** + * @brief This message originated from a message in another channel (via Channel Following). + */ m_is_crosspost = 1 << 1, - /// do not include any embeds when serializing this message + + /** + * @brief Do not include any embeds when serializing this message. + */ m_suppress_embeds = 1 << 2, - /// the source message for this crosspost has been deleted (via Channel Following) + + /** + * @brief The source message for this crosspost has been deleted (via Channel Following). + */ m_source_message_deleted = 1 << 3, - /// this message came from the urgent message system + + /** + * @brief This message came from the urgent message system. + */ m_urgent = 1 << 4, - /// this message has an associated thread, with the same id as the message + + /** + * @brief This message has an associated thread, with the same id as the message. + */ m_has_thread = 1 << 5, - /// this message is only visible to the user who invoked the Interaction + + /** + * @brief This message is only visible to the user who invoked the Interaction. + */ m_ephemeral = 1 << 6, - /// this message is an Interaction Response and the bot is "thinking" + + /** + * @brief This message is an Interaction Response and the bot is "thinking". + */ m_loading = 1 << 7, - /// this message failed to mention some roles and add their members to the thread + + /** + * @brief This message failed to mention some roles and add their members to the thread. + */ m_thread_mention_failed = 1 << 8, + + /** + * @brief This message will not trigger push and desktop notifications. + */ + m_suppress_notifications = 1 << 12, + + /** + * @brief This message is a voice message. + */ + m_is_voice_message = 1 << 13, }; /** * @brief Represents possible values for the dpp::embed type field. * These are loosely defined by the API docs and do not influence how the client renders embeds. - * The only type a bot can send is dpp::embed_type::emt_rich. + * + * @note The only type a bot can send is dpp::embed_type::emt_rich. */ namespace embed_type { /** * @brief Rich text */ const std::string emt_rich = "rich"; + /** * @brief Image */ const std::string emt_image = "image"; + /** * @brief Video link */ const std::string emt_video = "video"; + /** * @brief Animated gif */ const std::string emt_gifv = "gifv"; + /** * @brief Article */ const std::string emt_article = "article"; + /** * @brief Link URL */ const std::string emt_link = "link"; + /** * @brief Auto moderation filter */ const std::string emt_automod = "auto_moderation_message"; -}; +} // namespace embed_type /** * @brief Message types for dpp::message::type */ enum message_type { - /// Default - mt_default = 0, - /// Add recipient - mt_recipient_add = 1, - /// Remove recipient - mt_recipient_remove = 2, - /// Call - mt_call = 3, - /// Channel name change - mt_channel_name_change = 4, - /// Channel icon change - mt_channel_icon_change = 5, - /// Message pinned - mt_channel_pinned_message = 6, - /// Member joined - mt_guild_member_join = 7, - /// Boost - mt_user_premium_guild_subscription = 8, - /// Boost level 1 - mt_user_premium_guild_subscription_tier_1 = 9, - /// Boost level 2 - mt_user_premium_guild_subscription_tier_2 = 10, - /// Boost level 3 - mt_user_premium_guild_subscription_tier_3 = 11, - /// Follow channel - mt_channel_follow_add = 12, - /// Disqualified from discovery - mt_guild_discovery_disqualified = 14, - /// Re-qualified for discovery - mt_guild_discovery_requalified = 15, - /// Discovery grace period warning 1 + /** + * @brief Default + */ + mt_default = 0, + + /** + * @brief Add recipient + */ + mt_recipient_add = 1, + + /** + * @brief Remove recipient + */ + mt_recipient_remove = 2, + + /** + * @brief Call + */ + mt_call = 3, + + /** + * @brief Channel name change + */ + mt_channel_name_change = 4, + + /** + * @brief Channel icon change + */ + mt_channel_icon_change = 5, + + /** + * @brief Message pinned + */ + mt_channel_pinned_message = 6, + + /** + * @brief Member joined + */ + mt_guild_member_join = 7, + + /** + * @brief Boost + */ + mt_user_premium_guild_subscription = 8, + + /** + * @brief Boost level 1 + */ + mt_user_premium_guild_subscription_tier_1 = 9, + + /** + * @brief Boost level 2 + */ + mt_user_premium_guild_subscription_tier_2 = 10, + + /** + * @brief Boost level 3 + */ + mt_user_premium_guild_subscription_tier_3 = 11, + + /** + * @brief Follow channel + */ + mt_channel_follow_add = 12, + + /** + * @brief Disqualified from discovery + */ + mt_guild_discovery_disqualified = 14, + + /** + * @brief Re-qualified for discovery + */ + mt_guild_discovery_requalified = 15, + + /** + * @brief Discovery grace period warning 1 + */ mt_guild_discovery_grace_period_initial_warning = 16, - /// Discovery grace period warning 2 - mt_guild_discovery_grace_period_final_warning = 17, - /// Thread Created - mt_thread_created = 18, - /// Reply - mt_reply = 19, - /// Application command - mt_application_command = 20, - /// Thread starter message - mt_thread_starter_message = 21, - /// Invite reminder - mt_guild_invite_reminder = 22, - /// Context Menu Command - mt_context_menu_command = 23, - /// Auto moderation action - mt_auto_moderation_action = 24, + + /** + * @brief Discovery grace period warning 2 + */ + mt_guild_discovery_grace_period_final_warning = 17, + + /** + * @brief Thread Created + */ + mt_thread_created = 18, + + /** + * @brief Reply + */ + mt_reply = 19, + + /** + * @brief Application command + */ + mt_application_command = 20, + + /** + * @brief Thread starter message + */ + mt_thread_starter_message = 21, + + /** + * @brief Invite reminder + */ + mt_guild_invite_reminder = 22, + + /** + * @brief Context Menu Command + */ + mt_context_menu_command = 23, + + /** + * @brief Auto moderation action + */ + mt_auto_moderation_action = 24, + + /** + * @brief Role subscription purchase + */ + mt_role_subscription_purchase = 25, + + /** + * @brief Interaction premium upsell + * @depreciated Replaced with buttons with a style of cos_premium + * This message type may stop working at any point + */ + mt_interaction_premium_upsell = 26, + + /** + * @brief Stage start + */ + mt_stage_start = 27, + + /** + * @brief Stage end + */ + mt_stage_end = 28, + + /** + * @brief Stage speaker + */ + mt_stage_speaker = 29, + + /** + * @brief Stage topic + */ + mt_stage_topic = 31, + + /** + * @brief Guild application premium subscription + */ + mt_application_premium_subscription = 32, }; /** @@ -1082,11 +1937,13 @@ enum cache_policy_setting_t { * setting. */ cp_aggressive = 0, + /** * @brief only cache when there is relevant activity, e.g. a message to the bot. * This is a good middle-ground, memory usage will increase linearly over time. */ cp_lazy = 1, + /** * @brief Don't cache anything. Fill details when we see them. * This is the most memory-efficient option but consumes more CPU time @@ -1119,82 +1976,286 @@ struct DPP_EXPORT cache_policy_t { * @brief Caching policy for roles */ cache_policy_setting_t role_policy = cp_aggressive; + + /** + * @brief Caching policy for roles + */ + cache_policy_setting_t channel_policy = cp_aggressive; + + /** + * @brief Caching policy for roles + */ + cache_policy_setting_t guild_policy = cp_aggressive; +}; + +/** + * @brief Contains a set of predefined cache policies for use when constructing a dpp::cluster + */ +namespace cache_policy { + + /** + * @brief A shortcut constant for all caching enabled for use in dpp::cluster constructor + */ + inline constexpr cache_policy_t cpol_default = { cp_aggressive, cp_aggressive, cp_aggressive, cp_aggressive, cp_aggressive }; + + /** + * @brief A shortcut constant for a more balanced caching policy for use in dpp::cluster constructor + */ + inline constexpr cache_policy_t cpol_balanced = { cp_lazy, cp_lazy, cp_lazy, cp_aggressive, cp_aggressive }; + + /** + * @brief A shortcut constant for all caching disabled for use in dpp::cluster constructor + */ + inline constexpr cache_policy_t cpol_none = { cp_none, cp_none, cp_none, cp_none, cp_none }; + +}; + +/** + * @brief Metadata about the interaction, including the source of the interaction and relevant server and user IDs. + */ +struct DPP_EXPORT interaction_metadata_type { + + /** + * @brief ID of the interaction + */ + snowflake id; + + /** + * @brief User who triggered the interaction + */ + uint8_t type; + + /** + * @brief User who triggered the interaction + */ + user usr; + + /** + * @brief ID of the original response message, present only on follow-up messages + */ + snowflake original_response_message_id; + + /** + * @brief ID of the message that contained interactive component, present only on messages created from component interactions + */ + snowflake interacted_message_id; + + // FIXME: Add this field sometime + /** + * @brief Metadata for the interaction that was used to open the modal, present only on modal submit interactions + */ + // interaction_metadata_type triggering_interaction_metadata; }; /** + * @brief Message Reference type + */ +enum DPP_EXPORT message_ref_type : uint8_t { + /** + * A reply or crosspost + */ + mrt_default = 0, + /** + * A forwarded message + */ + mrt_forward = 1, +}; + +template struct message_snapshot { + std::vector messages; +}; + + /** * @brief Represents messages sent and received on Discord */ -struct DPP_EXPORT message : public managed { - /** id of the channel the message was sent in */ - snowflake channel_id; - /** Optional: id of the guild the message was sent in */ - snowflake guild_id; - /** the author of this message (not guaranteed to be a valid user) */ - user author; - /** Optional: member properties for this message's author */ - guild_member member; - /** contents of the message */ - std::string content; - /** message components */ +struct DPP_EXPORT message : public managed, json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + inline message& fill_from_json_impl(nlohmann::json *j) { + return fill_from_json(j, {cp_aggressive, cp_aggressive, cp_aggressive}); + } + + /** Build a JSON from this object. + * @param with_id True if an ID is to be included in the JSON + * @return JSON + */ + inline json to_json_impl(bool with_id = false) const { + return to_json(with_id, false); + } +public: + /** + * @brief ID of the channel the message was sent in. + */ + snowflake channel_id; + + /** + * @brief Optional: ID of the guild the message was sent in. + */ + snowflake guild_id; + + /** + * @brief The author of this message. + * + * @warning This is not guaranteed to be a valid user. + */ + user author; + + /** + * @brief Optional: member properties for this message's author + */ + guild_member member; + + /** + * @brief Contents of the message. + */ + std::string content; + + /** + * @brief Message components. + */ std::vector components; - /** when this message was sent */ - time_t sent; - /** when this message was edited (may be 0 if never edited) */ - time_t edited; - /** users specifically mentioned in the message */ - std::vector> mentions; - /** roles specifically mentioned in this message (only IDs currently)*/ + + /** + * @brief When this message was sent. + */ + time_t sent; + + /** + * @brief When this message was edited. + * + * @note This may be 0 if never edited. + */ + time_t edited; + + /** + * @brief Users specifically mentioned in the message. + */ + std::vector> mentions; + + /** + * @brief Roles specifically mentioned in this message (only IDs currently). + */ std::vector mention_roles; - /** Channels mentioned in the message. (Discord: not all types supported) - * Discord: Only textual channels that are visible to everyone in a lurkable guild will ever be included. Only crossposted messages (via Channel Following) currently include mention_channels at all. (includes ID, Guild ID, Type, Name)*/ + + /** + * @brief Channels mentioned in the message. + * + * @warning Not all types supported. + * + * @note Discord: Only textual channels that are visible to everyone in a lurkable guild will ever be included. + * Only crossposted messages (via Channel Following) currently include mention_channels at all. (includes ID, Guild ID, Type, Name). + */ std::vector mention_channels; - /** any attached files */ + + /** + * @brief Any attached files. + */ std::vector attachments; - /** zero or more dpp::embed objects */ + + /** + * @brief Up to 10 dpp::embed objects. + */ std::vector embeds; - /** Optional: reactions to the message */ + + /** + * @brief Optional: reactions to the message. + */ std::vector reactions; - /** Optional: used for validating a message was sent */ - std::string nonce; - /** Optional: if the message is generated by a webhook, its id will be here otherwise the field will be 0 */ - snowflake webhook_id; - /** Stickers */ - std::vector stickers; - /** Name of file to upload (for use server-side in discord's url) */ - std::vector filename; + /** + * @brief Optional: Used for validating a message was sent. + */ + std::string nonce; + + /** + * @brief Optional: Webhook ID. + * + * @note If the message is generated by a webhook, its ID will be here. Otherwise, the field will be 0. + */ + snowflake webhook_id; + + /** + * @brief Partial stickers. Only id, name and format_type are filled + */ + std::vector stickers; - /** File content to upload (raw binary) */ - std::vector filecontent; + /** + * @brief An array of file data to use for uploading files. + * + * @note You should use dpp::message::add_file to add data to this! + */ + std::vector file_data; /** * @brief Reference to another message, e.g. a reply */ struct message_ref { - /// id of the originating message + /** + * @brief Message reference type, set to 1 to forward a message + */ + message_ref_type type{mrt_default}; + /** + * @brief ID of the originating message. + */ snowflake message_id; - /// id of the originating message's channel + + /** + * @brief ID of the originating message's channel. + */ snowflake channel_id; - /// id of the originating message's guild + + /** + * @brief ID of the originating message's guild. + */ snowflake guild_id; - /// when sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true + + /** + * @brief when sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message. + * Default true. + */ bool fail_if_not_exists; } message_reference; + /** + * @brief Message snapshots for a forwarded message + */ + message_snapshot message_snapshots; + /** * @brief Reference to an interaction */ - struct message_interaction_struct{ - /// id of the interaction + struct message_interaction_struct { + /** + * @brief ID of the interaction. + */ snowflake id; - /// type of interaction + + /** + * @brief Type of interaction. + */ uint8_t type; - /// name of the application command + + /** + * @brief Name of the application command. + */ std::string name; - /// the user who invoked the interaction + + /** + * @brief The user who invoked the interaction. + */ user usr; } interaction; + /** + * @brief Sent if the message is sent as a result of an interaction + */ + interaction_metadata_type interaction_metadata; + /** * @brief Allowed mentions details */ @@ -1203,22 +2264,27 @@ struct DPP_EXPORT message : public managed { * @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. Default is false */ bool parse_everyone; + /** * @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. Default is false */ bool replied_user; + /** * @brief List of users to allow pings for */ std::vector users; + /** * @brief List of roles to allow pings for */ @@ -1226,28 +2292,57 @@ struct DPP_EXPORT message : public managed { } allowed_mentions; /** - * @brief The cluster which created this message object + * @brief The cluster which created this message object. */ class cluster* owner; - /** Message type */ + /** + * @brief Message type. + */ message_type type; - /** Flags. Made of bits in dpp::message_flags */ - uint16_t flags; + /** + * @brief Flags made from dpp::message_flags + */ + uint16_t flags; + + /** + * @brief Whether this message is pinned. + */ + bool pinned; + + /** + * @brief Whether this was a TTS message. + */ + bool tts; + + /** + * @brief Whether this message mentions everyone. + */ + bool mention_everyone; - /** whether this message is pinned */ - bool pinned; - /** whether this was a TTS message */ - bool tts; - /** whether this message mentions everyone */ - bool mention_everyone; + /** + * @brief Optional poll attached to this message + */ + std::optional attached_poll; /** * @brief Construct a new message object */ message(); + /* + * @brief Construct a new message object + * @param m Message to copy + */ + message(const message& m) = default; + + /* + * @brief Construct a new message object + * @param m Message to move + */ + message(message&& m) = default; + /** * @brief Construct a new message object * @param o Owning cluster, passed down to various things such as dpp::attachment. @@ -1256,11 +2351,6 @@ struct DPP_EXPORT message : public managed { */ message(class cluster* o); - /** - * @brief Destroy the message object - */ - virtual ~message(); - /** * @brief Construct a new message object with a channel and content * @@ -1268,7 +2358,14 @@ struct DPP_EXPORT message : public managed { * @param content The content of the message. It will be truncated to the maximum length of 4000 UTF-8 characters. * @param type The message type to create */ - message(snowflake channel_id, const std::string &content, message_type type = mt_default); + message(snowflake channel_id, std::string_view content, message_type type = mt_default); + + /** + * @brief Construct a new message object with content + * + * @param _embed An embed to send + */ + message(const embed& _embed); /** * @brief Construct a new message object with a channel and content @@ -1276,7 +2373,7 @@ struct DPP_EXPORT message : public managed { * @param channel_id The channel to send the message to * @param _embed An embed to send */ - message(snowflake channel_id, const embed & _embed); + message(snowflake channel_id, const embed& _embed); /** * @brief Construct a new message object with content @@ -1284,7 +2381,28 @@ struct DPP_EXPORT message : public managed { * @param content The content of the message. It will be truncated to the maximum length of 4000 UTF-8 characters. * @param type The message type to create */ - message(const std::string &content, message_type type = mt_default); + message(std::string_view content, message_type type = mt_default); + + /** + * @brief Destroy the message object + */ + ~message() override = default; + + /** + * @brief Copy a message object + * + * @param m Message to copy + * @return message& Reference to self + */ + message &operator=(const message& m) = default; + + /** + * @brief Move a message object + * + * @param m Message to move + * @return message& Reference to self + */ + message &operator=(message&& m) = default; /** * @brief Set the original message reference for replies/crossposts @@ -1293,29 +2411,33 @@ struct DPP_EXPORT message : public managed { * @param _guild_id guild id to reply to (optional) * @param _channel_id channel id to reply to (optional) * @param fail_if_not_exists true if the message send should fail if these values are invalid (optional) + * @param type Type of reference * @return message& reference to self */ - message& set_reference(snowflake _message_id, snowflake _guild_id = 0, snowflake _channel_id = 0, bool fail_if_not_exists = false); + message& set_reference(snowflake _message_id, snowflake _guild_id = 0, snowflake _channel_id = 0, bool fail_if_not_exists = false, message_ref_type type = mrt_default); /** * @brief Set the allowed mentions object for pings on the message * - * @param _parse_users whether or not to parse users in the message content or embeds - * @param _parse_roles whether or not to parse roles in the message content or embeds - * @param _parse_everyone whether or not to parse everyone/here in the message content or embeds - * @param _replied_user if set to true and this is a reply, then ping the user we reply to - * @param users list of user ids to allow pings for - * @param roles list of role ids to allow pings for + * @param _parse_users whether or not to parse users in the message content or embeds, default false + * @param _parse_roles whether or not to parse roles in the message content or embeds, default false + * @param _parse_everyone whether or not to parse everyone/here in the message content or embeds, default false + * @param _replied_user if set to true and this is a reply, then ping the user we reply to, default false + * @param users list of user ids to allow pings for, default an empty vector + * @param roles list of role ids to allow pings for, default an empty vector * @return message& reference to self */ - message& set_allowed_mentions(bool _parse_users, bool _parse_roles, bool _parse_everyone, bool _replied_user, const std::vector &users, const std::vector &roles); + message& set_allowed_mentions(bool _parse_users = false, bool _parse_roles = false, bool _parse_everyone = false, bool _replied_user = false, const std::vector &users = {}, const std::vector &roles = {}); + + using json_interface::fill_from_json; + using json_interface::to_json; /** Fill this object from json. * @param j JSON object to fill from * @param cp Cache policy for user records, whether or not we cache users when a message is received * @return A reference to self */ - message& fill_from_json(nlohmann::json* j, cache_policy_t cp = {cp_aggressive, cp_aggressive, cp_aggressive}); + message& fill_from_json(nlohmann::json* j, cache_policy_t cp); /** Build JSON from this object. * @param with_id True if the ID is to be included in the built JSON @@ -1323,7 +2445,7 @@ struct DPP_EXPORT message : public managed { * This will exclude some fields that are not valid in interactions at this time. * @return The JSON text of the message */ - virtual std::string build_json(bool with_id = false, bool is_interaction_response = false) const; + virtual json to_json(bool with_id, bool is_interaction_response) const; /** * @brief Returns true if the message was crossposted to other servers @@ -1346,6 +2468,14 @@ struct DPP_EXPORT message : public managed { */ bool suppress_embeds() const; + /** + * @brief Set whether embeds should be suppressed + * + * @param suppress whether embeds should be suppressed + * @return message& reference to self + */ + message& suppress_embeds(bool suppress); + /** * @brief True if source message was deleted * @@ -1388,6 +2518,20 @@ struct DPP_EXPORT message : public managed { */ bool is_thread_mention_failed() const; + /** + * @brief True if the message will not trigger push and desktop notifications + * + * @return True if notifications suppressed + */ + bool suppress_notifications() const; + + /** + * @brief True if the message is a voice message + * + * @return True if voice message + */ + bool is_voice_message() const; + /** * @brief Add a component (button) to message * @@ -1404,6 +2548,24 @@ struct DPP_EXPORT message : public managed { */ message& add_embed(const embed& e); + /** + * @brief Add a sticker to this message + * + * As of writing this, a message can only contain up to 3 stickers + * @param s sticker to add + * @return message& reference to self + */ + message& add_sticker(const sticker& s); + + /** + * @brief Add a sticker to this message + * + * As of writing this, a message can only contain up to 3 stickers + * @param id id of the sticker to add + * @return message& reference to self + */ + message& add_sticker(const snowflake& id); + /** * @brief Set the flags * @@ -1427,25 +2589,26 @@ struct DPP_EXPORT message : public managed { * @return message& reference to self * @deprecated Use message::add_file instead */ - message& set_filename(const std::string &fn); + message& set_filename(std::string_view fn); /** * @brief Set the file content of the last file in list * - * @param fc raw file content contained in std::string + * @param fc raw file content * @return message& reference to self * @deprecated Use message::add_file instead */ - message& set_file_content(const std::string &fc); + message& set_file_content(std::string_view fc); /** * @brief Add a file to the message * * @param filename filename - * @param filecontent raw file content contained in std::string + * @param filecontent raw file content + * @param filemimetype optional mime type of the file * @return message& reference to self */ - message& add_file(const std::string &filename, const std::string &filecontent); + message& add_file(std::string_view filename, std::string_view filecontent, std::string_view filemimetype = ""); /** * @brief Set the message content @@ -1453,7 +2616,7 @@ struct DPP_EXPORT message : public managed { * @param c message content. It will be truncated to the maximum length of 4000 UTF-8 characters. * @return message& reference to self */ - message& set_content(const std::string &c); + message& set_content(std::string_view c); /** * @brief Set the channel id @@ -1477,15 +2640,57 @@ struct DPP_EXPORT message : public managed { * @return true if message is a DM */ bool is_dm() const; + + /** + * @brief Returns true if message has remixed attachment + * + * @return true if message has remixed attachment + */ + bool has_remix_attachment() const; + + /** + * @brief Returns URL to message + * + * @return string of URL to message + */ + std::string get_url() const; + + /** + * @brief Convenience method to set the poll + * + * @return message& Self reference for method chaining + */ + message& set_poll(const poll& p); + + /** + * @brief Convenience method to get the poll attached to this message + * + * @throw std::bad_optional_access if has_poll() == false + * @return const poll& Poll attached to this object + */ + [[nodiscard]] const poll& get_poll() const; + + /** + * @brief Method to check if the message has a poll + * + * @return bool Whether the message has a poll + */ + [[nodiscard]] bool has_poll() const noexcept; }; -/** A group of messages */ +/** + * @brief A group of messages + */ typedef std::unordered_map message_map; -/** A group of stickers */ +/** + * @brief A group of stickers + */ typedef std::unordered_map sticker_map; -/** A group of sticker packs */ +/** + * @brief A group of sticker packs + */ typedef std::unordered_map sticker_pack_map; -}; +} diff --git a/3rdParty/dpp/misc-enum.h b/3rdParty/dpp/misc-enum.h index 19ef2a1edf..e9fbbf67b4 100644 --- a/3rdParty/dpp/misc-enum.h +++ b/3rdParty/dpp/misc-enum.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -20,34 +21,69 @@ ************************************************************************************/ #pragma once #include -#include +#include +#include namespace dpp { - /** @brief Supported image types for profile pictures */ - enum image_type { - /// image/png - i_png, - /// image/jpeg - i_jpg, - /// image/gif - i_gif - }; - - /** @brief Log levels */ - enum loglevel { - /// Trace - ll_trace = 0, - /// Debug - ll_debug, - /// Information - ll_info, - /// Warning - ll_warning, - /// Error - ll_error, - /// Critical - ll_critical - }; +/** + * @brief Supported image types for profile pictures and CDN endpoints + */ +enum image_type : uint8_t { + /** + * @brief image/png + */ + i_png, + /** + * @brief image/jpeg. + */ + i_jpg, + + /** + * @brief image/gif. + */ + i_gif, + + /** + * @brief Webp. + */ + i_webp, +}; + +/** + * @brief Log levels + */ +enum loglevel { + /** + * @brief Trace + */ + ll_trace = 0, + + /** + * @brief Debug + */ + ll_debug, + + /** + * @brief Information + */ + ll_info, + + /** + * @brief Warning + */ + ll_warning, + + /** + * @brief Error + */ + ll_error, + + /** + * @brief Critical + */ + ll_critical }; + +} diff --git a/3rdParty/dpp/once.h b/3rdParty/dpp/once.h index f77339673f..3860591d49 100644 --- a/3rdParty/dpp/once.h +++ b/3rdParty/dpp/once.h @@ -24,23 +24,23 @@ namespace dpp { - /** - * @brief Run some code within an if() statement only once. - * - * Use this template like this: - * - * ``` - * if (dpp::run_once()) { - * // Your code here - * } - * ``` - * - * @tparam T any unique 'tag' identifier name - * @return auto a true/false return to say if we should execute or not - */ - template auto run_once() { - static auto called = false; - return !std::exchange(called, true); - }; - +/** + * @brief Run some code within an if() statement only once. + * + * Use this template like this: + * + * ``` + * if (dpp::run_once()) { + * // Your code here + * } + * ``` + * + * @tparam T any unique 'tag' identifier name + * @return auto a true/false return to say if we should execute or not + */ +template auto run_once() { + static auto called = false; + return !std::exchange(called, true); }; + +} diff --git a/3rdParty/dpp/permissions.h b/3rdParty/dpp/permissions.h index 2f8b1fe310..80d9aedbfb 100644 --- a/3rdParty/dpp/permissions.h +++ b/3rdParty/dpp/permissions.h @@ -20,7 +20,7 @@ ************************************************************************************/ #pragma once #include -#include +#include #include #include @@ -30,47 +30,241 @@ 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 Allows creation of instant invites. + */ + p_create_instant_invite = 0x00000000001, + + /** + * @brief Allows kicking members. + */ + p_kick_members = 0x00000000002, + + /** + * @brief Allows banning members. + */ + p_ban_members = 0x00000000004, + + /** + * @brief Allows all permissions and bypasses channel permission overwrites. + */ + p_administrator = 0x00000000008, + + /** + * @brief Allows management and editing of channels. + */ + p_manage_channels = 0x00000000010, + + /** + * @brief Allows management and editing of the guild. + */ + p_manage_guild = 0x00000000020, + + /** + * @brief Allows for the addition of reactions to messages. + */ + p_add_reactions = 0x00000000040, + + /** + * @brief Allows for viewing of audit logs. + */ + p_view_audit_log = 0x00000000080, + + /** + * @brief Allows for using priority speaker in a voice channel. + */ + p_priority_speaker = 0x00000000100, + + /** + * @brief Allows the user to go live. + */ + p_stream = 0x00000000200, + + /** + * @brief Allows guild members to view a channel, + * which includes reading messages in text channels and joining voice channels. + */ + p_view_channel = 0x00000000400, + + /** + * @brief Allows for sending messages in a channel. + */ + p_send_messages = 0x00000000800, + + /** + * @brief Allows for sending of /tts messages. + */ + p_send_tts_messages = 0x00000001000, + + /** + * @brief Allows for deletion of other users messages. + */ + p_manage_messages = 0x00000002000, + + /** + * @brief Links sent by users with this permission will be auto-embedded. + */ + p_embed_links = 0x00000004000, + + /** + * @brief Allows for uploading images and files. + */ + p_attach_files = 0x00000008000, + + /** + * @brief Allows for reading of message history. + */ + p_read_message_history = 0x00000010000, + + /** + * @brief Allows for using the everyone and the here tag to notify users in a channel. + */ + p_mention_everyone = 0x00000020000, + + /** + * @brief Allows the usage of custom emojis from other servers. + */ + p_use_external_emojis = 0x00000040000, + + /** + * @brief Allows for viewing guild insights. + */ + p_view_guild_insights = 0x00000080000, + + /** + * @brief Allows for joining of a voice channel. + */ + p_connect = 0x00000100000, + + /** + * @brief Allows for speaking in a voice channel. + */ + p_speak = 0x00000200000, + + /** + * @brief Allows for muting members in a voice channel. + */ + p_mute_members = 0x00000400000, + + /** + * @brief Allows for deafening of members in a voice channel. + */ + p_deafen_members = 0x00000800000, + + /** + * @brief Allows for moving of members between voice channels. + */ + p_move_members = 0x00001000000, + + /** + * @brief Allows for using voice-activity-detection in a voice channel. + */ + p_use_vad = 0x00002000000, + + /** + * @brief Allows for modification of own nickname. + */ + p_change_nickname = 0x00004000000, + + /** + * @brief Allows for modification of other users nicknames. + */ + p_manage_nicknames = 0x00008000000, + + /** + * @brief Allows management and editing of roles. + */ + p_manage_roles = 0x00010000000, + + /** + * @brief Allows management and editing of webhooks. + */ + p_manage_webhooks = 0x00020000000, + + /** + * @brief Allows management and editing of emojis and stickers. + */ + p_manage_emojis_and_stickers = 0x00040000000, + + /** + * @brief Allows members to use application commands, + * including slash commands and context menus. + */ + p_use_application_commands = 0x00080000000, + + /** + * @brief Allows for requesting to speak in stage channels. + * + * @warning Discord: This permission is under active development and may be changed or removed. + */ + p_request_to_speak = 0x00100000000, + + /** + * @brief Allows for management (creation, updating, deleting, starting) of scheduled events. + */ + p_manage_events = 0x00200000000, + + /** + * @brief Allows for deleting and archiving threads, and viewing all private threads. + */ + p_manage_threads = 0x00400000000, + + /** + * @brief Allows for creating public and announcement threads. + */ + p_create_public_threads = 0x00800000000, + + /** + * @brief Allows for creating private threads. + */ + p_create_private_threads = 0x01000000000, + + /** + * @brief Allows the usage of custom stickers from other servers. + */ + p_use_external_stickers = 0x02000000000, + + /** + * @brief Allows for sending messages in threads. + */ + p_send_messages_in_threads = 0x04000000000, + + /** + * @brief Allows for using activities (applications with the EMBEDDED flag) in a voice channel. + */ + p_use_embedded_activities = 0x08000000000, + + /** + * @brief 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. + */ + p_moderate_members = 0x10000000000, + + /** + * @brief Allows for viewing role subscription insights. + */ + p_view_creator_monetization_analytics = 0x20000000000, + + /** + * @brief Allows for using soundboard in a voice channel. + */ + p_use_soundboard = 0x40000000000, + + /** + * @brief Allows the usage of custom soundboard sounds from other servers. + */ + p_use_external_sounds = 0x0000200000000000, + + /** + * @brief Allows sending voice messages. + */ + p_send_voice_messages = 0x0000400000000000, + + /** + * @brief Allows use of Clyde AI. + */ + p_use_clyde_ai = 0x0000800000000000, }; /** @@ -80,38 +274,42 @@ enum permissions : uint64_t { using role_permissions = permissions; /** - * @brief Represents a permission bitmask (refer to enum dpp::permissions) which are hold in an uint64_t + * @brief Represents a permission bitmask (refer to enum dpp::permissions) which are held in an uint64_t */ class DPP_EXPORT permission { protected: /** * @brief The permission bitmask value */ - uint64_t value; + uint64_t value{0}; public: /** - * @brief Construct a permission object - * @param value A permission bitmask + * @brief Default constructor, initializes permission to 0 */ - permission(const uint64_t& value); + constexpr permission() = default; /** - * @brief Construct a permission object - */ - permission(); + * @brief Bitmask constructor, initializes permission to the argument + * @param value The bitmask to initialize the permission to + */ + constexpr permission(uint64_t value) noexcept : value{value} {} /** * @brief For acting like an integer * @return The permission bitmask value */ - operator uint64_t() const; + constexpr operator uint64_t() const noexcept { + return value; + } /** * @brief For acting like an integer * @return A reference to the permission bitmask value */ - operator uint64_t &(); + constexpr operator uint64_t &() noexcept { + return value; + } /** * @brief For building json @@ -119,6 +317,44 @@ class DPP_EXPORT permission { */ operator nlohmann::json() const; + /** + * @brief Check for certain permissions, taking into account administrator privileges. 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.can(dpp::p_kick_members, dpp::p_ban_members); + * // Returns true if it has permission to p_kick_members and p_ban_members + * ``` + * + * @return bool True if it has **all** the given permissions or dpp::p_administrator + */ + template + constexpr bool can(T... values) const noexcept { + return has(values...) || (value & p_administrator); + } + + /** + * @brief Check for certain permissions, taking into account administrator privileges. 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.can_any(dpp::p_kick_members, dpp::p_ban_members); + * // Returns true if it has permission to p_kick_members or p_ban_members + * ``` + * + * @return bool True if it has **any** of the given permissions or dpp::p_administrator + */ + template + constexpr bool can_any(T... values) const noexcept { + return has_any(values...) || (value & p_administrator); + } + /** * @brief Check for permission flags set. It uses the Bitwise AND operator * @tparam T one or more uint64_t permission bits @@ -131,13 +367,32 @@ class DPP_EXPORT permission { * // Returns true if the permission bitmask contains p_kick_members and p_ban_members * ``` * - * @return bool True if it has all the given permissions + * @return bool True if it has **all** the given permissions */ template - bool has(T... values) const { + constexpr bool has(T... values) const noexcept { return (value & (0 | ... | values)) == (0 | ... | values); } + /** + * @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_any(dpp::p_administrator, dpp::p_ban_members); + * // Returns true if the permission bitmask contains p_administrator or p_ban_members + * ``` + * + * @return bool True if it has **any** of the given permissions + */ + template + constexpr bool has_any(T... values) const noexcept { + return (value & (0 | ... | values)) != 0; + } + /** * @brief Add a permission with the Bitwise OR operation * @tparam T one or more uint64_t permission bits @@ -153,14 +408,14 @@ class DPP_EXPORT permission { * @return permission& reference to self for chaining */ template - typename std::enable_if<(std::is_convertible::value && ...), permission&>::type - add(T... values) { + std::enable_if_t<(std::is_convertible_v && ...), permission&> + constexpr add(T... values) noexcept { value |= (0 | ... | values); return *this; } /** - * @brief Assign a permission. This will reset the bitmask to the new value. + * @brief Assign permissions. This will reset the bitmask to the new value. * @tparam T one or more uint64_t permission bits * @param values The permissions (from dpp::permissions) to set * @@ -173,8 +428,8 @@ class DPP_EXPORT permission { * @return permission& reference to self for chaining */ template - typename std::enable_if<(std::is_convertible::value && ...), permission&>::type - set(T... values) { + std::enable_if_t<(std::is_convertible_v && ...), permission&> + constexpr set(T... values) noexcept { value = (0 | ... | values); return *this; } @@ -194,8 +449,8 @@ class DPP_EXPORT permission { * @return permission& reference to self for chaining */ template - typename std::enable_if<(std::is_convertible::value && ...), permission&>::type - remove(T... values) { + std::enable_if_t<(std::is_convertible_v && ...), permission&> + constexpr remove(T... values) noexcept { value &= ~(0 | ... | values); return *this; } diff --git a/3rdParty/dpp/presence.h b/3rdParty/dpp/presence.h index 23c389420f..0a1e2ce5dd 100644 --- a/3rdParty/dpp/presence.h +++ b/3rdParty/dpp/presence.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -32,82 +33,174 @@ namespace dpp { * @brief Presence flags bitmask */ enum presence_flags { - /// Desktop: Online - p_desktop_online = 0b00000001, - /// Desktop: DND - p_desktop_dnd = 0b00000010, - /// Desktop: Idle - p_desktop_idle = 0b00000011, - /// Web: Online - p_web_online = 0b00000100, - /// Web: DND - p_web_dnd = 0b00001000, - /// Web: Idle - p_web_idle = 0b00001100, - /// Mobile: Online - p_mobile_online = 0b00010000, - /// Mobile: DND - p_mobile_dnd = 0b00100000, - /// Mobile: Idle - p_mobile_idle = 0b00110000, - /// General: Online - p_status_online = 0b01000000, - /// General: DND - p_status_dnd = 0b10000000, - /// General: Idle - p_status_idle = 0b11000000 + /** + * @brief Desktop: Online. + */ + p_desktop_online = 0b00000001, + + /** + * @brief Desktop: DND. + */ + p_desktop_dnd = 0b00000010, + + /** + * @brief Desktop: Idle. + */ + p_desktop_idle = 0b00000011, + + /** + * @brief Web: Online. + */ + p_web_online = 0b00000100, + + /** + * @brief Web: DND. + */ + p_web_dnd = 0b00001000, + + /** + * @brief Web: Idle. + */ + p_web_idle = 0b00001100, + + /** + * @brief Mobile: Online. + */ + p_mobile_online = 0b00010000, + + /** + * @brief Mobile: DND. + */ + p_mobile_dnd = 0b00100000, + + /** + * @brief Mobile: Idle. + */ + p_mobile_idle = 0b00110000, + + /** + * @brief General: Online. + */ + p_status_online = 0b01000000, + + /** + * @brief General: DND. + */ + p_status_dnd = 0b10000000, + + /** + * @brief General: Idle. + */ + p_status_idle = 0b11000000 }; /** * @brief Online presence status values */ enum presence_status : uint8_t { - /// Offline - ps_offline = 0, - /// Online - ps_online = 1, - /// DND - ps_dnd = 2, - /// Idle - ps_idle = 3 + /** + * @brief Offline. + */ + ps_offline = 0, + + /** + * @brief Online. + */ + ps_online = 1, + + /** + * @brief DND. + */ + ps_dnd = 2, + + /** + * @brief Idle. + */ + ps_idle = 3, + + /** + * @brief Invisible (show as offline). + */ + ps_invisible = 4, }; /** - * @brief Bit shift for desktop status + * @brief Bit shift for desktop status. */ #define PF_SHIFT_DESKTOP 0 -/** Bit shift for web status */ + +/** + * @brief Bit shift for web status. + */ #define PF_SHIFT_WEB 2 -/** Bit shift for mobile status */ + +/** + * @brief Bit shift for mobile status. + */ #define PF_SHIFT_MOBILE 4 -/** Bit shift for main status */ + +/** + * @brief Bit shift for main status. + */ #define PF_SHIFT_MAIN 6 -/** Bit mask for status */ + +/** + * @brief Bit mask for status. + */ #define PF_STATUS_MASK 0b00000011 -/** Bit mask for clearing desktop status */ + +/** + * @brief Bit mask for clearing desktop status. + */ #define PF_CLEAR_DESKTOP 0b11111100 -/** Bit mask for clearing web status */ + +/** + * @brief Bit mask for clearing web status. + */ #define PF_CLEAR_WEB 0b11110011 -/** Bit mask for clearing mobile status */ + +/** + * @brief Bit mask for clearing mobile status. + */ #define PF_CLEAR_MOBILE 0b11001111 -/** Bit mask for clearing main status */ + +/** + * @brief Bit mask for clearing main status. + */ #define PF_CLEAR_STATUS 0b00111111 /** * @brief Game types */ enum activity_type : uint8_t { - /// "Playing ..." + /** + * @brief "Playing ..." + */ at_game = 0, - /// "Streaming ..." + + /** + * @brief "Streaming ..." + */ at_streaming = 1, - /// "Listening to..." + + /** + * @brief "Listening to..." + */ at_listening = 2, - /// "Watching..." + + /** + * @brief "Watching..." + */ at_watching = 3, - /// "Emoji..." + + /** + * @brief "Emoji..." + */ at_custom = 4, - /// "Competing in..." + + /** + * @brief "Competing in..." + */ at_competing = 5 }; @@ -115,23 +208,49 @@ enum activity_type : uint8_t { * @brief Activity types for rich presence */ enum activity_flags { - /// In an instance + /** + * @brief In an instance. + */ af_instance = 0b000000001, - /// Joining + + /** + * @brief Joining. + */ af_join = 0b000000010, - /// Spectating + + /** + * @brief Spectating. + */ af_spectate = 0b000000100, - /// Sending join request + + /** + * @brief Sending join request. + */ af_join_request = 0b000001000, - /// Synchronising + + /** + * @brief Synchronising. + */ af_sync = 0b000010000, - /// Playing + + /** + * @brief Playing. + */ af_play = 0b000100000, - /// Party privacy friends + + /** + * @brief Party privacy friends. + */ af_party_privacy_friends = 0b001000000, - /// Party privacy voice channel + + /** + * @brief Party privacy voice channel. + */ af_party_privacy_voice_channel = 0b010000000, - /// Embedded + + /** + * @brief Embedded. + */ af_embedded = 0b100000000 }; @@ -140,10 +259,13 @@ enum activity_flags { */ struct DPP_EXPORT activity_button { public: - /** The text shown on the button (1-32 characters) + /** + * @brief The text shown on the button (1-32 characters). */ std::string label; - /** The url opened when clicking the button (1-512 characters). It's may be empty + + /** + * @brief The url opened when clicking the button (1-512 characters, can be empty). * * @note Bots cannot access the activity button URLs. */ @@ -158,16 +280,23 @@ struct DPP_EXPORT activity_button { */ struct DPP_EXPORT activity_assets { public: - /** The large asset image which usually contain snowflake ID or prefixed image ID + /** + * @brief The large asset image which usually contain snowflake ID or prefixed image ID. */ std::string large_image; - /** Text displayed when hovering over the large image of the activity + + /** + * @brief Text displayed when hovering over the large image of the activity. */ std::string large_text; - /** The small asset image which usually contain snowflake ID or prefixed image ID + + /** + * @brief The small asset image which usually contain snowflake ID or prefixed image ID. */ std::string small_image; - /** Text displayed when hovering over the small image of the activity + + /** + * @brief Text displayed when hovering over the small image of the activity. */ std::string small_text; @@ -176,17 +305,22 @@ struct DPP_EXPORT activity_assets { }; /** - * @brief Secrets for Rich Presence joining and spectating + * @brief Secrets for Rich Presence joining and spectating. */ struct DPP_EXPORT activity_secrets { public: - /** The secret for joining a party + /** + * @brief The secret for joining a party. */ std::string join; - /** The secret for spectating a game + + /** + * @brief The secret for spectating a game. */ std::string spectate; - /** The secret for a specific instanced match + + /** + * @brief The secret for a specific instanced match. */ std::string match; @@ -199,13 +333,20 @@ struct DPP_EXPORT activity_secrets { */ struct DPP_EXPORT activity_party { public: - /** The ID of the party + /** + * @brief The ID of the party. */ snowflake id; - /** The party's current size. Used to show the party's current size + + /** + * @brief The party's current size. + * Used to show the party's current size. */ int32_t current_size; - /** The party's maximum size. Used to show the party's maximum size + + /** + * @brief The party's maximum size. + * Used to show the party's maximum size. */ int32_t maximum_size; @@ -218,74 +359,116 @@ struct DPP_EXPORT activity_party { */ class DPP_EXPORT activity { public: - /** Name of activity - * e.g. "Fortnite" + /** + * @brief Name of activity. + * e.g. "Fortnite", "Mr Boom's Firework Factory", etc. */ std::string name; - /** State of activity or the custom user status. - * e.g. "Waiting in lobby" + + /** + * @brief State of activity or the custom user status. + * e.g. "Waiting in lobby". */ std::string state; - /** What the player is currently doing + + /** + * @brief What the player is currently doing. */ std::string details; - /** Images for the presence and their hover texts + + /** + * @brief Images for the presence and their hover texts. */ activity_assets assets; - /** URL. - * Only applicable for certain sites such a YouTube - * Alias: details + + /** + * @brief URL of activity (this is also named details). + * + * @note Only applicable for certain sites such a YouTube */ std::string url; - /** The custom buttons shown in the Rich Presence (max 2) + + /** + * @brief The custom buttons shown in the Rich Presence (max 2). */ std::vector buttons; - /** The emoji used for the custom status + + /** + * @brief The emoji used for the custom status. */ dpp::emoji emoji; - /** Information of the current party if there is one + + /** + * @brief Information of the current party if there is one. */ activity_party party; - /** Secrets for rich presence joining and spectating + + /** + * @brief Secrets for rich presence joining and spectating. */ activity_secrets secrets; - /** Activity type + + /** + * @brief Activity type. */ activity_type type; - /** Time activity was created + + /** + * @brief Time activity was created. */ time_t created_at; - /** Start time. e.g. when game was started + + /** + * @brief Start time. + * e.g. when game was started. */ time_t start; - /** End time, e.g. for songs on spotify + + /** + * @brief End time. + * e.g. for songs on spotify. */ time_t end; - /** Creating application (e.g. a linked account on the user's client) + + /** + * @brief Creating application. + * e.g. a linked account on the user's client. */ snowflake application_id; - /** Flags bitmask from dpp::activity_flags + + /** + * @brief Flags bitmask from dpp::activity_flags. */ uint8_t flags; - /** Whether or not the activity is an instanced game session + + /** + * @brief Whether or not the activity is an instanced game session. */ bool is_instance; /** * @brief Get the assets large image url if they have one, otherwise returns an empty string. In case of prefixed image IDs (mp:{image_id}) it returns an empty string. * - * @param size The size of the image in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized image is returned. - * @return image url or empty string + * @see https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-asset-image + * + * @param size The size of the image in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized image is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string image url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_large_asset_url(uint16_t size = 0) const; + std::string get_large_asset_url(uint16_t size = 0, const image_type format = i_png) const; /** * @brief Get the assets small image url if they have one, otherwise returns an empty string. In case of prefixed image IDs (mp:{image_id}) it returns an empty string. * - * @param size The size of the image in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized image is returned. - * @return image url or empty string + * @see https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-asset-image + * + * @param size The size of the image in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized image is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string image url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_small_asset_url(uint16_t size = 0) const; + std::string get_small_asset_url(uint16_t size = 0, const image_type format = i_png) const; activity(); @@ -304,17 +487,47 @@ class DPP_EXPORT activity { * @brief Represents user presence, e.g. what game they are playing and if they are online */ class DPP_EXPORT presence : public json_interface { +protected: + friend struct json_interface; + + /** Fill this object from json. + * @param j JSON object to fill from + * @return A reference to self + */ + presence& fill_from_json_impl(nlohmann::json* j); + + /** Build JSON from this object. + * + * @note This excludes any part of the presence object that are not valid for websockets and bots, + * and includes websocket opcode 3. You will not get what you expect if you call this on a user's + * presence received from on_presence_update or on_guild_create! + * + * @param with_id Add ID to output + * @return The JSON text of the presence + */ + virtual json to_json_impl(bool with_id = false) const; + public: - /** The user the presence applies to */ - snowflake user_id; + /** + * @brief The user the presence applies to. + */ + snowflake user_id; - /** Guild ID. Apparently, Discord supports this internally but the client doesn't... */ - snowflake guild_id; + /** + * @brief Guild ID. + * + * @note Apparently, Discord supports this internally, but the client doesn't... + */ + snowflake guild_id; - /** Flags bitmask containing dpp::presence_flags */ - uint8_t flags; + /** + * @brief Flags bitmask containing dpp::presence_flags + */ + uint8_t flags; - /** List of activities */ + /** + * @brief List of activities. + */ std::vector activities; /** Constructor */ @@ -340,45 +553,46 @@ class DPP_EXPORT presence : public json_interface { /** Destructor */ ~presence(); - /** Fill this object from json. - * @param j JSON object to fill from - * @return A reference to self - */ - presence& fill_from_json(nlohmann::json* j); - - /** Build JSON from this object. - * - * Note: This excludes any part of the presence object that are not valid for websockets and bots, - * and includes websocket opcode 3. You will not get what you expect if you call this on a user's - * presence received from on_presence_update or on_guild_create! - * - * @param with_id Add ID to output - * @return The JSON text of the presence - */ - virtual std::string build_json(bool with_id = false) const; - - /** The users status on desktop + /** + * @brief The users status on desktop * @return The user's status on desktop */ presence_status desktop_status() const; - /** The user's status on web + /** + * @brief The user's status on web * @return The user's status on web */ presence_status web_status() const; - /** The user's status on mobile + /** + * @brief The user's status on mobile * @return The user's status on mobile */ presence_status mobile_status() const; - /** The user's status as shown to other users + /** + * @brief The user's status as shown to other users * @return The user's status as shown to other users */ presence_status status() const; + + /** + * @brief Build JSON from this object. + * + * @note This excludes any part of the presence object that are not valid for websockets and bots, + * and includes websocket opcode 3. You will not get what you expect if you call this on a user's + * presence received from on_presence_update or on_guild_create! + * + * @param with_id Add ID to output + * @return The JSON of the presence + */ + json to_json(bool with_id = false) const; // Intentional shadow of json_interface, mostly present for documentation }; -/** A container of presences */ +/** + * @brief A container of presences + */ typedef std::unordered_map presence_map; -}; +} diff --git a/3rdParty/dpp/prune.h b/3rdParty/dpp/prune.h index 23f335fc25..7cdfb13e21 100644 --- a/3rdParty/dpp/prune.h +++ b/3rdParty/dpp/prune.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -21,7 +22,7 @@ #pragma once #include #include -#include +#include #include namespace dpp { @@ -30,34 +31,50 @@ namespace dpp { * @brief Defines a request to count prunable users, or start a prune operation */ struct DPP_EXPORT prune : public json_interface { +protected: + friend struct json_interface; + + /** Fill this object from json. + * @param j JSON object to fill from + * @return A reference to self + */ + prune& fill_from_json_impl(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_prune_count True if the prune count boolean is to be set in the built JSON + * @return The JSON of the prune object + */ + virtual json to_json_impl(bool with_prune_count = false) const; + +public: /** - * Destroy this prune object + * @brief Destroy this prune object */ virtual ~prune() = default; - /** Number of days to include in the prune + /** + * @brief Number of days to include in the prune. */ uint32_t days = 0; - /** Roles to include in the prune (empty to include everyone) + + /** + * @brief Roles to include in the prune (empty to include everyone). */ std::vector include_roles; - /** True if the count of pruneable users should be returned - * (discord recommend not using this on big guilds) - */ - bool compute_prune_count; - /** Fill this object from json. - * @param j JSON object to fill from - * @return A reference to self + /** + * @brief True if the count of pruneable users should be returned. + * @warning Discord recommend not using this on big guilds. */ - prune& fill_from_json(nlohmann::json* j); + bool compute_prune_count; - /** Build JSON from this object. - * @param with_prune_count True if the prune count boolean is to be set in the built JSON - * @return The JSON text of the prune object + /** + * @brief Build JSON from this object. + * + * @param with_id True if the prune count boolean is to be set in the built JSON + * @return The JSON of the prune object */ - virtual std::string build_json(bool with_prune_count = false) const; - + json to_json(bool with_id = false) const; // Intentional shadow of json_interface, mostly present for documentation }; -}; +} diff --git a/3rdParty/dpp/queues.h b/3rdParty/dpp/queues.h index ae5ee6e4c3..3fe2f57982 100644 --- a/3rdParty/dpp/queues.h +++ b/3rdParty/dpp/queues.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -29,6 +30,7 @@ #include #include #include +#include namespace dpp { @@ -36,31 +38,69 @@ namespace dpp { * @brief Error values. Most of these are currently unused in https_client. */ enum http_error { - /// Request successful + /** + * @brief Request successful. + */ h_success = 0, - /// Status unknown + + /** + * @brief Status unknown. + */ h_unknown, - /// Connect failed + + /** + * @brief Connect failed. + */ h_connection, - /// Invalid local ip address + + /** + * @brief Invalid local ip address. + */ h_bind_ip_address, - /// Read error + + /** + * @brief Read error. + */ h_read, - /// Write error + + /** + * @brief Write error. + */ h_write, - /// Too many 30x redirects + + /** + * @brief Too many 30x redirects. + */ h_exceed_redirect_count, - /// Request cancelled + + /** + * @brief Request cancelled. + */ h_canceled, - /// SSL connection error + + /** + * @brief SSL connection error. + */ h_ssl_connection, - /// SSL cert loading error + + /** + * @brief SSL cert loading error. + */ h_ssl_loading_certs, - /// SSL server verification error + + /** + * @brief SSL server verification error. + */ h_ssl_server_verification, - /// Unsupported multipart boundary characters + + /** + * @brief Unsupported multipart boundary characters. + */ h_unsupported_multipart_boundary_chars, - /// Compression error + + /** + * @brief Compression error. + */ h_compression, }; @@ -69,32 +109,67 @@ enum http_error { * rate limit figures, and returned request body. */ struct DPP_EXPORT http_request_completion_t { - /** @brief HTTP headers of response */ - std::map headers; - /** @brief HTTP status, e.g. 200 = OK, 404 = Not found, 429 = Rate limited */ + /** + * @brief HTTP headers of response. + */ + std::multimap headers; + + /** + * @brief HTTP status. + * e.g. 200 = OK, 404 = Not found, 429 = Rate limited, etc. + */ uint16_t status = 0; - /** @brief Error status (e.g. if the request could not connect at all) */ + + /** + * @brief Error status. + * e.g. if the request could not connect at all. + */ http_error error = h_success; - /** @brief Ratelimit bucket */ + + /** + * @brief Ratelimit bucket. + */ std::string ratelimit_bucket; - /** @brief Ratelimit limit of requests */ + + /** + * @brief Ratelimit limit of requests. + */ uint64_t ratelimit_limit = 0; - /** @brief Ratelimit remaining requests */ + + /** + * @brief Ratelimit remaining requests. + */ uint64_t ratelimit_remaining = 0; - /** @brief Ratelimit reset after (seconds) */ + + /** + * @brief Ratelimit reset after (seconds). + */ uint64_t ratelimit_reset_after = 0; - /** @brief Ratelimit retry after (seconds) */ + + /** + * @brief Ratelimit retry after (seconds). + */ uint64_t ratelimit_retry_after = 0; - /** @brief True if this request has caused us to be globally rate limited */ + + /** + * @brief True if this request has caused us to be globally rate limited. + */ bool ratelimit_global = false; - /** @brief Reply body */ + + /** + * @brief Reply body. + */ std::string body; - /** @brief Ping latency */ + + /** + * @brief Ping latency. + */ double latency; }; /** * @brief Results of HTTP requests are called back to these std::function types. + * * @note Returned http_completion_events are called ASYNCHRONOUSLY in your * code which means they execute in a separate thread. The completion events * arrive in order. @@ -105,15 +180,29 @@ typedef std::function http_completion_ev * @brief Various types of http method supported by the Discord API */ enum http_method { - /// GET + /** + * @brief GET. + */ m_get, - /// POST + + /** + * @brief POST. + */ m_post, - /// PUT + + /** + * @brief PUT. + */ m_put, - /// PATCH + + /** + * @brief PATCH. + */ m_patch, - /// DELETE + + /** + * @brief DELETE. + */ m_delete }; @@ -128,34 +217,90 @@ enum http_method { * request_queue class. */ class DPP_EXPORT http_request { - /** @brief Completion callback */ + /** + * @brief Completion callback. + */ http_completion_event complete_handler; - /** @brief True if request has been made */ + + /** + * @brief True if request has been made. + */ bool completed; - /** @brief True for requests that are not going to discord (rate limits code skipped) */ + + /** + * @brief True for requests that are not going to discord (rate limits code skipped). + */ bool non_discord; public: - /** @brief Endpoint name e.g. /api/users */ + /** + * @brief Endpoint name + * e.g. /api/users. + */ std::string endpoint; - /** @brief Major and minor parameters */ + + /** + * @brief Major and minor parameters. + */ std::string parameters; - /** @brief Postdata for POST and PUT */ + + /** + * @brief Postdata for POST and PUT. + */ std::string postdata; - /** @brief HTTP method for request */ + + /** + * @brief HTTP method for request. + */ http_method method; - /** @brief Audit log reason for Discord requests, if non-empty */ + + /** + * @brief Audit log reason for Discord requests, if non-empty. + */ std::string reason; - /** @brief Upload file name (server side) */ + + /** + * @brief Upload file name (server side). + */ std::vector file_name; - /** @brief Upload file contents (binary) */ + + /** + * @brief Upload file contents (binary). + */ std::vector file_content; - /** @brief Request mime type */ + + /** + * @brief Upload file mime types. + * application/octet-stream if unspecified. + */ + std::vector file_mimetypes; + + /** + * @brief Request mime type. + */ std::string mimetype; - /** @brief Request headers (non-discord requests only) */ + + /** + * @brief Request headers (non-discord requests only). + */ std::multimap req_headers; - /** @brief Waiting for rate limit to expire */ + + /** + * @brief Waiting for rate limit to expire. + */ bool waiting; + /** + * @brief HTTP protocol. + */ + std::string protocol; + + /** + * @brief How many seconds before the connection is considered failed if not finished + * + * @deprecated Please now use dpp::cluster::request_timeout + */ + DPP_DEPRECATED("Please now use dpp::cluster::request_timeout") time_t request_timeout; + /** * @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request(). * @param _endpoint The API endpoint, e.g. /api/guilds @@ -166,8 +311,10 @@ class DPP_EXPORT http_request { * @param audit_reason Audit log reason to send, empty to send none * @param filename The filename (server side) of any uploaded file * @param filecontent The binary content of any uploaded file for the request + * @param filemimetype The MIME type of any uploaded file for the request + * @param http_protocol HTTP protocol */ - http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata = "", http_method method = m_get, const std::string &audit_reason = "", const std::string &filename = "", const std::string &filecontent = ""); + http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata = "", http_method method = m_get, const std::string &audit_reason = "", const std::string &filename = "", const std::string &filecontent = "", const std::string &filemimetype = "", const std::string &http_protocol = "1.1"); /** * @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request(). @@ -179,8 +326,10 @@ class DPP_EXPORT http_request { * @param audit_reason Audit log reason to send, empty to send none * @param filename The filename (server side) of any uploaded file * @param filecontent The binary content of any uploaded file for the request + * @param filemimetypes The MIME type of any uploaded file for the request + * @param http_protocol HTTP protocol */ - http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata = "", http_method method = m_get, const std::string &audit_reason = "", const std::vector &filename = {}, const std::vector &filecontent = {}); + http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata = "", http_method method = m_get, const std::string &audit_reason = "", const std::vector &filename = {}, const std::vector &filecontent = {}, const std::vector &filemimetypes = {}, const std::string &http_protocol = "1.1"); /** * @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request(). @@ -190,8 +339,10 @@ class DPP_EXPORT http_request { * @param _postdata Data to send in POST and PUT requests * @param _mimetype POST data mime type * @param _headers HTTP headers to send + * @param http_protocol HTTP protocol + * @param _request_timeout How many seconds before the connection is considered failed if not finished */ - http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap &_headers = {}); + http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap &_headers = {}, const std::string &http_protocol = "1.1", time_t _request_timeout = 5); /** * @brief Destroy the http request object @@ -219,15 +370,29 @@ class DPP_EXPORT http_request { * each endpoint. */ struct DPP_EXPORT bucket_t { - /** @brief Request limit */ + /** + * @brief Request limit. + */ uint64_t limit; - /** @brief Requests remaining */ + + /** + * @brief Requests remaining. + */ uint64_t remaining; - /** @brief Ratelimit of this bucket resets after this many seconds */ + + /** + * @brief Rate-limit of this bucket resets after this many seconds. + */ uint64_t reset_after; - /** @brief Ratelimit of this bucket can be retried after this many seconds */ + + /** + * @brief Rate-limit of this bucket can be retried after this many seconds. + */ uint64_t retry_after; - /** @brief Timestamp this buckets counters were updated */ + + /** + * @brief Timestamp this buckets counters were updated. + */ time_t timestamp; }; @@ -243,47 +408,47 @@ struct DPP_EXPORT bucket_t { class DPP_EXPORT in_thread { private: /** - * @brief True if ending + * @brief True if ending. */ - bool terminating; + std::atomic terminating; /** - * @brief Request queue that owns this in_thread + * @brief Request queue that owns this in_thread. */ class request_queue* requests; /** - * @brief The cluster that owns this in_thread + * @brief The cluster that owns this in_thread. */ class cluster* creator; /** - * @brief Inbound queue mutex thread safety + * @brief Inbound queue mutex thread safety. */ std::shared_mutex in_mutex; /** - * @brief Inbound queue thread + * @brief Inbound queue thread. */ std::thread* in_thr; /** - * @brief Inbound queue condition, signalled when there are requests to fulfill + * @brief Inbound queue condition, signalled when there are requests to fulfill. */ std::condition_variable in_ready; /** - * @brief Ratelimit bucket counters + * @brief Rate-limit bucket counters. */ std::map buckets; /** - * @brief Queue of requests to be made + * @brief Queue of requests to be made. Sorted by http_request::endpoint. */ - std::map> requests_in; + std::vector> requests_in; /** - * @brief Inbound queue thread loop + * @brief Inbound queue thread loop. * @param index Thread index */ void in_loop(uint32_t index); @@ -303,13 +468,19 @@ class DPP_EXPORT in_thread { */ ~in_thread(); + /** + * @brief Terminates the thread + * This will end the thread that is owned by this object, but will not join it. + */ + void terminate(); + /** * @brief Post a http_request to this thread. * * @param req http_request to post. The pointer will be freed when it has * been executed. */ - void post_request(http_request* req); + void post_request(std::unique_ptr req); }; /** @@ -355,14 +526,30 @@ class DPP_EXPORT request_queue { std::thread* out_thread; /** - * @brief Outbound queue condition, signalled when there are requests completed to call callbacks for + * @brief Outbound queue condition. + * Signalled when there are requests completed to call callbacks for. */ std::condition_variable out_ready; + /** + * @brief A completed request. Contains both the request and the response + */ + struct completed_request { + /** + * @brief Request sent + */ + std::unique_ptr request; + + /** + * @brief Response to the request + */ + std::unique_ptr response; + }; + /** * @brief Completed requests queue */ - std::queue> responses_out; + std::queue responses_out; /** * @brief A vector of inbound request threads forming a pool. @@ -373,17 +560,46 @@ class DPP_EXPORT request_queue { * 2) Requests for different endpoints go into different buckets, so that they may be requested in parallel * A global ratelimit event pauses all threads in the pool. These are few and far between. */ - std::vector requests_in; + std::vector> requests_in; /** - * @brief Completed requests to delete + * @brief A request queued for deletion in the queue. */ - std::multimap> responses_to_delete; + struct queued_deleting_request { + /** + * @brief Time to delete the request + */ + time_t time_to_delete; + + /** + * @brief The request to delete + */ + completed_request request; + + /** + * @brief Comparator for sorting purposes + * @param other Other queued request to compare the deletion time with + * @return bool Whether this request comes before another in strict ordering + */ + bool operator<(const queued_deleting_request& other) const noexcept; + + /** + * @brief Comparator for sorting purposes + * @param time Time to compare with + * @return bool Whether this request's deletion time is lower than the time given, for strict ordering + */ + bool operator<(time_t time) const noexcept; + }; + + /** + * @brief Completed requests to delete. Sorted by deletion time + */ + std::vector responses_to_delete; /** * @brief Set to true if the threads should terminate */ - bool terminating; + std::atomic terminating; /** * @brief True if globally rate limited - makes the entire request thread wait @@ -391,7 +607,9 @@ class DPP_EXPORT request_queue { bool globally_ratelimited; /** - * @brief How many seconds we are globally rate limited for, if globally_ratelimited is true + * @brief How many seconds we are globally rate limited for + * + * @note Only if globally_ratelimited is true. */ uint64_t globally_limited_for; @@ -438,14 +656,13 @@ class DPP_EXPORT request_queue { ~request_queue(); /** - * @brief Put a http_request into the request queue. You should ALWAYS "new" an object - * to pass to here -- don't submit an object that's on the stack! + * @brief Put a http_request into the request queue. * @note Will use a simple hash function to determine which of the 'in queues' to place * this request onto. * @param req request to add * @return reference to self */ - request_queue& post_request(http_request *req); + request_queue& post_request(std::unique_ptr req); /** * @brief Returns true if the bot is currently globally rate limited @@ -454,4 +671,4 @@ class DPP_EXPORT request_queue { bool is_globally_ratelimited() const; }; -}; +} diff --git a/3rdParty/dpp/restrequest.h b/3rdParty/dpp/restrequest.h index 74ca431f6e..d4522963c8 100644 --- a/3rdParty/dpp/restrequest.h +++ b/3rdParty/dpp/restrequest.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace dpp { @@ -132,7 +132,7 @@ template inline void rest_request_list(dpp::cluster* c, const char* bas */ template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { - std::unordered_map list; + invite_map list; confirmation_callback_t e(c, confirmation(), http); if (!e.is_error()) { for (auto & curr_item : j) { @@ -144,6 +144,7 @@ template<> inline void rest_request_list(dpp::cluster* c, const char* ba } }); } + /** * @brief Templated REST request helper to save on typing (for returned lists, specialised for voiceregions) * @@ -160,7 +161,7 @@ template<> inline void rest_request_list(dpp::cluster* c, const char* ba */ template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { - std::unordered_map list; + voiceregion_map list; confirmation_callback_t e(c, confirmation(), http); if (!e.is_error()) { for (auto & curr_item : j) { @@ -174,7 +175,98 @@ template<> inline void rest_request_list(dpp::cluster* c, const cha } /** - * @brief Templated REST request helper to save on typing (for returned vectors) + * @brief Templated REST request helper to save on typing (for returned lists, specialised for bans) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + std::unordered_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j) { + ban curr_ban = ban().fill_from_json(&curr_item); + list[curr_ban.user_id] = curr_ban; + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} + +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for sticker packs) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { + c->post_rest(basepath, major, minor, method, postdata, [c, key, callback](json &j, const http_request_completion_t& http) { + std::unordered_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + if (j.contains("sticker_packs")) { + for (auto &curr_item: j["sticker_packs"]) { + list[snowflake_not_null(&curr_item, key.c_str())] = sticker_pack().fill_from_json(&curr_item); + } + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} + +/** + * @brief Templated REST request helper to save on typing (for returned lists) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param root Root element to look for + * @param callback Callback lambda + */ +template inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key, const std::string& root) { + c->post_rest(basepath, major, minor, method, postdata, [c, root, key, callback](json &j, const http_request_completion_t& http) { + std::unordered_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j[root]) { + list[snowflake_not_null(&curr_item, key.c_str())] = T().fill_from_json(&curr_item); + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} + +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for objects which doesn't have ids) * * @tparam T singular type to return in lambda callback * @tparam T vector type to return in lambda callback @@ -202,4 +294,4 @@ template inline void rest_request_vector(dpp::cluster* c, const char* b } -}; \ No newline at end of file +} diff --git a/3rdParty/dpp/restresults.h b/3rdParty/dpp/restresults.h index 8a5afb4c59..cf127bc081 100644 --- a/3rdParty/dpp/restresults.h +++ b/3rdParty/dpp/restresults.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -43,34 +43,11 @@ #include #include #include - -using json = nlohmann::json; +#include +#include namespace dpp { -#ifdef _WIN32 - #ifdef _DEBUG - extern "C" DPP_EXPORT void you_are_using_a_debug_build_of_dpp_on_a_release_project(); - #else - extern "C" DPP_EXPORT void you_are_using_a_release_build_of_dpp_on_a_debug_project(); - #endif -#endif - -struct DPP_EXPORT version_checker { - version_checker() { - #ifdef _WIN32 - #ifdef _DEBUG - you_are_using_a_debug_build_of_dpp_on_a_release_project(); - #else - you_are_using_a_release_build_of_dpp_on_a_debug_project(); - #endif - #endif - } -}; - -static version_checker dpp_vc; - - /** * @brief A list of shards */ @@ -79,23 +56,47 @@ typedef std::map shard_list; /** * @brief Represents the various information from the 'get gateway bot' api call */ -struct DPP_EXPORT gateway { - /// Gateway websocket url +struct DPP_EXPORT gateway : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill this object from json + * + * @param j json to fill from + * @return gateway& reference to self + */ + gateway& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief Gateway websocket url. + */ std::string url; - /// Number of suggested shards to start + /** + * @brief Number of suggested shards to start. + */ uint32_t shards; - /// Total number of sessions that can be started + /** + * @brief Total number of sessions that can be started. + */ uint32_t session_start_total; - /// How many sessions are left + /** + * @brief How many sessions are left. + */ uint32_t session_start_remaining; - /// How many seconds until the session start quota resets + /** + * @brief How many seconds until the session start quota resets. + */ uint32_t session_start_reset_after; - /// How many sessions can be started at the same time + /** + * @brief How many sessions can be started at the same time. + */ uint32_t session_start_max_concurrency; /** @@ -109,14 +110,6 @@ struct DPP_EXPORT gateway { * @brief Construct a new gateway object */ gateway(); - - /** - * @brief Fill this object from json - * - * @param j json to fill from - * @return gateway& reference to self - */ - gateway& fill_from_json(nlohmann::json* j); }; /** @@ -132,6 +125,7 @@ struct DPP_EXPORT confirmation { * */ typedef std::variant< + active_threads, application_role_connection, application_role_connection_metadata_list, confirmation, @@ -162,6 +156,7 @@ typedef std::variant< ban_map, voiceregion, voiceregion_map, + voicestate, integration, integration_map, webhook, @@ -190,7 +185,13 @@ typedef std::variant< event_member, event_member_map, automod_rule, - automod_rule_map + automod_rule_map, + onboarding, + welcome_screen, + entitlement, + entitlement_map, + sku, + sku_map > confirmable_t; /** @@ -201,18 +202,26 @@ struct DPP_EXPORT error_detail { * @brief Object name which is in error */ std::string object; + /** * @brief Field name which is in error */ std::string field; + /** * @brief Error code */ std::string code; + /** * @brief Error reason (full message) */ std::string reason; + + /** + * @brief Object field index + */ + DPP_DEPRECATED("index is unused and will be removed in a future version") int index = 0; }; /** @@ -223,32 +232,45 @@ struct DPP_EXPORT error_info { * @brief Error code */ uint32_t code = 0; + /** * @brief Error message * */ std::string message; + /** * @brief Field specific error descriptions */ std::vector errors; + + /** + * @brief Human readable error message constructed from the above + */ + std::string human_readable; }; /** * @brief The results of a REST call wrapped in a convenient struct */ struct DPP_EXPORT confirmation_callback_t { - /** Information about the HTTP call used to make the request */ + /** + * @brief Information about the HTTP call used to make the request. + */ http_request_completion_t http_info; - /** Value returned, wrapped in variant */ + /** + * @brief Value returned, wrapped in variant. + */ confirmable_t value; - /** Owner/creator of the callback object */ + /** + * @brief Owner/creator of the callback object. + */ const class cluster* bot; /** - * @brief Construct a new confirmation callback t object + * @brief Construct a new confirmation callback t object. */ confirmation_callback_t() = default; @@ -313,4 +335,4 @@ typedef std::function command_completion_e * @brief Automatically JSON encoded HTTP result */ typedef std::function json_encode_t; -}; +} diff --git a/3rdParty/dpp/role.h b/3rdParty/dpp/role.h index 1508c8d913..df733d6507 100644 --- a/3rdParty/dpp/role.h +++ b/3rdParty/dpp/role.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,72 +23,182 @@ #include #include #include -#include +#include #include #include #include namespace dpp { -/** Various flags related to dpp::role */ +/** + * @brief Various flags related to dpp::role + */ enum role_flags : uint8_t { - r_hoist = 0b00000001, //!< Hoisted role - r_managed = 0b00000010, //!< Managed role (introduced by a bot or application) - r_mentionable = 0b00000100, //!< Mentionable with a ping - r_premium_subscriber = 0b00001000, //!< This is set for the role given to nitro + /** + * @brief Hoisted role (if the role is pinned in the user listing). + */ + r_hoist = 0b00000001, + + /** + * @brief Managed role (introduced by a bot or application). + */ + r_managed = 0b00000010, + + /** + * @brief Whether this role is mentionable with a ping. + */ + r_mentionable = 0b00000100, + + /** + * @brief Whether this is the guild's booster role. + */ + r_premium_subscriber = 0b00001000, + + /** + * @brief Whether the role is available for purchase. + */ + r_available_for_purchase = 0b00010000, + + /** + * @brief Whether the role is a guild's linked role. + */ + r_guild_connections = 0b00100000, + + /** + * @brief Whether the role can be selected by members in an onboarding prompt. + */ + r_in_prompt = 0b01000000, }; /** * @brief Represents a role within a dpp::guild. * Roles are combined via logical OR of the permission bitmasks, then channel-specific overrides * can be applied on top, deny types apply a logic NOT to the bit mask, and allows apply a logical OR. + * * @note Every guild has at least one role, called the 'everyone' role, which always has the same role * ID as the guild's ID. This is the base permission set applied to all users where no other role or override * applies, and is the starting value of the bit mask looped through to calculate channel permissions. */ -class DPP_EXPORT role : public managed, public json_interface { +class DPP_EXPORT role : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill this role from json. + * + * @param j The json data + * @return A reference to self + */ + role& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build a json from this object. + * + * @param with_id true if the ID is to be included in the json + * @return The json of the role + */ + virtual json to_json_impl(bool with_id = false) const; + public: /** * @brief Role name * Between 1 and 100 characters. */ - std::string name; + std::string name{}; + /** * @brief Guild ID */ - snowflake guild_id; + snowflake guild_id{0}; + /** * @brief Role colour. * A colour of 0 means no colour. If you want a black role, * you must use the value 0x000001. */ - uint32_t colour; - /** Role position */ - uint8_t position; - /** Role permissions bitmask values from dpp::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) */ - snowflake integration_id; - /** Bot id if any (e.g. role is a bot's role created when it was invited) */ - snowflake bot_id; - /** The unicode emoji used for the role's icon, can be an empty string */ - std::string unicode_emoji; - /** The role icon hash, can be an empty string */ - utility::iconhash icon; - /** Image data for the role icon (if any) */ - std::string* image_data; + uint32_t colour{0}; + + /** + * @brief Role position. + */ + uint8_t position{0}; + + /** + * @brief Role permissions bitmask values from dpp::permissions. + */ + permission permissions{}; + + /** + * @brief Role flags from dpp::role_flags + */ + uint8_t flags{0}; + + /** + * @brief Integration id if any. + * (e.g. role is a bot's role created when it was invited). + */ + snowflake integration_id{}; + + /** + * @brief Bot id if any. + * (e.g. role is a bot's role created when it was invited) + */ + snowflake bot_id{}; + + /** + * @brief The id of the role's subscription sku and listing. + */ + snowflake subscription_listing_id{}; + + /** + * @brief The unicode emoji used for the role's icon. + * + * @note This can be an empty string. + */ + std::string unicode_emoji{}; + + /** + * @brief The role icon. + */ + utility::icon icon{}; /** * @brief Construct a new role object */ - role(); + role() = default; + + /** + * @brief Construct a new role object. + * + * @param rhs Role object to copy + */ + role(const role& rhs) = default; + + /** + * @brief Construct a new role object. + * + * @param rhs Role object to move + */ + role(role&& rhs) = default; + + /** + * @brief Copy another role object + * + * @param rhs Role object to copy + */ + role &operator=(const role& rhs) = default; + + /** + * @brief Move from another role object + * + * @param rhs Role object to copy + */ + role &operator=(role&& rhs) = default; /** * @brief Destroy the role object */ - virtual ~role(); + virtual ~role() = default; /** * @brief Create a mentionable role. @@ -97,7 +208,7 @@ class DPP_EXPORT role : public managed, public json_interface { static std::string get_mention(const snowflake& id); /** - * @brief Set the name of the role + * @brief Set the name of the role. * Maximum length: 100 * Minimum length: 1 * @param n Name to set @@ -107,7 +218,7 @@ class DPP_EXPORT role : public managed, public json_interface { role& set_name(const std::string& n); /** - * @brief Set the colour + * @brief Set the colour. * * @param c Colour to set * @note There is an americanised version of this method, role::set_color(). @@ -116,7 +227,7 @@ class DPP_EXPORT role : public managed, public json_interface { role& set_colour(uint32_t c); /** - * @brief Set the color + * @brief Set the color. * * @param c Colour to set * @note This is an alias of role::set_colour for American spelling. @@ -125,7 +236,7 @@ class DPP_EXPORT role : public managed, public json_interface { role& set_color(uint32_t c); /** - * @brief Set the flags + * @brief Set the flags. * * @param f Flags to set from dpp::role_flags * @return role& reference to self @@ -133,7 +244,7 @@ class DPP_EXPORT role : public managed, public json_interface { role& set_flags(uint8_t f); /** - * @brief Set the integration id + * @brief Set the integration ID. * * @param i Integration ID to set * @return role& reference to self @@ -141,7 +252,7 @@ class DPP_EXPORT role : public managed, public json_interface { role& set_integration_id(snowflake i); /** - * @brief Set the bot id + * @brief Set the bot ID. * * @param b Bot ID to set * @return role& reference to self @@ -149,20 +260,14 @@ class DPP_EXPORT role : public managed, public json_interface { role& set_bot_id(snowflake b); /** - * @brief Set the guild id + * @brief Set the guild ID. * * @param gid Guild ID to set * @return role& reference to self */ role& set_guild_id(snowflake gid); - /** - * @brief Fill this role from json. - * - * @param j The json data - * @return A reference to self - */ - role& fill_from_json(nlohmann::json* j); + using json_interface::fill_from_json; /** * @brief Fill this role from json. @@ -174,36 +279,40 @@ class DPP_EXPORT role : public managed, public json_interface { role& fill_from_json(snowflake guild_id, nlohmann::json* j); /** - * @brief Build a json string from this object. - * - * @param with_id true if the ID is to be included in the json text - * @return The json of the role - */ - virtual std::string build_json(bool with_id = false) const; - - /** - * @brief Get the mention/ping for the role + * @brief Get the mention/ping for the role. * * @return std::string mention */ std::string get_mention() const; /** - * @brief Returns the role's icon if they have one, otherwise returns an empty string + * @brief Returns the role's icon url if they have one, otherwise returns an empty string. * - * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096. If not specified, the default sized icon is returned. - * @return std::string icon url or empty string + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed */ - std::string get_icon_url(uint16_t size = 0) const; + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png) const; /** - * @brief Load an image into the object as base64 - * + * @brief Load a role icon. + * * @param image_blob Image binary data - * @param type Type of image + * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @return emoji& Reference to self + */ + role& load_image(std::string_view image_blob, const image_type type); + + /** + * @brief Load a role icon. + * + * @param data Image binary data + * @param size Size of the image. + * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. * @return emoji& Reference to self */ - role& load_image(const std::string &image_blob, const image_type type); + role& load_image(const std::byte* data, uint32_t size, const image_type type); /** * @brief Operator less than, used for checking if a role is below another. @@ -212,8 +321,7 @@ class DPP_EXPORT role : public managed, public json_interface { * @param rhs second role to compare * @return true if lhs is less than rhs */ - friend inline bool operator< (const role& lhs, const role& rhs) - { + friend inline bool operator< (const role& lhs, const role& rhs) { return lhs.position < rhs.position; } @@ -224,8 +332,7 @@ class DPP_EXPORT role : public managed, public json_interface { * @param rhs second role to compare * @return true if lhs is greater than rhs */ - friend inline bool operator> (const role& lhs, const role& rhs) - { + friend inline bool operator> (const role& lhs, const role& rhs) { return lhs.position > rhs.position; } @@ -235,8 +342,7 @@ class DPP_EXPORT role : public managed, public json_interface { * @param other role to compare * @return true if is equal to other */ - inline bool operator== (const role& other) const - { + inline bool operator== (const role& other) const { return this->position == other.position; } @@ -246,303 +352,422 @@ class DPP_EXPORT role : public managed, public json_interface { * @param other role to compare * @return true if is not equal to other */ - inline bool operator!= (const role& other) const - { + inline bool operator!= (const role& other) const { return this->position != other.position; } /** - * @brief True if the role is hoisted + * @brief True if the role is hoisted. + * * @return bool Role appears separated from others in the member list */ bool is_hoisted() const; + /** - * @brief True if the role is mentionable + * @brief True if the role is mentionable. + * * @return bool Role is mentionable */ bool is_mentionable() const; + /** - * @brief True if the role is managed (belongs to a bot or application) + * @brief True if the role is managed (belongs to a bot or application). + * * @return bool True if the role is managed (introduced for a bot or other application by Discord) */ bool is_managed() const; + + /** + * @brief True if the role is the guild's Booster role. + * + * @return bool whether the role is the premium subscriber, AKA "boost", role for the guild + */ + bool is_premium_subscriber() const; + + /** + * @brief True if the role is available for purchase. + * + * @return bool whether this role is available for purchase + */ + bool is_available_for_purchase() const; + + /** + * @brief True if the role is a linked role. + * + * @return bool True if the role is a linked role + */ + bool is_linked() const; + + /** + * @brief True if the role can be selected by members in an onboarding prompt. + * + * @return bool True if the role can be selected by members in an onboarding prompt + */ + bool is_selectable_in_prompt() const; + /** - * @brief True if has create instant invite permission + * @brief True if has create instant invite 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 instant invite permission or is administrator. */ bool has_create_instant_invite() const; + /** * @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. */ bool has_kick_members() const; + /** * @brief True if has the ban 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 ban members permission or is administrator. */ bool has_ban_members() const; + /** * @brief True if has the administrator 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 administrator permission or is administrator. */ bool has_administrator() const; + /** * @brief True if has the manage channels 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 manage channels permission or is administrator. */ bool has_manage_channels() const; + /** * @brief True if has the manage guild 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 manage guild permission or is administrator. */ bool has_manage_guild() const; + /** * @brief True if has the add reactions 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 add reactions permission or is administrator. */ bool has_add_reactions() const; + /** * @brief True if has the view audit log 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 view audit log permission or is administrator. */ bool has_view_audit_log() const; + /** * @brief True if has the priority speaker 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 priority speaker permission or is administrator. */ bool has_priority_speaker() const; + /** * @brief True if has the stream 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 stream permission or is administrator. */ bool has_stream() const; + /** * @brief True if has the view channel 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 view channel permission or is administrator. */ bool has_view_channel() const; + /** * @brief True if has the send messages 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 send messages permission or is administrator. */ bool has_send_messages() const; + /** * @brief True if has the send TTS messages 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 send TTS messages permission or is administrator. */ bool has_send_tts_messages() const; + /** * @brief True if has the manage messages 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 manage messages permission or is administrator. */ bool has_manage_messages() const; + /** * @brief True if has the embed links 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 embed links permission or is administrator. */ bool has_embed_links() const; + /** * @brief True if has the attach files 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 attach files permission or is administrator. */ bool has_attach_files() const; + /** * @brief True if has the read message history 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 read message history permission or is administrator. */ bool has_read_message_history() const; + /** * @brief True if has the mention \@everyone and \@here 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 mention \@everyone and \@here permission or is administrator. */ bool has_mention_everyone() const; + /** * @brief True if has the use external emojis 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 use external emojis permission or is administrator. */ bool has_use_external_emojis() const; + /** * @brief True if has the view guild insights 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 view guild insights permission or is administrator. */ bool has_view_guild_insights() const; + /** * @brief True if has the connect voice 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 connect voice permission or is administrator. */ bool has_connect() const; + /** * @brief True if has the speak 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 speak permission or is administrator. */ bool has_speak() const; + /** * @brief True if has the mute 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 mute members permission or is administrator. */ bool has_mute_members() const; + /** * @brief True if has the deafen 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 deafen members permission or is administrator. */ bool has_deafen_members() const; + /** * @brief True if has the move 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 move members permission or is administrator. */ bool has_move_members() const; - /** True if has use voice activity detection permission */ + + /** + * @brief True if has use voice activity detection 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 use voice activity detection permission or is administrator. + */ bool has_use_vad() const; + /** * @brief True if has the change nickname 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 change nickname permission or is administrator. */ bool has_change_nickname() const; + /** * @brief True if has the manage nicknames 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 manage nicknames permission or is administrator. */ bool has_manage_nicknames() const; + /** * @brief True if has the manage roles 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 manage roles permission or is administrator. */ bool has_manage_roles() const; + /** * @brief True if has the manage webhooks 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 manage webhooks permission or is administrator. */ bool has_manage_webhooks() const; + /** * @brief True if has the manage emojis and stickers 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 manage emojis and stickers permission or is administrator. */ bool has_manage_emojis_and_stickers() const; + /** * @brief True if has the use application commands 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 use application commands permission or is administrator. */ bool has_use_application_commands() const; + /** * @brief True if has the request to speak 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 request to speak permission or is administrator. */ bool has_request_to_speak() const; + /** * @brief True if has the manage threads 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 manage threads permission or is administrator. */ bool has_manage_threads() const; + /** * @brief True if has the create public threads 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 create public threads permission or is administrator. */ bool has_create_public_threads() const; + /** * @brief True if has the create private threads 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 create private threads permission or is administrator. */ bool has_create_private_threads() const; + /** * @brief True if has the use external stickers 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 use external stickers permission or is administrator. */ + bool has_use_external_stickers() const; /** * @brief True if has the send messages in threads 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 send messages in threads permission or is administrator. */ bool has_send_messages_in_threads() const; + /** * @brief True if has the start embedded activities 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 start embedded activities permission or is administrator. */ bool has_use_embedded_activities() const; + /** * @brief True if has the manage events 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 manage events permission or is administrator. */ bool has_manage_events() const; + /** * @brief True if has the moderate users 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 moderate users permission or is administrator. @@ -550,7 +775,53 @@ class DPP_EXPORT role : public managed, public json_interface { bool has_moderate_members() const; /** - * @brief Get guild members who have this role + * @brief True if has the view creator monetization analytics 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 view creator monetization analytics permission or is administrator. + */ + bool has_view_creator_monetization_analytics() const; + + /** + * @brief True if has the use soundboard 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 use soundboard permission or is administrator. + */ + bool has_use_soundboard() const; + + /** + * @brief True if has the use external sounds 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 use external sounds permission or is administrator. + */ + bool has_use_external_sounds() const; + + /** + * @brief True if has the send voice messages 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 send voice messages permission or is administrator. + */ + bool has_send_voice_messages() const; + + /** + * @brief True if has permission to use clyde AI. + * + * @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 clyde AI permission or is administrator. + */ + bool has_use_clyde_ai() const; + + /** + * @brief Get guild members who have this role. + * * @note This method requires user/members cache to be active * @return members_container List of members who have this role */ @@ -560,89 +831,168 @@ class DPP_EXPORT role : public managed, public json_interface { /** * @brief Application Role Connection Metadata Type * - * @note Each metadata type offers a comparison operation that allows guilds to configure role requirements based on metadata values stored by the bot. Bots specify a `metadata value` for each user and guilds specify the required `guild's configured value` within the guild role settings. + * @note Each metadata type offers a comparison operation that allows guilds + * to configure role requirements based on metadata values stored by the bot. + * Bots specify a `metadata value` for each user and guilds specify the + * required `guild's configured value` within the guild role settings. */ enum application_role_connection_metadata_type : uint8_t { - rc_integer_less_than_or_equal = 1, //!< The metadata value (integer) is less than or equal to the guild's configured value (integer) - rc_integer_greater_than_or_equal = 2, //!< The metadata value (integer) is greater than or equal to the guild's configured value (integer) - rc_integer_equal = 3, //!< The metadata value (integer) is equal to the guild's configured value (integer) - rc_integer_not_equal = 4, //!< The metadata value (integer) is not equal to the guild's configured value (integer) - rc_datetime_less_than_or_equal = 5, //!< The metadata value (ISO8601 string) is less than or equal to the guild's configured value (integer; days before current date) - rc_datetime_greater_than_or_equal = 6, //!< The metadata value (ISO8601 string) is greater than or equal to the guild's configured value (integer; days before current date) - rc_boolean_equal = 7, //!< The metadata value (integer) is equal to the guild's configured value (integer; 1) - rc_boolean_not_equal = 8, //!< The metadata value (integer) is not equal to the guild's configured value (integer; 1) + /** + * @brief The metadata value (integer) is less than or equal to the guild's configured value (integer). + */ + rc_integer_less_than_or_equal = 1, + + /** + * @brief The metadata value (integer) is greater than or equal to the guild's configured value (integer). + */ + rc_integer_greater_than_or_equal = 2, + + /** + * @brief The metadata value (integer) is equal to the guild's configured value (integer). + */ + rc_integer_equal = 3, + + /** + * @brief The metadata value (integer) is not equal to the guild's configured value (integer). + */ + rc_integer_not_equal = 4, + + /** + * @brief The metadata value (ISO8601 string) is less than or equal to the guild's configured value (integer; days before current date). + */ + rc_datetime_less_than_or_equal = 5, + + /** + * @brief The metadata value (ISO8601 string) is greater than or equal to the guild's configured value (integer; days before current date). + */ + rc_datetime_greater_than_or_equal = 6, + + /** + * @brief The metadata value (integer) is equal to the guild's configured value (integer; 1). + */ + rc_boolean_equal = 7, + + /** + * @brief The metadata value (integer) is not equal to the guild's configured value (integer; 1). + */ + rc_boolean_not_equal = 8, }; /** * @brief Application Role Connection Metadata. Represents a role connection metadata for an dpp::application */ class DPP_EXPORT application_role_connection_metadata : public json_interface { -public: - application_role_connection_metadata_type type; //!< Type of metadata value - std::string key; //!< Dictionary key for the metadata field (must be `a-z`, `0-9`, or `_` characters; max 50 characters) - std::string name; //!< Name of the metadata field (max 100 characters) - std::map name_localizations; //!< Translations of the name - std::string description; //!< Description of the metadata field (max 200 characters) - std::map description_localizations; //!< Translations of the description - - /** - * Constructor - */ - application_role_connection_metadata(); - - virtual ~application_role_connection_metadata() = default; +protected: + friend struct json_interface; /** Fill this record from json. * @param j The json to fill this record from * @return Reference to self */ - application_role_connection_metadata& fill_from_json(nlohmann::json* j); + application_role_connection_metadata& fill_from_json_impl(nlohmann::json* j); /** - * @brief Convert to JSON string + * @brief Convert to JSON * * @param with_id include ID in output - * @return std::string JSON output + * @return json JSON output + */ + virtual json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief Type of metadata value. + */ + application_role_connection_metadata_type type; + + /** + * @brief Dictionary key for the metadata field (must be `a-z`, `0-9`, or `_` characters; 1-50 characters). + */ + std::string key; + + /** + * @brief Name of the metadata field (1-100 characters). + */ + std::string name; + + /** + * @brief Translations of the name. */ - virtual std::string build_json(bool with_id = false) const; + std::map name_localizations; + + /** + * @brief Description of the metadata field (1-200 characters). + */ + std::string description; + + /** + * @brief Translations of the description. + */ + std::map description_localizations; + + /** + * @brief Constructor + */ + application_role_connection_metadata(); + + virtual ~application_role_connection_metadata() = default; }; /** * @brief The application role connection that an application has attached to a user. */ class DPP_EXPORT application_role_connection : public json_interface { -public: - std::string platform_name; //!< Optional: The vanity name of the platform a bot has connected (max 50 characters) - std::string platform_username; //!< Optional: The username on the platform a bot has connected (max 100 characters) - std::variant metadata; //!< Optional: Object mapping application role connection metadata keys to their stringified value (max 100 characters) for the user on the platform a bot has connected +protected: + friend struct json_interface; /** - * Constructor - */ - application_role_connection(); - - virtual ~application_role_connection() = default; - - /** Fill this record from json. + * @brief Fill this record from json. * @param j The json to fill this record from * @return Reference to self */ - application_role_connection& fill_from_json(nlohmann::json* j); + application_role_connection& fill_from_json_impl(nlohmann::json* j); /** - * @brief Convert to JSON string + * @brief Convert to JSON * * @param with_id include ID in output - * @return std::string JSON output + * @return json JSON output + */ + virtual json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief Optional: The vanity name of the platform a bot has connected (max 50 characters). + */ + std::string platform_name; + + /** + * @brief Optional: The username on the platform a bot has connected (max 100 characters). + */ + std::string platform_username; + + /** + * @brief Optional: Object mapping application role connection metadata keys to their "stringified" value (max 100 characters) for the user on the platform a bot has connected. + */ + std::variant metadata; + + /** + * @brief Constructor */ - virtual std::string build_json(bool with_id = false) const; + application_role_connection(); + + virtual ~application_role_connection() = default; }; -/** A group of roles */ +/** + * @brief A group of roles. + */ typedef std::unordered_map role_map; -/** A group of application_role_connection_metadata objects */ +/** + * @brief A group of dpp::application_role_connection_metadata objects. + */ typedef std::vector application_role_connection_metadata_list; -}; +} diff --git a/3rdParty/dpp/scheduled_event.h b/3rdParty/dpp/scheduled_event.h index ceec00d79f..c234fc73d6 100644 --- a/3rdParty/dpp/scheduled_event.h +++ b/3rdParty/dpp/scheduled_event.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -24,105 +25,215 @@ #include #include #include -#include +#include #include namespace dpp { /** - * @brief Represents the privacy of an event + * @brief Represents the privacy of an event. */ enum event_privacy_level : uint8_t { - /// The event is visible to only guild members. + /** + * @brief The event is visible to only guild members. + */ ep_guild_only = 2 }; /** - * @brief Event entity types + * @brief Event entity types. */ enum event_entity_type : uint8_t { - /// A stage instance + /** + * @brief A stage instance. + */ eet_stage_instance = 1, - /// A voice channel + + /** + * @brief A voice channel. + */ eet_voice = 2, - /// External to discord, or a text channel etc + + /** + * @brief External to discord, or a text channel etc. + */ eet_external = 3 }; /** - * @brief Event status types + * @brief Event status types. */ enum event_status : uint8_t { - /// Scheduled - es_scheduled = 1, - /// Active now - es_active = 2, - /// Completed - es_completed = 3, - /// Cancelled - es_cancelled = 4 + /** + * @brief Scheduled. + */ + es_scheduled = 1, + + /** + * @brief Active now. + */ + es_active = 2, + + /** + * @brief Completed. + */ + es_completed = 3, + + /** + * @brief Cancelled. + */ + es_cancelled = 4 }; /** - * @brief Entities for the event + * @brief Entities for the event. */ struct DPP_EXPORT event_entities { - /// location of the event + /** + * @brief Location of the event. + */ std::string location; }; /** - * @brief Represents a guild member/user who has registered interest in an event + * @brief Represents a guild member/user who has registered interest in an event. * */ struct DPP_EXPORT event_member { /** - * @brief Event ID associated with + * @brief Event ID associated with. */ snowflake guild_scheduled_event_id; + /** - * @brief User details of associated user + * @brief User details of associated user. * */ dpp::user user; + /** - * @brief Member details of user on the associated guild + * @brief Member details of user on the associated guild. */ dpp::guild_member member; }; /** - * @brief A scheduled event + * @brief A scheduled event. */ struct DPP_EXPORT scheduled_event : public managed, public json_interface { - snowflake guild_id; //!< the guild id which the scheduled event belongs to - snowflake channel_id; //!< the channel id in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL (may be empty) - snowflake creator_id; //!< Optional: the id of the user that created the scheduled event - std::string name; //!< the name of the scheduled event - std::string description; //!< Optional: the description of the scheduled event (1-1000 characters) - std::string image; //!< the image of the scheduled event (may be empty) - time_t scheduled_start_time; //!< the time the scheduled event will start - time_t scheduled_end_time; //!< the time the scheduled event will end, or null if the event does not have a scheduled time to end (may be empty) - event_privacy_level privacy_level; //!< the privacy level of the scheduled event - event_status status; //!< the status of the scheduled event - event_entity_type entity_type; //!< the type of hosting entity associated with a scheduled event, e.g. voice channel or stage channel - snowflake entity_id; //!< any additional id of the hosting entity associated with event, e.g. stage instance id) (may be empty) - event_entities entity_metadata; //!< the entity metadata for the scheduled event (may be empty) - user creator; //!< Optional: the creator of the scheduled event - uint32_t user_count; //!< Optional: the number of users subscribed to the scheduled event - - /** - * @brief Create a scheduled_event object +protected: + friend struct json_interface; + + /** + * @brief Serialise a scheduled_event object from json + * + * @return scheduled_event& a reference to self */ - scheduled_event(); + scheduled_event& fill_from_json_impl(const nlohmann::json* j); + + /** + * @brief Build json for this object + * @param with_id Include id field in json + * + * @return std::string Json of this object + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief The guild ID which the scheduled event belongs to. + */ + snowflake guild_id; + + /** + * @brief The channel ID in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL. + * + * @note This may be empty. + */ + snowflake channel_id; + + /** + * @brief Optional: The ID of the user that created the scheduled event. + */ + snowflake creator_id; /** - * @brief Destroy the scheduled_event object + * @brief The name of the scheduled event. */ - ~scheduled_event() = default; + std::string name; /** - * @brief Set the name of the event + * @brief Optional: The description of the scheduled event (1-1000 characters). + */ + std::string description; + + /** + * @brief The image of the scheduled event. + * + * @note This may be empty. + */ + utility::icon image; + + /** + * @brief The time the scheduled event will start. + */ + time_t scheduled_start_time; + + /** + * @brief The time the scheduled event will end, or null if the event does not have a scheduled time to end. + * + * @note This may be empty. + */ + time_t scheduled_end_time; + + /** + * @brief The privacy level of the scheduled event. + */ + event_privacy_level privacy_level; + + /** + * @brief The status of the scheduled event. + */ + event_status status; + + /** + * @brief The type of hosting entity associated with a scheduled event. + * e.g. voice channel or stage channel. + */ + event_entity_type entity_type; + + /** + * @brief Any additional ID of the hosting entity associated with event. + * e.g. stage instance ID. + * + * @note This may be empty. + */ + snowflake entity_id; + + /** + * @brief The entity metadata for the scheduled event. + * + * @note This may be empty. + */ + event_entities entity_metadata; + + /** + * @brief Optional: The creator of the scheduled event. + */ + user creator; + + /** + * @brief Optional: The number of users subscribed to the scheduled event. + */ + uint32_t user_count; + + /** + * @brief Create a scheduled_event object. + */ + scheduled_event(); + + /** + * @brief Set the name of the event. * Minimum length: 1, Maximum length: 100 * @param n event name * @return scheduled_event& reference to self @@ -131,7 +242,7 @@ struct DPP_EXPORT scheduled_event : public managed, public json_interface scheduled_event_map; /** - * @brief A group of scheduled event members + * @brief A group of scheduled event members. */ typedef std::unordered_map event_member_map; -}; +} // namespace dpp diff --git a/3rdParty/dpp/sku.h b/3rdParty/dpp/sku.h new file mode 100644 index 0000000000..59c149c89a --- /dev/null +++ b/3rdParty/dpp/sku.h @@ -0,0 +1,176 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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 { + +/** + * @brief The type of SKU. + * */ +enum sku_type : uint8_t { + /** + * @brief Represents a durable one-time purchase + */ + DURABLE = 2, + /** + * @brief Consumable one-time purchase + */ + CONSUMABLE = 3, + /** + * @brief Represents a recurring subscription + */ + SUBSCRIPTION = 5, + + /** + * @brief System-generated group for each SUBSCRIPTION SKU created + * @warning These are automatically created for each subscription SKU and are not used at this time. Please refrain from using these. + */ + SUBSCRIPTION_GROUP = 6, +}; + +/** + * @brief SKU flags. + */ +enum sku_flags : uint16_t { + /** + * @brief SKU is available for purchase + */ + sku_available = 0b000000000000100, + + /** + * @brief Recurring SKU that can be purchased by a user and applied to a single server. Grants access to every user in that server. + */ + sku_guild_subscription = 0b000000010000000, + + /** + * @brief Recurring SKU purchased by a user for themselves. Grants access to the purchasing user in every server. + */ + sku_user_subscription = 0b000000100000000, +}; + +/** + * @brief A definition of a discord SKU. + */ +class DPP_EXPORT sku : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + sku& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build json for this SKU object + * + * @param with_id include the ID in the json + * @return json JSON object + */ + json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief The type of SKU. + */ + sku_type type{sku_type::SUBSCRIPTION}; + + /** + * @brief ID of the parent application + */ + snowflake application_id{0}; + + /** + * @brief Customer-facing name of your premium offering + */ + std::string name{}; + + /** + * @brief System-generated URL slug based on the SKU's name + */ + std::string slug{}; + + /** + * @brief Flags bitmap from dpp::sku_flags + */ + uint16_t flags{0}; + + /** + * @brief Construct a new SKU object + */ + sku() = default; + + /** + * @brief Construct a new SKU object with all data required. + * + * @param id ID of the SKU. + * @param type Type of SKU (sku_type). + * @param application_id ID of the parent application. + * @param name Customer-facing name of your premium offering. + * @param slug System-generated URL slug based on the SKU's name. + * @param flags Flags bitmap from dpp::sku_flags. + * + */ + sku(const snowflake id, const sku_type type, const snowflake application_id, const std::string name, const std::string slug, const uint16_t flags); + + /** + * @brief Get the type of SKU. + * + * @return sku_type SKU type + */ + sku_type get_type() const; + + /** + * @brief Is the SKU available for purchase? + * + * @return true if the SKU can be purchased. + */ + bool is_available() const; + + /** + * @brief Is the SKU a guild subscription? + * + * @return true if the SKU is a guild subscription. + */ + bool is_guild_subscription() const; + + /** + * @brief Is the SKU a user subscription? + * + * @return true if the SKU is a user subscription + */ + bool is_user_subscription() const; +}; + +/** + * @brief Group of SKUs. + */ +typedef std::unordered_map sku_map; + +} diff --git a/3rdParty/dpp/snowflake.h b/3rdParty/dpp/snowflake.h index 033b1d10f5..a675964ebe 100644 --- a/3rdParty/dpp/snowflake.h +++ b/3rdParty/dpp/snowflake.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -20,9 +21,14 @@ ************************************************************************************/ #pragma once #include -#include -#include -#include +#include +#include +#include +#include + +#ifdef DPP_FORMATTERS +#include +#endif /** * @brief The main namespace for D++ functions. classes and types @@ -31,17 +37,17 @@ namespace dpp { /** @brief A container for a 64 bit unsigned value representing many things on discord. * This value is known in distributed computing as a snowflake value. - * + * * Snowflakes are: - * + * * - Performant (very fast to generate at source and to compare in code) * - Uncoordinated (allowing high availability across clusters, data centres etc) * - Time ordered (newer snowflakes have higher IDs) * - Directly Sortable (due to time ordering) * - Compact (64 bit numbers, not 128 bit, or string) - * + * * An identical format of snowflake is used by Twitter, Instagram and several other platforms. - * + * * @see https://en.wikipedia.org/wiki/Snowflake_ID * @see https://github.com/twitter-archive/snowflake/tree/b3f6a3c6ca8e1b6847baa6ff42bf72201e2c2231 */ @@ -51,146 +57,251 @@ class DPP_EXPORT snowflake final { /** * @brief The snowflake value */ - uint64_t value; + uint64_t value = 0; public: /** * @brief Construct a snowflake object - * @param value A snowflake value */ - snowflake(const uint64_t& value); + constexpr snowflake() noexcept = default; /** - * @brief Construct a snowflake object - * @param value A snowflake value + * @brief Copy a snowflake object */ - snowflake(const std::string& string_value); + constexpr snowflake(const snowflake &rhs) noexcept = default; /** - * @brief Construct a snowflake object - */ - snowflake(); + * @brief Move a snowflake object + */ + constexpr snowflake(snowflake &&rhs) noexcept = default; /** - * @brief Destroy the snowflake object + * @brief Construct a snowflake from an integer value + * + * @throw dpp::logic_exception on assigning a negative value. The function is noexcept if the type given is unsigned + * @param snowflake_val snowflake value as an integer type */ - ~snowflake() = default; + template && !std::is_same_v>> + constexpr snowflake(T snowflake_val) noexcept(std::is_unsigned_v) : value(static_cast>(snowflake_val)) { + /** + * we cast to the unsigned version of the type given - this maintains "possible loss of data" warnings for sizeof(T) > sizeof(value) + * while suppressing them for signed to unsigned conversion (for example snowflake(42) will call snowflake(int) which is a signed type) + */ + if constexpr (!std::is_unsigned_v) { + /* if the type is signed, at compile-time, add a check at runtime that the value is unsigned */ + if (snowflake_val < 0) { + value = 0; + throw dpp::logic_exception{"cannot assign a negative value to dpp::snowflake"}; + } + } + } /** - * @brief For acting like an integer - * @return The snowflake value + * @brief Construct a snowflake object from an unsigned integer in a string + * + * On invalid string the value will be 0 + * @param string_value A snowflake value + */ + snowflake(std::string_view string_value) noexcept; + + /** + * @brief Construct a snowflake object from an unsigned integer in a string + * + * On invalid string the value will be 0 + * @param string_value A snowflake value + */ + template >> + snowflake(const T &string_value) noexcept : snowflake(std::string_view{string_value}) {} + /* ^ this exists to preserve `message_cache.find(std::get(event.get_parameter("message_id")));` */ + + /** + * @brief Copy value from another snowflake + * + * @param rhs The snowflake to copy from + */ + constexpr dpp::snowflake &operator=(const dpp::snowflake& rhs) noexcept = default; + + /** + * @brief Move value from another snowflake + * + * @param rhs The snowflake to move from + */ + constexpr dpp::snowflake &operator=(dpp::snowflake&& rhs) noexcept = default; + + /** + * @brief Assign integer value to the snowflake + * + * @throw dpp::logic_exception on assigning a negative value. The function is noexcept if the type given is unsigned + * @param snowflake_val snowflake value as an integer type */ - operator uint64_t() const; + template >> + constexpr dpp::snowflake &operator=(T snowflake_val) noexcept(std::is_unsigned_v) { + return *this = dpp::snowflake{snowflake_val}; + } + + /** + * @brief Assign value converted from a string to the snowflake + * + * On invalid string the value will be 0 + * @param snowflake_val snowflake value as a string + */ + template >> + constexpr dpp::snowflake &operator=(T&& snowflake_val) noexcept { + return *this = dpp::snowflake{std::forward(snowflake_val)}; + } /** * @brief Returns true if the snowflake holds an empty value (is 0) - * + * * @return true if empty (zero) */ - inline bool empty() const - { + constexpr bool empty() const noexcept { return value == 0; } /** - * @brief Operator less than, used for maps/unordered maps - * when the snowflake is a key value. - * - * @param lhs fist snowflake to compare - * @param rhs second snowflake to compare - * @return true if lhs is less than rhs + * @brief Returns the stringified version of the snowflake value + * + * @return std::string string form of snowflake value */ - friend inline bool operator< (const snowflake& lhs, const snowflake& rhs) - { - return lhs.value < rhs.value; + inline std::string str() const { + return std::to_string(value); } /** - * @brief Assign from std::string - * - * @param snowflake_val string to assign from. + * @brief Comparison operator with another snowflake + * + * @param snowflake_val snowflake */ - snowflake& operator=(const std::string &snowflake_val); + constexpr bool operator==(dpp::snowflake snowflake_val) const noexcept { + return value == snowflake_val.value; + } /** - * @brief Assign from std::string - * - * @param snowflake_val value to assign from. + * @brief Comparison operator with a string + * + * @param snowflake_val snowflake value as a string */ - snowflake& operator=(const uint64_t &snowflake_val); + bool operator==(std::string_view snowflake_val) const noexcept; /** - * @brief Check if one snowflake value is equal to another - * - * @param other other snowflake to compare - * @return True if the snowflake objects match + * @brief Comparison operator with an integer + * + * @param snowflake_val snowflake value as an integer type */ - bool operator==(const snowflake& other) const; + template >> + constexpr bool operator==(T snowflake_val) const noexcept { + /* We use the std::enable_if_t trick to disable implicit conversions so there is a perfect candidate for overload resolution for integers, and it isn't ambiguous */ + return *this == dpp::snowflake{snowflake_val}; + } /** - * @brief Check if one snowflake value is equal to a uint64_t - * - * @param other other snowflake to compare - * @return True if the snowflake objects match + * @brief For acting like an integer + * @return The snowflake value */ - bool operator==(const uint64_t& other) const; + constexpr operator uint64_t() const noexcept { + return value; + } /** * @brief For acting like an integer * @return A reference to the snowflake value */ - operator uint64_t &(); + constexpr operator uint64_t &() noexcept { + return value; + } /** * @brief For building json * @return The snowflake value as a string */ - operator nlohmann::json() const; + operator json() const; /** * @brief Get the creation time of this snowflake according to Discord. - * + * * @return double creation time inferred from the snowflake ID. * The minimum possible value is the first second of 2015. */ - double get_creation_time() const; + constexpr double get_creation_time() const noexcept { + constexpr uint64_t first_january_2016 = 1420070400000ull; + return static_cast((value >> 22) + first_january_2016) / 1000.0; + } /** * @brief Get the worker id that produced this snowflake value - * + * * @return uint8_t worker id */ - uint8_t get_worker_id() const; + constexpr uint8_t get_worker_id() const noexcept { + return static_cast((value & 0x3E0000) >> 17); + } /** * @brief Get the process id that produced this snowflake value - * + * * @return uint8_t process id */ - uint8_t get_process_id() const; + constexpr uint8_t get_process_id() const noexcept { + return static_cast((value & 0x1F000) >> 12); + } /** * @brief Get the increment, which is incremented for every snowflake * created over the one millisecond resolution in the timestamp. - * + * * @return uint64_t millisecond increment */ - uint16_t get_increment() const; -}; + constexpr uint16_t get_increment() const noexcept { + return static_cast(value & 0xFFF); + } + /** + * @brief Helper function for libfmt so that a snowflake can be directly formatted as a uint64_t. + * + * @see https://fmt.dev/latest/api.html#formatting-user-defined-types + * @return uint64_t snowflake ID + */ + friend constexpr uint64_t format_as(snowflake s) noexcept { + /* note: this function must stay as "friend" - this declares it as a free function but makes it invisible unless the argument is snowflake + * this effectively means no implicit conversions are performed to snowflake, for example format_as(0) doesn't call this function */ + return s.value; + } }; +} + template<> struct std::hash { /** - * @brief Hashing function for dpp::slowflake + * @brief Hashing function for dpp::snowflake * Used by std::unordered_map. This just calls std::hash. - * + * * @param s Snowflake value to hash * @return std::size_t hash value */ - std::size_t operator()(dpp::snowflake const& s) const noexcept { + std::size_t operator()(dpp::snowflake s) const noexcept { return std::hash{}(s.value); } }; + +#ifdef DPP_FORMATTERS +/* + * @brief implementation of formater for dpp::snowflake for std::format support + * https://en.cppreference.com/w/cpp/utility/format/formatter + */ +template <> +struct std::formatter +{ + template + constexpr typename TP::iterator parse(TP& ctx) { + return ctx.begin(); + } + + template + typename TF::iterator format(const dpp::snowflake& snowflake, TF& ctx) const { + return std::format_to(ctx.out(), "{}", snowflake.str()); + } +}; +#endif //DPP_FORMATTERS diff --git a/3rdParty/dpp/socket.h b/3rdParty/dpp/socket.h index 21be6da1b7..b11ef8bcd5 100644 --- a/3rdParty/dpp/socket.h +++ b/3rdParty/dpp/socket.h @@ -1,19 +1,53 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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 -#ifndef _WIN32 -#ifndef SOCKET -#define SOCKET int -#endif +#include +#ifdef _WIN32 + #include + #include + #include + #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) + #define pollfd WSAPOLLFD +#else + #include + #include + #include #endif +#include +#include + namespace dpp { - /** - * @brief Represents a socket file descriptor. - * This is used to ensure parity between windows and unix-like systems. - */ - typedef SOCKET socket; -} +/** + * @brief Represents a socket file descriptor. + * This is used to ensure parity between windows and unix-like systems. + */ +#ifndef _WIN32 + using socket = int; +#else + using socket = SOCKET; +#endif #ifndef SOCKET_ERROR /** @@ -27,4 +61,93 @@ namespace dpp * @brief Represents a socket which is not yet assigned */ #define INVALID_SOCKET ~0 -#endif \ No newline at end of file +#endif + +/** + * @brief Represents an IPv4 address for use with socket functions such as + * bind(). + * + * Avoids type punning with C style casts from sockaddr_in to sockaddr pointers. + */ +class DPP_EXPORT address_t { + /** + * @brief Internal sockaddr struct + */ + sockaddr socket_addr{}; + +public: + + /** + * @brief Create a new address_t + * @param ip IPv4 address + * @param port Port number + * @note Leave both as defaults to create a default bind-to-any setting + */ + address_t(const std::string_view ip = "0.0.0.0", uint16_t port = 0); + + /** + * @brief Get sockaddr + * @return sockaddr pointer + */ + [[nodiscard]] sockaddr *get_socket_address(); + + /** + * @brief Returns size of sockaddr_in + * @return sockaddr_in size + * @note It is important the size this returns is sizeof(sockaddr_in) not + * sizeof(sockaddr), this is NOT a bug but requirement of C socket functions. + */ + [[nodiscard]] size_t size(); + + /** + * @brief Get the port bound to a file descriptor + * @param fd File descriptor + * @return Port number, or 0 if no port bound + */ + [[nodiscard]] uint16_t get_port(socket fd); +}; + +/** + * @brief Allocates a dpp::socket, closing it on destruction + */ +struct DPP_EXPORT raii_socket { + /** + * @brief File descriptor + */ + socket fd; + + /** + * @brief Construct a socket. + * Calls socket() and returns a new file descriptor + */ + raii_socket(); + + /** + * @brief Non-copyable + */ + raii_socket(raii_socket&) = delete; + + /** + * @brief Non-movable + */ + raii_socket(raii_socket&&) = delete; + + /** + * @brief Non-copyable + */ + raii_socket operator=(raii_socket&) = delete; + + /** + * @brief Non-movable + */ + raii_socket operator=(raii_socket&&) = delete; + + /** + * @brief Destructor + * Frees the socket by closing it + */ + ~raii_socket(); +}; + + +} diff --git a/3rdParty/dpp/sslclient.h b/3rdParty/dpp/sslclient.h index 8d07048a5f..6e278904cd 100644 --- a/3rdParty/dpp/sslclient.h +++ b/3rdParty/dpp/sslclient.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -23,7 +24,9 @@ #include #include #include +#include #include +#include namespace dpp { @@ -56,6 +59,7 @@ bool close_socket(dpp::socket sfd); * @brief Set a socket to blocking or non-blocking IO * * @param sockfd socket to act upon + * @param non_blocking should socket be non-blocking? * @return false on error, true on success */ bool set_nonblocking(dpp::socket sockfd, bool non_blocking); @@ -69,6 +73,11 @@ bool set_nonblocking(dpp::socket sockfd, bool non_blocking); */ class DPP_EXPORT ssl_client { +private: + /** + * @brief Clean up resources + */ + void cleanup(); protected: /** * @brief Input buffer received from socket @@ -231,10 +240,10 @@ class DPP_EXPORT ssl_client /** * @brief Write to the output buffer. - * @param data Data to be written to the buffer + * @param data Data to be written to the buffer. * @note The data may not be written immediately and may be written at a later time to the socket. */ - virtual void write(const std::string &data); + void socket_write(const std::string_view data); /** * @brief Close socket connection @@ -249,4 +258,4 @@ class DPP_EXPORT ssl_client virtual void log(dpp::loglevel severity, const std::string &msg) const; }; -}; +} diff --git a/3rdParty/dpp/stage_instance.h b/3rdParty/dpp/stage_instance.h index 6137320348..bffeac46fa 100644 --- a/3rdParty/dpp/stage_instance.h +++ b/3rdParty/dpp/stage_instance.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -32,9 +33,14 @@ namespace dpp { * @brief Represents the privacy of a stage instance */ enum stage_privacy_level : uint8_t { - /// The Stage instance is visible publicly, such as on Stage Discovery. + /** + * @brief The Stage instance is visible publicly, such as on Stage Discovery. + */ sp_public = 1, - /// The Stage instance is visible to only guild members. + + /** + * @brief The Stage instance is visible to only guild members. + */ sp_guild_only = 2 }; @@ -42,45 +48,65 @@ enum stage_privacy_level : uint8_t { * @brief A stage instance. * Stage instances are like a conference facility, with moderators/speakers and listeners. */ -struct DPP_EXPORT stage_instance : public managed, public json_interface { - /// The guild id of the associated Stage channel +struct DPP_EXPORT stage_instance : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Serialise a stage_instance object rom json + * + * @return stage_instance& a reference to self + */ + stage_instance& fill_from_json_impl(const nlohmann::json* j); + + /** + * @brief Build json for this object + * + * @param with_id include ID + * @return json Json of this object + */ + virtual json to_json_impl(bool with_id = false) const; + +public: + /** + * @brief The guild ID of the associated Stage channel. + */ snowflake guild_id; - /// The id of the associated Stage channel + + /** + * @brief The ID of the associated Stage channel. + */ snowflake channel_id; - /// The topic of the Stage instance (1-120 characters) + + /** + * @brief The topic of the Stage instance (1-120 characters). + */ std::string topic; - /// The privacy level of the Stage instance - stage_privacy_level privacy_level; - /// Whether or not Stage Discovery is disabled - bool discoverable_disabled; /** - * @brief Create a stage_instance object + * @brief The privacy level of the Stage instance. */ - stage_instance(); + stage_privacy_level privacy_level; /** - * @brief Destroy the stage_instance object + * @brief Whether or not Stage Discovery is disabled. */ - ~stage_instance() = default; + bool discoverable_disabled; /** - * @brief Serialise a stage_instance object rom json - * - * @return stage_instance& a reference to self + * @brief Create a stage_instance object */ - stage_instance& fill_from_json(const nlohmann::json* j); + stage_instance(); /** - * @brief Build json for this object - * - * @param with_id include ID - * @return std::string Dumped json of this object + * @brief Destroy the stage_instance object */ - virtual std::string build_json(bool with_id = false) const; + ~stage_instance() = default; }; -/** A group of stage instances */ +/** + * @brief A group of stage instances + */ typedef std::unordered_map stage_instance_map; -}; +} diff --git a/3rdParty/dpp/stringops.h b/3rdParty/dpp/stringops.h index c749f1f370..60a5045a46 100644 --- a/3rdParty/dpp/stringops.h +++ b/3rdParty/dpp/stringops.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace dpp { /** @@ -40,9 +41,9 @@ namespace dpp { */ template std::basic_string lowercase(const std::basic_string& s) { - std::basic_string s2 = s; - std::transform(s2.begin(), s2.end(), s2.begin(), tolower); - return s2; + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), tolower); + return s2; } /** @@ -54,9 +55,9 @@ template std::basic_string lowercase(const std::basic_string& */ template std::basic_string uppercase(const std::basic_string& s) { - std::basic_string s2 = s; - std::transform(s2.begin(), s2.end(), s2.begin(), toupper); - return s2; + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), toupper); + return s2; } /** @@ -138,6 +139,9 @@ template T from_string(const std::string &s, std::ios_base & (*f)(s */ template T from_string(const std::string &s) { + if (s.empty()) { + return static_cast(0); + } T t; std::istringstream iss(s); iss >> t; @@ -165,7 +169,7 @@ template uint64_t from_string(const std::string &s) */ template uint32_t from_string(const std::string &s) { - return std::stoul(s, 0, 10); + return (uint32_t) std::stoul(s, 0, 10); } /** @@ -185,13 +189,19 @@ template int from_string(const std::string &s) * * @tparam T numeric type * @param i numeric value + * @param leading_zeroes set to false if you don't want the leading zeroes in the output * @return std::string value in hex, the length will be 2* the raw size of the type */ -template std::string to_hex(T i) +template std::string to_hex(T i, bool leading_zeroes = true) { - std::stringstream stream; - stream << std::setfill('0') << std::setw(sizeof(T)*2) << std::hex << i; - return stream.str(); + char str[26] = { 0 }; + size_t size = sizeof(T) * 2; + std::to_chars(std::begin(str), std::end(str), i, 16); + std::string out{str}; + if (leading_zeroes && out.length() < size) { + out.insert(out.begin(), size - out.length(), '0'); + } + return out; } /** @@ -204,9 +214,10 @@ template std::string to_hex(T i) */ template std::string leading_zeroes(T i, size_t width) { - std::stringstream stream; - stream << std::setfill('0') << std::setw(width) << std::dec << i; - return stream.str(); + std::stringstream stream; + stream.imbue(std::locale::classic()); + stream << std::setfill('0') << std::setw((int)width) << std::dec << i; + return stream.str(); } -}; +} diff --git a/3rdParty/dpp/sync.h b/3rdParty/dpp/sync.h index e4d278fb5b..afb6405391 100644 --- a/3rdParty/dpp/sync.h +++ b/3rdParty/dpp/sync.h @@ -27,54 +27,55 @@ namespace dpp { - /** - * @brief Call a D++ REST function synchronously. - * - * Synchronously calling a REST function means *IT WILL BLOCK* - This is a Bad Thing™ and strongly discouraged. - * There are very few circumstances you actually need this. If you do need to use this, you'll know it. - * - * Example: - * - * ```cpp - * dpp::message m = dpp::sync(&bot, &dpp::cluster::message_create, dpp::message(channel_id, "moo.")); - * ``` - * - * @warning As previously mentioned, this template will block. It is ill-advised to call this outside of - * a separate thread and this should never be directly used in any event such as dpp::cluster::on_interaction_create! - * @tparam T type of expected return value, should match up with the method called - * @tparam F Type of class method in dpp::cluster to call. - * @tparam Ts Function parameters in method call - * @param c A pointer to dpp::cluster object - * @param func pointer to class method in dpp::cluster to call. This can call any - * dpp::cluster member function who's last parameter is a dpp::command_completion_event_t callback type. - * @param args Zero or more arguments for the method call - * @return An instantiated object of type T - * @throw dpp::rest_exception On failure of the method call, an exception is thrown - */ - template T sync(class cluster* c, F func, Ts&&... args) { - std::promise _p; - std::future _f = _p.get_future(); - /* (obj ->* func) is the obscure syntax for calling a method pointer on an object instance */ - (c ->* func)(std::forward(args)..., [&_p](const auto& cc) { - try { - if (cc.is_error()) { - throw dpp::rest_exception(cc.get_error().message); - } else { - try { - _p.set_value(std::get(cc.value)); - } catch (const std::exception& e) { - throw dpp::rest_exception(e.what()); - } - } - } catch (const dpp::rest_exception&) { - _p.set_exception(std::current_exception()); - } - }); +/** + * @brief Call a D++ REST function synchronously. + * + * Synchronously calling a REST function means *IT WILL BLOCK* - This is a Bad Thing™ and strongly discouraged. + * There are very few circumstances you actually need this. If you do need to use this, you'll know it. + * + * Example: + * + * ```cpp + * dpp::message m = dpp::sync(&bot, &dpp::cluster::message_create, dpp::message(channel_id, "moo.")); + * ``` + * + * @warning As previously mentioned, this template will block. It is ill-advised to call this outside of + * a separate thread and this should never be directly used in any event such as dpp::cluster::on_interaction_create! + * @tparam T type of expected return value, should match up with the method called + * @tparam F Type of class method in dpp::cluster to call. + * @tparam Ts Function parameters in method call + * @param c A pointer to dpp::cluster object + * @param func pointer to class method in dpp::cluster to call. This can call any + * dpp::cluster member function who's last parameter is a dpp::command_completion_event_t callback type. + * @param args Zero or more arguments for the method call + * @return An instantiated object of type T + * @throw dpp::rest_exception On failure of the method call, an exception is thrown + */ +template T sync(class cluster* c, F func, Ts&&... args) { + std::promise _p; + std::future _f = _p.get_future(); + /* (obj ->* func) is the obscure syntax for calling a method pointer on an object instance */ + (c ->* func)(std::forward(args)..., [&_p](const auto& cc) { + try { + if (cc.is_error()) { + const auto& error = cc.get_error(); + throw dpp::rest_exception((exception_error_code)error.code, error.message); + } else { + try { + _p.set_value(std::get(cc.value)); + } catch (const std::exception& e) { + throw dpp::rest_exception(err_unknown, e.what()); + } + } + } catch (const dpp::rest_exception&) { + _p.set_exception(std::current_exception()); + } + }); - /* Blocking calling thread until rest request is completed. - * Exceptions encountered on the other thread are re-thrown. - */ - return _f.get(); - } + /* Blocking calling thread until rest request is completed. + * Exceptions encountered on the other thread are re-thrown. + */ + return _f.get(); +} -}; +} diff --git a/3rdParty/dpp/thread.h b/3rdParty/dpp/thread.h new file mode 100644 index 0000000000..7cb160869f --- /dev/null +++ b/3rdParty/dpp/thread.h @@ -0,0 +1,236 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * 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 + +namespace dpp { + +/** + * @brief represents membership of a user with a thread + */ +struct DPP_EXPORT thread_member : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Read struct values from a json object + * @param j json to read values from + * @return A reference to self + */ + thread_member& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief ID of the thread member is part of. + */ + snowflake thread_id = {}; + + /** + * @brief ID of the member. + */ + snowflake user_id = {}; + + /** + * @brief The time when user last joined the thread. + */ + time_t joined = 0; + + /** + * @brief Any user-thread settings, currently only used for notifications. + */ + uint32_t flags = 0; +}; + +/** + * @brief A group of thread member objects. the key is the user_id of the dpp::thread_member + */ +typedef std::unordered_map thread_member_map; + +/** + * @brief metadata for threads + */ +struct DPP_EXPORT thread_metadata { + /** + * @brief Timestamp when the thread's archive status was last changed, used for calculating recent activity. + */ + time_t archive_timestamp; + + /** + * @brief The duration in minutes to automatically archive the thread after recent activity (60, 1440, 4320, 10080). + */ + uint16_t auto_archive_duration; + + /** + * @brief Whether a thread is archived + */ + bool archived; + + /** + * @brief Whether a thread is locked. When a thread is locked, + * only users with `MANAGE_THREADS` can un-archive it. + */ + bool locked; + + /** + * @brief Whether non-moderators can add other non-moderators. Only for private threads. + */ + bool invitable; +}; + +/** @brief A definition of a discord thread. + * A thread is a superset of a channel. Not to be confused with `std::thread`! + */ +class DPP_EXPORT thread : public channel, public json_interface { +protected: + friend struct json_interface; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + thread& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build json for this thread object + * + * @param with_id include the ID in the json + * @return std::string JSON string + */ + json to_json_impl(bool with_id = false) const override; + +public: + using json_interface::fill_from_json; + using json_interface::build_json; + using json_interface::to_json; + + /** + * @brief Thread member of current user if joined to the thread. + * Note this is only set by certain api calls otherwise contains default data + */ + thread_member member = {}; + + /** + * @brief Thread metadata (threads) + */ + thread_metadata metadata = {}; + + /** + * @brief Created message. Only filled within the cluster::thread_create_in_forum() method + */ + message msg = {}; + + /** + * @brief A list of dpp::forum_tag IDs that have been applied to a thread in a forum or media channel. + */ + std::vector applied_tags = {}; + + /** + * @brief Number of messages ever sent in the thread. + * It's similar to thread::message_count on message creation, but will not decrement the number when a message is deleted + */ + uint32_t total_messages_sent = 0; + + /** + * @brief Number of messages (not including the initial message or deleted messages) of the thread. + * For threads created before July 1, 2022, the message count is inaccurate when it's greater than 50. + */ + uint8_t message_count = 0; + + /** + * @brief Approximate count of members in a thread (stops counting at 50) + */ + uint8_t member_count = 0; + + /** + * @brief Was this thread newly created? + * @note This will only show in dpp::cluster::on_thread_create if the thread was just made. + */ + bool newly_created{false}; + + /** + * @brief Returns true if the thread is within an announcement channel + * + * @return true if announcement thread + */ + constexpr bool is_news_thread() const noexcept { + return (flags & channel::CHANNEL_TYPE_MASK) == CHANNEL_ANNOUNCEMENT_THREAD; + } + + /** + * @brief Returns true if the channel is a public thread + * + * @return true if public thread + */ + constexpr bool is_public_thread() const noexcept { + return (flags & channel::CHANNEL_TYPE_MASK) == CHANNEL_PUBLIC_THREAD; + } + + /** + * @brief Returns true if the channel is a private thread + * + * @return true if private thread + */ + constexpr bool is_private_thread() const noexcept { + return (flags & channel::CHANNEL_TYPE_MASK) == CHANNEL_PRIVATE_THREAD; + } +}; + + +/** + * @brief Serialize a thread_metadata object to json + * + * @param j JSON object to serialize to + * @param tmdata object to serialize + */ +void to_json(nlohmann::json& j, const thread_metadata& tmdata); + +/** + * @brief A group of threads + */ +typedef std::unordered_map thread_map; + +/** + * @brief A thread alongside the bot's optional thread_member object tied to it + */ +struct active_thread_info { + /** + * @brief The thread object + */ + thread active_thread; + + /** + * @brief The bot as a thread member, only present if the bot is in the thread + */ + std::optional bot_member; +}; + +/** + * @brief A map of threads alongside optionally the thread_member tied to the bot if it is in the thread. The map's key is the thread id. Returned from the cluster::threads_get_active method + */ +using active_threads = std::map; + +} diff --git a/3rdParty/dpp/timed_listener.h b/3rdParty/dpp/timed_listener.h index 2316a7797a..48051ff19b 100644 --- a/3rdParty/dpp/timed_listener.h +++ b/3rdParty/dpp/timed_listener.h @@ -40,20 +40,30 @@ namespace dpp { template class timed_listener { private: - /// Owning cluster + /** + * @brief Owning cluster. + */ cluster* owner; - /// Duration of listen + /** + * @brief Duration of listen. + */ time_t duration; - /// Reference to attached event in cluster + /** + * @brief Reference to attached event in cluster. + */ //event_router_t on_thread_member_update; attached_event& ev; - /// Timer handle + /** + * @brief Timer handle. + */ timer th; - /// Event handle + /** + * @brief Event handle. + */ event_handle listener_handle; public: @@ -73,7 +83,7 @@ template class timed_listene /* Attach event */ listener_handle = ev(listener); /* Create timer */ - th = cl->start_timer([this](dpp::timer timer_handle) { + th = cl->start_timer([this]([[maybe_unused]] dpp::timer timer_handle) { /* Timer has finished, detach it from event. * Only allowed to tick once. */ @@ -92,4 +102,4 @@ template class timed_listene } }; -}; \ No newline at end of file +} diff --git a/3rdParty/dpp/timer.h b/3rdParty/dpp/timer.h index fdc8b536b6..8e185c2072 100644 --- a/3rdParty/dpp/timer.h +++ b/3rdParty/dpp/timer.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -50,18 +51,22 @@ struct DPP_EXPORT timer_t { * @brief Timer handle */ timer handle; + /** * @brief Next timer tick as unix epoch */ time_t next_tick; + /** * @brief Frequency between ticks */ uint64_t frequency; + /** * @brief Lambda to call on tick */ timer_callback_t on_tick; + /** * @brief Lambda to call on stop (optional) */ @@ -86,9 +91,14 @@ typedef std::unordered_map timer_reg_t; class DPP_EXPORT oneshot_timer { private: - /// Owning cluster + /** + * @brief Owning cluster. + */ class cluster* owner; - /// Timer handle + + /** + * @brief Timer handle. + */ timer th; public: /** @@ -119,6 +129,4 @@ class DPP_EXPORT oneshot_timer ~oneshot_timer(); }; - - -}; +} diff --git a/3rdParty/dpp/unicode_emoji.h b/3rdParty/dpp/unicode_emoji.h new file mode 100644 index 0000000000..accb13fe3d --- /dev/null +++ b/3rdParty/dpp/unicode_emoji.h @@ -0,0 +1,5739 @@ +#pragma once + +namespace dpp { + +/** + * @brief Emoji unicodes. + * + * @note The unicode emojis in this namespace are auto-generated from https://raw.githubusercontent.com/ArkinSolomon/discord-emoji-converter/master/emojis.json + * + * @warning If you want to use this, you have to pull the header in separately. For example: + * ```cpp + * #include + * #include + * ``` + */ +namespace unicode_emoji { + inline constexpr const char _100[] = "💯"; + inline constexpr const char _1234[] = "🔢"; + inline constexpr const char soccer[] = "⚽"; + inline constexpr const char soccer_ball[] = "⚽"; + inline constexpr const char basketball[] = "🏀"; + inline constexpr const char football[] = "🏈"; + inline constexpr const char baseball[] = "⚾"; + inline constexpr const char softball[] = "🥎"; + inline constexpr const char tennis[] = "🎾"; + inline constexpr const char volleyball[] = "🏐"; + inline constexpr const char rugby_football[] = "🏉"; + inline constexpr const char flying_disc[] = "🥏"; + inline constexpr const char _8ball[] = "🎱"; + inline constexpr const char yo_yo[] = "🪀"; + inline constexpr const char ping_pong[] = "🏓"; + inline constexpr const char table_tennis[] = "🏓"; + inline constexpr const char badminton[] = "🏸"; + inline constexpr const char hockey[] = "🏒"; + inline constexpr const char ice_hockey[] = "🏒"; + inline constexpr const char field_hockey[] = "🏑"; + inline constexpr const char lacrosse[] = "🥍"; + inline constexpr const char cricket_game[] = "🏏"; + inline constexpr const char cricket_bat_ball[] = "🏏"; + inline constexpr const char boomerang[] = "🪃"; + inline constexpr const char goal[] = "🥅"; + inline constexpr const char goal_net[] = "🥅"; + inline constexpr const char golf[] = "⛳"; + inline constexpr const char flag_in_hole[] = "⛳"; + inline constexpr const char kite[] = "🪁"; + inline constexpr const char playground_slide[] = "🛝"; + inline constexpr const char bow_and_arrow[] = "🏹"; + inline constexpr const char archery[] = "🏹"; + inline constexpr const char fishing_pole_and_fish[] = "🎣"; + inline constexpr const char fishing_pole[] = "🎣"; + inline constexpr const char diving_mask[] = "🤿"; + inline constexpr const char boxing_glove[] = "🥊"; + inline constexpr const char boxing_gloves[] = "🥊"; + inline constexpr const char martial_arts_uniform[] = "🥋"; + inline constexpr const char karate_uniform[] = "🥋"; + inline constexpr const char running_shirt_with_sash[] = "🎽"; + inline constexpr const char running_shirt[] = "🎽"; + inline constexpr const char skateboard[] = "🛹"; + inline constexpr const char roller_skate[] = "🛼"; + inline constexpr const char sled[] = "🛷"; + inline constexpr const char ice_skate[] = "⛸️"; + inline constexpr const char curling_stone[] = "🥌"; + inline constexpr const char ski[] = "🎿"; + inline constexpr const char skis[] = "🎿"; + inline constexpr const char skier[] = "⛷️"; + inline constexpr const char snowboarder[] = "🏂"; + inline constexpr const char snowboarder_tone1[] = "🏂🏻"; + inline constexpr const char snowboarder_light_skin_tone[] = "🏂🏻"; + inline constexpr const char snowboarder_tone2[] = "🏂🏼"; + inline constexpr const char snowboarder_medium_light_skin_tone[] = "🏂🏼"; + inline constexpr const char snowboarder_tone3[] = "🏂🏽"; + inline constexpr const char snowboarder_medium_skin_tone[] = "🏂🏽"; + inline constexpr const char snowboarder_tone4[] = "🏂🏾"; + inline constexpr const char snowboarder_medium_dark_skin_tone[] = "🏂🏾"; + inline constexpr const char snowboarder_tone5[] = "🏂🏿"; + inline constexpr const char snowboarder_dark_skin_tone[] = "🏂🏿"; + inline constexpr const char parachute[] = "🪂"; + inline constexpr const char person_lifting_weights[] = "🏋️"; + inline constexpr const char lifter[] = "🏋️"; + inline constexpr const char weight_lifter[] = "🏋️"; + inline constexpr const char person_lifting_weights_tone1[] = "🏋🏻"; + inline constexpr const char lifter_tone1[] = "🏋🏻"; + inline constexpr const char weight_lifter_tone1[] = "🏋🏻"; + inline constexpr const char person_lifting_weights_tone2[] = "🏋🏼"; + inline constexpr const char lifter_tone2[] = "🏋🏼"; + inline constexpr const char weight_lifter_tone2[] = "🏋🏼"; + inline constexpr const char person_lifting_weights_tone3[] = "🏋🏽"; + inline constexpr const char lifter_tone3[] = "🏋🏽"; + inline constexpr const char weight_lifter_tone3[] = "🏋🏽"; + inline constexpr const char person_lifting_weights_tone4[] = "🏋🏾"; + inline constexpr const char lifter_tone4[] = "🏋🏾"; + inline constexpr const char weight_lifter_tone4[] = "🏋🏾"; + inline constexpr const char person_lifting_weights_tone5[] = "🏋🏿"; + inline constexpr const char lifter_tone5[] = "🏋🏿"; + inline constexpr const char weight_lifter_tone5[] = "🏋🏿"; + inline constexpr const char woman_lifting_weights[] = "🏋️‍♀️"; + inline constexpr const char woman_lifting_weights_tone1[] = "🏋🏻‍♀️"; + inline constexpr const char woman_lifting_weights_light_skin_tone[] = "🏋🏻‍♀️"; + inline constexpr const char woman_lifting_weights_tone2[] = "🏋🏼‍♀️"; + inline constexpr const char woman_lifting_weights_medium_light_skin_tone[] = "🏋🏼‍♀️"; + inline constexpr const char woman_lifting_weights_tone3[] = "🏋🏽‍♀️"; + inline constexpr const char woman_lifting_weights_medium_skin_tone[] = "🏋🏽‍♀️"; + inline constexpr const char woman_lifting_weights_tone4[] = "🏋🏾‍♀️"; + inline constexpr const char woman_lifting_weights_medium_dark_skin_tone[] = "🏋🏾‍♀️"; + inline constexpr const char woman_lifting_weights_tone5[] = "🏋🏿‍♀️"; + inline constexpr const char woman_lifting_weights_dark_skin_tone[] = "🏋🏿‍♀️"; + inline constexpr const char man_lifting_weights[] = "🏋️‍♂️"; + inline constexpr const char man_lifting_weights_tone1[] = "🏋🏻‍♂️"; + inline constexpr const char man_lifting_weights_light_skin_tone[] = "🏋🏻‍♂️"; + inline constexpr const char man_lifting_weights_tone2[] = "🏋🏼‍♂️"; + inline constexpr const char man_lifting_weights_medium_light_skin_tone[] = "🏋🏼‍♂️"; + inline constexpr const char man_lifting_weights_tone3[] = "🏋🏽‍♂️"; + inline constexpr const char man_lifting_weights_medium_skin_tone[] = "🏋🏽‍♂️"; + inline constexpr const char man_lifting_weights_tone4[] = "🏋🏾‍♂️"; + inline constexpr const char man_lifting_weights_medium_dark_skin_tone[] = "🏋🏾‍♂️"; + inline constexpr const char man_lifting_weights_tone5[] = "🏋🏿‍♂️"; + inline constexpr const char man_lifting_weights_dark_skin_tone[] = "🏋🏿‍♂️"; + inline constexpr const char people_wrestling[] = "🤼"; + inline constexpr const char wrestlers[] = "🤼"; + inline constexpr const char wrestling[] = "🤼"; + inline constexpr const char women_wrestling[] = "🤼‍♀️"; + inline constexpr const char men_wrestling[] = "🤼‍♂️"; + inline constexpr const char person_doing_cartwheel[] = "🤸"; + inline constexpr const char cartwheel[] = "🤸"; + inline constexpr const char person_doing_cartwheel_tone1[] = "🤸🏻"; + inline constexpr const char cartwheel_tone1[] = "🤸🏻"; + inline constexpr const char person_doing_cartwheel_tone2[] = "🤸🏼"; + inline constexpr const char cartwheel_tone2[] = "🤸🏼"; + inline constexpr const char person_doing_cartwheel_tone3[] = "🤸🏽"; + inline constexpr const char cartwheel_tone3[] = "🤸🏽"; + inline constexpr const char person_doing_cartwheel_tone4[] = "🤸🏾"; + inline constexpr const char cartwheel_tone4[] = "🤸🏾"; + inline constexpr const char person_doing_cartwheel_tone5[] = "🤸🏿"; + inline constexpr const char cartwheel_tone5[] = "🤸🏿"; + inline constexpr const char woman_cartwheeling[] = "🤸‍♀️"; + inline constexpr const char woman_cartwheeling_tone1[] = "🤸🏻‍♀️"; + inline constexpr const char woman_cartwheeling_light_skin_tone[] = "🤸🏻‍♀️"; + inline constexpr const char woman_cartwheeling_tone2[] = "🤸🏼‍♀️"; + inline constexpr const char woman_cartwheeling_medium_light_skin_tone[] = "🤸🏼‍♀️"; + inline constexpr const char woman_cartwheeling_tone3[] = "🤸🏽‍♀️"; + inline constexpr const char woman_cartwheeling_medium_skin_tone[] = "🤸🏽‍♀️"; + inline constexpr const char woman_cartwheeling_tone4[] = "🤸🏾‍♀️"; + inline constexpr const char woman_cartwheeling_medium_dark_skin_tone[] = "🤸🏾‍♀️"; + inline constexpr const char woman_cartwheeling_tone5[] = "🤸🏿‍♀️"; + inline constexpr const char woman_cartwheeling_dark_skin_tone[] = "🤸🏿‍♀️"; + inline constexpr const char man_cartwheeling[] = "🤸‍♂️"; + inline constexpr const char man_cartwheeling_tone1[] = "🤸🏻‍♂️"; + inline constexpr const char man_cartwheeling_light_skin_tone[] = "🤸🏻‍♂️"; + inline constexpr const char man_cartwheeling_tone2[] = "🤸🏼‍♂️"; + inline constexpr const char man_cartwheeling_medium_light_skin_tone[] = "🤸🏼‍♂️"; + inline constexpr const char man_cartwheeling_tone3[] = "🤸🏽‍♂️"; + inline constexpr const char man_cartwheeling_medium_skin_tone[] = "🤸🏽‍♂️"; + inline constexpr const char man_cartwheeling_tone4[] = "🤸🏾‍♂️"; + inline constexpr const char man_cartwheeling_medium_dark_skin_tone[] = "🤸🏾‍♂️"; + inline constexpr const char man_cartwheeling_tone5[] = "🤸🏿‍♂️"; + inline constexpr const char man_cartwheeling_dark_skin_tone[] = "🤸🏿‍♂️"; + inline constexpr const char person_bouncing_ball[] = "⛹️"; + inline constexpr const char basketball_player[] = "⛹️"; + inline constexpr const char person_with_ball[] = "⛹️"; + inline constexpr const char person_bouncing_ball_tone1[] = "⛹🏻"; + inline constexpr const char basketball_player_tone1[] = "⛹🏻"; + inline constexpr const char person_with_ball_tone1[] = "⛹🏻"; + inline constexpr const char person_bouncing_ball_tone2[] = "⛹🏼"; + inline constexpr const char basketball_player_tone2[] = "⛹🏼"; + inline constexpr const char person_with_ball_tone2[] = "⛹🏼"; + inline constexpr const char person_bouncing_ball_tone3[] = "⛹🏽"; + inline constexpr const char basketball_player_tone3[] = "⛹🏽"; + inline constexpr const char person_with_ball_tone3[] = "⛹🏽"; + inline constexpr const char person_bouncing_ball_tone4[] = "⛹🏾"; + inline constexpr const char basketball_player_tone4[] = "⛹🏾"; + inline constexpr const char person_with_ball_tone4[] = "⛹🏾"; + inline constexpr const char person_bouncing_ball_tone5[] = "⛹🏿"; + inline constexpr const char basketball_player_tone5[] = "⛹🏿"; + inline constexpr const char person_with_ball_tone5[] = "⛹🏿"; + inline constexpr const char woman_bouncing_ball[] = "⛹️‍♀️"; + inline constexpr const char woman_bouncing_ball_tone1[] = "⛹🏻‍♀️"; + inline constexpr const char woman_bouncing_ball_light_skin_tone[] = "⛹🏻‍♀️"; + inline constexpr const char woman_bouncing_ball_tone2[] = "⛹🏼‍♀️"; + inline constexpr const char woman_bouncing_ball_medium_light_skin_tone[] = "⛹🏼‍♀️"; + inline constexpr const char woman_bouncing_ball_tone3[] = "⛹🏽‍♀️"; + inline constexpr const char woman_bouncing_ball_medium_skin_tone[] = "⛹🏽‍♀️"; + inline constexpr const char woman_bouncing_ball_tone4[] = "⛹🏾‍♀️"; + inline constexpr const char woman_bouncing_ball_medium_dark_skin_tone[] = "⛹🏾‍♀️"; + inline constexpr const char woman_bouncing_ball_tone5[] = "⛹🏿‍♀️"; + inline constexpr const char woman_bouncing_ball_dark_skin_tone[] = "⛹🏿‍♀️"; + inline constexpr const char man_bouncing_ball[] = "⛹️‍♂️"; + inline constexpr const char man_bouncing_ball_tone1[] = "⛹🏻‍♂️"; + inline constexpr const char man_bouncing_ball_light_skin_tone[] = "⛹🏻‍♂️"; + inline constexpr const char man_bouncing_ball_tone2[] = "⛹🏼‍♂️"; + inline constexpr const char man_bouncing_ball_medium_light_skin_tone[] = "⛹🏼‍♂️"; + inline constexpr const char man_bouncing_ball_tone3[] = "⛹🏽‍♂️"; + inline constexpr const char man_bouncing_ball_medium_skin_tone[] = "⛹🏽‍♂️"; + inline constexpr const char man_bouncing_ball_tone4[] = "⛹🏾‍♂️"; + inline constexpr const char man_bouncing_ball_medium_dark_skin_tone[] = "⛹🏾‍♂️"; + inline constexpr const char man_bouncing_ball_tone5[] = "⛹🏿‍♂️"; + inline constexpr const char man_bouncing_ball_dark_skin_tone[] = "⛹🏿‍♂️"; + inline constexpr const char person_fencing[] = "🤺"; + inline constexpr const char fencer[] = "🤺"; + inline constexpr const char fencing[] = "🤺"; + inline constexpr const char person_playing_handball[] = "🤾"; + inline constexpr const char handball[] = "🤾"; + inline constexpr const char person_playing_handball_tone1[] = "🤾🏻"; + inline constexpr const char handball_tone1[] = "🤾🏻"; + inline constexpr const char person_playing_handball_tone2[] = "🤾🏼"; + inline constexpr const char handball_tone2[] = "🤾🏼"; + inline constexpr const char person_playing_handball_tone3[] = "🤾🏽"; + inline constexpr const char handball_tone3[] = "🤾🏽"; + inline constexpr const char person_playing_handball_tone4[] = "🤾🏾"; + inline constexpr const char handball_tone4[] = "🤾🏾"; + inline constexpr const char person_playing_handball_tone5[] = "🤾🏿"; + inline constexpr const char handball_tone5[] = "🤾🏿"; + inline constexpr const char woman_playing_handball[] = "🤾‍♀️"; + inline constexpr const char woman_playing_handball_tone1[] = "🤾🏻‍♀️"; + inline constexpr const char woman_playing_handball_light_skin_tone[] = "🤾🏻‍♀️"; + inline constexpr const char woman_playing_handball_tone2[] = "🤾🏼‍♀️"; + inline constexpr const char woman_playing_handball_medium_light_skin_tone[] = "🤾🏼‍♀️"; + inline constexpr const char woman_playing_handball_tone3[] = "🤾🏽‍♀️"; + inline constexpr const char woman_playing_handball_medium_skin_tone[] = "🤾🏽‍♀️"; + inline constexpr const char woman_playing_handball_tone4[] = "🤾🏾‍♀️"; + inline constexpr const char woman_playing_handball_medium_dark_skin_tone[] = "🤾🏾‍♀️"; + inline constexpr const char woman_playing_handball_tone5[] = "🤾🏿‍♀️"; + inline constexpr const char woman_playing_handball_dark_skin_tone[] = "🤾🏿‍♀️"; + inline constexpr const char man_playing_handball[] = "🤾‍♂️"; + inline constexpr const char man_playing_handball_tone1[] = "🤾🏻‍♂️"; + inline constexpr const char man_playing_handball_light_skin_tone[] = "🤾🏻‍♂️"; + inline constexpr const char man_playing_handball_tone2[] = "🤾🏼‍♂️"; + inline constexpr const char man_playing_handball_medium_light_skin_tone[] = "🤾🏼‍♂️"; + inline constexpr const char man_playing_handball_tone3[] = "🤾🏽‍♂️"; + inline constexpr const char man_playing_handball_medium_skin_tone[] = "🤾🏽‍♂️"; + inline constexpr const char man_playing_handball_tone4[] = "🤾🏾‍♂️"; + inline constexpr const char man_playing_handball_medium_dark_skin_tone[] = "🤾🏾‍♂️"; + inline constexpr const char man_playing_handball_tone5[] = "🤾🏿‍♂️"; + inline constexpr const char man_playing_handball_dark_skin_tone[] = "🤾🏿‍♂️"; + inline constexpr const char person_golfing[] = "🏌️"; + inline constexpr const char golfer[] = "🏌️"; + inline constexpr const char person_golfing_tone1[] = "🏌🏻"; + inline constexpr const char person_golfing_light_skin_tone[] = "🏌🏻"; + inline constexpr const char person_golfing_tone2[] = "🏌🏼"; + inline constexpr const char person_golfing_medium_light_skin_tone[] = "🏌🏼"; + inline constexpr const char person_golfing_tone3[] = "🏌🏽"; + inline constexpr const char person_golfing_medium_skin_tone[] = "🏌🏽"; + inline constexpr const char person_golfing_tone4[] = "🏌🏾"; + inline constexpr const char person_golfing_medium_dark_skin_tone[] = "🏌🏾"; + inline constexpr const char person_golfing_tone5[] = "🏌🏿"; + inline constexpr const char person_golfing_dark_skin_tone[] = "🏌🏿"; + inline constexpr const char woman_golfing[] = "🏌️‍♀️"; + inline constexpr const char woman_golfing_tone1[] = "🏌🏻‍♀️"; + inline constexpr const char woman_golfing_light_skin_tone[] = "🏌🏻‍♀️"; + inline constexpr const char woman_golfing_tone2[] = "🏌🏼‍♀️"; + inline constexpr const char woman_golfing_medium_light_skin_tone[] = "🏌🏼‍♀️"; + inline constexpr const char woman_golfing_tone3[] = "🏌🏽‍♀️"; + inline constexpr const char woman_golfing_medium_skin_tone[] = "🏌🏽‍♀️"; + inline constexpr const char woman_golfing_tone4[] = "🏌🏾‍♀️"; + inline constexpr const char woman_golfing_medium_dark_skin_tone[] = "🏌🏾‍♀️"; + inline constexpr const char woman_golfing_tone5[] = "🏌🏿‍♀️"; + inline constexpr const char woman_golfing_dark_skin_tone[] = "🏌🏿‍♀️"; + inline constexpr const char man_golfing[] = "🏌️‍♂️"; + inline constexpr const char man_golfing_tone1[] = "🏌🏻‍♂️"; + inline constexpr const char man_golfing_light_skin_tone[] = "🏌🏻‍♂️"; + inline constexpr const char man_golfing_tone2[] = "🏌🏼‍♂️"; + inline constexpr const char man_golfing_medium_light_skin_tone[] = "🏌🏼‍♂️"; + inline constexpr const char man_golfing_tone3[] = "🏌🏽‍♂️"; + inline constexpr const char man_golfing_medium_skin_tone[] = "🏌🏽‍♂️"; + inline constexpr const char man_golfing_tone4[] = "🏌🏾‍♂️"; + inline constexpr const char man_golfing_medium_dark_skin_tone[] = "🏌🏾‍♂️"; + inline constexpr const char man_golfing_tone5[] = "🏌🏿‍♂️"; + inline constexpr const char man_golfing_dark_skin_tone[] = "🏌🏿‍♂️"; + inline constexpr const char horse_racing[] = "🏇"; + inline constexpr const char horse_racing_tone1[] = "🏇🏻"; + inline constexpr const char horse_racing_tone2[] = "🏇🏼"; + inline constexpr const char horse_racing_tone3[] = "🏇🏽"; + inline constexpr const char horse_racing_tone4[] = "🏇🏾"; + inline constexpr const char horse_racing_tone5[] = "🏇🏿"; + inline constexpr const char person_in_lotus_position[] = "🧘"; + inline constexpr const char person_in_lotus_position_tone1[] = "🧘🏻"; + inline constexpr const char person_in_lotus_position_light_skin_tone[] = "🧘🏻"; + inline constexpr const char person_in_lotus_position_tone2[] = "🧘🏼"; + inline constexpr const char person_in_lotus_position_medium_light_skin_tone[] = "🧘🏼"; + inline constexpr const char person_in_lotus_position_tone3[] = "🧘🏽"; + inline constexpr const char person_in_lotus_position_medium_skin_tone[] = "🧘🏽"; + inline constexpr const char person_in_lotus_position_tone4[] = "🧘🏾"; + inline constexpr const char person_in_lotus_position_medium_dark_skin_tone[] = "🧘🏾"; + inline constexpr const char person_in_lotus_position_tone5[] = "🧘🏿"; + inline constexpr const char person_in_lotus_position_dark_skin_tone[] = "🧘🏿"; + inline constexpr const char woman_in_lotus_position[] = "🧘‍♀️"; + inline constexpr const char woman_in_lotus_position_tone1[] = "🧘🏻‍♀️"; + inline constexpr const char woman_in_lotus_position_light_skin_tone[] = "🧘🏻‍♀️"; + inline constexpr const char woman_in_lotus_position_tone2[] = "🧘🏼‍♀️"; + inline constexpr const char woman_in_lotus_position_medium_light_skin_tone[] = "🧘🏼‍♀️"; + inline constexpr const char woman_in_lotus_position_tone3[] = "🧘🏽‍♀️"; + inline constexpr const char woman_in_lotus_position_medium_skin_tone[] = "🧘🏽‍♀️"; + inline constexpr const char woman_in_lotus_position_tone4[] = "🧘🏾‍♀️"; + inline constexpr const char woman_in_lotus_position_medium_dark_skin_tone[] = "🧘🏾‍♀️"; + inline constexpr const char woman_in_lotus_position_tone5[] = "🧘🏿‍♀️"; + inline constexpr const char woman_in_lotus_position_dark_skin_tone[] = "🧘🏿‍♀️"; + inline constexpr const char man_in_lotus_position[] = "🧘‍♂️"; + inline constexpr const char man_in_lotus_position_tone1[] = "🧘🏻‍♂️"; + inline constexpr const char man_in_lotus_position_light_skin_tone[] = "🧘🏻‍♂️"; + inline constexpr const char man_in_lotus_position_tone2[] = "🧘🏼‍♂️"; + inline constexpr const char man_in_lotus_position_medium_light_skin_tone[] = "🧘🏼‍♂️"; + inline constexpr const char man_in_lotus_position_tone3[] = "🧘🏽‍♂️"; + inline constexpr const char man_in_lotus_position_medium_skin_tone[] = "🧘🏽‍♂️"; + inline constexpr const char man_in_lotus_position_tone4[] = "🧘🏾‍♂️"; + inline constexpr const char man_in_lotus_position_medium_dark_skin_tone[] = "🧘🏾‍♂️"; + inline constexpr const char man_in_lotus_position_tone5[] = "🧘🏿‍♂️"; + inline constexpr const char man_in_lotus_position_dark_skin_tone[] = "🧘🏿‍♂️"; + inline constexpr const char person_surfing[] = "🏄"; + inline constexpr const char surfer[] = "🏄"; + inline constexpr const char person_surfing_tone1[] = "🏄🏻"; + inline constexpr const char surfer_tone1[] = "🏄🏻"; + inline constexpr const char person_surfing_tone2[] = "🏄🏼"; + inline constexpr const char surfer_tone2[] = "🏄🏼"; + inline constexpr const char person_surfing_tone3[] = "🏄🏽"; + inline constexpr const char surfer_tone3[] = "🏄🏽"; + inline constexpr const char person_surfing_tone4[] = "🏄🏾"; + inline constexpr const char surfer_tone4[] = "🏄🏾"; + inline constexpr const char person_surfing_tone5[] = "🏄🏿"; + inline constexpr const char surfer_tone5[] = "🏄🏿"; + inline constexpr const char woman_surfing[] = "🏄‍♀️"; + inline constexpr const char woman_surfing_tone1[] = "🏄🏻‍♀️"; + inline constexpr const char woman_surfing_light_skin_tone[] = "🏄🏻‍♀️"; + inline constexpr const char woman_surfing_tone2[] = "🏄🏼‍♀️"; + inline constexpr const char woman_surfing_medium_light_skin_tone[] = "🏄🏼‍♀️"; + inline constexpr const char woman_surfing_tone3[] = "🏄🏽‍♀️"; + inline constexpr const char woman_surfing_medium_skin_tone[] = "🏄🏽‍♀️"; + inline constexpr const char woman_surfing_tone4[] = "🏄🏾‍♀️"; + inline constexpr const char woman_surfing_medium_dark_skin_tone[] = "🏄🏾‍♀️"; + inline constexpr const char woman_surfing_tone5[] = "🏄🏿‍♀️"; + inline constexpr const char woman_surfing_dark_skin_tone[] = "🏄🏿‍♀️"; + inline constexpr const char man_surfing[] = "🏄‍♂️"; + inline constexpr const char man_surfing_tone1[] = "🏄🏻‍♂️"; + inline constexpr const char man_surfing_light_skin_tone[] = "🏄🏻‍♂️"; + inline constexpr const char man_surfing_tone2[] = "🏄🏼‍♂️"; + inline constexpr const char man_surfing_medium_light_skin_tone[] = "🏄🏼‍♂️"; + inline constexpr const char man_surfing_tone3[] = "🏄🏽‍♂️"; + inline constexpr const char man_surfing_medium_skin_tone[] = "🏄🏽‍♂️"; + inline constexpr const char man_surfing_tone4[] = "🏄🏾‍♂️"; + inline constexpr const char man_surfing_medium_dark_skin_tone[] = "🏄🏾‍♂️"; + inline constexpr const char man_surfing_tone5[] = "🏄🏿‍♂️"; + inline constexpr const char man_surfing_dark_skin_tone[] = "🏄🏿‍♂️"; + inline constexpr const char person_swimming[] = "🏊"; + inline constexpr const char swimmer[] = "🏊"; + inline constexpr const char person_swimming_tone1[] = "🏊🏻"; + inline constexpr const char swimmer_tone1[] = "🏊🏻"; + inline constexpr const char person_swimming_tone2[] = "🏊🏼"; + inline constexpr const char swimmer_tone2[] = "🏊🏼"; + inline constexpr const char person_swimming_tone3[] = "🏊🏽"; + inline constexpr const char swimmer_tone3[] = "🏊🏽"; + inline constexpr const char person_swimming_tone4[] = "🏊🏾"; + inline constexpr const char swimmer_tone4[] = "🏊🏾"; + inline constexpr const char person_swimming_tone5[] = "🏊🏿"; + inline constexpr const char swimmer_tone5[] = "🏊🏿"; + inline constexpr const char woman_swimming[] = "🏊‍♀️"; + inline constexpr const char woman_swimming_tone1[] = "🏊🏻‍♀️"; + inline constexpr const char woman_swimming_light_skin_tone[] = "🏊🏻‍♀️"; + inline constexpr const char woman_swimming_tone2[] = "🏊🏼‍♀️"; + inline constexpr const char woman_swimming_medium_light_skin_tone[] = "🏊🏼‍♀️"; + inline constexpr const char woman_swimming_tone3[] = "🏊🏽‍♀️"; + inline constexpr const char woman_swimming_medium_skin_tone[] = "🏊🏽‍♀️"; + inline constexpr const char woman_swimming_tone4[] = "🏊🏾‍♀️"; + inline constexpr const char woman_swimming_medium_dark_skin_tone[] = "🏊🏾‍♀️"; + inline constexpr const char woman_swimming_tone5[] = "🏊🏿‍♀️"; + inline constexpr const char woman_swimming_dark_skin_tone[] = "🏊🏿‍♀️"; + inline constexpr const char man_swimming[] = "🏊‍♂️"; + inline constexpr const char man_swimming_tone1[] = "🏊🏻‍♂️"; + inline constexpr const char man_swimming_light_skin_tone[] = "🏊🏻‍♂️"; + inline constexpr const char man_swimming_tone2[] = "🏊🏼‍♂️"; + inline constexpr const char man_swimming_medium_light_skin_tone[] = "🏊🏼‍♂️"; + inline constexpr const char man_swimming_tone3[] = "🏊🏽‍♂️"; + inline constexpr const char man_swimming_medium_skin_tone[] = "🏊🏽‍♂️"; + inline constexpr const char man_swimming_tone4[] = "🏊🏾‍♂️"; + inline constexpr const char man_swimming_medium_dark_skin_tone[] = "🏊🏾‍♂️"; + inline constexpr const char man_swimming_tone5[] = "🏊🏿‍♂️"; + inline constexpr const char man_swimming_dark_skin_tone[] = "🏊🏿‍♂️"; + inline constexpr const char person_playing_water_polo[] = "🤽"; + inline constexpr const char water_polo[] = "🤽"; + inline constexpr const char person_playing_water_polo_tone1[] = "🤽🏻"; + inline constexpr const char water_polo_tone1[] = "🤽🏻"; + inline constexpr const char person_playing_water_polo_tone2[] = "🤽🏼"; + inline constexpr const char water_polo_tone2[] = "🤽🏼"; + inline constexpr const char person_playing_water_polo_tone3[] = "🤽🏽"; + inline constexpr const char water_polo_tone3[] = "🤽🏽"; + inline constexpr const char person_playing_water_polo_tone4[] = "🤽🏾"; + inline constexpr const char water_polo_tone4[] = "🤽🏾"; + inline constexpr const char person_playing_water_polo_tone5[] = "🤽🏿"; + inline constexpr const char water_polo_tone5[] = "🤽🏿"; + inline constexpr const char woman_playing_water_polo[] = "🤽‍♀️"; + inline constexpr const char woman_playing_water_polo_tone1[] = "🤽🏻‍♀️"; + inline constexpr const char woman_playing_water_polo_light_skin_tone[] = "🤽🏻‍♀️"; + inline constexpr const char woman_playing_water_polo_tone2[] = "🤽🏼‍♀️"; + inline constexpr const char woman_playing_water_polo_medium_light_skin_tone[] = "🤽🏼‍♀️"; + inline constexpr const char woman_playing_water_polo_tone3[] = "🤽🏽‍♀️"; + inline constexpr const char woman_playing_water_polo_medium_skin_tone[] = "🤽🏽‍♀️"; + inline constexpr const char woman_playing_water_polo_tone4[] = "🤽🏾‍♀️"; + inline constexpr const char woman_playing_water_polo_medium_dark_skin_tone[] = "🤽🏾‍♀️"; + inline constexpr const char woman_playing_water_polo_tone5[] = "🤽🏿‍♀️"; + inline constexpr const char woman_playing_water_polo_dark_skin_tone[] = "🤽🏿‍♀️"; + inline constexpr const char man_playing_water_polo[] = "🤽‍♂️"; + inline constexpr const char man_playing_water_polo_tone1[] = "🤽🏻‍♂️"; + inline constexpr const char man_playing_water_polo_light_skin_tone[] = "🤽🏻‍♂️"; + inline constexpr const char man_playing_water_polo_tone2[] = "🤽🏼‍♂️"; + inline constexpr const char man_playing_water_polo_medium_light_skin_tone[] = "🤽🏼‍♂️"; + inline constexpr const char man_playing_water_polo_tone3[] = "🤽🏽‍♂️"; + inline constexpr const char man_playing_water_polo_medium_skin_tone[] = "🤽🏽‍♂️"; + inline constexpr const char man_playing_water_polo_tone4[] = "🤽🏾‍♂️"; + inline constexpr const char man_playing_water_polo_medium_dark_skin_tone[] = "🤽🏾‍♂️"; + inline constexpr const char man_playing_water_polo_tone5[] = "🤽🏿‍♂️"; + inline constexpr const char man_playing_water_polo_dark_skin_tone[] = "🤽🏿‍♂️"; + inline constexpr const char person_rowing_boat[] = "🚣"; + inline constexpr const char rowboat[] = "🚣"; + inline constexpr const char person_rowing_boat_tone1[] = "🚣🏻"; + inline constexpr const char rowboat_tone1[] = "🚣🏻"; + inline constexpr const char person_rowing_boat_tone2[] = "🚣🏼"; + inline constexpr const char rowboat_tone2[] = "🚣🏼"; + inline constexpr const char person_rowing_boat_tone3[] = "🚣🏽"; + inline constexpr const char rowboat_tone3[] = "🚣🏽"; + inline constexpr const char person_rowing_boat_tone4[] = "🚣🏾"; + inline constexpr const char rowboat_tone4[] = "🚣🏾"; + inline constexpr const char person_rowing_boat_tone5[] = "🚣🏿"; + inline constexpr const char rowboat_tone5[] = "🚣🏿"; + inline constexpr const char woman_rowing_boat[] = "🚣‍♀️"; + inline constexpr const char woman_rowing_boat_tone1[] = "🚣🏻‍♀️"; + inline constexpr const char woman_rowing_boat_light_skin_tone[] = "🚣🏻‍♀️"; + inline constexpr const char woman_rowing_boat_tone2[] = "🚣🏼‍♀️"; + inline constexpr const char woman_rowing_boat_medium_light_skin_tone[] = "🚣🏼‍♀️"; + inline constexpr const char woman_rowing_boat_tone3[] = "🚣🏽‍♀️"; + inline constexpr const char woman_rowing_boat_medium_skin_tone[] = "🚣🏽‍♀️"; + inline constexpr const char woman_rowing_boat_tone4[] = "🚣🏾‍♀️"; + inline constexpr const char woman_rowing_boat_medium_dark_skin_tone[] = "🚣🏾‍♀️"; + inline constexpr const char woman_rowing_boat_tone5[] = "🚣🏿‍♀️"; + inline constexpr const char woman_rowing_boat_dark_skin_tone[] = "🚣🏿‍♀️"; + inline constexpr const char man_rowing_boat[] = "🚣‍♂️"; + inline constexpr const char man_rowing_boat_tone1[] = "🚣🏻‍♂️"; + inline constexpr const char man_rowing_boat_light_skin_tone[] = "🚣🏻‍♂️"; + inline constexpr const char man_rowing_boat_tone2[] = "🚣🏼‍♂️"; + inline constexpr const char man_rowing_boat_medium_light_skin_tone[] = "🚣🏼‍♂️"; + inline constexpr const char man_rowing_boat_tone3[] = "🚣🏽‍♂️"; + inline constexpr const char man_rowing_boat_medium_skin_tone[] = "🚣🏽‍♂️"; + inline constexpr const char man_rowing_boat_tone4[] = "🚣🏾‍♂️"; + inline constexpr const char man_rowing_boat_medium_dark_skin_tone[] = "🚣🏾‍♂️"; + inline constexpr const char man_rowing_boat_tone5[] = "🚣🏿‍♂️"; + inline constexpr const char man_rowing_boat_dark_skin_tone[] = "🚣🏿‍♂️"; + inline constexpr const char person_climbing[] = "🧗"; + inline constexpr const char person_climbing_tone1[] = "🧗🏻"; + inline constexpr const char person_climbing_light_skin_tone[] = "🧗🏻"; + inline constexpr const char person_climbing_tone2[] = "🧗🏼"; + inline constexpr const char person_climbing_medium_light_skin_tone[] = "🧗🏼"; + inline constexpr const char person_climbing_tone3[] = "🧗🏽"; + inline constexpr const char person_climbing_medium_skin_tone[] = "🧗🏽"; + inline constexpr const char person_climbing_tone4[] = "🧗🏾"; + inline constexpr const char person_climbing_medium_dark_skin_tone[] = "🧗🏾"; + inline constexpr const char person_climbing_tone5[] = "🧗🏿"; + inline constexpr const char person_climbing_dark_skin_tone[] = "🧗🏿"; + inline constexpr const char woman_climbing[] = "🧗‍♀️"; + inline constexpr const char woman_climbing_tone1[] = "🧗🏻‍♀️"; + inline constexpr const char woman_climbing_light_skin_tone[] = "🧗🏻‍♀️"; + inline constexpr const char woman_climbing_tone2[] = "🧗🏼‍♀️"; + inline constexpr const char woman_climbing_medium_light_skin_tone[] = "🧗🏼‍♀️"; + inline constexpr const char woman_climbing_tone3[] = "🧗🏽‍♀️"; + inline constexpr const char woman_climbing_medium_skin_tone[] = "🧗🏽‍♀️"; + inline constexpr const char woman_climbing_tone4[] = "🧗🏾‍♀️"; + inline constexpr const char woman_climbing_medium_dark_skin_tone[] = "🧗🏾‍♀️"; + inline constexpr const char woman_climbing_tone5[] = "🧗🏿‍♀️"; + inline constexpr const char woman_climbing_dark_skin_tone[] = "🧗🏿‍♀️"; + inline constexpr const char man_climbing[] = "🧗‍♂️"; + inline constexpr const char man_climbing_tone1[] = "🧗🏻‍♂️"; + inline constexpr const char man_climbing_light_skin_tone[] = "🧗🏻‍♂️"; + inline constexpr const char man_climbing_tone2[] = "🧗🏼‍♂️"; + inline constexpr const char man_climbing_medium_light_skin_tone[] = "🧗🏼‍♂️"; + inline constexpr const char man_climbing_tone3[] = "🧗🏽‍♂️"; + inline constexpr const char man_climbing_medium_skin_tone[] = "🧗🏽‍♂️"; + inline constexpr const char man_climbing_tone4[] = "🧗🏾‍♂️"; + inline constexpr const char man_climbing_medium_dark_skin_tone[] = "🧗🏾‍♂️"; + inline constexpr const char man_climbing_tone5[] = "🧗🏿‍♂️"; + inline constexpr const char man_climbing_dark_skin_tone[] = "🧗🏿‍♂️"; + inline constexpr const char person_mountain_biking[] = "🚵"; + inline constexpr const char mountain_bicyclist[] = "🚵"; + inline constexpr const char person_mountain_biking_tone1[] = "🚵🏻"; + inline constexpr const char mountain_bicyclist_tone1[] = "🚵🏻"; + inline constexpr const char person_mountain_biking_tone2[] = "🚵🏼"; + inline constexpr const char mountain_bicyclist_tone2[] = "🚵🏼"; + inline constexpr const char person_mountain_biking_tone3[] = "🚵🏽"; + inline constexpr const char mountain_bicyclist_tone3[] = "🚵🏽"; + inline constexpr const char person_mountain_biking_tone4[] = "🚵🏾"; + inline constexpr const char mountain_bicyclist_tone4[] = "🚵🏾"; + inline constexpr const char person_mountain_biking_tone5[] = "🚵🏿"; + inline constexpr const char mountain_bicyclist_tone5[] = "🚵🏿"; + inline constexpr const char woman_mountain_biking[] = "🚵‍♀️"; + inline constexpr const char woman_mountain_biking_tone1[] = "🚵🏻‍♀️"; + inline constexpr const char woman_mountain_biking_light_skin_tone[] = "🚵🏻‍♀️"; + inline constexpr const char woman_mountain_biking_tone2[] = "🚵🏼‍♀️"; + inline constexpr const char woman_mountain_biking_medium_light_skin_tone[] = "🚵🏼‍♀️"; + inline constexpr const char woman_mountain_biking_tone3[] = "🚵🏽‍♀️"; + inline constexpr const char woman_mountain_biking_medium_skin_tone[] = "🚵🏽‍♀️"; + inline constexpr const char woman_mountain_biking_tone4[] = "🚵🏾‍♀️"; + inline constexpr const char woman_mountain_biking_medium_dark_skin_tone[] = "🚵🏾‍♀️"; + inline constexpr const char woman_mountain_biking_tone5[] = "🚵🏿‍♀️"; + inline constexpr const char woman_mountain_biking_dark_skin_tone[] = "🚵🏿‍♀️"; + inline constexpr const char man_mountain_biking[] = "🚵‍♂️"; + inline constexpr const char man_mountain_biking_tone1[] = "🚵🏻‍♂️"; + inline constexpr const char man_mountain_biking_light_skin_tone[] = "🚵🏻‍♂️"; + inline constexpr const char man_mountain_biking_tone2[] = "🚵🏼‍♂️"; + inline constexpr const char man_mountain_biking_medium_light_skin_tone[] = "🚵🏼‍♂️"; + inline constexpr const char man_mountain_biking_tone3[] = "🚵🏽‍♂️"; + inline constexpr const char man_mountain_biking_medium_skin_tone[] = "🚵🏽‍♂️"; + inline constexpr const char man_mountain_biking_tone4[] = "🚵🏾‍♂️"; + inline constexpr const char man_mountain_biking_medium_dark_skin_tone[] = "🚵🏾‍♂️"; + inline constexpr const char man_mountain_biking_tone5[] = "🚵🏿‍♂️"; + inline constexpr const char man_mountain_biking_dark_skin_tone[] = "🚵🏿‍♂️"; + inline constexpr const char person_biking[] = "🚴"; + inline constexpr const char bicyclist[] = "🚴"; + inline constexpr const char person_biking_tone1[] = "🚴🏻"; + inline constexpr const char bicyclist_tone1[] = "🚴🏻"; + inline constexpr const char person_biking_tone2[] = "🚴🏼"; + inline constexpr const char bicyclist_tone2[] = "🚴🏼"; + inline constexpr const char person_biking_tone3[] = "🚴🏽"; + inline constexpr const char bicyclist_tone3[] = "🚴🏽"; + inline constexpr const char person_biking_tone4[] = "🚴🏾"; + inline constexpr const char bicyclist_tone4[] = "🚴🏾"; + inline constexpr const char person_biking_tone5[] = "🚴🏿"; + inline constexpr const char bicyclist_tone5[] = "🚴🏿"; + inline constexpr const char woman_biking[] = "🚴‍♀️"; + inline constexpr const char woman_biking_tone1[] = "🚴🏻‍♀️"; + inline constexpr const char woman_biking_light_skin_tone[] = "🚴🏻‍♀️"; + inline constexpr const char woman_biking_tone2[] = "🚴🏼‍♀️"; + inline constexpr const char woman_biking_medium_light_skin_tone[] = "🚴🏼‍♀️"; + inline constexpr const char woman_biking_tone3[] = "🚴🏽‍♀️"; + inline constexpr const char woman_biking_medium_skin_tone[] = "🚴🏽‍♀️"; + inline constexpr const char woman_biking_tone4[] = "🚴🏾‍♀️"; + inline constexpr const char woman_biking_medium_dark_skin_tone[] = "🚴🏾‍♀️"; + inline constexpr const char woman_biking_tone5[] = "🚴🏿‍♀️"; + inline constexpr const char woman_biking_dark_skin_tone[] = "🚴🏿‍♀️"; + inline constexpr const char man_biking[] = "🚴‍♂️"; + inline constexpr const char man_biking_tone1[] = "🚴🏻‍♂️"; + inline constexpr const char man_biking_light_skin_tone[] = "🚴🏻‍♂️"; + inline constexpr const char man_biking_tone2[] = "🚴🏼‍♂️"; + inline constexpr const char man_biking_medium_light_skin_tone[] = "🚴🏼‍♂️"; + inline constexpr const char man_biking_tone3[] = "🚴🏽‍♂️"; + inline constexpr const char man_biking_medium_skin_tone[] = "🚴🏽‍♂️"; + inline constexpr const char man_biking_tone4[] = "🚴🏾‍♂️"; + inline constexpr const char man_biking_medium_dark_skin_tone[] = "🚴🏾‍♂️"; + inline constexpr const char man_biking_tone5[] = "🚴🏿‍♂️"; + inline constexpr const char man_biking_dark_skin_tone[] = "🚴🏿‍♂️"; + inline constexpr const char trophy[] = "🏆"; + inline constexpr const char first_place[] = "🥇"; + inline constexpr const char first_place_medal[] = "🥇"; + inline constexpr const char second_place[] = "🥈"; + inline constexpr const char second_place_medal[] = "🥈"; + inline constexpr const char third_place[] = "🥉"; + inline constexpr const char third_place_medal[] = "🥉"; + inline constexpr const char medal[] = "🏅"; + inline constexpr const char sports_medal[] = "🏅"; + inline constexpr const char military_medal[] = "🎖️"; + inline constexpr const char rosette[] = "🏵️"; + inline constexpr const char reminder_ribbon[] = "🎗️"; + inline constexpr const char ticket[] = "🎫"; + inline constexpr const char tickets[] = "🎟️"; + inline constexpr const char admission_tickets[] = "🎟️"; + inline constexpr const char circus_tent[] = "🎪"; + inline constexpr const char person_juggling[] = "🤹"; + inline constexpr const char juggling[] = "🤹"; + inline constexpr const char juggler[] = "🤹"; + inline constexpr const char person_juggling_tone1[] = "🤹🏻"; + inline constexpr const char juggling_tone1[] = "🤹🏻"; + inline constexpr const char juggler_tone1[] = "🤹🏻"; + inline constexpr const char person_juggling_tone2[] = "🤹🏼"; + inline constexpr const char juggling_tone2[] = "🤹🏼"; + inline constexpr const char juggler_tone2[] = "🤹🏼"; + inline constexpr const char person_juggling_tone3[] = "🤹🏽"; + inline constexpr const char juggling_tone3[] = "🤹🏽"; + inline constexpr const char juggler_tone3[] = "🤹🏽"; + inline constexpr const char person_juggling_tone4[] = "🤹🏾"; + inline constexpr const char juggling_tone4[] = "🤹🏾"; + inline constexpr const char juggler_tone4[] = "🤹🏾"; + inline constexpr const char person_juggling_tone5[] = "🤹🏿"; + inline constexpr const char juggling_tone5[] = "🤹🏿"; + inline constexpr const char juggler_tone5[] = "🤹🏿"; + inline constexpr const char woman_juggling[] = "🤹‍♀️"; + inline constexpr const char woman_juggling_tone1[] = "🤹🏻‍♀️"; + inline constexpr const char woman_juggling_light_skin_tone[] = "🤹🏻‍♀️"; + inline constexpr const char woman_juggling_tone2[] = "🤹🏼‍♀️"; + inline constexpr const char woman_juggling_medium_light_skin_tone[] = "🤹🏼‍♀️"; + inline constexpr const char woman_juggling_tone3[] = "🤹🏽‍♀️"; + inline constexpr const char woman_juggling_medium_skin_tone[] = "🤹🏽‍♀️"; + inline constexpr const char woman_juggling_tone4[] = "🤹🏾‍♀️"; + inline constexpr const char woman_juggling_medium_dark_skin_tone[] = "🤹🏾‍♀️"; + inline constexpr const char woman_juggling_tone5[] = "🤹🏿‍♀️"; + inline constexpr const char woman_juggling_dark_skin_tone[] = "🤹🏿‍♀️"; + inline constexpr const char man_juggling[] = "🤹‍♂️"; + inline constexpr const char man_juggling_tone1[] = "🤹🏻‍♂️"; + inline constexpr const char man_juggling_light_skin_tone[] = "🤹🏻‍♂️"; + inline constexpr const char man_juggling_tone2[] = "🤹🏼‍♂️"; + inline constexpr const char man_juggling_medium_light_skin_tone[] = "🤹🏼‍♂️"; + inline constexpr const char man_juggling_tone3[] = "🤹🏽‍♂️"; + inline constexpr const char man_juggling_medium_skin_tone[] = "🤹🏽‍♂️"; + inline constexpr const char man_juggling_tone4[] = "🤹🏾‍♂️"; + inline constexpr const char man_juggling_medium_dark_skin_tone[] = "🤹🏾‍♂️"; + inline constexpr const char man_juggling_tone5[] = "🤹🏿‍♂️"; + inline constexpr const char man_juggling_dark_skin_tone[] = "🤹🏿‍♂️"; + inline constexpr const char performing_arts[] = "🎭"; + inline constexpr const char ballet_shoes[] = "🩰"; + inline constexpr const char art[] = "🎨"; + inline constexpr const char clapper[] = "🎬"; + inline constexpr const char clapper_board[] = "🎬"; + inline constexpr const char microphone[] = "🎤"; + inline constexpr const char headphones[] = "🎧"; + inline constexpr const char headphone[] = "🎧"; + inline constexpr const char musical_score[] = "🎼"; + inline constexpr const char musical_keyboard[] = "🎹"; + inline constexpr const char maracas[] = "🪇"; + inline constexpr const char drum[] = "🥁"; + inline constexpr const char drum_with_drumsticks[] = "🥁"; + inline constexpr const char long_drum[] = "🪘"; + inline constexpr const char saxophone[] = "🎷"; + inline constexpr const char trumpet[] = "🎺"; + inline constexpr const char accordion[] = "🪗"; + inline constexpr const char guitar[] = "🎸"; + inline constexpr const char banjo[] = "🪕"; + inline constexpr const char violin[] = "🎻"; + inline constexpr const char flute[] = "🪈"; + inline constexpr const char game_die[] = "🎲"; + inline constexpr const char chess_pawn[] = "♟️"; + inline constexpr const char dart[] = "🎯"; + inline constexpr const char direct_hit[] = "🎯"; + inline constexpr const char bowling[] = "🎳"; + inline constexpr const char video_game[] = "🎮"; + inline constexpr const char slot_machine[] = "🎰"; + inline constexpr const char jigsaw[] = "🧩"; + inline constexpr const char puzzle_piece[] = "🧩"; + inline constexpr const char flag_white[] = "🏳️"; + inline constexpr const char flag_black[] = "🏴"; + inline constexpr const char pirate_flag[] = "🏴‍☠️"; + inline constexpr const char checkered_flag[] = "🏁"; + inline constexpr const char triangular_flag_on_post[] = "🚩"; + inline constexpr const char rainbow_flag[] = "🏳️‍🌈"; + inline constexpr const char gay_pride_flag[] = "🏳️‍🌈"; + inline constexpr const char transgender_flag[] = "🏳️‍⚧️"; + inline constexpr const char united_nations[] = "🇺🇳"; + inline constexpr const char flag_af[] = "🇦🇫"; + inline constexpr const char flag_ax[] = "🇦🇽"; + inline constexpr const char flag_al[] = "🇦🇱"; + inline constexpr const char flag_dz[] = "🇩🇿"; + inline constexpr const char flag_as[] = "🇦🇸"; + inline constexpr const char flag_ad[] = "🇦🇩"; + inline constexpr const char flag_ao[] = "🇦🇴"; + inline constexpr const char flag_ai[] = "🇦🇮"; + inline constexpr const char flag_aq[] = "🇦🇶"; + inline constexpr const char flag_ag[] = "🇦🇬"; + inline constexpr const char flag_ar[] = "🇦🇷"; + inline constexpr const char flag_am[] = "🇦🇲"; + inline constexpr const char flag_aw[] = "🇦🇼"; + inline constexpr const char flag_au[] = "🇦🇺"; + inline constexpr const char flag_at[] = "🇦🇹"; + inline constexpr const char flag_az[] = "🇦🇿"; + inline constexpr const char flag_bs[] = "🇧🇸"; + inline constexpr const char flag_bh[] = "🇧🇭"; + inline constexpr const char flag_bd[] = "🇧🇩"; + inline constexpr const char flag_bb[] = "🇧🇧"; + inline constexpr const char flag_by[] = "🇧🇾"; + inline constexpr const char flag_be[] = "🇧🇪"; + inline constexpr const char flag_bz[] = "🇧🇿"; + inline constexpr const char flag_bj[] = "🇧🇯"; + inline constexpr const char flag_bm[] = "🇧🇲"; + inline constexpr const char flag_bt[] = "🇧🇹"; + inline constexpr const char flag_bo[] = "🇧🇴"; + inline constexpr const char flag_ba[] = "🇧🇦"; + inline constexpr const char flag_bw[] = "🇧🇼"; + inline constexpr const char flag_br[] = "🇧🇷"; + inline constexpr const char flag_io[] = "🇮🇴"; + inline constexpr const char flag_vg[] = "🇻🇬"; + inline constexpr const char flag_bn[] = "🇧🇳"; + inline constexpr const char flag_bg[] = "🇧🇬"; + inline constexpr const char flag_bf[] = "🇧🇫"; + inline constexpr const char flag_bi[] = "🇧🇮"; + inline constexpr const char flag_kh[] = "🇰🇭"; + inline constexpr const char flag_cm[] = "🇨🇲"; + inline constexpr const char flag_ca[] = "🇨🇦"; + inline constexpr const char flag_ic[] = "🇮🇨"; + inline constexpr const char flag_cv[] = "🇨🇻"; + inline constexpr const char flag_bq[] = "🇧🇶"; + inline constexpr const char flag_ky[] = "🇰🇾"; + inline constexpr const char flag_cf[] = "🇨🇫"; + inline constexpr const char flag_td[] = "🇹🇩"; + inline constexpr const char flag_cl[] = "🇨🇱"; + inline constexpr const char flag_cn[] = "🇨🇳"; + inline constexpr const char flag_cx[] = "🇨🇽"; + inline constexpr const char flag_cc[] = "🇨🇨"; + inline constexpr const char flag_co[] = "🇨🇴"; + inline constexpr const char flag_km[] = "🇰🇲"; + inline constexpr const char flag_cg[] = "🇨🇬"; + inline constexpr const char flag_cd[] = "🇨🇩"; + inline constexpr const char flag_ck[] = "🇨🇰"; + inline constexpr const char flag_cr[] = "🇨🇷"; + inline constexpr const char flag_ci[] = "🇨🇮"; + inline constexpr const char flag_hr[] = "🇭🇷"; + inline constexpr const char flag_cu[] = "🇨🇺"; + inline constexpr const char flag_cw[] = "🇨🇼"; + inline constexpr const char flag_cy[] = "🇨🇾"; + inline constexpr const char flag_cz[] = "🇨🇿"; + inline constexpr const char flag_dk[] = "🇩🇰"; + inline constexpr const char flag_dj[] = "🇩🇯"; + inline constexpr const char flag_dm[] = "🇩🇲"; + inline constexpr const char flag_do[] = "🇩🇴"; + inline constexpr const char flag_ec[] = "🇪🇨"; + inline constexpr const char flag_eg[] = "🇪🇬"; + inline constexpr const char flag_sv[] = "🇸🇻"; + inline constexpr const char flag_gq[] = "🇬🇶"; + inline constexpr const char flag_er[] = "🇪🇷"; + inline constexpr const char flag_ee[] = "🇪🇪"; + inline constexpr const char flag_et[] = "🇪🇹"; + inline constexpr const char flag_eu[] = "🇪🇺"; + inline constexpr const char flag_fk[] = "🇫🇰"; + inline constexpr const char flag_fo[] = "🇫🇴"; + inline constexpr const char flag_fj[] = "🇫🇯"; + inline constexpr const char flag_fi[] = "🇫🇮"; + inline constexpr const char flag_fr[] = "🇫🇷"; + inline constexpr const char flag_gf[] = "🇬🇫"; + inline constexpr const char flag_pf[] = "🇵🇫"; + inline constexpr const char flag_tf[] = "🇹🇫"; + inline constexpr const char flag_ga[] = "🇬🇦"; + inline constexpr const char flag_gm[] = "🇬🇲"; + inline constexpr const char flag_ge[] = "🇬🇪"; + inline constexpr const char flag_de[] = "🇩🇪"; + inline constexpr const char flag_gh[] = "🇬🇭"; + inline constexpr const char flag_gi[] = "🇬🇮"; + inline constexpr const char flag_gr[] = "🇬🇷"; + inline constexpr const char flag_gl[] = "🇬🇱"; + inline constexpr const char flag_gd[] = "🇬🇩"; + inline constexpr const char flag_gp[] = "🇬🇵"; + inline constexpr const char flag_gu[] = "🇬🇺"; + inline constexpr const char flag_gt[] = "🇬🇹"; + inline constexpr const char flag_gg[] = "🇬🇬"; + inline constexpr const char flag_gn[] = "🇬🇳"; + inline constexpr const char flag_gw[] = "🇬🇼"; + inline constexpr const char flag_gy[] = "🇬🇾"; + inline constexpr const char flag_ht[] = "🇭🇹"; + inline constexpr const char flag_hn[] = "🇭🇳"; + inline constexpr const char flag_hk[] = "🇭🇰"; + inline constexpr const char flag_hu[] = "🇭🇺"; + inline constexpr const char flag_is[] = "🇮🇸"; + inline constexpr const char flag_in[] = "🇮🇳"; + inline constexpr const char flag_id[] = "🇮🇩"; + inline constexpr const char flag_ir[] = "🇮🇷"; + inline constexpr const char flag_iq[] = "🇮🇶"; + inline constexpr const char flag_ie[] = "🇮🇪"; + inline constexpr const char flag_im[] = "🇮🇲"; + inline constexpr const char flag_il[] = "🇮🇱"; + inline constexpr const char flag_it[] = "🇮🇹"; + inline constexpr const char flag_jm[] = "🇯🇲"; + inline constexpr const char flag_jp[] = "🇯🇵"; + inline constexpr const char crossed_flags[] = "🎌"; + inline constexpr const char flag_je[] = "🇯🇪"; + inline constexpr const char flag_jo[] = "🇯🇴"; + inline constexpr const char flag_kz[] = "🇰🇿"; + inline constexpr const char flag_ke[] = "🇰🇪"; + inline constexpr const char flag_ki[] = "🇰🇮"; + inline constexpr const char flag_xk[] = "🇽🇰"; + inline constexpr const char flag_kw[] = "🇰🇼"; + inline constexpr const char flag_kg[] = "🇰🇬"; + inline constexpr const char flag_la[] = "🇱🇦"; + inline constexpr const char flag_lv[] = "🇱🇻"; + inline constexpr const char flag_lb[] = "🇱🇧"; + inline constexpr const char flag_ls[] = "🇱🇸"; + inline constexpr const char flag_lr[] = "🇱🇷"; + inline constexpr const char flag_ly[] = "🇱🇾"; + inline constexpr const char flag_li[] = "🇱🇮"; + inline constexpr const char flag_lt[] = "🇱🇹"; + inline constexpr const char flag_lu[] = "🇱🇺"; + inline constexpr const char flag_mo[] = "🇲🇴"; + inline constexpr const char flag_mk[] = "🇲🇰"; + inline constexpr const char flag_mg[] = "🇲🇬"; + inline constexpr const char flag_mw[] = "🇲🇼"; + inline constexpr const char flag_my[] = "🇲🇾"; + inline constexpr const char flag_mv[] = "🇲🇻"; + inline constexpr const char flag_ml[] = "🇲🇱"; + inline constexpr const char flag_mt[] = "🇲🇹"; + inline constexpr const char flag_mh[] = "🇲🇭"; + inline constexpr const char flag_mq[] = "🇲🇶"; + inline constexpr const char flag_mr[] = "🇲🇷"; + inline constexpr const char flag_mu[] = "🇲🇺"; + inline constexpr const char flag_yt[] = "🇾🇹"; + inline constexpr const char flag_mx[] = "🇲🇽"; + inline constexpr const char flag_fm[] = "🇫🇲"; + inline constexpr const char flag_md[] = "🇲🇩"; + inline constexpr const char flag_mc[] = "🇲🇨"; + inline constexpr const char flag_mn[] = "🇲🇳"; + inline constexpr const char flag_me[] = "🇲🇪"; + inline constexpr const char flag_ms[] = "🇲🇸"; + inline constexpr const char flag_ma[] = "🇲🇦"; + inline constexpr const char flag_mz[] = "🇲🇿"; + inline constexpr const char flag_mm[] = "🇲🇲"; + inline constexpr const char flag_na[] = "🇳🇦"; + inline constexpr const char flag_nr[] = "🇳🇷"; + inline constexpr const char flag_np[] = "🇳🇵"; + inline constexpr const char flag_nl[] = "🇳🇱"; + inline constexpr const char flag_nc[] = "🇳🇨"; + inline constexpr const char flag_nz[] = "🇳🇿"; + inline constexpr const char flag_ni[] = "🇳🇮"; + inline constexpr const char flag_ne[] = "🇳🇪"; + inline constexpr const char flag_ng[] = "🇳🇬"; + inline constexpr const char flag_nu[] = "🇳🇺"; + inline constexpr const char flag_nf[] = "🇳🇫"; + inline constexpr const char flag_kp[] = "🇰🇵"; + inline constexpr const char flag_mp[] = "🇲🇵"; + inline constexpr const char flag_no[] = "🇳🇴"; + inline constexpr const char flag_om[] = "🇴🇲"; + inline constexpr const char flag_pk[] = "🇵🇰"; + inline constexpr const char flag_pw[] = "🇵🇼"; + inline constexpr const char flag_ps[] = "🇵🇸"; + inline constexpr const char flag_pa[] = "🇵🇦"; + inline constexpr const char flag_pg[] = "🇵🇬"; + inline constexpr const char flag_py[] = "🇵🇾"; + inline constexpr const char flag_pe[] = "🇵🇪"; + inline constexpr const char flag_ph[] = "🇵🇭"; + inline constexpr const char flag_pn[] = "🇵🇳"; + inline constexpr const char flag_pl[] = "🇵🇱"; + inline constexpr const char flag_pt[] = "🇵🇹"; + inline constexpr const char flag_pr[] = "🇵🇷"; + inline constexpr const char flag_qa[] = "🇶🇦"; + inline constexpr const char flag_re[] = "🇷🇪"; + inline constexpr const char flag_ro[] = "🇷🇴"; + inline constexpr const char flag_ru[] = "🇷🇺"; + inline constexpr const char flag_rw[] = "🇷🇼"; + inline constexpr const char flag_ws[] = "🇼🇸"; + inline constexpr const char flag_sm[] = "🇸🇲"; + inline constexpr const char flag_st[] = "🇸🇹"; + inline constexpr const char flag_sa[] = "🇸🇦"; + inline constexpr const char flag_sn[] = "🇸🇳"; + inline constexpr const char flag_rs[] = "🇷🇸"; + inline constexpr const char flag_sc[] = "🇸🇨"; + inline constexpr const char flag_sl[] = "🇸🇱"; + inline constexpr const char flag_sg[] = "🇸🇬"; + inline constexpr const char flag_sx[] = "🇸🇽"; + inline constexpr const char flag_sk[] = "🇸🇰"; + inline constexpr const char flag_si[] = "🇸🇮"; + inline constexpr const char flag_gs[] = "🇬🇸"; + inline constexpr const char flag_sb[] = "🇸🇧"; + inline constexpr const char flag_so[] = "🇸🇴"; + inline constexpr const char flag_za[] = "🇿🇦"; + inline constexpr const char flag_kr[] = "🇰🇷"; + inline constexpr const char flag_ss[] = "🇸🇸"; + inline constexpr const char flag_es[] = "🇪🇸"; + inline constexpr const char flag_lk[] = "🇱🇰"; + inline constexpr const char flag_bl[] = "🇧🇱"; + inline constexpr const char flag_sh[] = "🇸🇭"; + inline constexpr const char flag_kn[] = "🇰🇳"; + inline constexpr const char flag_lc[] = "🇱🇨"; + inline constexpr const char flag_pm[] = "🇵🇲"; + inline constexpr const char flag_vc[] = "🇻🇨"; + inline constexpr const char flag_sd[] = "🇸🇩"; + inline constexpr const char flag_sr[] = "🇸🇷"; + inline constexpr const char flag_sz[] = "🇸🇿"; + inline constexpr const char flag_se[] = "🇸🇪"; + inline constexpr const char flag_ch[] = "🇨🇭"; + inline constexpr const char flag_sy[] = "🇸🇾"; + inline constexpr const char flag_tw[] = "🇹🇼"; + inline constexpr const char flag_tj[] = "🇹🇯"; + inline constexpr const char flag_tz[] = "🇹🇿"; + inline constexpr const char flag_th[] = "🇹🇭"; + inline constexpr const char flag_tl[] = "🇹🇱"; + inline constexpr const char flag_tg[] = "🇹🇬"; + inline constexpr const char flag_tk[] = "🇹🇰"; + inline constexpr const char flag_to[] = "🇹🇴"; + inline constexpr const char flag_tt[] = "🇹🇹"; + inline constexpr const char flag_tn[] = "🇹🇳"; + inline constexpr const char flag_tr[] = "🇹🇷"; + inline constexpr const char flag_tm[] = "🇹🇲"; + inline constexpr const char flag_tc[] = "🇹🇨"; + inline constexpr const char flag_vi[] = "🇻🇮"; + inline constexpr const char flag_tv[] = "🇹🇻"; + inline constexpr const char flag_ug[] = "🇺🇬"; + inline constexpr const char flag_ua[] = "🇺🇦"; + inline constexpr const char flag_ae[] = "🇦🇪"; + inline constexpr const char flag_gb[] = "🇬🇧"; + inline constexpr const char england[] = "🏴󠁧󠁢󠁥󠁮󠁧󠁿"; + inline constexpr const char scotland[] = "🏴󠁧󠁢󠁳󠁣󠁴󠁿"; + inline constexpr const char wales[] = "🏴󠁧󠁢󠁷󠁬󠁳󠁿"; + inline constexpr const char flag_us[] = "🇺🇸"; + inline constexpr const char flag_uy[] = "🇺🇾"; + inline constexpr const char flag_uz[] = "🇺🇿"; + inline constexpr const char flag_vu[] = "🇻🇺"; + inline constexpr const char flag_va[] = "🇻🇦"; + inline constexpr const char flag_ve[] = "🇻🇪"; + inline constexpr const char flag_vn[] = "🇻🇳"; + inline constexpr const char flag_wf[] = "🇼🇫"; + inline constexpr const char flag_eh[] = "🇪🇭"; + inline constexpr const char flag_ye[] = "🇾🇪"; + inline constexpr const char flag_zm[] = "🇿🇲"; + inline constexpr const char flag_zw[] = "🇿🇼"; + inline constexpr const char flag_ac[] = "🇦🇨"; + inline constexpr const char flag_bv[] = "🇧🇻"; + inline constexpr const char flag_cp[] = "🇨🇵"; + inline constexpr const char flag_ea[] = "🇪🇦"; + inline constexpr const char flag_dg[] = "🇩🇬"; + inline constexpr const char flag_hm[] = "🇭🇲"; + inline constexpr const char flag_mf[] = "🇲🇫"; + inline constexpr const char flag_sj[] = "🇸🇯"; + inline constexpr const char flag_ta[] = "🇹🇦"; + inline constexpr const char flag_um[] = "🇺🇲"; + inline constexpr const char green_apple[] = "🍏"; + inline constexpr const char apple[] = "🍎"; + inline constexpr const char red_apple[] = "🍎"; + inline constexpr const char pear[] = "🍐"; + inline constexpr const char tangerine[] = "🍊"; + inline constexpr const char lemon[] = "🍋"; + inline constexpr const char banana[] = "🍌"; + inline constexpr const char watermelon[] = "🍉"; + inline constexpr const char grapes[] = "🍇"; + inline constexpr const char strawberry[] = "🍓"; + inline constexpr const char blueberries[] = "🫐"; + inline constexpr const char melon[] = "🍈"; + inline constexpr const char cherries[] = "🍒"; + inline constexpr const char peach[] = "🍑"; + inline constexpr const char mango[] = "🥭"; + inline constexpr const char pineapple[] = "🍍"; + inline constexpr const char coconut[] = "🥥"; + inline constexpr const char kiwi[] = "🥝"; + inline constexpr const char kiwifruit[] = "🥝"; + inline constexpr const char kiwi_fruit[] = "🥝"; + inline constexpr const char tomato[] = "🍅"; + inline constexpr const char eggplant[] = "🍆"; + inline constexpr const char avocado[] = "🥑"; + inline constexpr const char pea_pod[] = "🫛"; + inline constexpr const char broccoli[] = "🥦"; + inline constexpr const char leafy_green[] = "🥬"; + inline constexpr const char cucumber[] = "🥒"; + inline constexpr const char hot_pepper[] = "🌶️"; + inline constexpr const char bell_pepper[] = "🫑"; + inline constexpr const char corn[] = "🌽"; + inline constexpr const char ear_of_corn[] = "🌽"; + inline constexpr const char carrot[] = "🥕"; + inline constexpr const char olive[] = "🫒"; + inline constexpr const char garlic[] = "🧄"; + inline constexpr const char onion[] = "🧅"; + inline constexpr const char potato[] = "🥔"; + inline constexpr const char sweet_potato[] = "🍠"; + inline constexpr const char ginger_root[] = "🫚"; + inline constexpr const char croissant[] = "🥐"; + inline constexpr const char bagel[] = "🥯"; + inline constexpr const char bread[] = "🍞"; + inline constexpr const char french_bread[] = "🥖"; + inline constexpr const char baguette_bread[] = "🥖"; + inline constexpr const char pretzel[] = "🥨"; + inline constexpr const char cheese[] = "🧀"; + inline constexpr const char cheese_wedge[] = "🧀"; + inline constexpr const char egg[] = "🥚"; + inline constexpr const char cooking[] = "🍳"; + inline constexpr const char butter[] = "🧈"; + inline constexpr const char pancakes[] = "🥞"; + inline constexpr const char waffle[] = "🧇"; + inline constexpr const char bacon[] = "🥓"; + inline constexpr const char cut_of_meat[] = "🥩"; + inline constexpr const char poultry_leg[] = "🍗"; + inline constexpr const char meat_on_bone[] = "🍖"; + inline constexpr const char bone[] = "🦴"; + inline constexpr const char hotdog[] = "🌭"; + inline constexpr const char hot_dog[] = "🌭"; + inline constexpr const char hamburger[] = "🍔"; + inline constexpr const char fries[] = "🍟"; + inline constexpr const char french_fries[] = "🍟"; + inline constexpr const char pizza[] = "🍕"; + inline constexpr const char flatbread[] = "🫓"; + inline constexpr const char sandwich[] = "🥪"; + inline constexpr const char stuffed_flatbread[] = "🥙"; + inline constexpr const char stuffed_pita[] = "🥙"; + inline constexpr const char falafel[] = "🧆"; + inline constexpr const char taco[] = "🌮"; + inline constexpr const char burrito[] = "🌯"; + inline constexpr const char tamale[] = "🫔"; + inline constexpr const char salad[] = "🥗"; + inline constexpr const char green_salad[] = "🥗"; + inline constexpr const char shallow_pan_of_food[] = "🥘"; + inline constexpr const char paella[] = "🥘"; + inline constexpr const char fondue[] = "🫕"; + inline constexpr const char canned_food[] = "🥫"; + inline constexpr const char jar[] = "🫙"; + inline constexpr const char spaghetti[] = "🍝"; + inline constexpr const char ramen[] = "🍜"; + inline constexpr const char steaming_bowl[] = "🍜"; + inline constexpr const char stew[] = "🍲"; + inline constexpr const char pot_of_food[] = "🍲"; + inline constexpr const char curry[] = "🍛"; + inline constexpr const char curry_rice[] = "🍛"; + inline constexpr const char sushi[] = "🍣"; + inline constexpr const char bento[] = "🍱"; + inline constexpr const char bento_box[] = "🍱"; + inline constexpr const char dumpling[] = "🥟"; + inline constexpr const char oyster[] = "🦪"; + inline constexpr const char fried_shrimp[] = "🍤"; + inline constexpr const char rice_ball[] = "🍙"; + inline constexpr const char rice[] = "🍚"; + inline constexpr const char cooked_rice[] = "🍚"; + inline constexpr const char rice_cracker[] = "🍘"; + inline constexpr const char fish_cake[] = "🍥"; + inline constexpr const char fortune_cookie[] = "🥠"; + inline constexpr const char moon_cake[] = "🥮"; + inline constexpr const char oden[] = "🍢"; + inline constexpr const char dango[] = "🍡"; + inline constexpr const char shaved_ice[] = "🍧"; + inline constexpr const char ice_cream[] = "🍨"; + inline constexpr const char icecream[] = "🍦"; + inline constexpr const char pie[] = "🥧"; + inline constexpr const char cupcake[] = "🧁"; + inline constexpr const char cake[] = "🍰"; + inline constexpr const char shortcake[] = "🍰"; + inline constexpr const char birthday[] = "🎂"; + inline constexpr const char birthday_cake[] = "🎂"; + inline constexpr const char custard[] = "🍮"; + inline constexpr const char pudding[] = "🍮"; + inline constexpr const char flan[] = "🍮"; + inline constexpr const char lollipop[] = "🍭"; + inline constexpr const char candy[] = "🍬"; + inline constexpr const char chocolate_bar[] = "🍫"; + inline constexpr const char popcorn[] = "🍿"; + inline constexpr const char doughnut[] = "🍩"; + inline constexpr const char cookie[] = "🍪"; + inline constexpr const char chestnut[] = "🌰"; + inline constexpr const char peanuts[] = "🥜"; + inline constexpr const char shelled_peanut[] = "🥜"; + inline constexpr const char beans[] = "🫘"; + inline constexpr const char honey_pot[] = "🍯"; + inline constexpr const char milk[] = "🥛"; + inline constexpr const char glass_of_milk[] = "🥛"; + inline constexpr const char pouring_liquid[] = "🫗"; + inline constexpr const char baby_bottle[] = "🍼"; + inline constexpr const char teapot[] = "🫖"; + inline constexpr const char coffee[] = "☕"; + inline constexpr const char hot_beverage[] = "☕"; + inline constexpr const char tea[] = "🍵"; + inline constexpr const char mate[] = "🧉"; + inline constexpr const char beverage_box[] = "🧃"; + inline constexpr const char cup_with_straw[] = "🥤"; + inline constexpr const char bubble_tea[] = "🧋"; + inline constexpr const char sake[] = "🍶"; + inline constexpr const char beer[] = "🍺"; + inline constexpr const char beer_mug[] = "🍺"; + inline constexpr const char beers[] = "🍻"; + inline constexpr const char champagne_glass[] = "🥂"; + inline constexpr const char clinking_glass[] = "🥂"; + inline constexpr const char wine_glass[] = "🍷"; + inline constexpr const char tumbler_glass[] = "🥃"; + inline constexpr const char whisky[] = "🥃"; + inline constexpr const char cocktail[] = "🍸"; + inline constexpr const char tropical_drink[] = "🍹"; + inline constexpr const char champagne[] = "🍾"; + inline constexpr const char bottle_with_popping_cork[] = "🍾"; + inline constexpr const char ice_cube[] = "🧊"; + inline constexpr const char spoon[] = "🥄"; + inline constexpr const char fork_and_knife[] = "🍴"; + inline constexpr const char fork_knife_plate[] = "🍽️"; + inline constexpr const char fork_and_knife_with_plate[] = "🍽️"; + inline constexpr const char bowl_with_spoon[] = "🥣"; + inline constexpr const char takeout_box[] = "🥡"; + inline constexpr const char chopsticks[] = "🥢"; + inline constexpr const char salt[] = "🧂"; + inline constexpr const char dog[] = "🐶"; + inline constexpr const char dog_face[] = "🐶"; + inline constexpr const char cat[] = "🐱"; + inline constexpr const char cat_face[] = "🐱"; + inline constexpr const char mouse[] = "🐭"; + inline constexpr const char mouse_face[] = "🐭"; + inline constexpr const char hamster[] = "🐹"; + inline constexpr const char rabbit[] = "🐰"; + inline constexpr const char rabbit_face[] = "🐰"; + inline constexpr const char fox[] = "🦊"; + inline constexpr const char fox_face[] = "🦊"; + inline constexpr const char bear[] = "🐻"; + inline constexpr const char panda_face[] = "🐼"; + inline constexpr const char panda[] = "🐼"; + inline constexpr const char polar_bear[] = "🐻‍❄️"; + inline constexpr const char koala[] = "🐨"; + inline constexpr const char tiger[] = "🐯"; + inline constexpr const char tiger_face[] = "🐯"; + inline constexpr const char lion_face[] = "🦁"; + inline constexpr const char lion[] = "🦁"; + inline constexpr const char cow[] = "🐮"; + inline constexpr const char cow_face[] = "🐮"; + inline constexpr const char pig[] = "🐷"; + inline constexpr const char pig_face[] = "🐷"; + inline constexpr const char pig_nose[] = "🐽"; + inline constexpr const char frog[] = "🐸"; + inline constexpr const char monkey_face[] = "🐵"; + inline constexpr const char see_no_evil[] = "🙈"; + inline constexpr const char hear_no_evil[] = "🙉"; + inline constexpr const char speak_no_evil[] = "🙊"; + inline constexpr const char monkey[] = "🐒"; + inline constexpr const char chicken[] = "🐔"; + inline constexpr const char penguin[] = "🐧"; + inline constexpr const char bird[] = "🐦"; + inline constexpr const char baby_chick[] = "🐤"; + inline constexpr const char hatching_chick[] = "🐣"; + inline constexpr const char hatched_chick[] = "🐥"; + inline constexpr const char goose[] = "🪿"; + inline constexpr const char duck[] = "🦆"; + inline constexpr const char black_bird[] = "🐦‍⬛"; + inline constexpr const char eagle[] = "🦅"; + inline constexpr const char owl[] = "🦉"; + inline constexpr const char bat[] = "🦇"; + inline constexpr const char wolf[] = "🐺"; + inline constexpr const char boar[] = "🐗"; + inline constexpr const char horse[] = "🐴"; + inline constexpr const char horse_face[] = "🐴"; + inline constexpr const char unicorn[] = "🦄"; + inline constexpr const char unicorn_face[] = "🦄"; + inline constexpr const char moose[] = "🫎"; + inline constexpr const char bee[] = "🐝"; + inline constexpr const char honeybee[] = "🐝"; + inline constexpr const char worm[] = "🪱"; + inline constexpr const char bug[] = "🐛"; + inline constexpr const char butterfly[] = "🦋"; + inline constexpr const char snail[] = "🐌"; + inline constexpr const char lady_beetle[] = "🐞"; + inline constexpr const char ant[] = "🐜"; + inline constexpr const char fly[] = "🪰"; + inline constexpr const char beetle[] = "🪲"; + inline constexpr const char cockroach[] = "🪳"; + inline constexpr const char mosquito[] = "🦟"; + inline constexpr const char cricket[] = "🦗"; + inline constexpr const char spider[] = "🕷️"; + inline constexpr const char spider_web[] = "🕸️"; + inline constexpr const char scorpion[] = "🦂"; + inline constexpr const char turtle[] = "🐢"; + inline constexpr const char snake[] = "🐍"; + inline constexpr const char lizard[] = "🦎"; + inline constexpr const char t_rex[] = "🦖"; + inline constexpr const char sauropod[] = "🦕"; + inline constexpr const char octopus[] = "🐙"; + inline constexpr const char squid[] = "🦑"; + inline constexpr const char jellyfish[] = "🪼"; + inline constexpr const char shrimp[] = "🦐"; + inline constexpr const char lobster[] = "🦞"; + inline constexpr const char crab[] = "🦀"; + inline constexpr const char blowfish[] = "🐡"; + inline constexpr const char tropical_fish[] = "🐠"; + inline constexpr const char fish[] = "🐟"; + inline constexpr const char dolphin[] = "🐬"; + inline constexpr const char whale[] = "🐳"; + inline constexpr const char whale2[] = "🐋"; + inline constexpr const char shark[] = "🦈"; + inline constexpr const char seal[] = "🦭"; + inline constexpr const char crocodile[] = "🐊"; + inline constexpr const char tiger2[] = "🐅"; + inline constexpr const char leopard[] = "🐆"; + inline constexpr const char zebra[] = "🦓"; + inline constexpr const char gorilla[] = "🦍"; + inline constexpr const char orangutan[] = "🦧"; + inline constexpr const char mammoth[] = "🦣"; + inline constexpr const char elephant[] = "🐘"; + inline constexpr const char hippopotamus[] = "🦛"; + inline constexpr const char rhino[] = "🦏"; + inline constexpr const char rhinoceros[] = "🦏"; + inline constexpr const char dromedary_camel[] = "🐪"; + inline constexpr const char camel[] = "🐫"; + inline constexpr const char giraffe[] = "🦒"; + inline constexpr const char kangaroo[] = "🦘"; + inline constexpr const char bison[] = "🦬"; + inline constexpr const char water_buffalo[] = "🐃"; + inline constexpr const char ox[] = "🐂"; + inline constexpr const char cow2[] = "🐄"; + inline constexpr const char donkey[] = "🫏"; + inline constexpr const char racehorse[] = "🐎"; + inline constexpr const char pig2[] = "🐖"; + inline constexpr const char ram[] = "🐏"; + inline constexpr const char sheep[] = "🐑"; + inline constexpr const char ewe[] = "🐑"; + inline constexpr const char llama[] = "🦙"; + inline constexpr const char goat[] = "🐐"; + inline constexpr const char deer[] = "🦌"; + inline constexpr const char dog2[] = "🐕"; + inline constexpr const char poodle[] = "🐩"; + inline constexpr const char guide_dog[] = "🦮"; + inline constexpr const char service_dog[] = "🐕‍🦺"; + inline constexpr const char cat2[] = "🐈"; + inline constexpr const char black_cat[] = "🐈‍⬛"; + inline constexpr const char feather[] = "🪶"; + inline constexpr const char wing[] = "🪽"; + inline constexpr const char rooster[] = "🐓"; + inline constexpr const char turkey[] = "🦃"; + inline constexpr const char dodo[] = "🦤"; + inline constexpr const char peacock[] = "🦚"; + inline constexpr const char parrot[] = "🦜"; + inline constexpr const char swan[] = "🦢"; + inline constexpr const char flamingo[] = "🦩"; + inline constexpr const char dove[] = "🕊️"; + inline constexpr const char dove_of_peace[] = "🕊️"; + inline constexpr const char rabbit2[] = "🐇"; + inline constexpr const char raccoon[] = "🦝"; + inline constexpr const char skunk[] = "🦨"; + inline constexpr const char badger[] = "🦡"; + inline constexpr const char beaver[] = "🦫"; + inline constexpr const char otter[] = "🦦"; + inline constexpr const char sloth[] = "🦥"; + inline constexpr const char mouse2[] = "🐁"; + inline constexpr const char rat[] = "🐀"; + inline constexpr const char chipmunk[] = "🐿️"; + inline constexpr const char hedgehog[] = "🦔"; + inline constexpr const char feet[] = "🐾"; + inline constexpr const char paw_prints[] = "🐾"; + inline constexpr const char dragon[] = "🐉"; + inline constexpr const char dragon_face[] = "🐲"; + inline constexpr const char cactus[] = "🌵"; + inline constexpr const char christmas_tree[] = "🎄"; + inline constexpr const char evergreen_tree[] = "🌲"; + inline constexpr const char deciduous_tree[] = "🌳"; + inline constexpr const char palm_tree[] = "🌴"; + inline constexpr const char wood[] = "🪵"; + inline constexpr const char seedling[] = "🌱"; + inline constexpr const char herb[] = "🌿"; + inline constexpr const char shamrock[] = "☘️"; + inline constexpr const char four_leaf_clover[] = "🍀"; + inline constexpr const char bamboo[] = "🎍"; + inline constexpr const char potted_plant[] = "🪴"; + inline constexpr const char tanabata_tree[] = "🎋"; + inline constexpr const char leaves[] = "🍃"; + inline constexpr const char fallen_leaf[] = "🍂"; + inline constexpr const char maple_leaf[] = "🍁"; + inline constexpr const char nest_with_eggs[] = "🪺"; + inline constexpr const char empty_nest[] = "🪹"; + inline constexpr const char mushroom[] = "🍄"; + inline constexpr const char shell[] = "🐚"; + inline constexpr const char spiral_shell[] = "🐚"; + inline constexpr const char coral[] = "🪸"; + inline constexpr const char rock[] = "🪨"; + inline constexpr const char ear_of_rice[] = "🌾"; + inline constexpr const char sheaf_of_rice[] = "🌾"; + inline constexpr const char bouquet[] = "💐"; + inline constexpr const char tulip[] = "🌷"; + inline constexpr const char rose[] = "🌹"; + inline constexpr const char wilted_rose[] = "🥀"; + inline constexpr const char wilted_flower[] = "🥀"; + inline constexpr const char hyacinth[] = "🪻"; + inline constexpr const char lotus[] = "🪷"; + inline constexpr const char hibiscus[] = "🌺"; + inline constexpr const char cherry_blossom[] = "🌸"; + inline constexpr const char blossom[] = "🌼"; + inline constexpr const char sunflower[] = "🌻"; + inline constexpr const char sun_with_face[] = "🌞"; + inline constexpr const char full_moon_with_face[] = "🌝"; + inline constexpr const char first_quarter_moon_with_face[] = "🌛"; + inline constexpr const char last_quarter_moon_with_face[] = "🌜"; + inline constexpr const char new_moon_with_face[] = "🌚"; + inline constexpr const char new_moon_face[] = "🌚"; + inline constexpr const char full_moon[] = "🌕"; + inline constexpr const char waning_gibbous_moon[] = "🌖"; + inline constexpr const char last_quarter_moon[] = "🌗"; + inline constexpr const char waning_crescent_moon[] = "🌘"; + inline constexpr const char new_moon[] = "🌑"; + inline constexpr const char waxing_crescent_moon[] = "🌒"; + inline constexpr const char first_quarter_moon[] = "🌓"; + inline constexpr const char waxing_gibbous_moon[] = "🌔"; + inline constexpr const char crescent_moon[] = "🌙"; + inline constexpr const char earth_americas[] = "🌎"; + inline constexpr const char earth_africa[] = "🌍"; + inline constexpr const char earth_asia[] = "🌏"; + inline constexpr const char ringed_planet[] = "🪐"; + inline constexpr const char dizzy[] = "💫"; + inline constexpr const char star[] = "⭐"; + inline constexpr const char star2[] = "🌟"; + inline constexpr const char glowing_star[] = "🌟"; + inline constexpr const char sparkles[] = "✨"; + inline constexpr const char zap[] = "⚡"; + inline constexpr const char high_voltage[] = "⚡"; + inline constexpr const char comet[] = "☄️"; + inline constexpr const char boom[] = "💥"; + inline constexpr const char collision[] = "💥"; + inline constexpr const char fire[] = "🔥"; + inline constexpr const char flame[] = "🔥"; + inline constexpr const char cloud_tornado[] = "🌪️"; + inline constexpr const char cloud_with_tornado[] = "🌪️"; + inline constexpr const char tornado[] = "🌪️"; + inline constexpr const char rainbow[] = "🌈"; + inline constexpr const char sunny[] = "☀️"; + inline constexpr const char sun[] = "☀️"; + inline constexpr const char white_sun_small_cloud[] = "🌤️"; + inline constexpr const char white_sun_with_small_cloud[] = "🌤️"; + inline constexpr const char partly_sunny[] = "⛅"; + inline constexpr const char white_sun_cloud[] = "🌥️"; + inline constexpr const char white_sun_behind_cloud[] = "🌥️"; + inline constexpr const char cloud[] = "☁️"; + inline constexpr const char white_sun_rain_cloud[] = "🌦️"; + inline constexpr const char white_sun_behind_cloud_with_rain[] = "🌦️"; + inline constexpr const char cloud_rain[] = "🌧️"; + inline constexpr const char cloud_with_rain[] = "🌧️"; + inline constexpr const char thunder_cloud_rain[] = "⛈️"; + inline constexpr const char thunder_cloud_and_rain[] = "⛈️"; + inline constexpr const char cloud_lightning[] = "🌩️"; + inline constexpr const char cloud_with_lightning[] = "🌩️"; + inline constexpr const char cloud_snow[] = "🌨️"; + inline constexpr const char cloud_with_snow[] = "🌨️"; + inline constexpr const char snowflake[] = "❄️"; + inline constexpr const char snowman2[] = "☃️"; + inline constexpr const char snowman[] = "⛄"; + inline constexpr const char wind_blowing_face[] = "🌬️"; + inline constexpr const char wind_face[] = "🌬️"; + inline constexpr const char dash[] = "💨"; + inline constexpr const char dashing_away[] = "💨"; + inline constexpr const char droplet[] = "💧"; + inline constexpr const char sweat_drops[] = "💦"; + inline constexpr const char bubbles[] = "🫧"; + inline constexpr const char umbrella[] = "☔"; + inline constexpr const char umbrella2[] = "☂️"; + inline constexpr const char ocean[] = "🌊"; + inline constexpr const char water_wave[] = "🌊"; + inline constexpr const char fog[] = "🌫️"; + inline constexpr const char watch[] = "⌚"; + inline constexpr const char mobile_phone[] = "📱"; + inline constexpr const char iphone[] = "📱"; + inline constexpr const char calling[] = "📲"; + inline constexpr const char computer[] = "💻"; + inline constexpr const char keyboard[] = "⌨️"; + inline constexpr const char desktop[] = "🖥️"; + inline constexpr const char desktop_computer[] = "🖥️"; + inline constexpr const char printer[] = "🖨️"; + inline constexpr const char mouse_three_button[] = "🖱️"; + inline constexpr const char three_button_mouse[] = "🖱️"; + inline constexpr const char trackball[] = "🖲️"; + inline constexpr const char joystick[] = "🕹️"; + inline constexpr const char compression[] = "🗜️"; + inline constexpr const char clamp[] = "🗜️"; + inline constexpr const char minidisc[] = "💽"; + inline constexpr const char computer_disk[] = "💽"; + inline constexpr const char floppy_disk[] = "💾"; + inline constexpr const char cd[] = "💿"; + inline constexpr const char optical_disk[] = "💿"; + inline constexpr const char dvd[] = "📀"; + inline constexpr const char vhs[] = "📼"; + inline constexpr const char videocassette[] = "📼"; + inline constexpr const char camera[] = "📷"; + inline constexpr const char camera_with_flash[] = "📸"; + inline constexpr const char video_camera[] = "📹"; + inline constexpr const char movie_camera[] = "🎥"; + inline constexpr const char projector[] = "📽️"; + inline constexpr const char film_projector[] = "📽️"; + inline constexpr const char film_frames[] = "🎞️"; + inline constexpr const char telephone_receiver[] = "📞"; + inline constexpr const char telephone[] = "☎️"; + inline constexpr const char pager[] = "📟"; + inline constexpr const char fax[] = "📠"; + inline constexpr const char fax_machine[] = "📠"; + inline constexpr const char tv[] = "📺"; + inline constexpr const char television[] = "📺"; + inline constexpr const char radio[] = "📻"; + inline constexpr const char microphone2[] = "🎙️"; + inline constexpr const char studio_microphone[] = "🎙️"; + inline constexpr const char level_slider[] = "🎚️"; + inline constexpr const char control_knobs[] = "🎛️"; + inline constexpr const char compass[] = "🧭"; + inline constexpr const char stopwatch[] = "⏱️"; + inline constexpr const char timer[] = "⏲️"; + inline constexpr const char timer_clock[] = "⏲️"; + inline constexpr const char alarm_clock[] = "⏰"; + inline constexpr const char clock[] = "🕰️"; + inline constexpr const char mantlepiece_clock[] = "🕰️"; + inline constexpr const char hourglass[] = "⌛"; + inline constexpr const char hourglass_flowing_sand[] = "⏳"; + inline constexpr const char satellite[] = "📡"; + inline constexpr const char battery[] = "🔋"; + inline constexpr const char low_battery[] = "🪫"; + inline constexpr const char electric_plug[] = "🔌"; + inline constexpr const char bulb[] = "💡"; + inline constexpr const char light_bulb[] = "💡"; + inline constexpr const char flashlight[] = "🔦"; + inline constexpr const char candle[] = "🕯️"; + inline constexpr const char diya_lamp[] = "🪔"; + inline constexpr const char fire_extinguisher[] = "🧯"; + inline constexpr const char oil[] = "🛢️"; + inline constexpr const char oil_drum[] = "🛢️"; + inline constexpr const char money_with_wings[] = "💸"; + inline constexpr const char dollar[] = "💵"; + inline constexpr const char yen[] = "💴"; + inline constexpr const char yen_banknote[] = "💴"; + inline constexpr const char euro[] = "💶"; + inline constexpr const char euro_banknote[] = "💶"; + inline constexpr const char pound[] = "💷"; + inline constexpr const char coin[] = "🪙"; + inline constexpr const char moneybag[] = "💰"; + inline constexpr const char money_bag[] = "💰"; + inline constexpr const char credit_card[] = "💳"; + inline constexpr const char identification_card[] = "🪪"; + inline constexpr const char gem[] = "💎"; + inline constexpr const char gem_stone[] = "💎"; + inline constexpr const char scales[] = "⚖️"; + inline constexpr const char balance_scale[] = "⚖️"; + inline constexpr const char ladder[] = "🪜"; + inline constexpr const char toolbox[] = "🧰"; + inline constexpr const char screwdriver[] = "🪛"; + inline constexpr const char wrench[] = "🔧"; + inline constexpr const char hammer[] = "🔨"; + inline constexpr const char hammer_pick[] = "⚒️"; + inline constexpr const char hammer_and_pick[] = "⚒️"; + inline constexpr const char tools[] = "🛠️"; + inline constexpr const char hammer_and_wrench[] = "🛠️"; + inline constexpr const char pick[] = "⛏️"; + inline constexpr const char carpentry_saw[] = "🪚"; + inline constexpr const char nut_and_bolt[] = "🔩"; + inline constexpr const char gear[] = "⚙️"; + inline constexpr const char mouse_trap[] = "🪤"; + inline constexpr const char bricks[] = "🧱"; + inline constexpr const char brick[] = "🧱"; + inline constexpr const char chains[] = "⛓️"; + inline constexpr const char magnet[] = "🧲"; + inline constexpr const char gun[] = "🔫"; + inline constexpr const char pistol[] = "🔫"; + inline constexpr const char bomb[] = "💣"; + inline constexpr const char firecracker[] = "🧨"; + inline constexpr const char axe[] = "🪓"; + inline constexpr const char knife[] = "🔪"; + inline constexpr const char kitchen_knife[] = "🔪"; + inline constexpr const char dagger[] = "🗡️"; + inline constexpr const char dagger_knife[] = "🗡️"; + inline constexpr const char crossed_swords[] = "⚔️"; + inline constexpr const char shield[] = "🛡️"; + inline constexpr const char smoking[] = "🚬"; + inline constexpr const char cigarette[] = "🚬"; + inline constexpr const char coffin[] = "⚰️"; + inline constexpr const char headstone[] = "🪦"; + inline constexpr const char urn[] = "⚱️"; + inline constexpr const char funeral_urn[] = "⚱️"; + inline constexpr const char amphora[] = "🏺"; + inline constexpr const char crystal_ball[] = "🔮"; + inline constexpr const char prayer_beads[] = "📿"; + inline constexpr const char nazar_amulet[] = "🧿"; + inline constexpr const char hamsa[] = "🪬"; + inline constexpr const char barber[] = "💈"; + inline constexpr const char barber_pole[] = "💈"; + inline constexpr const char alembic[] = "⚗️"; + inline constexpr const char telescope[] = "🔭"; + inline constexpr const char microscope[] = "🔬"; + inline constexpr const char hole[] = "🕳️"; + inline constexpr const char x_ray[] = "🩻"; + inline constexpr const char adhesive_bandage[] = "🩹"; + inline constexpr const char stethoscope[] = "🩺"; + inline constexpr const char pill[] = "💊"; + inline constexpr const char syringe[] = "💉"; + inline constexpr const char drop_of_blood[] = "🩸"; + inline constexpr const char dna[] = "🧬"; + inline constexpr const char microbe[] = "🦠"; + inline constexpr const char petri_dish[] = "🧫"; + inline constexpr const char test_tube[] = "🧪"; + inline constexpr const char thermometer[] = "🌡️"; + inline constexpr const char broom[] = "🧹"; + inline constexpr const char plunger[] = "🪠"; + inline constexpr const char basket[] = "🧺"; + inline constexpr const char roll_of_paper[] = "🧻"; + inline constexpr const char toilet[] = "🚽"; + inline constexpr const char potable_water[] = "🚰"; + inline constexpr const char shower[] = "🚿"; + inline constexpr const char bathtub[] = "🛁"; + inline constexpr const char bath[] = "🛀"; + inline constexpr const char bath_tone1[] = "🛀🏻"; + inline constexpr const char bath_tone2[] = "🛀🏼"; + inline constexpr const char bath_tone3[] = "🛀🏽"; + inline constexpr const char bath_tone4[] = "🛀🏾"; + inline constexpr const char bath_tone5[] = "🛀🏿"; + inline constexpr const char soap[] = "🧼"; + inline constexpr const char toothbrush[] = "🪥"; + inline constexpr const char razor[] = "🪒"; + inline constexpr const char hair_pick[] = "🪮"; + inline constexpr const char sponge[] = "🧽"; + inline constexpr const char bucket[] = "🪣"; + inline constexpr const char squeeze_bottle[] = "🧴"; + inline constexpr const char lotion_bottle[] = "🧴"; + inline constexpr const char bellhop[] = "🛎️"; + inline constexpr const char bellhop_bell[] = "🛎️"; + inline constexpr const char key[] = "🔑"; + inline constexpr const char key2[] = "🗝️"; + inline constexpr const char old_key[] = "🗝️"; + inline constexpr const char door[] = "🚪"; + inline constexpr const char chair[] = "🪑"; + inline constexpr const char couch[] = "🛋️"; + inline constexpr const char couch_and_lamp[] = "🛋️"; + inline constexpr const char bed[] = "🛏️"; + inline constexpr const char sleeping_accommodation[] = "🛌"; + inline constexpr const char person_in_bed[] = "🛌"; + inline constexpr const char person_in_bed_tone1[] = "🛌🏻"; + inline constexpr const char person_in_bed_light_skin_tone[] = "🛌🏻"; + inline constexpr const char person_in_bed_tone2[] = "🛌🏼"; + inline constexpr const char person_in_bed_medium_light_skin_tone[] = "🛌🏼"; + inline constexpr const char person_in_bed_tone3[] = "🛌🏽"; + inline constexpr const char person_in_bed_medium_skin_tone[] = "🛌🏽"; + inline constexpr const char person_in_bed_tone4[] = "🛌🏾"; + inline constexpr const char person_in_bed_medium_dark_skin_tone[] = "🛌🏾"; + inline constexpr const char person_in_bed_tone5[] = "🛌🏿"; + inline constexpr const char person_in_bed_dark_skin_tone[] = "🛌🏿"; + inline constexpr const char teddy_bear[] = "🧸"; + inline constexpr const char nesting_dolls[] = "🪆"; + inline constexpr const char frame_photo[] = "🖼️"; + inline constexpr const char frame_with_picture[] = "🖼️"; + inline constexpr const char mirror[] = "🪞"; + inline constexpr const char window[] = "🪟"; + inline constexpr const char shopping_bags[] = "🛍️"; + inline constexpr const char shopping_cart[] = "🛒"; + inline constexpr const char shopping_trolley[] = "🛒"; + inline constexpr const char gift[] = "🎁"; + inline constexpr const char wrapped_gift[] = "🎁"; + inline constexpr const char balloon[] = "🎈"; + inline constexpr const char flags[] = "🎏"; + inline constexpr const char carp_streamer[] = "🎏"; + inline constexpr const char ribbon[] = "🎀"; + inline constexpr const char magic_wand[] = "🪄"; + inline constexpr const char pinata[] = "🪅"; + inline constexpr const char confetti_ball[] = "🎊"; + inline constexpr const char tada[] = "🎉"; + inline constexpr const char party_popper[] = "🎉"; + inline constexpr const char dolls[] = "🎎"; + inline constexpr const char folding_hand_fan[] = "🪭"; + inline constexpr const char izakaya_lantern[] = "🏮"; + inline constexpr const char wind_chime[] = "🎐"; + inline constexpr const char mirror_ball[] = "🪩"; + inline constexpr const char red_envelope[] = "🧧"; + inline constexpr const char envelope[] = "✉️"; + inline constexpr const char envelope_with_arrow[] = "📩"; + inline constexpr const char incoming_envelope[] = "📨"; + inline constexpr const char e_mail[] = "📧"; + inline constexpr const char email[] = "📧"; + inline constexpr const char love_letter[] = "💌"; + inline constexpr const char inbox_tray[] = "📥"; + inline constexpr const char outbox_tray[] = "📤"; + inline constexpr const char package[] = "📦"; + inline constexpr const char label[] = "🏷️"; + inline constexpr const char placard[] = "🪧"; + inline constexpr const char mailbox_closed[] = "📪"; + inline constexpr const char mailbox[] = "📫"; + inline constexpr const char mailbox_with_mail[] = "📬"; + inline constexpr const char mailbox_with_no_mail[] = "📭"; + inline constexpr const char postbox[] = "📮"; + inline constexpr const char postal_horn[] = "📯"; + inline constexpr const char scroll[] = "📜"; + inline constexpr const char page_with_curl[] = "📃"; + inline constexpr const char page_facing_up[] = "📄"; + inline constexpr const char bookmark_tabs[] = "📑"; + inline constexpr const char receipt[] = "🧾"; + inline constexpr const char bar_chart[] = "📊"; + inline constexpr const char chart_with_upwards_trend[] = "📈"; + inline constexpr const char chart_with_downwards_trend[] = "📉"; + inline constexpr const char notepad_spiral[] = "🗒️"; + inline constexpr const char spiral_note_pad[] = "🗒️"; + inline constexpr const char calendar_spiral[] = "🗓️"; + inline constexpr const char spiral_calendar_pad[] = "🗓️"; + inline constexpr const char calendar[] = "📆"; + inline constexpr const char date[] = "📅"; + inline constexpr const char wastebasket[] = "🗑️"; + inline constexpr const char card_index[] = "📇"; + inline constexpr const char card_box[] = "🗃️"; + inline constexpr const char card_file_box[] = "🗃️"; + inline constexpr const char ballot_box[] = "🗳️"; + inline constexpr const char ballot_box_with_ballot[] = "🗳️"; + inline constexpr const char file_cabinet[] = "🗄️"; + inline constexpr const char clipboard[] = "📋"; + inline constexpr const char file_folder[] = "📁"; + inline constexpr const char open_file_folder[] = "📂"; + inline constexpr const char dividers[] = "🗂️"; + inline constexpr const char card_index_dividers[] = "🗂️"; + inline constexpr const char newspaper2[] = "🗞️"; + inline constexpr const char rolled_up_newspaper[] = "🗞️"; + inline constexpr const char newspaper[] = "📰"; + inline constexpr const char notebook[] = "📓"; + inline constexpr const char notebook_with_decorative_cover[] = "📔"; + inline constexpr const char ledger[] = "📒"; + inline constexpr const char closed_book[] = "📕"; + inline constexpr const char green_book[] = "📗"; + inline constexpr const char blue_book[] = "📘"; + inline constexpr const char orange_book[] = "📙"; + inline constexpr const char books[] = "📚"; + inline constexpr const char book[] = "📖"; + inline constexpr const char open_book[] = "📖"; + inline constexpr const char bookmark[] = "🔖"; + inline constexpr const char safety_pin[] = "🧷"; + inline constexpr const char link[] = "🔗"; + inline constexpr const char paperclip[] = "📎"; + inline constexpr const char paperclips[] = "🖇️"; + inline constexpr const char linked_paperclips[] = "🖇️"; + inline constexpr const char triangular_ruler[] = "📐"; + inline constexpr const char straight_ruler[] = "📏"; + inline constexpr const char abacus[] = "🧮"; + inline constexpr const char pushpin[] = "📌"; + inline constexpr const char round_pushpin[] = "📍"; + inline constexpr const char scissors[] = "✂️"; + inline constexpr const char pen_ballpoint[] = "🖊️"; + inline constexpr const char lower_left_ballpoint_pen[] = "🖊️"; + inline constexpr const char pen[] = "🖊️"; + inline constexpr const char pen_fountain[] = "🖋️"; + inline constexpr const char lower_left_fountain_pen[] = "🖋️"; + inline constexpr const char fountain_pen[] = "🖋️"; + inline constexpr const char black_nib[] = "✒️"; + inline constexpr const char paintbrush[] = "🖌️"; + inline constexpr const char lower_left_paintbrush[] = "🖌️"; + inline constexpr const char crayon[] = "🖍️"; + inline constexpr const char lower_left_crayon[] = "🖍️"; + inline constexpr const char pencil[] = "📝"; + inline constexpr const char memo[] = "📝"; + inline constexpr const char pencil2[] = "✏️"; + inline constexpr const char mag[] = "🔍"; + inline constexpr const char mag_right[] = "🔎"; + inline constexpr const char lock_with_ink_pen[] = "🔏"; + inline constexpr const char closed_lock_with_key[] = "🔐"; + inline constexpr const char lock[] = "🔒"; + inline constexpr const char locked[] = "🔒"; + inline constexpr const char unlock[] = "🔓"; + inline constexpr const char unlocked[] = "🔓"; + inline constexpr const char grinning[] = "😀"; + inline constexpr const char grinning_face[] = "😀"; + inline constexpr const char smiley[] = "😃"; + inline constexpr const char smile[] = "😄"; + inline constexpr const char grin[] = "😁"; + inline constexpr const char laughing[] = "😆"; + inline constexpr const char satisfied[] = "😆"; + inline constexpr const char face_holding_back_tears[] = "🥹"; + inline constexpr const char sweat_smile[] = "😅"; + inline constexpr const char joy[] = "😂"; + inline constexpr const char rofl[] = "🤣"; + inline constexpr const char rolling_on_the_floor_laughing[] = "🤣"; + inline constexpr const char smiling_face_with_tear[] = "🥲"; + inline constexpr const char relaxed[] = "☺️"; + inline constexpr const char smiling_face[] = "☺️"; + inline constexpr const char blush[] = "😊"; + inline constexpr const char innocent[] = "😇"; + inline constexpr const char slight_smile[] = "🙂"; + inline constexpr const char slightly_smiling_face[] = "🙂"; + inline constexpr const char upside_down[] = "🙃"; + inline constexpr const char upside_down_face[] = "🙃"; + inline constexpr const char wink[] = "😉"; + inline constexpr const char winking_face[] = "😉"; + inline constexpr const char relieved[] = "😌"; + inline constexpr const char relieved_face[] = "😌"; + inline constexpr const char heart_eyes[] = "😍"; + inline constexpr const char smiling_face_with_3_hearts[] = "🥰"; + inline constexpr const char kissing_heart[] = "😘"; + inline constexpr const char kissing[] = "😗"; + inline constexpr const char kissing_face[] = "😗"; + inline constexpr const char kissing_smiling_eyes[] = "😙"; + inline constexpr const char kissing_closed_eyes[] = "😚"; + inline constexpr const char yum[] = "😋"; + inline constexpr const char stuck_out_tongue[] = "😛"; + inline constexpr const char stuck_out_tongue_closed_eyes[] = "😝"; + inline constexpr const char stuck_out_tongue_winking_eye[] = "😜"; + inline constexpr const char zany_face[] = "🤪"; + inline constexpr const char face_with_raised_eyebrow[] = "🤨"; + inline constexpr const char face_with_monocle[] = "🧐"; + inline constexpr const char nerd[] = "🤓"; + inline constexpr const char nerd_face[] = "🤓"; + inline constexpr const char sunglasses[] = "😎"; + inline constexpr const char disguised_face[] = "🥸"; + inline constexpr const char star_struck[] = "🤩"; + inline constexpr const char partying_face[] = "🥳"; + inline constexpr const char smirk[] = "😏"; + inline constexpr const char smirking_face[] = "😏"; + inline constexpr const char unamused[] = "😒"; + inline constexpr const char unamused_face[] = "😒"; + inline constexpr const char disappointed[] = "😞"; + inline constexpr const char pensive[] = "😔"; + inline constexpr const char pensive_face[] = "😔"; + inline constexpr const char worried[] = "😟"; + inline constexpr const char worried_face[] = "😟"; + inline constexpr const char confused[] = "😕"; + inline constexpr const char confused_face[] = "😕"; + inline constexpr const char slight_frown[] = "🙁"; + inline constexpr const char slightly_frowning_face[] = "🙁"; + inline constexpr const char frowning2[] = "☹️"; + inline constexpr const char white_frowning_face[] = "☹️"; + inline constexpr const char frowning_face[] = "☹️"; + inline constexpr const char persevere[] = "😣"; + inline constexpr const char confounded[] = "😖"; + inline constexpr const char tired_face[] = "😫"; + inline constexpr const char weary[] = "😩"; + inline constexpr const char weary_face[] = "😩"; + inline constexpr const char pleading_face[] = "🥺"; + inline constexpr const char cry[] = "😢"; + inline constexpr const char crying_face[] = "😢"; + inline constexpr const char sob[] = "😭"; + inline constexpr const char triumph[] = "😤"; + inline constexpr const char angry[] = "😠"; + inline constexpr const char angry_face[] = "😠"; + inline constexpr const char rage[] = "😡"; + inline constexpr const char pouting_face[] = "😡"; + inline constexpr const char face_with_symbols_over_mouth[] = "🤬"; + inline constexpr const char exploding_head[] = "🤯"; + inline constexpr const char flushed[] = "😳"; + inline constexpr const char flushed_face[] = "😳"; + inline constexpr const char hot_face[] = "🥵"; + inline constexpr const char cold_face[] = "🥶"; + inline constexpr const char face_in_clouds[] = "😶‍🌫️"; + inline constexpr const char scream[] = "😱"; + inline constexpr const char fearful[] = "😨"; + inline constexpr const char fearful_face[] = "😨"; + inline constexpr const char cold_sweat[] = "😰"; + inline constexpr const char disappointed_relieved[] = "😥"; + inline constexpr const char sweat[] = "😓"; + inline constexpr const char hugging[] = "🤗"; + inline constexpr const char hugging_face[] = "🤗"; + inline constexpr const char thinking[] = "🤔"; + inline constexpr const char thinking_face[] = "🤔"; + inline constexpr const char face_with_peeking_eye[] = "🫣"; + inline constexpr const char face_with_hand_over_mouth[] = "🤭"; + inline constexpr const char face_with_open_eyes_and_hand_over_mouth[] = "🫢"; + inline constexpr const char saluting_face[] = "🫡"; + inline constexpr const char shushing_face[] = "🤫"; + inline constexpr const char melting_face[] = "🫠"; + inline constexpr const char lying_face[] = "🤥"; + inline constexpr const char liar[] = "🤥"; + inline constexpr const char no_mouth[] = "😶"; + inline constexpr const char dotted_line_face[] = "🫥"; + inline constexpr const char neutral_face[] = "😐"; + inline constexpr const char face_with_diagonal_mouth[] = "🫤"; + inline constexpr const char expressionless[] = "😑"; + inline constexpr const char shaking_face[] = "🫨"; + inline constexpr const char grimacing[] = "😬"; + inline constexpr const char rolling_eyes[] = "🙄"; + inline constexpr const char face_with_rolling_eyes[] = "🙄"; + inline constexpr const char hushed[] = "😯"; + inline constexpr const char hushed_face[] = "😯"; + inline constexpr const char frowning[] = "😦"; + inline constexpr const char anguished[] = "😧"; + inline constexpr const char open_mouth[] = "😮"; + inline constexpr const char astonished[] = "😲"; + inline constexpr const char yawning_face[] = "🥱"; + inline constexpr const char sleeping[] = "😴"; + inline constexpr const char sleeping_face[] = "😴"; + inline constexpr const char drooling_face[] = "🤤"; + inline constexpr const char drool[] = "🤤"; + inline constexpr const char sleepy[] = "😪"; + inline constexpr const char sleepy_face[] = "😪"; + inline constexpr const char face_exhaling[] = "😮‍💨"; + inline constexpr const char dizzy_face[] = "😵"; + inline constexpr const char face_with_spiral_eyes[] = "😵‍💫"; + inline constexpr const char zipper_mouth[] = "🤐"; + inline constexpr const char zipper_mouth_face[] = "🤐"; + inline constexpr const char woozy_face[] = "🥴"; + inline constexpr const char nauseated_face[] = "🤢"; + inline constexpr const char sick[] = "🤢"; + inline constexpr const char face_vomiting[] = "🤮"; + inline constexpr const char sneezing_face[] = "🤧"; + inline constexpr const char sneeze[] = "🤧"; + inline constexpr const char mask[] = "😷"; + inline constexpr const char thermometer_face[] = "🤒"; + inline constexpr const char face_with_thermometer[] = "🤒"; + inline constexpr const char head_bandage[] = "🤕"; + inline constexpr const char face_with_head_bandage[] = "🤕"; + inline constexpr const char money_mouth[] = "🤑"; + inline constexpr const char money_mouth_face[] = "🤑"; + inline constexpr const char cowboy[] = "🤠"; + inline constexpr const char face_with_cowboy_hat[] = "🤠"; + inline constexpr const char smiling_imp[] = "😈"; + inline constexpr const char imp[] = "👿"; + inline constexpr const char japanese_ogre[] = "👹"; + inline constexpr const char ogre[] = "👹"; + inline constexpr const char japanese_goblin[] = "👺"; + inline constexpr const char goblin[] = "👺"; + inline constexpr const char clown[] = "🤡"; + inline constexpr const char clown_face[] = "🤡"; + inline constexpr const char poop[] = "💩"; + inline constexpr const char shit[] = "💩"; + inline constexpr const char hankey[] = "💩"; + inline constexpr const char poo[] = "💩"; + inline constexpr const char pile_of_poo[] = "💩"; + inline constexpr const char ghost[] = "👻"; + inline constexpr const char skull[] = "💀"; + inline constexpr const char skeleton[] = "💀"; + inline constexpr const char skull_crossbones[] = "☠️"; + inline constexpr const char skull_and_crossbones[] = "☠️"; + inline constexpr const char alien[] = "👽"; + inline constexpr const char space_invader[] = "👾"; + inline constexpr const char alien_monster[] = "👾"; + inline constexpr const char robot[] = "🤖"; + inline constexpr const char robot_face[] = "🤖"; + inline constexpr const char jack_o_lantern[] = "🎃"; + inline constexpr const char smiley_cat[] = "😺"; + inline constexpr const char grinning_cat[] = "😺"; + inline constexpr const char smile_cat[] = "😸"; + inline constexpr const char joy_cat[] = "😹"; + inline constexpr const char heart_eyes_cat[] = "😻"; + inline constexpr const char smirk_cat[] = "😼"; + inline constexpr const char kissing_cat[] = "😽"; + inline constexpr const char scream_cat[] = "🙀"; + inline constexpr const char weary_cat[] = "🙀"; + inline constexpr const char crying_cat_face[] = "😿"; + inline constexpr const char crying_cat[] = "😿"; + inline constexpr const char pouting_cat[] = "😾"; + inline constexpr const char heart_hands[] = "🫶"; + inline constexpr const char heart_hands_tone1[] = "🫶🏻"; + inline constexpr const char heart_hands_light_skin_tone[] = "🫶🏻"; + inline constexpr const char heart_hands_tone2[] = "🫶🏼"; + inline constexpr const char heart_hands_medium_light_skin_tone[] = "🫶🏼"; + inline constexpr const char heart_hands_tone3[] = "🫶🏽"; + inline constexpr const char heart_hands_medium_skin_tone[] = "🫶🏽"; + inline constexpr const char heart_hands_tone4[] = "🫶🏾"; + inline constexpr const char heart_hands_medium_dark_skin_tone[] = "🫶🏾"; + inline constexpr const char heart_hands_tone5[] = "🫶🏿"; + inline constexpr const char heart_hands_dark_skin_tone[] = "🫶🏿"; + inline constexpr const char palms_up_together[] = "🤲"; + inline constexpr const char palms_up_together_tone1[] = "🤲🏻"; + inline constexpr const char palms_up_together_light_skin_tone[] = "🤲🏻"; + inline constexpr const char palms_up_together_tone2[] = "🤲🏼"; + inline constexpr const char palms_up_together_medium_light_skin_tone[] = "🤲🏼"; + inline constexpr const char palms_up_together_tone3[] = "🤲🏽"; + inline constexpr const char palms_up_together_medium_skin_tone[] = "🤲🏽"; + inline constexpr const char palms_up_together_tone4[] = "🤲🏾"; + inline constexpr const char palms_up_together_medium_dark_skin_tone[] = "🤲🏾"; + inline constexpr const char palms_up_together_tone5[] = "🤲🏿"; + inline constexpr const char palms_up_together_dark_skin_tone[] = "🤲🏿"; + inline constexpr const char open_hands[] = "👐"; + inline constexpr const char open_hands_tone1[] = "👐🏻"; + inline constexpr const char open_hands_tone2[] = "👐🏼"; + inline constexpr const char open_hands_tone3[] = "👐🏽"; + inline constexpr const char open_hands_tone4[] = "👐🏾"; + inline constexpr const char open_hands_tone5[] = "👐🏿"; + inline constexpr const char raised_hands[] = "🙌"; + inline constexpr const char raising_hands[] = "🙌"; + inline constexpr const char raised_hands_tone1[] = "🙌🏻"; + inline constexpr const char raised_hands_tone2[] = "🙌🏼"; + inline constexpr const char raised_hands_tone3[] = "🙌🏽"; + inline constexpr const char raised_hands_tone4[] = "🙌🏾"; + inline constexpr const char raised_hands_tone5[] = "🙌🏿"; + inline constexpr const char clap[] = "👏"; + inline constexpr const char clap_tone1[] = "👏🏻"; + inline constexpr const char clap_tone2[] = "👏🏼"; + inline constexpr const char clap_tone3[] = "👏🏽"; + inline constexpr const char clap_tone4[] = "👏🏾"; + inline constexpr const char clap_tone5[] = "👏🏿"; + inline constexpr const char handshake[] = "🤝"; + inline constexpr const char shaking_hands[] = "🤝"; + inline constexpr const char handshake_tone1[] = "🤝🏻"; + inline constexpr const char handshake_light_skin_tone[] = "🤝🏻"; + inline constexpr const char handshake_tone1_tone2[] = "🫱🏻‍🫲🏼"; + inline constexpr const char handshake_light_skin_tone_medium_light_skin_tone[] = "🫱🏻‍🫲🏼"; + inline constexpr const char handshake_tone1_tone3[] = "🫱🏻‍🫲🏽"; + inline constexpr const char handshake_light_skin_tone_medium_skin_tone[] = "🫱🏻‍🫲🏽"; + inline constexpr const char handshake_tone1_tone4[] = "🫱🏻‍🫲🏾"; + inline constexpr const char handshake_light_skin_tone_medium_dark_skin_tone[] = "🫱🏻‍🫲🏾"; + inline constexpr const char handshake_tone1_tone5[] = "🫱🏻‍🫲🏿"; + inline constexpr const char handshake_light_skin_tone_dark_skin_tone[] = "🫱🏻‍🫲🏿"; + inline constexpr const char handshake_tone2_tone1[] = "🫱🏼‍🫲🏻"; + inline constexpr const char handshake_medium_light_skin_tone_light_skin_tone[] = "🫱🏼‍🫲🏻"; + inline constexpr const char handshake_tone2[] = "🤝🏼"; + inline constexpr const char handshake_medium_light_skin_tone[] = "🤝🏼"; + inline constexpr const char handshake_tone2_tone3[] = "🫱🏼‍🫲🏽"; + inline constexpr const char handshake_medium_light_skin_tone_medium_skin_tone[] = "🫱🏼‍🫲🏽"; + inline constexpr const char handshake_tone2_tone4[] = "🫱🏼‍🫲🏾"; + inline constexpr const char handshake_medium_light_skin_tone_medium_dark_skin_tone[] = "🫱🏼‍🫲🏾"; + inline constexpr const char handshake_tone2_tone5[] = "🫱🏼‍🫲🏿"; + inline constexpr const char handshake_medium_light_skin_tone_dark_skin_tone[] = "🫱🏼‍🫲🏿"; + inline constexpr const char handshake_tone3_tone1[] = "🫱🏽‍🫲🏻"; + inline constexpr const char handshake_medium_skin_tone_light_skin_tone[] = "🫱🏽‍🫲🏻"; + inline constexpr const char handshake_tone3_tone2[] = "🫱🏽‍🫲🏼"; + inline constexpr const char handshake_medium_skin_tone_medium_light_skin_tone[] = "🫱🏽‍🫲🏼"; + inline constexpr const char handshake_tone3[] = "🤝🏽"; + inline constexpr const char handshake_medium_skin_tone[] = "🤝🏽"; + inline constexpr const char handshake_tone3_tone4[] = "🫱🏽‍🫲🏾"; + inline constexpr const char handshake_medium_skin_tone_medium_dark_skin_tone[] = "🫱🏽‍🫲🏾"; + inline constexpr const char handshake_tone3_tone5[] = "🫱🏽‍🫲🏿"; + inline constexpr const char handshake_medium_skin_tone_dark_skin_tone[] = "🫱🏽‍🫲🏿"; + inline constexpr const char handshake_tone4_tone1[] = "🫱🏾‍🫲🏻"; + inline constexpr const char handshake_medium_dark_skin_tone_light_skin_tone[] = "🫱🏾‍🫲🏻"; + inline constexpr const char handshake_tone4_tone2[] = "🫱🏾‍🫲🏼"; + inline constexpr const char handshake_medium_dark_skin_tone_medium_light_skin_tone[] = "🫱🏾‍🫲🏼"; + inline constexpr const char handshake_tone4_tone3[] = "🫱🏾‍🫲🏽"; + inline constexpr const char handshake_medium_dark_skin_tone_medium_skin_tone[] = "🫱🏾‍🫲🏽"; + inline constexpr const char handshake_tone4[] = "🤝🏾"; + inline constexpr const char handshake_medium_dark_skin_tone[] = "🤝🏾"; + inline constexpr const char handshake_tone4_tone5[] = "🫱🏾‍🫲🏿"; + inline constexpr const char handshake_medium_dark_skin_tone_dark_skin_tone[] = "🫱🏾‍🫲🏿"; + inline constexpr const char handshake_tone5_tone1[] = "🫱🏿‍🫲🏻"; + inline constexpr const char handshake_dark_skin_tone_light_skin_tone[] = "🫱🏿‍🫲🏻"; + inline constexpr const char handshake_tone5_tone2[] = "🫱🏿‍🫲🏼"; + inline constexpr const char handshake_dark_skin_tone_medium_light_skin_tone[] = "🫱🏿‍🫲🏼"; + inline constexpr const char handshake_tone5_tone3[] = "🫱🏿‍🫲🏽"; + inline constexpr const char handshake_dark_skin_tone_medium_skin_tone[] = "🫱🏿‍🫲🏽"; + inline constexpr const char handshake_tone5_tone4[] = "🫱🏿‍🫲🏾"; + inline constexpr const char handshake_dark_skin_tone_medium_dark_skin_tone[] = "🫱🏿‍🫲🏾"; + inline constexpr const char handshake_tone5[] = "🤝🏿"; + inline constexpr const char handshake_dark_skin_tone[] = "🤝🏿"; + inline constexpr const char thumbsup[] = "👍"; + inline constexpr const char plus1[] = "👍"; + inline constexpr const char thumbup[] = "👍"; + inline constexpr const char thumbs_up[] = "👍"; + inline constexpr const char thumbsup_tone1[] = "👍🏻"; + inline constexpr const char plus1_tone1[] = "👍🏻"; + inline constexpr const char thumbup_tone1[] = "👍🏻"; + inline constexpr const char thumbsup_tone2[] = "👍🏼"; + inline constexpr const char plus1_tone2[] = "👍🏼"; + inline constexpr const char thumbup_tone2[] = "👍🏼"; + inline constexpr const char thumbsup_tone3[] = "👍🏽"; + inline constexpr const char plus1_tone3[] = "👍🏽"; + inline constexpr const char thumbup_tone3[] = "👍🏽"; + inline constexpr const char thumbsup_tone4[] = "👍🏾"; + inline constexpr const char plus1_tone4[] = "👍🏾"; + inline constexpr const char thumbup_tone4[] = "👍🏾"; + inline constexpr const char thumbsup_tone5[] = "👍🏿"; + inline constexpr const char plus1_tone5[] = "👍🏿"; + inline constexpr const char thumbup_tone5[] = "👍🏿"; + inline constexpr const char thumbsdown[] = "👎"; + inline constexpr const char minus1[] = "👎"; + inline constexpr const char thumbdown[] = "👎"; + inline constexpr const char thumbs_down[] = "👎"; + inline constexpr const char thumbsdown_tone1[] = "👎🏻"; + inline constexpr const char _1_tone1[] = "👎🏻"; + inline constexpr const char thumbdown_tone1[] = "👎🏻"; + inline constexpr const char thumbsdown_tone2[] = "👎🏼"; + inline constexpr const char _1_tone2[] = "👎🏼"; + inline constexpr const char thumbdown_tone2[] = "👎🏼"; + inline constexpr const char thumbsdown_tone3[] = "👎🏽"; + inline constexpr const char _1_tone3[] = "👎🏽"; + inline constexpr const char thumbdown_tone3[] = "👎🏽"; + inline constexpr const char thumbsdown_tone4[] = "👎🏾"; + inline constexpr const char _1_tone4[] = "👎🏾"; + inline constexpr const char thumbdown_tone4[] = "👎🏾"; + inline constexpr const char thumbsdown_tone5[] = "👎🏿"; + inline constexpr const char _1_tone5[] = "👎🏿"; + inline constexpr const char thumbdown_tone5[] = "👎🏿"; + inline constexpr const char punch[] = "👊"; + inline constexpr const char oncoming_fist[] = "👊"; + inline constexpr const char punch_tone1[] = "👊🏻"; + inline constexpr const char punch_tone2[] = "👊🏼"; + inline constexpr const char punch_tone3[] = "👊🏽"; + inline constexpr const char punch_tone4[] = "👊🏾"; + inline constexpr const char punch_tone5[] = "👊🏿"; + inline constexpr const char fist[] = "✊"; + inline constexpr const char raised_fist[] = "✊"; + inline constexpr const char fist_tone1[] = "✊🏻"; + inline constexpr const char fist_tone2[] = "✊🏼"; + inline constexpr const char fist_tone3[] = "✊🏽"; + inline constexpr const char fist_tone4[] = "✊🏾"; + inline constexpr const char fist_tone5[] = "✊🏿"; + inline constexpr const char left_facing_fist[] = "🤛"; + inline constexpr const char left_fist[] = "🤛"; + inline constexpr const char left_facing_fist_tone1[] = "🤛🏻"; + inline constexpr const char left_fist_tone1[] = "🤛🏻"; + inline constexpr const char left_facing_fist_tone2[] = "🤛🏼"; + inline constexpr const char left_fist_tone2[] = "🤛🏼"; + inline constexpr const char left_facing_fist_tone3[] = "🤛🏽"; + inline constexpr const char left_fist_tone3[] = "🤛🏽"; + inline constexpr const char left_facing_fist_tone4[] = "🤛🏾"; + inline constexpr const char left_fist_tone4[] = "🤛🏾"; + inline constexpr const char left_facing_fist_tone5[] = "🤛🏿"; + inline constexpr const char left_fist_tone5[] = "🤛🏿"; + inline constexpr const char right_facing_fist[] = "🤜"; + inline constexpr const char right_fist[] = "🤜"; + inline constexpr const char right_facing_fist_tone1[] = "🤜🏻"; + inline constexpr const char right_fist_tone1[] = "🤜🏻"; + inline constexpr const char right_facing_fist_tone2[] = "🤜🏼"; + inline constexpr const char right_fist_tone2[] = "🤜🏼"; + inline constexpr const char right_facing_fist_tone3[] = "🤜🏽"; + inline constexpr const char right_fist_tone3[] = "🤜🏽"; + inline constexpr const char right_facing_fist_tone4[] = "🤜🏾"; + inline constexpr const char right_fist_tone4[] = "🤜🏾"; + inline constexpr const char right_facing_fist_tone5[] = "🤜🏿"; + inline constexpr const char right_fist_tone5[] = "🤜🏿"; + inline constexpr const char leftwards_pushing_hand[] = "🫷"; + inline constexpr const char leftwards_pushing_hand_tone1[] = "🫷🏻"; + inline constexpr const char leftwards_pushing_hand_light_skin_tone[] = "🫷🏻"; + inline constexpr const char leftwards_pushing_hand_tone2[] = "🫷🏼"; + inline constexpr const char leftwards_pushing_hand_medium_light_skin_tone[] = "🫷🏼"; + inline constexpr const char leftwards_pushing_hand_tone3[] = "🫷🏽"; + inline constexpr const char leftwards_pushing_hand_medium_skin_tone[] = "🫷🏽"; + inline constexpr const char leftwards_pushing_hand_tone4[] = "🫷🏾"; + inline constexpr const char leftwards_pushing_hand_medium_dark_skin_tone[] = "🫷🏾"; + inline constexpr const char leftwards_pushing_hand_tone5[] = "🫷🏿"; + inline constexpr const char leftwards_pushing_hand_dark_skin_tone[] = "🫷🏿"; + inline constexpr const char rightwards_pushing_hand[] = "🫸"; + inline constexpr const char rightwards_pushing_hand_tone1[] = "🫸🏻"; + inline constexpr const char rightwards_pushing_hand_light_skin_tone[] = "🫸🏻"; + inline constexpr const char rightwards_pushing_hand_tone2[] = "🫸🏼"; + inline constexpr const char rightwards_pushing_hand_medium_light_skin_tone[] = "🫸🏼"; + inline constexpr const char rightwards_pushing_hand_tone3[] = "🫸🏽"; + inline constexpr const char rightwards_pushing_hand_medium_skin_tone[] = "🫸🏽"; + inline constexpr const char rightwards_pushing_hand_tone4[] = "🫸🏾"; + inline constexpr const char rightwards_pushing_hand_medium_dark_skin_tone[] = "🫸🏾"; + inline constexpr const char rightwards_pushing_hand_tone5[] = "🫸🏿"; + inline constexpr const char rightwards_pushing_hand_dark_skin_tone[] = "🫸🏿"; + inline constexpr const char fingers_crossed[] = "🤞"; + inline constexpr const char hand_with_index_and_middle_finger_crossed[] = "🤞"; + inline constexpr const char fingers_crossed_tone1[] = "🤞🏻"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone1[] = "🤞🏻"; + inline constexpr const char fingers_crossed_tone2[] = "🤞🏼"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone2[] = "🤞🏼"; + inline constexpr const char fingers_crossed_tone3[] = "🤞🏽"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone3[] = "🤞🏽"; + inline constexpr const char fingers_crossed_tone4[] = "🤞🏾"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone4[] = "🤞🏾"; + inline constexpr const char fingers_crossed_tone5[] = "🤞🏿"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone5[] = "🤞🏿"; + inline constexpr const char v[] = "✌️"; + inline constexpr const char victory_hand[] = "✌️"; + inline constexpr const char v_tone1[] = "✌🏻"; + inline constexpr const char v_tone2[] = "✌🏼"; + inline constexpr const char v_tone3[] = "✌🏽"; + inline constexpr const char v_tone4[] = "✌🏾"; + inline constexpr const char v_tone5[] = "✌🏿"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed[] = "🫰"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone1[] = "🫰🏻"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_light_skin_tone[] = "🫰🏻"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone2[] = "🫰🏼"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_medium_light_skin_tone[] = "🫰🏼"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone3[] = "🫰🏽"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_medium_skin_tone[] = "🫰🏽"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone4[] = "🫰🏾"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_medium_dark_skin_tone[] = "🫰🏾"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone5[] = "🫰🏿"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_dark_skin_tone[] = "🫰🏿"; + inline constexpr const char love_you_gesture[] = "🤟"; + inline constexpr const char love_you_gesture_tone1[] = "🤟🏻"; + inline constexpr const char love_you_gesture_light_skin_tone[] = "🤟🏻"; + inline constexpr const char love_you_gesture_tone2[] = "🤟🏼"; + inline constexpr const char love_you_gesture_medium_light_skin_tone[] = "🤟🏼"; + inline constexpr const char love_you_gesture_tone3[] = "🤟🏽"; + inline constexpr const char love_you_gesture_medium_skin_tone[] = "🤟🏽"; + inline constexpr const char love_you_gesture_tone4[] = "🤟🏾"; + inline constexpr const char love_you_gesture_medium_dark_skin_tone[] = "🤟🏾"; + inline constexpr const char love_you_gesture_tone5[] = "🤟🏿"; + inline constexpr const char love_you_gesture_dark_skin_tone[] = "🤟🏿"; + inline constexpr const char metal[] = "🤘"; + inline constexpr const char sign_of_the_horns[] = "🤘"; + inline constexpr const char metal_tone1[] = "🤘🏻"; + inline constexpr const char sign_of_the_horns_tone1[] = "🤘🏻"; + inline constexpr const char metal_tone2[] = "🤘🏼"; + inline constexpr const char sign_of_the_horns_tone2[] = "🤘🏼"; + inline constexpr const char metal_tone3[] = "🤘🏽"; + inline constexpr const char sign_of_the_horns_tone3[] = "🤘🏽"; + inline constexpr const char metal_tone4[] = "🤘🏾"; + inline constexpr const char sign_of_the_horns_tone4[] = "🤘🏾"; + inline constexpr const char metal_tone5[] = "🤘🏿"; + inline constexpr const char sign_of_the_horns_tone5[] = "🤘🏿"; + inline constexpr const char ok_hand[] = "👌"; + inline constexpr const char ok_hand_tone1[] = "👌🏻"; + inline constexpr const char ok_hand_tone2[] = "👌🏼"; + inline constexpr const char ok_hand_tone3[] = "👌🏽"; + inline constexpr const char ok_hand_tone4[] = "👌🏾"; + inline constexpr const char ok_hand_tone5[] = "👌🏿"; + inline constexpr const char pinched_fingers[] = "🤌"; + inline constexpr const char pinched_fingers_tone2[] = "🤌🏼"; + inline constexpr const char pinched_fingers_medium_light_skin_tone[] = "🤌🏼"; + inline constexpr const char pinched_fingers_tone1[] = "🤌🏻"; + inline constexpr const char pinched_fingers_light_skin_tone[] = "🤌🏻"; + inline constexpr const char pinched_fingers_tone3[] = "🤌🏽"; + inline constexpr const char pinched_fingers_medium_skin_tone[] = "🤌🏽"; + inline constexpr const char pinched_fingers_tone4[] = "🤌🏾"; + inline constexpr const char pinched_fingers_medium_dark_skin_tone[] = "🤌🏾"; + inline constexpr const char pinched_fingers_tone5[] = "🤌🏿"; + inline constexpr const char pinched_fingers_dark_skin_tone[] = "🤌🏿"; + inline constexpr const char pinching_hand[] = "🤏"; + inline constexpr const char pinching_hand_tone1[] = "🤏🏻"; + inline constexpr const char pinching_hand_light_skin_tone[] = "🤏🏻"; + inline constexpr const char pinching_hand_tone2[] = "🤏🏼"; + inline constexpr const char pinching_hand_medium_light_skin_tone[] = "🤏🏼"; + inline constexpr const char pinching_hand_tone3[] = "🤏🏽"; + inline constexpr const char pinching_hand_medium_skin_tone[] = "🤏🏽"; + inline constexpr const char pinching_hand_tone4[] = "🤏🏾"; + inline constexpr const char pinching_hand_medium_dark_skin_tone[] = "🤏🏾"; + inline constexpr const char pinching_hand_tone5[] = "🤏🏿"; + inline constexpr const char pinching_hand_dark_skin_tone[] = "🤏🏿"; + inline constexpr const char palm_down_hand[] = "🫳"; + inline constexpr const char palm_down_hand_tone1[] = "🫳🏻"; + inline constexpr const char palm_down_hand_light_skin_tone[] = "🫳🏻"; + inline constexpr const char palm_down_hand_tone2[] = "🫳🏼"; + inline constexpr const char palm_down_hand_medium_light_skin_tone[] = "🫳🏼"; + inline constexpr const char palm_down_hand_tone3[] = "🫳🏽"; + inline constexpr const char palm_down_hand_medium_skin_tone[] = "🫳🏽"; + inline constexpr const char palm_down_hand_tone4[] = "🫳🏾"; + inline constexpr const char palm_down_hand_medium_dark_skin_tone[] = "🫳🏾"; + inline constexpr const char palm_down_hand_tone5[] = "🫳🏿"; + inline constexpr const char palm_down_hand_dark_skin_tone[] = "🫳🏿"; + inline constexpr const char palm_up_hand[] = "🫴"; + inline constexpr const char palm_up_hand_tone1[] = "🫴🏻"; + inline constexpr const char palm_up_hand_light_skin_tone[] = "🫴🏻"; + inline constexpr const char palm_up_hand_tone2[] = "🫴🏼"; + inline constexpr const char palm_up_hand_medium_light_skin_tone[] = "🫴🏼"; + inline constexpr const char palm_up_hand_tone3[] = "🫴🏽"; + inline constexpr const char palm_up_hand_medium_skin_tone[] = "🫴🏽"; + inline constexpr const char palm_up_hand_tone4[] = "🫴🏾"; + inline constexpr const char palm_up_hand_medium_dark_skin_tone[] = "🫴🏾"; + inline constexpr const char palm_up_hand_tone5[] = "🫴🏿"; + inline constexpr const char palm_up_hand_dark_skin_tone[] = "🫴🏿"; + inline constexpr const char point_left[] = "👈"; + inline constexpr const char point_left_tone1[] = "👈🏻"; + inline constexpr const char point_left_tone2[] = "👈🏼"; + inline constexpr const char point_left_tone3[] = "👈🏽"; + inline constexpr const char point_left_tone4[] = "👈🏾"; + inline constexpr const char point_left_tone5[] = "👈🏿"; + inline constexpr const char point_right[] = "👉"; + inline constexpr const char point_right_tone1[] = "👉🏻"; + inline constexpr const char point_right_tone2[] = "👉🏼"; + inline constexpr const char point_right_tone3[] = "👉🏽"; + inline constexpr const char point_right_tone4[] = "👉🏾"; + inline constexpr const char point_right_tone5[] = "👉🏿"; + inline constexpr const char point_up_2[] = "👆"; + inline constexpr const char point_up_2_tone1[] = "👆🏻"; + inline constexpr const char point_up_2_tone2[] = "👆🏼"; + inline constexpr const char point_up_2_tone3[] = "👆🏽"; + inline constexpr const char point_up_2_tone4[] = "👆🏾"; + inline constexpr const char point_up_2_tone5[] = "👆🏿"; + inline constexpr const char point_down[] = "👇"; + inline constexpr const char point_down_tone1[] = "👇🏻"; + inline constexpr const char point_down_tone2[] = "👇🏼"; + inline constexpr const char point_down_tone3[] = "👇🏽"; + inline constexpr const char point_down_tone4[] = "👇🏾"; + inline constexpr const char point_down_tone5[] = "👇🏿"; + inline constexpr const char point_up[] = "☝️"; + inline constexpr const char point_up_tone1[] = "☝🏻"; + inline constexpr const char point_up_tone2[] = "☝🏼"; + inline constexpr const char point_up_tone3[] = "☝🏽"; + inline constexpr const char point_up_tone4[] = "☝🏾"; + inline constexpr const char point_up_tone5[] = "☝🏿"; + inline constexpr const char raised_hand[] = "✋"; + inline constexpr const char raised_hand_tone1[] = "✋🏻"; + inline constexpr const char raised_hand_tone2[] = "✋🏼"; + inline constexpr const char raised_hand_tone3[] = "✋🏽"; + inline constexpr const char raised_hand_tone4[] = "✋🏾"; + inline constexpr const char raised_hand_tone5[] = "✋🏿"; + inline constexpr const char raised_back_of_hand[] = "🤚"; + inline constexpr const char back_of_hand[] = "🤚"; + inline constexpr const char raised_back_of_hand_tone1[] = "🤚🏻"; + inline constexpr const char back_of_hand_tone1[] = "🤚🏻"; + inline constexpr const char raised_back_of_hand_tone2[] = "🤚🏼"; + inline constexpr const char back_of_hand_tone2[] = "🤚🏼"; + inline constexpr const char raised_back_of_hand_tone3[] = "🤚🏽"; + inline constexpr const char back_of_hand_tone3[] = "🤚🏽"; + inline constexpr const char raised_back_of_hand_tone4[] = "🤚🏾"; + inline constexpr const char back_of_hand_tone4[] = "🤚🏾"; + inline constexpr const char raised_back_of_hand_tone5[] = "🤚🏿"; + inline constexpr const char back_of_hand_tone5[] = "🤚🏿"; + inline constexpr const char hand_splayed[] = "🖐️"; + inline constexpr const char raised_hand_with_fingers_splayed[] = "🖐️"; + inline constexpr const char hand_splayed_tone1[] = "🖐🏻"; + inline constexpr const char raised_hand_with_fingers_splayed_tone1[] = "🖐🏻"; + inline constexpr const char hand_splayed_tone2[] = "🖐🏼"; + inline constexpr const char raised_hand_with_fingers_splayed_tone2[] = "🖐🏼"; + inline constexpr const char hand_splayed_tone3[] = "🖐🏽"; + inline constexpr const char raised_hand_with_fingers_splayed_tone3[] = "🖐🏽"; + inline constexpr const char hand_splayed_tone4[] = "🖐🏾"; + inline constexpr const char raised_hand_with_fingers_splayed_tone4[] = "🖐🏾"; + inline constexpr const char hand_splayed_tone5[] = "🖐🏿"; + inline constexpr const char raised_hand_with_fingers_splayed_tone5[] = "🖐🏿"; + inline constexpr const char vulcan[] = "🖖"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers[] = "🖖"; + inline constexpr const char vulcan_salute[] = "🖖"; + inline constexpr const char vulcan_tone1[] = "🖖🏻"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone1[] = "🖖🏻"; + inline constexpr const char vulcan_tone2[] = "🖖🏼"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone2[] = "🖖🏼"; + inline constexpr const char vulcan_tone3[] = "🖖🏽"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone3[] = "🖖🏽"; + inline constexpr const char vulcan_tone4[] = "🖖🏾"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone4[] = "🖖🏾"; + inline constexpr const char vulcan_tone5[] = "🖖🏿"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone5[] = "🖖🏿"; + inline constexpr const char wave[] = "👋"; + inline constexpr const char waving_hand[] = "👋"; + inline constexpr const char wave_tone1[] = "👋🏻"; + inline constexpr const char wave_tone2[] = "👋🏼"; + inline constexpr const char wave_tone3[] = "👋🏽"; + inline constexpr const char wave_tone4[] = "👋🏾"; + inline constexpr const char wave_tone5[] = "👋🏿"; + inline constexpr const char call_me[] = "🤙"; + inline constexpr const char call_me_hand[] = "🤙"; + inline constexpr const char call_me_tone1[] = "🤙🏻"; + inline constexpr const char call_me_hand_tone1[] = "🤙🏻"; + inline constexpr const char call_me_tone2[] = "🤙🏼"; + inline constexpr const char call_me_hand_tone2[] = "🤙🏼"; + inline constexpr const char call_me_tone3[] = "🤙🏽"; + inline constexpr const char call_me_hand_tone3[] = "🤙🏽"; + inline constexpr const char call_me_tone4[] = "🤙🏾"; + inline constexpr const char call_me_hand_tone4[] = "🤙🏾"; + inline constexpr const char call_me_tone5[] = "🤙🏿"; + inline constexpr const char call_me_hand_tone5[] = "🤙🏿"; + inline constexpr const char leftwards_hand[] = "🫲"; + inline constexpr const char leftwards_hand_tone1[] = "🫲🏻"; + inline constexpr const char leftwards_hand_light_skin_tone[] = "🫲🏻"; + inline constexpr const char leftwards_hand_tone2[] = "🫲🏼"; + inline constexpr const char leftwards_hand_medium_light_skin_tone[] = "🫲🏼"; + inline constexpr const char leftwards_hand_tone3[] = "🫲🏽"; + inline constexpr const char leftwards_hand_medium_skin_tone[] = "🫲🏽"; + inline constexpr const char leftwards_hand_tone4[] = "🫲🏾"; + inline constexpr const char leftwards_hand_medium_dark_skin_tone[] = "🫲🏾"; + inline constexpr const char leftwards_hand_tone5[] = "🫲🏿"; + inline constexpr const char leftwards_hand_dark_skin_tone[] = "🫲🏿"; + inline constexpr const char rightwards_hand[] = "🫱"; + inline constexpr const char rightwards_hand_tone1[] = "🫱🏻"; + inline constexpr const char rightwards_hand_light_skin_tone[] = "🫱🏻"; + inline constexpr const char rightwards_hand_tone2[] = "🫱🏼"; + inline constexpr const char rightwards_hand_medium_light_skin_tone[] = "🫱🏼"; + inline constexpr const char rightwards_hand_tone3[] = "🫱🏽"; + inline constexpr const char rightwards_hand_medium_skin_tone[] = "🫱🏽"; + inline constexpr const char rightwards_hand_tone4[] = "🫱🏾"; + inline constexpr const char rightwards_hand_medium_dark_skin_tone[] = "🫱🏾"; + inline constexpr const char rightwards_hand_tone5[] = "🫱🏿"; + inline constexpr const char rightwards_hand_dark_skin_tone[] = "🫱🏿"; + inline constexpr const char muscle[] = "💪"; + inline constexpr const char flexed_biceps[] = "💪"; + inline constexpr const char muscle_tone1[] = "💪🏻"; + inline constexpr const char muscle_tone2[] = "💪🏼"; + inline constexpr const char muscle_tone3[] = "💪🏽"; + inline constexpr const char muscle_tone4[] = "💪🏾"; + inline constexpr const char muscle_tone5[] = "💪🏿"; + inline constexpr const char mechanical_arm[] = "🦾"; + inline constexpr const char middle_finger[] = "🖕"; + inline constexpr const char reversed_hand_with_middle_finger_extended[] = "🖕"; + inline constexpr const char middle_finger_tone1[] = "🖕🏻"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone1[] = "🖕🏻"; + inline constexpr const char middle_finger_tone2[] = "🖕🏼"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone2[] = "🖕🏼"; + inline constexpr const char middle_finger_tone3[] = "🖕🏽"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone3[] = "🖕🏽"; + inline constexpr const char middle_finger_tone4[] = "🖕🏾"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone4[] = "🖕🏾"; + inline constexpr const char middle_finger_tone5[] = "🖕🏿"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone5[] = "🖕🏿"; + inline constexpr const char writing_hand[] = "✍️"; + inline constexpr const char writing_hand_tone1[] = "✍🏻"; + inline constexpr const char writing_hand_tone2[] = "✍🏼"; + inline constexpr const char writing_hand_tone3[] = "✍🏽"; + inline constexpr const char writing_hand_tone4[] = "✍🏾"; + inline constexpr const char writing_hand_tone5[] = "✍🏿"; + inline constexpr const char pray[] = "🙏"; + inline constexpr const char folded_hands[] = "🙏"; + inline constexpr const char pray_tone1[] = "🙏🏻"; + inline constexpr const char pray_tone2[] = "🙏🏼"; + inline constexpr const char pray_tone3[] = "🙏🏽"; + inline constexpr const char pray_tone4[] = "🙏🏾"; + inline constexpr const char pray_tone5[] = "🙏🏿"; + inline constexpr const char index_pointing_at_the_viewer[] = "🫵"; + inline constexpr const char index_pointing_at_the_viewer_tone1[] = "🫵🏻"; + inline constexpr const char index_pointing_at_the_viewer_light_skin_tone[] = "🫵🏻"; + inline constexpr const char index_pointing_at_the_viewer_tone2[] = "🫵🏼"; + inline constexpr const char index_pointing_at_the_viewer_medium_light_skin_tone[] = "🫵🏼"; + inline constexpr const char index_pointing_at_the_viewer_tone3[] = "🫵🏽"; + inline constexpr const char index_pointing_at_the_viewer_medium_skin_tone[] = "🫵🏽"; + inline constexpr const char index_pointing_at_the_viewer_tone4[] = "🫵🏾"; + inline constexpr const char index_pointing_at_the_viewer_medium_dark_skin_tone[] = "🫵🏾"; + inline constexpr const char index_pointing_at_the_viewer_tone5[] = "🫵🏿"; + inline constexpr const char index_pointing_at_the_viewer_dark_skin_tone[] = "🫵🏿"; + inline constexpr const char foot[] = "🦶"; + inline constexpr const char foot_tone1[] = "🦶🏻"; + inline constexpr const char foot_light_skin_tone[] = "🦶🏻"; + inline constexpr const char foot_tone2[] = "🦶🏼"; + inline constexpr const char foot_medium_light_skin_tone[] = "🦶🏼"; + inline constexpr const char foot_tone3[] = "🦶🏽"; + inline constexpr const char foot_medium_skin_tone[] = "🦶🏽"; + inline constexpr const char foot_tone4[] = "🦶🏾"; + inline constexpr const char foot_medium_dark_skin_tone[] = "🦶🏾"; + inline constexpr const char foot_tone5[] = "🦶🏿"; + inline constexpr const char foot_dark_skin_tone[] = "🦶🏿"; + inline constexpr const char leg[] = "🦵"; + inline constexpr const char leg_tone1[] = "🦵🏻"; + inline constexpr const char leg_light_skin_tone[] = "🦵🏻"; + inline constexpr const char leg_tone2[] = "🦵🏼"; + inline constexpr const char leg_medium_light_skin_tone[] = "🦵🏼"; + inline constexpr const char leg_tone3[] = "🦵🏽"; + inline constexpr const char leg_medium_skin_tone[] = "🦵🏽"; + inline constexpr const char leg_tone4[] = "🦵🏾"; + inline constexpr const char leg_medium_dark_skin_tone[] = "🦵🏾"; + inline constexpr const char leg_tone5[] = "🦵🏿"; + inline constexpr const char leg_dark_skin_tone[] = "🦵🏿"; + inline constexpr const char mechanical_leg[] = "🦿"; + inline constexpr const char lipstick[] = "💄"; + inline constexpr const char kiss[] = "💋"; + inline constexpr const char kiss_mark[] = "💋"; + inline constexpr const char lips[] = "👄"; + inline constexpr const char mouth[] = "👄"; + inline constexpr const char biting_lip[] = "🫦"; + inline constexpr const char tooth[] = "🦷"; + inline constexpr const char tongue[] = "👅"; + inline constexpr const char ear[] = "👂"; + inline constexpr const char ear_tone1[] = "👂🏻"; + inline constexpr const char ear_tone2[] = "👂🏼"; + inline constexpr const char ear_tone3[] = "👂🏽"; + inline constexpr const char ear_tone4[] = "👂🏾"; + inline constexpr const char ear_tone5[] = "👂🏿"; + inline constexpr const char ear_with_hearing_aid[] = "🦻"; + inline constexpr const char ear_with_hearing_aid_tone1[] = "🦻🏻"; + inline constexpr const char ear_with_hearing_aid_light_skin_tone[] = "🦻🏻"; + inline constexpr const char ear_with_hearing_aid_tone2[] = "🦻🏼"; + inline constexpr const char ear_with_hearing_aid_medium_light_skin_tone[] = "🦻🏼"; + inline constexpr const char ear_with_hearing_aid_tone3[] = "🦻🏽"; + inline constexpr const char ear_with_hearing_aid_medium_skin_tone[] = "🦻🏽"; + inline constexpr const char ear_with_hearing_aid_tone4[] = "🦻🏾"; + inline constexpr const char ear_with_hearing_aid_medium_dark_skin_tone[] = "🦻🏾"; + inline constexpr const char ear_with_hearing_aid_tone5[] = "🦻🏿"; + inline constexpr const char ear_with_hearing_aid_dark_skin_tone[] = "🦻🏿"; + inline constexpr const char nose[] = "👃"; + inline constexpr const char nose_tone1[] = "👃🏻"; + inline constexpr const char nose_tone2[] = "👃🏼"; + inline constexpr const char nose_tone3[] = "👃🏽"; + inline constexpr const char nose_tone4[] = "👃🏾"; + inline constexpr const char nose_tone5[] = "👃🏿"; + inline constexpr const char footprints[] = "👣"; + inline constexpr const char eye[] = "👁️"; + inline constexpr const char eyes[] = "👀"; + inline constexpr const char anatomical_heart[] = "🫀"; + inline constexpr const char lungs[] = "🫁"; + inline constexpr const char brain[] = "🧠"; + inline constexpr const char speaking_head[] = "🗣️"; + inline constexpr const char speaking_head_in_silhouette[] = "🗣️"; + inline constexpr const char bust_in_silhouette[] = "👤"; + inline constexpr const char busts_in_silhouette[] = "👥"; + inline constexpr const char people_hugging[] = "🫂"; + inline constexpr const char baby[] = "👶"; + inline constexpr const char baby_tone1[] = "👶🏻"; + inline constexpr const char baby_tone2[] = "👶🏼"; + inline constexpr const char baby_tone3[] = "👶🏽"; + inline constexpr const char baby_tone4[] = "👶🏾"; + inline constexpr const char baby_tone5[] = "👶🏿"; + inline constexpr const char child[] = "🧒"; + inline constexpr const char child_tone1[] = "🧒🏻"; + inline constexpr const char child_light_skin_tone[] = "🧒🏻"; + inline constexpr const char child_tone2[] = "🧒🏼"; + inline constexpr const char child_medium_light_skin_tone[] = "🧒🏼"; + inline constexpr const char child_tone3[] = "🧒🏽"; + inline constexpr const char child_medium_skin_tone[] = "🧒🏽"; + inline constexpr const char child_tone4[] = "🧒🏾"; + inline constexpr const char child_medium_dark_skin_tone[] = "🧒🏾"; + inline constexpr const char child_tone5[] = "🧒🏿"; + inline constexpr const char child_dark_skin_tone[] = "🧒🏿"; + inline constexpr const char girl[] = "👧"; + inline constexpr const char girl_tone1[] = "👧🏻"; + inline constexpr const char girl_tone2[] = "👧🏼"; + inline constexpr const char girl_tone3[] = "👧🏽"; + inline constexpr const char girl_tone4[] = "👧🏾"; + inline constexpr const char girl_tone5[] = "👧🏿"; + inline constexpr const char boy[] = "👦"; + inline constexpr const char boy_tone1[] = "👦🏻"; + inline constexpr const char boy_tone2[] = "👦🏼"; + inline constexpr const char boy_tone3[] = "👦🏽"; + inline constexpr const char boy_tone4[] = "👦🏾"; + inline constexpr const char boy_tone5[] = "👦🏿"; + inline constexpr const char adult[] = "🧑"; + inline constexpr const char person[] = "🧑"; + inline constexpr const char adult_tone1[] = "🧑🏻"; + inline constexpr const char adult_light_skin_tone[] = "🧑🏻"; + inline constexpr const char adult_tone2[] = "🧑🏼"; + inline constexpr const char adult_medium_light_skin_tone[] = "🧑🏼"; + inline constexpr const char adult_tone3[] = "🧑🏽"; + inline constexpr const char adult_medium_skin_tone[] = "🧑🏽"; + inline constexpr const char adult_tone4[] = "🧑🏾"; + inline constexpr const char adult_medium_dark_skin_tone[] = "🧑🏾"; + inline constexpr const char adult_tone5[] = "🧑🏿"; + inline constexpr const char adult_dark_skin_tone[] = "🧑🏿"; + inline constexpr const char woman[] = "👩"; + inline constexpr const char woman_tone1[] = "👩🏻"; + inline constexpr const char woman_tone2[] = "👩🏼"; + inline constexpr const char woman_tone3[] = "👩🏽"; + inline constexpr const char woman_tone4[] = "👩🏾"; + inline constexpr const char woman_tone5[] = "👩🏿"; + inline constexpr const char man[] = "👨"; + inline constexpr const char man_tone1[] = "👨🏻"; + inline constexpr const char man_tone2[] = "👨🏼"; + inline constexpr const char man_tone3[] = "👨🏽"; + inline constexpr const char man_tone4[] = "👨🏾"; + inline constexpr const char man_tone5[] = "👨🏿"; + inline constexpr const char person_curly_hair[] = "🧑‍🦱"; + inline constexpr const char person_tone1_curly_hair[] = "🧑🏻‍🦱"; + inline constexpr const char person_light_skin_tone_curly_hair[] = "🧑🏻‍🦱"; + inline constexpr const char person_tone2_curly_hair[] = "🧑🏼‍🦱"; + inline constexpr const char person_medium_light_skin_tone_curly_hair[] = "🧑🏼‍🦱"; + inline constexpr const char person_tone3_curly_hair[] = "🧑🏽‍🦱"; + inline constexpr const char person_medium_skin_tone_curly_hair[] = "🧑🏽‍🦱"; + inline constexpr const char person_tone4_curly_hair[] = "🧑🏾‍🦱"; + inline constexpr const char person_medium_dark_skin_tone_curly_hair[] = "🧑🏾‍🦱"; + inline constexpr const char person_tone5_curly_hair[] = "🧑🏿‍🦱"; + inline constexpr const char person_dark_skin_tone_curly_hair[] = "🧑🏿‍🦱"; + inline constexpr const char woman_curly_haired[] = "👩‍🦱"; + inline constexpr const char woman_curly_haired_tone1[] = "👩🏻‍🦱"; + inline constexpr const char woman_curly_haired_light_skin_tone[] = "👩🏻‍🦱"; + inline constexpr const char woman_curly_haired_tone2[] = "👩🏼‍🦱"; + inline constexpr const char woman_curly_haired_medium_light_skin_tone[] = "👩🏼‍🦱"; + inline constexpr const char woman_curly_haired_tone3[] = "👩🏽‍🦱"; + inline constexpr const char woman_curly_haired_medium_skin_tone[] = "👩🏽‍🦱"; + inline constexpr const char woman_curly_haired_tone4[] = "👩🏾‍🦱"; + inline constexpr const char woman_curly_haired_medium_dark_skin_tone[] = "👩🏾‍🦱"; + inline constexpr const char woman_curly_haired_tone5[] = "👩🏿‍🦱"; + inline constexpr const char woman_curly_haired_dark_skin_tone[] = "👩🏿‍🦱"; + inline constexpr const char man_curly_haired[] = "👨‍🦱"; + inline constexpr const char man_curly_haired_tone1[] = "👨🏻‍🦱"; + inline constexpr const char man_curly_haired_light_skin_tone[] = "👨🏻‍🦱"; + inline constexpr const char man_curly_haired_tone2[] = "👨🏼‍🦱"; + inline constexpr const char man_curly_haired_medium_light_skin_tone[] = "👨🏼‍🦱"; + inline constexpr const char man_curly_haired_tone3[] = "👨🏽‍🦱"; + inline constexpr const char man_curly_haired_medium_skin_tone[] = "👨🏽‍🦱"; + inline constexpr const char man_curly_haired_tone4[] = "👨🏾‍🦱"; + inline constexpr const char man_curly_haired_medium_dark_skin_tone[] = "👨🏾‍🦱"; + inline constexpr const char man_curly_haired_tone5[] = "👨🏿‍🦱"; + inline constexpr const char man_curly_haired_dark_skin_tone[] = "👨🏿‍🦱"; + inline constexpr const char person_red_hair[] = "🧑‍🦰"; + inline constexpr const char person_tone1_red_hair[] = "🧑🏻‍🦰"; + inline constexpr const char person_light_skin_tone_red_hair[] = "🧑🏻‍🦰"; + inline constexpr const char person_tone2_red_hair[] = "🧑🏼‍🦰"; + inline constexpr const char person_medium_light_skin_tone_red_hair[] = "🧑🏼‍🦰"; + inline constexpr const char person_tone3_red_hair[] = "🧑🏽‍🦰"; + inline constexpr const char person_medium_skin_tone_red_hair[] = "🧑🏽‍🦰"; + inline constexpr const char person_tone4_red_hair[] = "🧑🏾‍🦰"; + inline constexpr const char person_medium_dark_skin_tone_red_hair[] = "🧑🏾‍🦰"; + inline constexpr const char person_tone5_red_hair[] = "🧑🏿‍🦰"; + inline constexpr const char person_dark_skin_tone_red_hair[] = "🧑🏿‍🦰"; + inline constexpr const char woman_red_haired[] = "👩‍🦰"; + inline constexpr const char woman_red_haired_tone1[] = "👩🏻‍🦰"; + inline constexpr const char woman_red_haired_light_skin_tone[] = "👩🏻‍🦰"; + inline constexpr const char woman_red_haired_tone2[] = "👩🏼‍🦰"; + inline constexpr const char woman_red_haired_medium_light_skin_tone[] = "👩🏼‍🦰"; + inline constexpr const char woman_red_haired_tone3[] = "👩🏽‍🦰"; + inline constexpr const char woman_red_haired_medium_skin_tone[] = "👩🏽‍🦰"; + inline constexpr const char woman_red_haired_tone4[] = "👩🏾‍🦰"; + inline constexpr const char woman_red_haired_medium_dark_skin_tone[] = "👩🏾‍🦰"; + inline constexpr const char woman_red_haired_tone5[] = "👩🏿‍🦰"; + inline constexpr const char woman_red_haired_dark_skin_tone[] = "👩🏿‍🦰"; + inline constexpr const char man_red_haired[] = "👨‍🦰"; + inline constexpr const char man_red_hair[] = "👨‍🦰"; + inline constexpr const char man_red_haired_tone1[] = "👨🏻‍🦰"; + inline constexpr const char man_red_haired_light_skin_tone[] = "👨🏻‍🦰"; + inline constexpr const char man_red_haired_tone2[] = "👨🏼‍🦰"; + inline constexpr const char man_red_haired_medium_light_skin_tone[] = "👨🏼‍🦰"; + inline constexpr const char man_red_haired_tone3[] = "👨🏽‍🦰"; + inline constexpr const char man_red_haired_medium_skin_tone[] = "👨🏽‍🦰"; + inline constexpr const char man_red_haired_tone4[] = "👨🏾‍🦰"; + inline constexpr const char man_red_haired_medium_dark_skin_tone[] = "👨🏾‍🦰"; + inline constexpr const char man_red_haired_tone5[] = "👨🏿‍🦰"; + inline constexpr const char man_red_haired_dark_skin_tone[] = "👨🏿‍🦰"; + inline constexpr const char blond_haired_person[] = "👱"; + inline constexpr const char person_with_blond_hair[] = "👱"; + inline constexpr const char blond_haired_person_tone1[] = "👱🏻"; + inline constexpr const char person_with_blond_hair_tone1[] = "👱🏻"; + inline constexpr const char blond_haired_person_tone2[] = "👱🏼"; + inline constexpr const char person_with_blond_hair_tone2[] = "👱🏼"; + inline constexpr const char blond_haired_person_tone3[] = "👱🏽"; + inline constexpr const char person_with_blond_hair_tone3[] = "👱🏽"; + inline constexpr const char blond_haired_person_tone4[] = "👱🏾"; + inline constexpr const char person_with_blond_hair_tone4[] = "👱🏾"; + inline constexpr const char blond_haired_person_tone5[] = "👱🏿"; + inline constexpr const char person_with_blond_hair_tone5[] = "👱🏿"; + inline constexpr const char blond_haired_woman[] = "👱‍♀️"; + inline constexpr const char blond_haired_woman_tone1[] = "👱🏻‍♀️"; + inline constexpr const char blond_haired_woman_light_skin_tone[] = "👱🏻‍♀️"; + inline constexpr const char blond_haired_woman_tone2[] = "👱🏼‍♀️"; + inline constexpr const char blond_haired_woman_medium_light_skin_tone[] = "👱🏼‍♀️"; + inline constexpr const char blond_haired_woman_tone3[] = "👱🏽‍♀️"; + inline constexpr const char blond_haired_woman_medium_skin_tone[] = "👱🏽‍♀️"; + inline constexpr const char blond_haired_woman_tone4[] = "👱🏾‍♀️"; + inline constexpr const char blond_haired_woman_medium_dark_skin_tone[] = "👱🏾‍♀️"; + inline constexpr const char blond_haired_woman_tone5[] = "👱🏿‍♀️"; + inline constexpr const char blond_haired_woman_dark_skin_tone[] = "👱🏿‍♀️"; + inline constexpr const char blond_haired_man[] = "👱‍♂️"; + inline constexpr const char blond_haired_man_tone1[] = "👱🏻‍♂️"; + inline constexpr const char blond_haired_man_light_skin_tone[] = "👱🏻‍♂️"; + inline constexpr const char blond_haired_man_tone2[] = "👱🏼‍♂️"; + inline constexpr const char blond_haired_man_medium_light_skin_tone[] = "👱🏼‍♂️"; + inline constexpr const char blond_haired_man_tone3[] = "👱🏽‍♂️"; + inline constexpr const char blond_haired_man_medium_skin_tone[] = "👱🏽‍♂️"; + inline constexpr const char blond_haired_man_tone4[] = "👱🏾‍♂️"; + inline constexpr const char blond_haired_man_medium_dark_skin_tone[] = "👱🏾‍♂️"; + inline constexpr const char blond_haired_man_tone5[] = "👱🏿‍♂️"; + inline constexpr const char blond_haired_man_dark_skin_tone[] = "👱🏿‍♂️"; + inline constexpr const char person_white_hair[] = "🧑‍🦳"; + inline constexpr const char person_tone1_white_hair[] = "🧑🏻‍🦳"; + inline constexpr const char person_light_skin_tone_white_hair[] = "🧑🏻‍🦳"; + inline constexpr const char person_tone2_white_hair[] = "🧑🏼‍🦳"; + inline constexpr const char person_medium_light_skin_tone_white_hair[] = "🧑🏼‍🦳"; + inline constexpr const char person_tone3_white_hair[] = "🧑🏽‍🦳"; + inline constexpr const char person_medium_skin_tone_white_hair[] = "🧑🏽‍🦳"; + inline constexpr const char person_tone4_white_hair[] = "🧑🏾‍🦳"; + inline constexpr const char person_medium_dark_skin_tone_white_hair[] = "🧑🏾‍🦳"; + inline constexpr const char person_tone5_white_hair[] = "🧑🏿‍🦳"; + inline constexpr const char person_dark_skin_tone_white_hair[] = "🧑🏿‍🦳"; + inline constexpr const char woman_white_haired[] = "👩‍🦳"; + inline constexpr const char woman_white_haired_tone1[] = "👩🏻‍🦳"; + inline constexpr const char woman_white_haired_light_skin_tone[] = "👩🏻‍🦳"; + inline constexpr const char woman_white_haired_tone2[] = "👩🏼‍🦳"; + inline constexpr const char woman_white_haired_medium_light_skin_tone[] = "👩🏼‍🦳"; + inline constexpr const char woman_white_haired_tone3[] = "👩🏽‍🦳"; + inline constexpr const char woman_white_haired_medium_skin_tone[] = "👩🏽‍🦳"; + inline constexpr const char woman_white_haired_tone4[] = "👩🏾‍🦳"; + inline constexpr const char woman_white_haired_medium_dark_skin_tone[] = "👩🏾‍🦳"; + inline constexpr const char woman_white_haired_tone5[] = "👩🏿‍🦳"; + inline constexpr const char woman_white_haired_dark_skin_tone[] = "👩🏿‍🦳"; + inline constexpr const char man_white_haired[] = "👨‍🦳"; + inline constexpr const char man_white_haired_tone1[] = "👨🏻‍🦳"; + inline constexpr const char man_white_haired_light_skin_tone[] = "👨🏻‍🦳"; + inline constexpr const char man_white_haired_tone2[] = "👨🏼‍🦳"; + inline constexpr const char man_white_haired_medium_light_skin_tone[] = "👨🏼‍🦳"; + inline constexpr const char man_white_haired_tone3[] = "👨🏽‍🦳"; + inline constexpr const char man_white_haired_medium_skin_tone[] = "👨🏽‍🦳"; + inline constexpr const char man_white_haired_tone4[] = "👨🏾‍🦳"; + inline constexpr const char man_white_haired_medium_dark_skin_tone[] = "👨🏾‍🦳"; + inline constexpr const char man_white_haired_tone5[] = "👨🏿‍🦳"; + inline constexpr const char man_white_haired_dark_skin_tone[] = "👨🏿‍🦳"; + inline constexpr const char person_bald[] = "🧑‍🦲"; + inline constexpr const char person_tone1_bald[] = "🧑🏻‍🦲"; + inline constexpr const char person_light_skin_tone_bald[] = "🧑🏻‍🦲"; + inline constexpr const char person_tone2_bald[] = "🧑🏼‍🦲"; + inline constexpr const char person_medium_light_skin_tone_bald[] = "🧑🏼‍🦲"; + inline constexpr const char person_tone3_bald[] = "🧑🏽‍🦲"; + inline constexpr const char person_medium_skin_tone_bald[] = "🧑🏽‍🦲"; + inline constexpr const char person_tone4_bald[] = "🧑🏾‍🦲"; + inline constexpr const char person_medium_dark_skin_tone_bald[] = "🧑🏾‍🦲"; + inline constexpr const char person_tone5_bald[] = "🧑🏿‍🦲"; + inline constexpr const char person_dark_skin_tone_bald[] = "🧑🏿‍🦲"; + inline constexpr const char woman_bald[] = "👩‍🦲"; + inline constexpr const char woman_bald_tone1[] = "👩🏻‍🦲"; + inline constexpr const char woman_bald_light_skin_tone[] = "👩🏻‍🦲"; + inline constexpr const char woman_bald_tone2[] = "👩🏼‍🦲"; + inline constexpr const char woman_bald_medium_light_skin_tone[] = "👩🏼‍🦲"; + inline constexpr const char woman_bald_tone3[] = "👩🏽‍🦲"; + inline constexpr const char woman_bald_medium_skin_tone[] = "👩🏽‍🦲"; + inline constexpr const char woman_bald_tone4[] = "👩🏾‍🦲"; + inline constexpr const char woman_bald_medium_dark_skin_tone[] = "👩🏾‍🦲"; + inline constexpr const char woman_bald_tone5[] = "👩🏿‍🦲"; + inline constexpr const char woman_bald_dark_skin_tone[] = "👩🏿‍🦲"; + inline constexpr const char man_bald[] = "👨‍🦲"; + inline constexpr const char man_bald_tone1[] = "👨🏻‍🦲"; + inline constexpr const char man_bald_light_skin_tone[] = "👨🏻‍🦲"; + inline constexpr const char man_bald_tone2[] = "👨🏼‍🦲"; + inline constexpr const char man_bald_medium_light_skin_tone[] = "👨🏼‍🦲"; + inline constexpr const char man_bald_tone3[] = "👨🏽‍🦲"; + inline constexpr const char man_bald_medium_skin_tone[] = "👨🏽‍🦲"; + inline constexpr const char man_bald_tone4[] = "👨🏾‍🦲"; + inline constexpr const char man_bald_medium_dark_skin_tone[] = "👨🏾‍🦲"; + inline constexpr const char man_bald_tone5[] = "👨🏿‍🦲"; + inline constexpr const char man_bald_dark_skin_tone[] = "👨🏿‍🦲"; + inline constexpr const char bearded_person[] = "🧔"; + inline constexpr const char person_beard[] = "🧔"; + inline constexpr const char bearded_person_tone1[] = "🧔🏻"; + inline constexpr const char bearded_person_light_skin_tone[] = "🧔🏻"; + inline constexpr const char bearded_person_tone2[] = "🧔🏼"; + inline constexpr const char bearded_person_medium_light_skin_tone[] = "🧔🏼"; + inline constexpr const char bearded_person_tone3[] = "🧔🏽"; + inline constexpr const char bearded_person_medium_skin_tone[] = "🧔🏽"; + inline constexpr const char bearded_person_tone4[] = "🧔🏾"; + inline constexpr const char bearded_person_medium_dark_skin_tone[] = "🧔🏾"; + inline constexpr const char bearded_person_tone5[] = "🧔🏿"; + inline constexpr const char bearded_person_dark_skin_tone[] = "🧔🏿"; + inline constexpr const char woman_beard[] = "🧔‍♀️"; + inline constexpr const char woman_tone1_beard[] = "🧔🏻‍♀️"; + inline constexpr const char woman_light_skin_tone_beard[] = "🧔🏻‍♀️"; + inline constexpr const char woman_tone2_beard[] = "🧔🏼‍♀️"; + inline constexpr const char woman_medium_light_skin_tone_beard[] = "🧔🏼‍♀️"; + inline constexpr const char woman_tone3_beard[] = "🧔🏽‍♀️"; + inline constexpr const char woman_medium_skin_tone_beard[] = "🧔🏽‍♀️"; + inline constexpr const char woman_tone4_beard[] = "🧔🏾‍♀️"; + inline constexpr const char woman_medium_dark_skin_tone_beard[] = "🧔🏾‍♀️"; + inline constexpr const char woman_tone5_beard[] = "🧔🏿‍♀️"; + inline constexpr const char woman_dark_skin_tone_beard[] = "🧔🏿‍♀️"; + inline constexpr const char man_beard[] = "🧔‍♂️"; + inline constexpr const char man_tone1_beard[] = "🧔🏻‍♂️"; + inline constexpr const char man_light_skin_tone_beard[] = "🧔🏻‍♂️"; + inline constexpr const char man_tone2_beard[] = "🧔🏼‍♂️"; + inline constexpr const char man_medium_light_skin_tone_beard[] = "🧔🏼‍♂️"; + inline constexpr const char man_tone3_beard[] = "🧔🏽‍♂️"; + inline constexpr const char man_medium_skin_tone_beard[] = "🧔🏽‍♂️"; + inline constexpr const char man_tone4_beard[] = "🧔🏾‍♂️"; + inline constexpr const char man_medium_dark_skin_tone_beard[] = "🧔🏾‍♂️"; + inline constexpr const char man_tone5_beard[] = "🧔🏿‍♂️"; + inline constexpr const char man_dark_skin_tone_beard[] = "🧔🏿‍♂️"; + inline constexpr const char older_adult[] = "🧓"; + inline constexpr const char older_person[] = "🧓"; + inline constexpr const char older_adult_tone1[] = "🧓🏻"; + inline constexpr const char older_adult_light_skin_tone[] = "🧓🏻"; + inline constexpr const char older_adult_tone2[] = "🧓🏼"; + inline constexpr const char older_adult_medium_light_skin_tone[] = "🧓🏼"; + inline constexpr const char older_adult_tone3[] = "🧓🏽"; + inline constexpr const char older_adult_medium_skin_tone[] = "🧓🏽"; + inline constexpr const char older_adult_tone4[] = "🧓🏾"; + inline constexpr const char older_adult_medium_dark_skin_tone[] = "🧓🏾"; + inline constexpr const char older_adult_tone5[] = "🧓🏿"; + inline constexpr const char older_adult_dark_skin_tone[] = "🧓🏿"; + inline constexpr const char older_woman[] = "👵"; + inline constexpr const char grandma[] = "👵"; + inline constexpr const char old_woman[] = "👵"; + inline constexpr const char older_woman_tone1[] = "👵🏻"; + inline constexpr const char grandma_tone1[] = "👵🏻"; + inline constexpr const char older_woman_tone2[] = "👵🏼"; + inline constexpr const char grandma_tone2[] = "👵🏼"; + inline constexpr const char older_woman_tone3[] = "👵🏽"; + inline constexpr const char grandma_tone3[] = "👵🏽"; + inline constexpr const char older_woman_tone4[] = "👵🏾"; + inline constexpr const char grandma_tone4[] = "👵🏾"; + inline constexpr const char older_woman_tone5[] = "👵🏿"; + inline constexpr const char grandma_tone5[] = "👵🏿"; + inline constexpr const char older_man[] = "👴"; + inline constexpr const char old_man[] = "👴"; + inline constexpr const char older_man_tone1[] = "👴🏻"; + inline constexpr const char older_man_tone2[] = "👴🏼"; + inline constexpr const char older_man_tone3[] = "👴🏽"; + inline constexpr const char older_man_tone4[] = "👴🏾"; + inline constexpr const char older_man_tone5[] = "👴🏿"; + inline constexpr const char man_with_chinese_cap[] = "👲"; + inline constexpr const char man_with_gua_pi_mao[] = "👲"; + inline constexpr const char man_with_chinese_cap_tone1[] = "👲🏻"; + inline constexpr const char man_with_gua_pi_mao_tone1[] = "👲🏻"; + inline constexpr const char man_with_chinese_cap_tone2[] = "👲🏼"; + inline constexpr const char man_with_gua_pi_mao_tone2[] = "👲🏼"; + inline constexpr const char man_with_chinese_cap_tone3[] = "👲🏽"; + inline constexpr const char man_with_gua_pi_mao_tone3[] = "👲🏽"; + inline constexpr const char man_with_chinese_cap_tone4[] = "👲🏾"; + inline constexpr const char man_with_gua_pi_mao_tone4[] = "👲🏾"; + inline constexpr const char man_with_chinese_cap_tone5[] = "👲🏿"; + inline constexpr const char man_with_gua_pi_mao_tone5[] = "👲🏿"; + inline constexpr const char person_wearing_turban[] = "👳"; + inline constexpr const char man_with_turban[] = "👳"; + inline constexpr const char person_wearing_turban_tone1[] = "👳🏻"; + inline constexpr const char man_with_turban_tone1[] = "👳🏻"; + inline constexpr const char person_wearing_turban_tone2[] = "👳🏼"; + inline constexpr const char man_with_turban_tone2[] = "👳🏼"; + inline constexpr const char person_wearing_turban_tone3[] = "👳🏽"; + inline constexpr const char man_with_turban_tone3[] = "👳🏽"; + inline constexpr const char person_wearing_turban_tone4[] = "👳🏾"; + inline constexpr const char man_with_turban_tone4[] = "👳🏾"; + inline constexpr const char person_wearing_turban_tone5[] = "👳🏿"; + inline constexpr const char man_with_turban_tone5[] = "👳🏿"; + inline constexpr const char woman_wearing_turban[] = "👳‍♀️"; + inline constexpr const char woman_wearing_turban_tone1[] = "👳🏻‍♀️"; + inline constexpr const char woman_wearing_turban_light_skin_tone[] = "👳🏻‍♀️"; + inline constexpr const char woman_wearing_turban_tone2[] = "👳🏼‍♀️"; + inline constexpr const char woman_wearing_turban_medium_light_skin_tone[] = "👳🏼‍♀️"; + inline constexpr const char woman_wearing_turban_tone3[] = "👳🏽‍♀️"; + inline constexpr const char woman_wearing_turban_medium_skin_tone[] = "👳🏽‍♀️"; + inline constexpr const char woman_wearing_turban_tone4[] = "👳🏾‍♀️"; + inline constexpr const char woman_wearing_turban_medium_dark_skin_tone[] = "👳🏾‍♀️"; + inline constexpr const char woman_wearing_turban_tone5[] = "👳🏿‍♀️"; + inline constexpr const char woman_wearing_turban_dark_skin_tone[] = "👳🏿‍♀️"; + inline constexpr const char man_wearing_turban[] = "👳‍♂️"; + inline constexpr const char man_wearing_turban_tone1[] = "👳🏻‍♂️"; + inline constexpr const char man_wearing_turban_light_skin_tone[] = "👳🏻‍♂️"; + inline constexpr const char man_wearing_turban_tone2[] = "👳🏼‍♂️"; + inline constexpr const char man_wearing_turban_medium_light_skin_tone[] = "👳🏼‍♂️"; + inline constexpr const char man_wearing_turban_tone3[] = "👳🏽‍♂️"; + inline constexpr const char man_wearing_turban_medium_skin_tone[] = "👳🏽‍♂️"; + inline constexpr const char man_wearing_turban_tone4[] = "👳🏾‍♂️"; + inline constexpr const char man_wearing_turban_medium_dark_skin_tone[] = "👳🏾‍♂️"; + inline constexpr const char man_wearing_turban_tone5[] = "👳🏿‍♂️"; + inline constexpr const char man_wearing_turban_dark_skin_tone[] = "👳🏿‍♂️"; + inline constexpr const char woman_with_headscarf[] = "🧕"; + inline constexpr const char woman_with_headscarf_tone1[] = "🧕🏻"; + inline constexpr const char woman_with_headscarf_light_skin_tone[] = "🧕🏻"; + inline constexpr const char woman_with_headscarf_tone2[] = "🧕🏼"; + inline constexpr const char woman_with_headscarf_medium_light_skin_tone[] = "🧕🏼"; + inline constexpr const char woman_with_headscarf_tone3[] = "🧕🏽"; + inline constexpr const char woman_with_headscarf_medium_skin_tone[] = "🧕🏽"; + inline constexpr const char woman_with_headscarf_tone4[] = "🧕🏾"; + inline constexpr const char woman_with_headscarf_medium_dark_skin_tone[] = "🧕🏾"; + inline constexpr const char woman_with_headscarf_tone5[] = "🧕🏿"; + inline constexpr const char woman_with_headscarf_dark_skin_tone[] = "🧕🏿"; + inline constexpr const char police_officer[] = "👮"; + inline constexpr const char cop[] = "👮"; + inline constexpr const char police_officer_tone1[] = "👮🏻"; + inline constexpr const char cop_tone1[] = "👮🏻"; + inline constexpr const char police_officer_tone2[] = "👮🏼"; + inline constexpr const char cop_tone2[] = "👮🏼"; + inline constexpr const char police_officer_tone3[] = "👮🏽"; + inline constexpr const char cop_tone3[] = "👮🏽"; + inline constexpr const char police_officer_tone4[] = "👮🏾"; + inline constexpr const char cop_tone4[] = "👮🏾"; + inline constexpr const char police_officer_tone5[] = "👮🏿"; + inline constexpr const char cop_tone5[] = "👮🏿"; + inline constexpr const char woman_police_officer[] = "👮‍♀️"; + inline constexpr const char woman_police_officer_tone1[] = "👮🏻‍♀️"; + inline constexpr const char woman_police_officer_light_skin_tone[] = "👮🏻‍♀️"; + inline constexpr const char woman_police_officer_tone2[] = "👮🏼‍♀️"; + inline constexpr const char woman_police_officer_medium_light_skin_tone[] = "👮🏼‍♀️"; + inline constexpr const char woman_police_officer_tone3[] = "👮🏽‍♀️"; + inline constexpr const char woman_police_officer_medium_skin_tone[] = "👮🏽‍♀️"; + inline constexpr const char woman_police_officer_tone4[] = "👮🏾‍♀️"; + inline constexpr const char woman_police_officer_medium_dark_skin_tone[] = "👮🏾‍♀️"; + inline constexpr const char woman_police_officer_tone5[] = "👮🏿‍♀️"; + inline constexpr const char woman_police_officer_dark_skin_tone[] = "👮🏿‍♀️"; + inline constexpr const char man_police_officer[] = "👮‍♂️"; + inline constexpr const char man_police_officer_tone1[] = "👮🏻‍♂️"; + inline constexpr const char man_police_officer_light_skin_tone[] = "👮🏻‍♂️"; + inline constexpr const char man_police_officer_tone2[] = "👮🏼‍♂️"; + inline constexpr const char man_police_officer_medium_light_skin_tone[] = "👮🏼‍♂️"; + inline constexpr const char man_police_officer_tone3[] = "👮🏽‍♂️"; + inline constexpr const char man_police_officer_medium_skin_tone[] = "👮🏽‍♂️"; + inline constexpr const char man_police_officer_tone4[] = "👮🏾‍♂️"; + inline constexpr const char man_police_officer_medium_dark_skin_tone[] = "👮🏾‍♂️"; + inline constexpr const char man_police_officer_tone5[] = "👮🏿‍♂️"; + inline constexpr const char man_police_officer_dark_skin_tone[] = "👮🏿‍♂️"; + inline constexpr const char construction_worker[] = "👷"; + inline constexpr const char construction_worker_tone1[] = "👷🏻"; + inline constexpr const char construction_worker_tone2[] = "👷🏼"; + inline constexpr const char construction_worker_tone3[] = "👷🏽"; + inline constexpr const char construction_worker_tone4[] = "👷🏾"; + inline constexpr const char construction_worker_tone5[] = "👷🏿"; + inline constexpr const char woman_construction_worker[] = "👷‍♀️"; + inline constexpr const char woman_construction_worker_tone1[] = "👷🏻‍♀️"; + inline constexpr const char woman_construction_worker_light_skin_tone[] = "👷🏻‍♀️"; + inline constexpr const char woman_construction_worker_tone2[] = "👷🏼‍♀️"; + inline constexpr const char woman_construction_worker_medium_light_skin_tone[] = "👷🏼‍♀️"; + inline constexpr const char woman_construction_worker_tone3[] = "👷🏽‍♀️"; + inline constexpr const char woman_construction_worker_medium_skin_tone[] = "👷🏽‍♀️"; + inline constexpr const char woman_construction_worker_tone4[] = "👷🏾‍♀️"; + inline constexpr const char woman_construction_worker_medium_dark_skin_tone[] = "👷🏾‍♀️"; + inline constexpr const char woman_construction_worker_tone5[] = "👷🏿‍♀️"; + inline constexpr const char woman_construction_worker_dark_skin_tone[] = "👷🏿‍♀️"; + inline constexpr const char man_construction_worker[] = "👷‍♂️"; + inline constexpr const char man_construction_worker_tone1[] = "👷🏻‍♂️"; + inline constexpr const char man_construction_worker_light_skin_tone[] = "👷🏻‍♂️"; + inline constexpr const char man_construction_worker_tone2[] = "👷🏼‍♂️"; + inline constexpr const char man_construction_worker_medium_light_skin_tone[] = "👷🏼‍♂️"; + inline constexpr const char man_construction_worker_tone3[] = "👷🏽‍♂️"; + inline constexpr const char man_construction_worker_medium_skin_tone[] = "👷🏽‍♂️"; + inline constexpr const char man_construction_worker_tone4[] = "👷🏾‍♂️"; + inline constexpr const char man_construction_worker_medium_dark_skin_tone[] = "👷🏾‍♂️"; + inline constexpr const char man_construction_worker_tone5[] = "👷🏿‍♂️"; + inline constexpr const char man_construction_worker_dark_skin_tone[] = "👷🏿‍♂️"; + inline constexpr const char guard[] = "💂"; + inline constexpr const char guardsman[] = "💂"; + inline constexpr const char guard_tone1[] = "💂🏻"; + inline constexpr const char guardsman_tone1[] = "💂🏻"; + inline constexpr const char guard_tone2[] = "💂🏼"; + inline constexpr const char guardsman_tone2[] = "💂🏼"; + inline constexpr const char guard_tone3[] = "💂🏽"; + inline constexpr const char guardsman_tone3[] = "💂🏽"; + inline constexpr const char guard_tone4[] = "💂🏾"; + inline constexpr const char guardsman_tone4[] = "💂🏾"; + inline constexpr const char guard_tone5[] = "💂🏿"; + inline constexpr const char guardsman_tone5[] = "💂🏿"; + inline constexpr const char woman_guard[] = "💂‍♀️"; + inline constexpr const char woman_guard_tone1[] = "💂🏻‍♀️"; + inline constexpr const char woman_guard_light_skin_tone[] = "💂🏻‍♀️"; + inline constexpr const char woman_guard_tone2[] = "💂🏼‍♀️"; + inline constexpr const char woman_guard_medium_light_skin_tone[] = "💂🏼‍♀️"; + inline constexpr const char woman_guard_tone3[] = "💂🏽‍♀️"; + inline constexpr const char woman_guard_medium_skin_tone[] = "💂🏽‍♀️"; + inline constexpr const char woman_guard_tone4[] = "💂🏾‍♀️"; + inline constexpr const char woman_guard_medium_dark_skin_tone[] = "💂🏾‍♀️"; + inline constexpr const char woman_guard_tone5[] = "💂🏿‍♀️"; + inline constexpr const char woman_guard_dark_skin_tone[] = "💂🏿‍♀️"; + inline constexpr const char man_guard[] = "💂‍♂️"; + inline constexpr const char man_guard_tone1[] = "💂🏻‍♂️"; + inline constexpr const char man_guard_light_skin_tone[] = "💂🏻‍♂️"; + inline constexpr const char man_guard_tone2[] = "💂🏼‍♂️"; + inline constexpr const char man_guard_medium_light_skin_tone[] = "💂🏼‍♂️"; + inline constexpr const char man_guard_tone3[] = "💂🏽‍♂️"; + inline constexpr const char man_guard_medium_skin_tone[] = "💂🏽‍♂️"; + inline constexpr const char man_guard_tone4[] = "💂🏾‍♂️"; + inline constexpr const char man_guard_medium_dark_skin_tone[] = "💂🏾‍♂️"; + inline constexpr const char man_guard_tone5[] = "💂🏿‍♂️"; + inline constexpr const char man_guard_dark_skin_tone[] = "💂🏿‍♂️"; + inline constexpr const char detective[] = "🕵️"; + inline constexpr const char spy[] = "🕵️"; + inline constexpr const char sleuth_or_spy[] = "🕵️"; + inline constexpr const char detective_tone1[] = "🕵🏻"; + inline constexpr const char spy_tone1[] = "🕵🏻"; + inline constexpr const char sleuth_or_spy_tone1[] = "🕵🏻"; + inline constexpr const char detective_tone2[] = "🕵🏼"; + inline constexpr const char spy_tone2[] = "🕵🏼"; + inline constexpr const char sleuth_or_spy_tone2[] = "🕵🏼"; + inline constexpr const char detective_tone3[] = "🕵🏽"; + inline constexpr const char spy_tone3[] = "🕵🏽"; + inline constexpr const char sleuth_or_spy_tone3[] = "🕵🏽"; + inline constexpr const char detective_tone4[] = "🕵🏾"; + inline constexpr const char spy_tone4[] = "🕵🏾"; + inline constexpr const char sleuth_or_spy_tone4[] = "🕵🏾"; + inline constexpr const char detective_tone5[] = "🕵🏿"; + inline constexpr const char spy_tone5[] = "🕵🏿"; + inline constexpr const char sleuth_or_spy_tone5[] = "🕵🏿"; + inline constexpr const char woman_detective[] = "🕵️‍♀️"; + inline constexpr const char woman_detective_tone1[] = "🕵🏻‍♀️"; + inline constexpr const char woman_detective_light_skin_tone[] = "🕵🏻‍♀️"; + inline constexpr const char woman_detective_tone2[] = "🕵🏼‍♀️"; + inline constexpr const char woman_detective_medium_light_skin_tone[] = "🕵🏼‍♀️"; + inline constexpr const char woman_detective_tone3[] = "🕵🏽‍♀️"; + inline constexpr const char woman_detective_medium_skin_tone[] = "🕵🏽‍♀️"; + inline constexpr const char woman_detective_tone4[] = "🕵🏾‍♀️"; + inline constexpr const char woman_detective_medium_dark_skin_tone[] = "🕵🏾‍♀️"; + inline constexpr const char woman_detective_tone5[] = "🕵🏿‍♀️"; + inline constexpr const char woman_detective_dark_skin_tone[] = "🕵🏿‍♀️"; + inline constexpr const char man_detective[] = "🕵️‍♂️"; + inline constexpr const char man_detective_tone1[] = "🕵🏻‍♂️"; + inline constexpr const char man_detective_light_skin_tone[] = "🕵🏻‍♂️"; + inline constexpr const char man_detective_tone2[] = "🕵🏼‍♂️"; + inline constexpr const char man_detective_medium_light_skin_tone[] = "🕵🏼‍♂️"; + inline constexpr const char man_detective_tone3[] = "🕵🏽‍♂️"; + inline constexpr const char man_detective_medium_skin_tone[] = "🕵🏽‍♂️"; + inline constexpr const char man_detective_tone4[] = "🕵🏾‍♂️"; + inline constexpr const char man_detective_medium_dark_skin_tone[] = "🕵🏾‍♂️"; + inline constexpr const char man_detective_tone5[] = "🕵🏿‍♂️"; + inline constexpr const char man_detective_dark_skin_tone[] = "🕵🏿‍♂️"; + inline constexpr const char health_worker[] = "🧑‍⚕️"; + inline constexpr const char health_worker_tone1[] = "🧑🏻‍⚕️"; + inline constexpr const char health_worker_light_skin_tone[] = "🧑🏻‍⚕️"; + inline constexpr const char health_worker_tone2[] = "🧑🏼‍⚕️"; + inline constexpr const char health_worker_medium_light_skin_tone[] = "🧑🏼‍⚕️"; + inline constexpr const char health_worker_tone3[] = "🧑🏽‍⚕️"; + inline constexpr const char health_worker_medium_skin_tone[] = "🧑🏽‍⚕️"; + inline constexpr const char health_worker_tone4[] = "🧑🏾‍⚕️"; + inline constexpr const char health_worker_medium_dark_skin_tone[] = "🧑🏾‍⚕️"; + inline constexpr const char health_worker_tone5[] = "🧑🏿‍⚕️"; + inline constexpr const char health_worker_dark_skin_tone[] = "🧑🏿‍⚕️"; + inline constexpr const char woman_health_worker[] = "👩‍⚕️"; + inline constexpr const char woman_health_worker_tone1[] = "👩🏻‍⚕️"; + inline constexpr const char woman_health_worker_light_skin_tone[] = "👩🏻‍⚕️"; + inline constexpr const char woman_health_worker_tone2[] = "👩🏼‍⚕️"; + inline constexpr const char woman_health_worker_medium_light_skin_tone[] = "👩🏼‍⚕️"; + inline constexpr const char woman_health_worker_tone3[] = "👩🏽‍⚕️"; + inline constexpr const char woman_health_worker_medium_skin_tone[] = "👩🏽‍⚕️"; + inline constexpr const char woman_health_worker_tone4[] = "👩🏾‍⚕️"; + inline constexpr const char woman_health_worker_medium_dark_skin_tone[] = "👩🏾‍⚕️"; + inline constexpr const char woman_health_worker_tone5[] = "👩🏿‍⚕️"; + inline constexpr const char woman_health_worker_dark_skin_tone[] = "👩🏿‍⚕️"; + inline constexpr const char man_health_worker[] = "👨‍⚕️"; + inline constexpr const char man_health_worker_tone1[] = "👨🏻‍⚕️"; + inline constexpr const char man_health_worker_light_skin_tone[] = "👨🏻‍⚕️"; + inline constexpr const char man_health_worker_tone2[] = "👨🏼‍⚕️"; + inline constexpr const char man_health_worker_medium_light_skin_tone[] = "👨🏼‍⚕️"; + inline constexpr const char man_health_worker_tone3[] = "👨🏽‍⚕️"; + inline constexpr const char man_health_worker_medium_skin_tone[] = "👨🏽‍⚕️"; + inline constexpr const char man_health_worker_tone4[] = "👨🏾‍⚕️"; + inline constexpr const char man_health_worker_medium_dark_skin_tone[] = "👨🏾‍⚕️"; + inline constexpr const char man_health_worker_tone5[] = "👨🏿‍⚕️"; + inline constexpr const char man_health_worker_dark_skin_tone[] = "👨🏿‍⚕️"; + inline constexpr const char farmer[] = "🧑‍🌾"; + inline constexpr const char farmer_tone1[] = "🧑🏻‍🌾"; + inline constexpr const char farmer_light_skin_tone[] = "🧑🏻‍🌾"; + inline constexpr const char farmer_tone2[] = "🧑🏼‍🌾"; + inline constexpr const char farmer_medium_light_skin_tone[] = "🧑🏼‍🌾"; + inline constexpr const char farmer_tone3[] = "🧑🏽‍🌾"; + inline constexpr const char farmer_medium_skin_tone[] = "🧑🏽‍🌾"; + inline constexpr const char farmer_tone4[] = "🧑🏾‍🌾"; + inline constexpr const char farmer_medium_dark_skin_tone[] = "🧑🏾‍🌾"; + inline constexpr const char farmer_tone5[] = "🧑🏿‍🌾"; + inline constexpr const char farmer_dark_skin_tone[] = "🧑🏿‍🌾"; + inline constexpr const char woman_farmer[] = "👩‍🌾"; + inline constexpr const char woman_farmer_tone1[] = "👩🏻‍🌾"; + inline constexpr const char woman_farmer_light_skin_tone[] = "👩🏻‍🌾"; + inline constexpr const char woman_farmer_tone2[] = "👩🏼‍🌾"; + inline constexpr const char woman_farmer_medium_light_skin_tone[] = "👩🏼‍🌾"; + inline constexpr const char woman_farmer_tone3[] = "👩🏽‍🌾"; + inline constexpr const char woman_farmer_medium_skin_tone[] = "👩🏽‍🌾"; + inline constexpr const char woman_farmer_tone4[] = "👩🏾‍🌾"; + inline constexpr const char woman_farmer_medium_dark_skin_tone[] = "👩🏾‍🌾"; + inline constexpr const char woman_farmer_tone5[] = "👩🏿‍🌾"; + inline constexpr const char woman_farmer_dark_skin_tone[] = "👩🏿‍🌾"; + inline constexpr const char man_farmer[] = "👨‍🌾"; + inline constexpr const char man_farmer_tone1[] = "👨🏻‍🌾"; + inline constexpr const char man_farmer_light_skin_tone[] = "👨🏻‍🌾"; + inline constexpr const char man_farmer_tone2[] = "👨🏼‍🌾"; + inline constexpr const char man_farmer_medium_light_skin_tone[] = "👨🏼‍🌾"; + inline constexpr const char man_farmer_tone3[] = "👨🏽‍🌾"; + inline constexpr const char man_farmer_medium_skin_tone[] = "👨🏽‍🌾"; + inline constexpr const char man_farmer_tone4[] = "👨🏾‍🌾"; + inline constexpr const char man_farmer_medium_dark_skin_tone[] = "👨🏾‍🌾"; + inline constexpr const char man_farmer_tone5[] = "👨🏿‍🌾"; + inline constexpr const char man_farmer_dark_skin_tone[] = "👨🏿‍🌾"; + inline constexpr const char cook[] = "🧑‍🍳"; + inline constexpr const char cook_tone1[] = "🧑🏻‍🍳"; + inline constexpr const char cook_light_skin_tone[] = "🧑🏻‍🍳"; + inline constexpr const char cook_tone2[] = "🧑🏼‍🍳"; + inline constexpr const char cook_medium_light_skin_tone[] = "🧑🏼‍🍳"; + inline constexpr const char cook_tone3[] = "🧑🏽‍🍳"; + inline constexpr const char cook_medium_skin_tone[] = "🧑🏽‍🍳"; + inline constexpr const char cook_tone4[] = "🧑🏾‍🍳"; + inline constexpr const char cook_medium_dark_skin_tone[] = "🧑🏾‍🍳"; + inline constexpr const char cook_tone5[] = "🧑🏿‍🍳"; + inline constexpr const char cook_dark_skin_tone[] = "🧑🏿‍🍳"; + inline constexpr const char woman_cook[] = "👩‍🍳"; + inline constexpr const char woman_cook_tone1[] = "👩🏻‍🍳"; + inline constexpr const char woman_cook_light_skin_tone[] = "👩🏻‍🍳"; + inline constexpr const char woman_cook_tone2[] = "👩🏼‍🍳"; + inline constexpr const char woman_cook_medium_light_skin_tone[] = "👩🏼‍🍳"; + inline constexpr const char woman_cook_tone3[] = "👩🏽‍🍳"; + inline constexpr const char woman_cook_medium_skin_tone[] = "👩🏽‍🍳"; + inline constexpr const char woman_cook_tone4[] = "👩🏾‍🍳"; + inline constexpr const char woman_cook_medium_dark_skin_tone[] = "👩🏾‍🍳"; + inline constexpr const char woman_cook_tone5[] = "👩🏿‍🍳"; + inline constexpr const char woman_cook_dark_skin_tone[] = "👩🏿‍🍳"; + inline constexpr const char man_cook[] = "👨‍🍳"; + inline constexpr const char man_cook_tone1[] = "👨🏻‍🍳"; + inline constexpr const char man_cook_light_skin_tone[] = "👨🏻‍🍳"; + inline constexpr const char man_cook_tone2[] = "👨🏼‍🍳"; + inline constexpr const char man_cook_medium_light_skin_tone[] = "👨🏼‍🍳"; + inline constexpr const char man_cook_tone3[] = "👨🏽‍🍳"; + inline constexpr const char man_cook_medium_skin_tone[] = "👨🏽‍🍳"; + inline constexpr const char man_cook_tone4[] = "👨🏾‍🍳"; + inline constexpr const char man_cook_medium_dark_skin_tone[] = "👨🏾‍🍳"; + inline constexpr const char man_cook_tone5[] = "👨🏿‍🍳"; + inline constexpr const char man_cook_dark_skin_tone[] = "👨🏿‍🍳"; + inline constexpr const char student[] = "🧑‍🎓"; + inline constexpr const char student_tone1[] = "🧑🏻‍🎓"; + inline constexpr const char student_light_skin_tone[] = "🧑🏻‍🎓"; + inline constexpr const char student_tone2[] = "🧑🏼‍🎓"; + inline constexpr const char student_medium_light_skin_tone[] = "🧑🏼‍🎓"; + inline constexpr const char student_tone3[] = "🧑🏽‍🎓"; + inline constexpr const char student_medium_skin_tone[] = "🧑🏽‍🎓"; + inline constexpr const char student_tone4[] = "🧑🏾‍🎓"; + inline constexpr const char student_medium_dark_skin_tone[] = "🧑🏾‍🎓"; + inline constexpr const char student_tone5[] = "🧑🏿‍🎓"; + inline constexpr const char student_dark_skin_tone[] = "🧑🏿‍🎓"; + inline constexpr const char woman_student[] = "👩‍🎓"; + inline constexpr const char woman_student_tone1[] = "👩🏻‍🎓"; + inline constexpr const char woman_student_light_skin_tone[] = "👩🏻‍🎓"; + inline constexpr const char woman_student_tone2[] = "👩🏼‍🎓"; + inline constexpr const char woman_student_medium_light_skin_tone[] = "👩🏼‍🎓"; + inline constexpr const char woman_student_tone3[] = "👩🏽‍🎓"; + inline constexpr const char woman_student_medium_skin_tone[] = "👩🏽‍🎓"; + inline constexpr const char woman_student_tone4[] = "👩🏾‍🎓"; + inline constexpr const char woman_student_medium_dark_skin_tone[] = "👩🏾‍🎓"; + inline constexpr const char woman_student_tone5[] = "👩🏿‍🎓"; + inline constexpr const char woman_student_dark_skin_tone[] = "👩🏿‍🎓"; + inline constexpr const char man_student[] = "👨‍🎓"; + inline constexpr const char man_student_tone1[] = "👨🏻‍🎓"; + inline constexpr const char man_student_light_skin_tone[] = "👨🏻‍🎓"; + inline constexpr const char man_student_tone2[] = "👨🏼‍🎓"; + inline constexpr const char man_student_medium_light_skin_tone[] = "👨🏼‍🎓"; + inline constexpr const char man_student_tone3[] = "👨🏽‍🎓"; + inline constexpr const char man_student_medium_skin_tone[] = "👨🏽‍🎓"; + inline constexpr const char man_student_tone4[] = "👨🏾‍🎓"; + inline constexpr const char man_student_medium_dark_skin_tone[] = "👨🏾‍🎓"; + inline constexpr const char man_student_tone5[] = "👨🏿‍🎓"; + inline constexpr const char man_student_dark_skin_tone[] = "👨🏿‍🎓"; + inline constexpr const char singer[] = "🧑‍🎤"; + inline constexpr const char singer_tone1[] = "🧑🏻‍🎤"; + inline constexpr const char singer_light_skin_tone[] = "🧑🏻‍🎤"; + inline constexpr const char singer_tone2[] = "🧑🏼‍🎤"; + inline constexpr const char singer_medium_light_skin_tone[] = "🧑🏼‍🎤"; + inline constexpr const char singer_tone3[] = "🧑🏽‍🎤"; + inline constexpr const char singer_medium_skin_tone[] = "🧑🏽‍🎤"; + inline constexpr const char singer_tone4[] = "🧑🏾‍🎤"; + inline constexpr const char singer_medium_dark_skin_tone[] = "🧑🏾‍🎤"; + inline constexpr const char singer_tone5[] = "🧑🏿‍🎤"; + inline constexpr const char singer_dark_skin_tone[] = "🧑🏿‍🎤"; + inline constexpr const char woman_singer[] = "👩‍🎤"; + inline constexpr const char woman_singer_tone1[] = "👩🏻‍🎤"; + inline constexpr const char woman_singer_light_skin_tone[] = "👩🏻‍🎤"; + inline constexpr const char woman_singer_tone2[] = "👩🏼‍🎤"; + inline constexpr const char woman_singer_medium_light_skin_tone[] = "👩🏼‍🎤"; + inline constexpr const char woman_singer_tone3[] = "👩🏽‍🎤"; + inline constexpr const char woman_singer_medium_skin_tone[] = "👩🏽‍🎤"; + inline constexpr const char woman_singer_tone4[] = "👩🏾‍🎤"; + inline constexpr const char woman_singer_medium_dark_skin_tone[] = "👩🏾‍🎤"; + inline constexpr const char woman_singer_tone5[] = "👩🏿‍🎤"; + inline constexpr const char woman_singer_dark_skin_tone[] = "👩🏿‍🎤"; + inline constexpr const char man_singer[] = "👨‍🎤"; + inline constexpr const char man_singer_tone1[] = "👨🏻‍🎤"; + inline constexpr const char man_singer_light_skin_tone[] = "👨🏻‍🎤"; + inline constexpr const char man_singer_tone2[] = "👨🏼‍🎤"; + inline constexpr const char man_singer_medium_light_skin_tone[] = "👨🏼‍🎤"; + inline constexpr const char man_singer_tone3[] = "👨🏽‍🎤"; + inline constexpr const char man_singer_medium_skin_tone[] = "👨🏽‍🎤"; + inline constexpr const char man_singer_tone4[] = "👨🏾‍🎤"; + inline constexpr const char man_singer_medium_dark_skin_tone[] = "👨🏾‍🎤"; + inline constexpr const char man_singer_tone5[] = "👨🏿‍🎤"; + inline constexpr const char man_singer_dark_skin_tone[] = "👨🏿‍🎤"; + inline constexpr const char teacher[] = "🧑‍🏫"; + inline constexpr const char teacher_tone1[] = "🧑🏻‍🏫"; + inline constexpr const char teacher_light_skin_tone[] = "🧑🏻‍🏫"; + inline constexpr const char teacher_tone2[] = "🧑🏼‍🏫"; + inline constexpr const char teacher_medium_light_skin_tone[] = "🧑🏼‍🏫"; + inline constexpr const char teacher_tone3[] = "🧑🏽‍🏫"; + inline constexpr const char teacher_medium_skin_tone[] = "🧑🏽‍🏫"; + inline constexpr const char teacher_tone4[] = "🧑🏾‍🏫"; + inline constexpr const char teacher_medium_dark_skin_tone[] = "🧑🏾‍🏫"; + inline constexpr const char teacher_tone5[] = "🧑🏿‍🏫"; + inline constexpr const char teacher_dark_skin_tone[] = "🧑🏿‍🏫"; + inline constexpr const char woman_teacher[] = "👩‍🏫"; + inline constexpr const char woman_teacher_tone1[] = "👩🏻‍🏫"; + inline constexpr const char woman_teacher_light_skin_tone[] = "👩🏻‍🏫"; + inline constexpr const char woman_teacher_tone2[] = "👩🏼‍🏫"; + inline constexpr const char woman_teacher_medium_light_skin_tone[] = "👩🏼‍🏫"; + inline constexpr const char woman_teacher_tone3[] = "👩🏽‍🏫"; + inline constexpr const char woman_teacher_medium_skin_tone[] = "👩🏽‍🏫"; + inline constexpr const char woman_teacher_tone4[] = "👩🏾‍🏫"; + inline constexpr const char woman_teacher_medium_dark_skin_tone[] = "👩🏾‍🏫"; + inline constexpr const char woman_teacher_tone5[] = "👩🏿‍🏫"; + inline constexpr const char woman_teacher_dark_skin_tone[] = "👩🏿‍🏫"; + inline constexpr const char man_teacher[] = "👨‍🏫"; + inline constexpr const char man_teacher_tone1[] = "👨🏻‍🏫"; + inline constexpr const char man_teacher_light_skin_tone[] = "👨🏻‍🏫"; + inline constexpr const char man_teacher_tone2[] = "👨🏼‍🏫"; + inline constexpr const char man_teacher_medium_light_skin_tone[] = "👨🏼‍🏫"; + inline constexpr const char man_teacher_tone3[] = "👨🏽‍🏫"; + inline constexpr const char man_teacher_medium_skin_tone[] = "👨🏽‍🏫"; + inline constexpr const char man_teacher_tone4[] = "👨🏾‍🏫"; + inline constexpr const char man_teacher_medium_dark_skin_tone[] = "👨🏾‍🏫"; + inline constexpr const char man_teacher_tone5[] = "👨🏿‍🏫"; + inline constexpr const char man_teacher_dark_skin_tone[] = "👨🏿‍🏫"; + inline constexpr const char factory_worker[] = "🧑‍🏭"; + inline constexpr const char factory_worker_tone1[] = "🧑🏻‍🏭"; + inline constexpr const char factory_worker_light_skin_tone[] = "🧑🏻‍🏭"; + inline constexpr const char factory_worker_tone2[] = "🧑🏼‍🏭"; + inline constexpr const char factory_worker_medium_light_skin_tone[] = "🧑🏼‍🏭"; + inline constexpr const char factory_worker_tone3[] = "🧑🏽‍🏭"; + inline constexpr const char factory_worker_medium_skin_tone[] = "🧑🏽‍🏭"; + inline constexpr const char factory_worker_tone4[] = "🧑🏾‍🏭"; + inline constexpr const char factory_worker_medium_dark_skin_tone[] = "🧑🏾‍🏭"; + inline constexpr const char factory_worker_tone5[] = "🧑🏿‍🏭"; + inline constexpr const char factory_worker_dark_skin_tone[] = "🧑🏿‍🏭"; + inline constexpr const char woman_factory_worker[] = "👩‍🏭"; + inline constexpr const char woman_factory_worker_tone1[] = "👩🏻‍🏭"; + inline constexpr const char woman_factory_worker_light_skin_tone[] = "👩🏻‍🏭"; + inline constexpr const char woman_factory_worker_tone2[] = "👩🏼‍🏭"; + inline constexpr const char woman_factory_worker_medium_light_skin_tone[] = "👩🏼‍🏭"; + inline constexpr const char woman_factory_worker_tone3[] = "👩🏽‍🏭"; + inline constexpr const char woman_factory_worker_medium_skin_tone[] = "👩🏽‍🏭"; + inline constexpr const char woman_factory_worker_tone4[] = "👩🏾‍🏭"; + inline constexpr const char woman_factory_worker_medium_dark_skin_tone[] = "👩🏾‍🏭"; + inline constexpr const char woman_factory_worker_tone5[] = "👩🏿‍🏭"; + inline constexpr const char woman_factory_worker_dark_skin_tone[] = "👩🏿‍🏭"; + inline constexpr const char man_factory_worker[] = "👨‍🏭"; + inline constexpr const char man_factory_worker_tone1[] = "👨🏻‍🏭"; + inline constexpr const char man_factory_worker_light_skin_tone[] = "👨🏻‍🏭"; + inline constexpr const char man_factory_worker_tone2[] = "👨🏼‍🏭"; + inline constexpr const char man_factory_worker_medium_light_skin_tone[] = "👨🏼‍🏭"; + inline constexpr const char man_factory_worker_tone3[] = "👨🏽‍🏭"; + inline constexpr const char man_factory_worker_medium_skin_tone[] = "👨🏽‍🏭"; + inline constexpr const char man_factory_worker_tone4[] = "👨🏾‍🏭"; + inline constexpr const char man_factory_worker_medium_dark_skin_tone[] = "👨🏾‍🏭"; + inline constexpr const char man_factory_worker_tone5[] = "👨🏿‍🏭"; + inline constexpr const char man_factory_worker_dark_skin_tone[] = "👨🏿‍🏭"; + inline constexpr const char technologist[] = "🧑‍💻"; + inline constexpr const char technologist_tone1[] = "🧑🏻‍💻"; + inline constexpr const char technologist_light_skin_tone[] = "🧑🏻‍💻"; + inline constexpr const char technologist_tone2[] = "🧑🏼‍💻"; + inline constexpr const char technologist_medium_light_skin_tone[] = "🧑🏼‍💻"; + inline constexpr const char technologist_tone3[] = "🧑🏽‍💻"; + inline constexpr const char technologist_medium_skin_tone[] = "🧑🏽‍💻"; + inline constexpr const char technologist_tone4[] = "🧑🏾‍💻"; + inline constexpr const char technologist_medium_dark_skin_tone[] = "🧑🏾‍💻"; + inline constexpr const char technologist_tone5[] = "🧑🏿‍💻"; + inline constexpr const char technologist_dark_skin_tone[] = "🧑🏿‍💻"; + inline constexpr const char woman_technologist[] = "👩‍💻"; + inline constexpr const char woman_technologist_tone1[] = "👩🏻‍💻"; + inline constexpr const char woman_technologist_light_skin_tone[] = "👩🏻‍💻"; + inline constexpr const char woman_technologist_tone2[] = "👩🏼‍💻"; + inline constexpr const char woman_technologist_medium_light_skin_tone[] = "👩🏼‍💻"; + inline constexpr const char woman_technologist_tone3[] = "👩🏽‍💻"; + inline constexpr const char woman_technologist_medium_skin_tone[] = "👩🏽‍💻"; + inline constexpr const char woman_technologist_tone4[] = "👩🏾‍💻"; + inline constexpr const char woman_technologist_medium_dark_skin_tone[] = "👩🏾‍💻"; + inline constexpr const char woman_technologist_tone5[] = "👩🏿‍💻"; + inline constexpr const char woman_technologist_dark_skin_tone[] = "👩🏿‍💻"; + inline constexpr const char man_technologist[] = "👨‍💻"; + inline constexpr const char man_technologist_tone1[] = "👨🏻‍💻"; + inline constexpr const char man_technologist_light_skin_tone[] = "👨🏻‍💻"; + inline constexpr const char man_technologist_tone2[] = "👨🏼‍💻"; + inline constexpr const char man_technologist_medium_light_skin_tone[] = "👨🏼‍💻"; + inline constexpr const char man_technologist_tone3[] = "👨🏽‍💻"; + inline constexpr const char man_technologist_medium_skin_tone[] = "👨🏽‍💻"; + inline constexpr const char man_technologist_tone4[] = "👨🏾‍💻"; + inline constexpr const char man_technologist_medium_dark_skin_tone[] = "👨🏾‍💻"; + inline constexpr const char man_technologist_tone5[] = "👨🏿‍💻"; + inline constexpr const char man_technologist_dark_skin_tone[] = "👨🏿‍💻"; + inline constexpr const char office_worker[] = "🧑‍💼"; + inline constexpr const char office_worker_tone1[] = "🧑🏻‍💼"; + inline constexpr const char office_worker_light_skin_tone[] = "🧑🏻‍💼"; + inline constexpr const char office_worker_tone2[] = "🧑🏼‍💼"; + inline constexpr const char office_worker_medium_light_skin_tone[] = "🧑🏼‍💼"; + inline constexpr const char office_worker_tone3[] = "🧑🏽‍💼"; + inline constexpr const char office_worker_medium_skin_tone[] = "🧑🏽‍💼"; + inline constexpr const char office_worker_tone4[] = "🧑🏾‍💼"; + inline constexpr const char office_worker_medium_dark_skin_tone[] = "🧑🏾‍💼"; + inline constexpr const char office_worker_tone5[] = "🧑🏿‍💼"; + inline constexpr const char office_worker_dark_skin_tone[] = "🧑🏿‍💼"; + inline constexpr const char woman_office_worker[] = "👩‍💼"; + inline constexpr const char woman_office_worker_tone1[] = "👩🏻‍💼"; + inline constexpr const char woman_office_worker_light_skin_tone[] = "👩🏻‍💼"; + inline constexpr const char woman_office_worker_tone2[] = "👩🏼‍💼"; + inline constexpr const char woman_office_worker_medium_light_skin_tone[] = "👩🏼‍💼"; + inline constexpr const char woman_office_worker_tone3[] = "👩🏽‍💼"; + inline constexpr const char woman_office_worker_medium_skin_tone[] = "👩🏽‍💼"; + inline constexpr const char woman_office_worker_tone4[] = "👩🏾‍💼"; + inline constexpr const char woman_office_worker_medium_dark_skin_tone[] = "👩🏾‍💼"; + inline constexpr const char woman_office_worker_tone5[] = "👩🏿‍💼"; + inline constexpr const char woman_office_worker_dark_skin_tone[] = "👩🏿‍💼"; + inline constexpr const char man_office_worker[] = "👨‍💼"; + inline constexpr const char man_office_worker_tone1[] = "👨🏻‍💼"; + inline constexpr const char man_office_worker_light_skin_tone[] = "👨🏻‍💼"; + inline constexpr const char man_office_worker_tone2[] = "👨🏼‍💼"; + inline constexpr const char man_office_worker_medium_light_skin_tone[] = "👨🏼‍💼"; + inline constexpr const char man_office_worker_tone3[] = "👨🏽‍💼"; + inline constexpr const char man_office_worker_medium_skin_tone[] = "👨🏽‍💼"; + inline constexpr const char man_office_worker_tone4[] = "👨🏾‍💼"; + inline constexpr const char man_office_worker_medium_dark_skin_tone[] = "👨🏾‍💼"; + inline constexpr const char man_office_worker_tone5[] = "👨🏿‍💼"; + inline constexpr const char man_office_worker_dark_skin_tone[] = "👨🏿‍💼"; + inline constexpr const char mechanic[] = "🧑‍🔧"; + inline constexpr const char mechanic_tone1[] = "🧑🏻‍🔧"; + inline constexpr const char mechanic_light_skin_tone[] = "🧑🏻‍🔧"; + inline constexpr const char mechanic_tone2[] = "🧑🏼‍🔧"; + inline constexpr const char mechanic_medium_light_skin_tone[] = "🧑🏼‍🔧"; + inline constexpr const char mechanic_tone3[] = "🧑🏽‍🔧"; + inline constexpr const char mechanic_medium_skin_tone[] = "🧑🏽‍🔧"; + inline constexpr const char mechanic_tone4[] = "🧑🏾‍🔧"; + inline constexpr const char mechanic_medium_dark_skin_tone[] = "🧑🏾‍🔧"; + inline constexpr const char mechanic_tone5[] = "🧑🏿‍🔧"; + inline constexpr const char mechanic_dark_skin_tone[] = "🧑🏿‍🔧"; + inline constexpr const char woman_mechanic[] = "👩‍🔧"; + inline constexpr const char woman_mechanic_tone1[] = "👩🏻‍🔧"; + inline constexpr const char woman_mechanic_light_skin_tone[] = "👩🏻‍🔧"; + inline constexpr const char woman_mechanic_tone2[] = "👩🏼‍🔧"; + inline constexpr const char woman_mechanic_medium_light_skin_tone[] = "👩🏼‍🔧"; + inline constexpr const char woman_mechanic_tone3[] = "👩🏽‍🔧"; + inline constexpr const char woman_mechanic_medium_skin_tone[] = "👩🏽‍🔧"; + inline constexpr const char woman_mechanic_tone4[] = "👩🏾‍🔧"; + inline constexpr const char woman_mechanic_medium_dark_skin_tone[] = "👩🏾‍🔧"; + inline constexpr const char woman_mechanic_tone5[] = "👩🏿‍🔧"; + inline constexpr const char woman_mechanic_dark_skin_tone[] = "👩🏿‍🔧"; + inline constexpr const char man_mechanic[] = "👨‍🔧"; + inline constexpr const char man_mechanic_tone1[] = "👨🏻‍🔧"; + inline constexpr const char man_mechanic_light_skin_tone[] = "👨🏻‍🔧"; + inline constexpr const char man_mechanic_tone2[] = "👨🏼‍🔧"; + inline constexpr const char man_mechanic_medium_light_skin_tone[] = "👨🏼‍🔧"; + inline constexpr const char man_mechanic_tone3[] = "👨🏽‍🔧"; + inline constexpr const char man_mechanic_medium_skin_tone[] = "👨🏽‍🔧"; + inline constexpr const char man_mechanic_tone4[] = "👨🏾‍🔧"; + inline constexpr const char man_mechanic_medium_dark_skin_tone[] = "👨🏾‍🔧"; + inline constexpr const char man_mechanic_tone5[] = "👨🏿‍🔧"; + inline constexpr const char man_mechanic_dark_skin_tone[] = "👨🏿‍🔧"; + inline constexpr const char scientist[] = "🧑‍🔬"; + inline constexpr const char scientist_tone1[] = "🧑🏻‍🔬"; + inline constexpr const char scientist_light_skin_tone[] = "🧑🏻‍🔬"; + inline constexpr const char scientist_tone2[] = "🧑🏼‍🔬"; + inline constexpr const char scientist_medium_light_skin_tone[] = "🧑🏼‍🔬"; + inline constexpr const char scientist_tone3[] = "🧑🏽‍🔬"; + inline constexpr const char scientist_medium_skin_tone[] = "🧑🏽‍🔬"; + inline constexpr const char scientist_tone4[] = "🧑🏾‍🔬"; + inline constexpr const char scientist_medium_dark_skin_tone[] = "🧑🏾‍🔬"; + inline constexpr const char scientist_tone5[] = "🧑🏿‍🔬"; + inline constexpr const char scientist_dark_skin_tone[] = "🧑🏿‍🔬"; + inline constexpr const char woman_scientist[] = "👩‍🔬"; + inline constexpr const char woman_scientist_tone1[] = "👩🏻‍🔬"; + inline constexpr const char woman_scientist_light_skin_tone[] = "👩🏻‍🔬"; + inline constexpr const char woman_scientist_tone2[] = "👩🏼‍🔬"; + inline constexpr const char woman_scientist_medium_light_skin_tone[] = "👩🏼‍🔬"; + inline constexpr const char woman_scientist_tone3[] = "👩🏽‍🔬"; + inline constexpr const char woman_scientist_medium_skin_tone[] = "👩🏽‍🔬"; + inline constexpr const char woman_scientist_tone4[] = "👩🏾‍🔬"; + inline constexpr const char woman_scientist_medium_dark_skin_tone[] = "👩🏾‍🔬"; + inline constexpr const char woman_scientist_tone5[] = "👩🏿‍🔬"; + inline constexpr const char woman_scientist_dark_skin_tone[] = "👩🏿‍🔬"; + inline constexpr const char man_scientist[] = "👨‍🔬"; + inline constexpr const char man_scientist_tone1[] = "👨🏻‍🔬"; + inline constexpr const char man_scientist_light_skin_tone[] = "👨🏻‍🔬"; + inline constexpr const char man_scientist_tone2[] = "👨🏼‍🔬"; + inline constexpr const char man_scientist_medium_light_skin_tone[] = "👨🏼‍🔬"; + inline constexpr const char man_scientist_tone3[] = "👨🏽‍🔬"; + inline constexpr const char man_scientist_medium_skin_tone[] = "👨🏽‍🔬"; + inline constexpr const char man_scientist_tone4[] = "👨🏾‍🔬"; + inline constexpr const char man_scientist_medium_dark_skin_tone[] = "👨🏾‍🔬"; + inline constexpr const char man_scientist_tone5[] = "👨🏿‍🔬"; + inline constexpr const char man_scientist_dark_skin_tone[] = "👨🏿‍🔬"; + inline constexpr const char artist[] = "🧑‍🎨"; + inline constexpr const char artist_tone1[] = "🧑🏻‍🎨"; + inline constexpr const char artist_light_skin_tone[] = "🧑🏻‍🎨"; + inline constexpr const char artist_tone2[] = "🧑🏼‍🎨"; + inline constexpr const char artist_medium_light_skin_tone[] = "🧑🏼‍🎨"; + inline constexpr const char artist_tone3[] = "🧑🏽‍🎨"; + inline constexpr const char artist_medium_skin_tone[] = "🧑🏽‍🎨"; + inline constexpr const char artist_tone4[] = "🧑🏾‍🎨"; + inline constexpr const char artist_medium_dark_skin_tone[] = "🧑🏾‍🎨"; + inline constexpr const char artist_tone5[] = "🧑🏿‍🎨"; + inline constexpr const char artist_dark_skin_tone[] = "🧑🏿‍🎨"; + inline constexpr const char woman_artist[] = "👩‍🎨"; + inline constexpr const char woman_artist_tone1[] = "👩🏻‍🎨"; + inline constexpr const char woman_artist_light_skin_tone[] = "👩🏻‍🎨"; + inline constexpr const char woman_artist_tone2[] = "👩🏼‍🎨"; + inline constexpr const char woman_artist_medium_light_skin_tone[] = "👩🏼‍🎨"; + inline constexpr const char woman_artist_tone3[] = "👩🏽‍🎨"; + inline constexpr const char woman_artist_medium_skin_tone[] = "👩🏽‍🎨"; + inline constexpr const char woman_artist_tone4[] = "👩🏾‍🎨"; + inline constexpr const char woman_artist_medium_dark_skin_tone[] = "👩🏾‍🎨"; + inline constexpr const char woman_artist_tone5[] = "👩🏿‍🎨"; + inline constexpr const char woman_artist_dark_skin_tone[] = "👩🏿‍🎨"; + inline constexpr const char man_artist[] = "👨‍🎨"; + inline constexpr const char man_artist_tone1[] = "👨🏻‍🎨"; + inline constexpr const char man_artist_light_skin_tone[] = "👨🏻‍🎨"; + inline constexpr const char man_artist_tone2[] = "👨🏼‍🎨"; + inline constexpr const char man_artist_medium_light_skin_tone[] = "👨🏼‍🎨"; + inline constexpr const char man_artist_tone3[] = "👨🏽‍🎨"; + inline constexpr const char man_artist_medium_skin_tone[] = "👨🏽‍🎨"; + inline constexpr const char man_artist_tone4[] = "👨🏾‍🎨"; + inline constexpr const char man_artist_medium_dark_skin_tone[] = "👨🏾‍🎨"; + inline constexpr const char man_artist_tone5[] = "👨🏿‍🎨"; + inline constexpr const char man_artist_dark_skin_tone[] = "👨🏿‍🎨"; + inline constexpr const char firefighter[] = "🧑‍🚒"; + inline constexpr const char firefighter_tone1[] = "🧑🏻‍🚒"; + inline constexpr const char firefighter_light_skin_tone[] = "🧑🏻‍🚒"; + inline constexpr const char firefighter_tone2[] = "🧑🏼‍🚒"; + inline constexpr const char firefighter_medium_light_skin_tone[] = "🧑🏼‍🚒"; + inline constexpr const char firefighter_tone3[] = "🧑🏽‍🚒"; + inline constexpr const char firefighter_medium_skin_tone[] = "🧑🏽‍🚒"; + inline constexpr const char firefighter_tone4[] = "🧑🏾‍🚒"; + inline constexpr const char firefighter_medium_dark_skin_tone[] = "🧑🏾‍🚒"; + inline constexpr const char firefighter_tone5[] = "🧑🏿‍🚒"; + inline constexpr const char firefighter_dark_skin_tone[] = "🧑🏿‍🚒"; + inline constexpr const char woman_firefighter[] = "👩‍🚒"; + inline constexpr const char woman_firefighter_tone1[] = "👩🏻‍🚒"; + inline constexpr const char woman_firefighter_light_skin_tone[] = "👩🏻‍🚒"; + inline constexpr const char woman_firefighter_tone2[] = "👩🏼‍🚒"; + inline constexpr const char woman_firefighter_medium_light_skin_tone[] = "👩🏼‍🚒"; + inline constexpr const char woman_firefighter_tone3[] = "👩🏽‍🚒"; + inline constexpr const char woman_firefighter_medium_skin_tone[] = "👩🏽‍🚒"; + inline constexpr const char woman_firefighter_tone4[] = "👩🏾‍🚒"; + inline constexpr const char woman_firefighter_medium_dark_skin_tone[] = "👩🏾‍🚒"; + inline constexpr const char woman_firefighter_tone5[] = "👩🏿‍🚒"; + inline constexpr const char woman_firefighter_dark_skin_tone[] = "👩🏿‍🚒"; + inline constexpr const char man_firefighter[] = "👨‍🚒"; + inline constexpr const char man_firefighter_tone1[] = "👨🏻‍🚒"; + inline constexpr const char man_firefighter_light_skin_tone[] = "👨🏻‍🚒"; + inline constexpr const char man_firefighter_tone2[] = "👨🏼‍🚒"; + inline constexpr const char man_firefighter_medium_light_skin_tone[] = "👨🏼‍🚒"; + inline constexpr const char man_firefighter_tone3[] = "👨🏽‍🚒"; + inline constexpr const char man_firefighter_medium_skin_tone[] = "👨🏽‍🚒"; + inline constexpr const char man_firefighter_tone4[] = "👨🏾‍🚒"; + inline constexpr const char man_firefighter_medium_dark_skin_tone[] = "👨🏾‍🚒"; + inline constexpr const char man_firefighter_tone5[] = "👨🏿‍🚒"; + inline constexpr const char man_firefighter_dark_skin_tone[] = "👨🏿‍🚒"; + inline constexpr const char pilot[] = "🧑‍✈️"; + inline constexpr const char pilot_tone1[] = "🧑🏻‍✈️"; + inline constexpr const char pilot_light_skin_tone[] = "🧑🏻‍✈️"; + inline constexpr const char pilot_tone2[] = "🧑🏼‍✈️"; + inline constexpr const char pilot_medium_light_skin_tone[] = "🧑🏼‍✈️"; + inline constexpr const char pilot_tone3[] = "🧑🏽‍✈️"; + inline constexpr const char pilot_medium_skin_tone[] = "🧑🏽‍✈️"; + inline constexpr const char pilot_tone4[] = "🧑🏾‍✈️"; + inline constexpr const char pilot_medium_dark_skin_tone[] = "🧑🏾‍✈️"; + inline constexpr const char pilot_tone5[] = "🧑🏿‍✈️"; + inline constexpr const char pilot_dark_skin_tone[] = "🧑🏿‍✈️"; + inline constexpr const char woman_pilot[] = "👩‍✈️"; + inline constexpr const char woman_pilot_tone1[] = "👩🏻‍✈️"; + inline constexpr const char woman_pilot_light_skin_tone[] = "👩🏻‍✈️"; + inline constexpr const char woman_pilot_tone2[] = "👩🏼‍✈️"; + inline constexpr const char woman_pilot_medium_light_skin_tone[] = "👩🏼‍✈️"; + inline constexpr const char woman_pilot_tone3[] = "👩🏽‍✈️"; + inline constexpr const char woman_pilot_medium_skin_tone[] = "👩🏽‍✈️"; + inline constexpr const char woman_pilot_tone4[] = "👩🏾‍✈️"; + inline constexpr const char woman_pilot_medium_dark_skin_tone[] = "👩🏾‍✈️"; + inline constexpr const char woman_pilot_tone5[] = "👩🏿‍✈️"; + inline constexpr const char woman_pilot_dark_skin_tone[] = "👩🏿‍✈️"; + inline constexpr const char man_pilot[] = "👨‍✈️"; + inline constexpr const char man_pilot_tone1[] = "👨🏻‍✈️"; + inline constexpr const char man_pilot_light_skin_tone[] = "👨🏻‍✈️"; + inline constexpr const char man_pilot_tone2[] = "👨🏼‍✈️"; + inline constexpr const char man_pilot_medium_light_skin_tone[] = "👨🏼‍✈️"; + inline constexpr const char man_pilot_tone3[] = "👨🏽‍✈️"; + inline constexpr const char man_pilot_medium_skin_tone[] = "👨🏽‍✈️"; + inline constexpr const char man_pilot_tone4[] = "👨🏾‍✈️"; + inline constexpr const char man_pilot_medium_dark_skin_tone[] = "👨🏾‍✈️"; + inline constexpr const char man_pilot_tone5[] = "👨🏿‍✈️"; + inline constexpr const char man_pilot_dark_skin_tone[] = "👨🏿‍✈️"; + inline constexpr const char astronaut[] = "🧑‍🚀"; + inline constexpr const char astronaut_tone1[] = "🧑🏻‍🚀"; + inline constexpr const char astronaut_light_skin_tone[] = "🧑🏻‍🚀"; + inline constexpr const char astronaut_tone2[] = "🧑🏼‍🚀"; + inline constexpr const char astronaut_medium_light_skin_tone[] = "🧑🏼‍🚀"; + inline constexpr const char astronaut_tone3[] = "🧑🏽‍🚀"; + inline constexpr const char astronaut_medium_skin_tone[] = "🧑🏽‍🚀"; + inline constexpr const char astronaut_tone4[] = "🧑🏾‍🚀"; + inline constexpr const char astronaut_medium_dark_skin_tone[] = "🧑🏾‍🚀"; + inline constexpr const char astronaut_tone5[] = "🧑🏿‍🚀"; + inline constexpr const char astronaut_dark_skin_tone[] = "🧑🏿‍🚀"; + inline constexpr const char woman_astronaut[] = "👩‍🚀"; + inline constexpr const char woman_astronaut_tone1[] = "👩🏻‍🚀"; + inline constexpr const char woman_astronaut_light_skin_tone[] = "👩🏻‍🚀"; + inline constexpr const char woman_astronaut_tone2[] = "👩🏼‍🚀"; + inline constexpr const char woman_astronaut_medium_light_skin_tone[] = "👩🏼‍🚀"; + inline constexpr const char woman_astronaut_tone3[] = "👩🏽‍🚀"; + inline constexpr const char woman_astronaut_medium_skin_tone[] = "👩🏽‍🚀"; + inline constexpr const char woman_astronaut_tone4[] = "👩🏾‍🚀"; + inline constexpr const char woman_astronaut_medium_dark_skin_tone[] = "👩🏾‍🚀"; + inline constexpr const char woman_astronaut_tone5[] = "👩🏿‍🚀"; + inline constexpr const char woman_astronaut_dark_skin_tone[] = "👩🏿‍🚀"; + inline constexpr const char man_astronaut[] = "👨‍🚀"; + inline constexpr const char man_astronaut_tone1[] = "👨🏻‍🚀"; + inline constexpr const char man_astronaut_light_skin_tone[] = "👨🏻‍🚀"; + inline constexpr const char man_astronaut_tone2[] = "👨🏼‍🚀"; + inline constexpr const char man_astronaut_medium_light_skin_tone[] = "👨🏼‍🚀"; + inline constexpr const char man_astronaut_tone3[] = "👨🏽‍🚀"; + inline constexpr const char man_astronaut_medium_skin_tone[] = "👨🏽‍🚀"; + inline constexpr const char man_astronaut_tone4[] = "👨🏾‍🚀"; + inline constexpr const char man_astronaut_medium_dark_skin_tone[] = "👨🏾‍🚀"; + inline constexpr const char man_astronaut_tone5[] = "👨🏿‍🚀"; + inline constexpr const char man_astronaut_dark_skin_tone[] = "👨🏿‍🚀"; + inline constexpr const char judge[] = "🧑‍⚖️"; + inline constexpr const char judge_tone1[] = "🧑🏻‍⚖️"; + inline constexpr const char judge_light_skin_tone[] = "🧑🏻‍⚖️"; + inline constexpr const char judge_tone2[] = "🧑🏼‍⚖️"; + inline constexpr const char judge_medium_light_skin_tone[] = "🧑🏼‍⚖️"; + inline constexpr const char judge_tone3[] = "🧑🏽‍⚖️"; + inline constexpr const char judge_medium_skin_tone[] = "🧑🏽‍⚖️"; + inline constexpr const char judge_tone4[] = "🧑🏾‍⚖️"; + inline constexpr const char judge_medium_dark_skin_tone[] = "🧑🏾‍⚖️"; + inline constexpr const char judge_tone5[] = "🧑🏿‍⚖️"; + inline constexpr const char judge_dark_skin_tone[] = "🧑🏿‍⚖️"; + inline constexpr const char woman_judge[] = "👩‍⚖️"; + inline constexpr const char woman_judge_tone1[] = "👩🏻‍⚖️"; + inline constexpr const char woman_judge_light_skin_tone[] = "👩🏻‍⚖️"; + inline constexpr const char woman_judge_tone2[] = "👩🏼‍⚖️"; + inline constexpr const char woman_judge_medium_light_skin_tone[] = "👩🏼‍⚖️"; + inline constexpr const char woman_judge_tone3[] = "👩🏽‍⚖️"; + inline constexpr const char woman_judge_medium_skin_tone[] = "👩🏽‍⚖️"; + inline constexpr const char woman_judge_tone4[] = "👩🏾‍⚖️"; + inline constexpr const char woman_judge_medium_dark_skin_tone[] = "👩🏾‍⚖️"; + inline constexpr const char woman_judge_tone5[] = "👩🏿‍⚖️"; + inline constexpr const char woman_judge_dark_skin_tone[] = "👩🏿‍⚖️"; + inline constexpr const char man_judge[] = "👨‍⚖️"; + inline constexpr const char man_judge_tone1[] = "👨🏻‍⚖️"; + inline constexpr const char man_judge_light_skin_tone[] = "👨🏻‍⚖️"; + inline constexpr const char man_judge_tone2[] = "👨🏼‍⚖️"; + inline constexpr const char man_judge_medium_light_skin_tone[] = "👨🏼‍⚖️"; + inline constexpr const char man_judge_tone3[] = "👨🏽‍⚖️"; + inline constexpr const char man_judge_medium_skin_tone[] = "👨🏽‍⚖️"; + inline constexpr const char man_judge_tone4[] = "👨🏾‍⚖️"; + inline constexpr const char man_judge_medium_dark_skin_tone[] = "👨🏾‍⚖️"; + inline constexpr const char man_judge_tone5[] = "👨🏿‍⚖️"; + inline constexpr const char man_judge_dark_skin_tone[] = "👨🏿‍⚖️"; + inline constexpr const char person_with_veil[] = "👰"; + inline constexpr const char person_with_veil_tone1[] = "👰🏻"; + inline constexpr const char person_with_veil_tone2[] = "👰🏼"; + inline constexpr const char person_with_veil_tone3[] = "👰🏽"; + inline constexpr const char person_with_veil_tone4[] = "👰🏾"; + inline constexpr const char person_with_veil_tone5[] = "👰🏿"; + inline constexpr const char woman_with_veil[] = "👰‍♀️"; + inline constexpr const char bride_with_veil[] = "👰‍♀️"; + inline constexpr const char woman_with_veil_tone1[] = "👰🏻‍♀️"; + inline constexpr const char woman_with_veil_light_skin_tone[] = "👰🏻‍♀️"; + inline constexpr const char woman_with_veil_tone2[] = "👰🏼‍♀️"; + inline constexpr const char woman_with_veil_medium_light_skin_tone[] = "👰🏼‍♀️"; + inline constexpr const char woman_with_veil_tone3[] = "👰🏽‍♀️"; + inline constexpr const char woman_with_veil_medium_skin_tone[] = "👰🏽‍♀️"; + inline constexpr const char woman_with_veil_tone4[] = "👰🏾‍♀️"; + inline constexpr const char woman_with_veil_medium_dark_skin_tone[] = "👰🏾‍♀️"; + inline constexpr const char woman_with_veil_tone5[] = "👰🏿‍♀️"; + inline constexpr const char woman_with_veil_dark_skin_tone[] = "👰🏿‍♀️"; + inline constexpr const char man_with_veil[] = "👰‍♂️"; + inline constexpr const char man_with_veil_tone1[] = "👰🏻‍♂️"; + inline constexpr const char man_with_veil_light_skin_tone[] = "👰🏻‍♂️"; + inline constexpr const char man_with_veil_tone2[] = "👰🏼‍♂️"; + inline constexpr const char man_with_veil_medium_light_skin_tone[] = "👰🏼‍♂️"; + inline constexpr const char man_with_veil_tone3[] = "👰🏽‍♂️"; + inline constexpr const char man_with_veil_medium_skin_tone[] = "👰🏽‍♂️"; + inline constexpr const char man_with_veil_tone4[] = "👰🏾‍♂️"; + inline constexpr const char man_with_veil_medium_dark_skin_tone[] = "👰🏾‍♂️"; + inline constexpr const char man_with_veil_tone5[] = "👰🏿‍♂️"; + inline constexpr const char man_with_veil_dark_skin_tone[] = "👰🏿‍♂️"; + inline constexpr const char person_in_tuxedo[] = "🤵"; + inline constexpr const char person_in_tuxedo_tone1[] = "🤵🏻"; + inline constexpr const char tuxedo_tone1[] = "🤵🏻"; + inline constexpr const char person_in_tuxedo_tone2[] = "🤵🏼"; + inline constexpr const char tuxedo_tone2[] = "🤵🏼"; + inline constexpr const char person_in_tuxedo_tone3[] = "🤵🏽"; + inline constexpr const char tuxedo_tone3[] = "🤵🏽"; + inline constexpr const char person_in_tuxedo_tone4[] = "🤵🏾"; + inline constexpr const char tuxedo_tone4[] = "🤵🏾"; + inline constexpr const char person_in_tuxedo_tone5[] = "🤵🏿"; + inline constexpr const char tuxedo_tone5[] = "🤵🏿"; + inline constexpr const char woman_in_tuxedo[] = "🤵‍♀️"; + inline constexpr const char woman_in_tuxedo_tone1[] = "🤵🏻‍♀️"; + inline constexpr const char woman_in_tuxedo_light_skin_tone[] = "🤵🏻‍♀️"; + inline constexpr const char woman_in_tuxedo_tone2[] = "🤵🏼‍♀️"; + inline constexpr const char woman_in_tuxedo_medium_light_skin_tone[] = "🤵🏼‍♀️"; + inline constexpr const char woman_in_tuxedo_tone3[] = "🤵🏽‍♀️"; + inline constexpr const char woman_in_tuxedo_medium_skin_tone[] = "🤵🏽‍♀️"; + inline constexpr const char woman_in_tuxedo_tone4[] = "🤵🏾‍♀️"; + inline constexpr const char woman_in_tuxedo_medium_dark_skin_tone[] = "🤵🏾‍♀️"; + inline constexpr const char woman_in_tuxedo_tone5[] = "🤵🏿‍♀️"; + inline constexpr const char woman_in_tuxedo_dark_skin_tone[] = "🤵🏿‍♀️"; + inline constexpr const char man_in_tuxedo[] = "🤵‍♂️"; + inline constexpr const char man_in_tuxedo_tone1[] = "🤵🏻‍♂️"; + inline constexpr const char man_in_tuxedo_light_skin_tone[] = "🤵🏻‍♂️"; + inline constexpr const char man_in_tuxedo_tone2[] = "🤵🏼‍♂️"; + inline constexpr const char man_in_tuxedo_medium_light_skin_tone[] = "🤵🏼‍♂️"; + inline constexpr const char man_in_tuxedo_tone3[] = "🤵🏽‍♂️"; + inline constexpr const char man_in_tuxedo_medium_skin_tone[] = "🤵🏽‍♂️"; + inline constexpr const char man_in_tuxedo_tone4[] = "🤵🏾‍♂️"; + inline constexpr const char man_in_tuxedo_medium_dark_skin_tone[] = "🤵🏾‍♂️"; + inline constexpr const char man_in_tuxedo_tone5[] = "🤵🏿‍♂️"; + inline constexpr const char man_in_tuxedo_dark_skin_tone[] = "🤵🏿‍♂️"; + inline constexpr const char person_with_crown[] = "🫅"; + inline constexpr const char person_with_crown_tone1[] = "🫅🏻"; + inline constexpr const char person_with_crown_light_skin_tone[] = "🫅🏻"; + inline constexpr const char person_with_crown_tone2[] = "🫅🏼"; + inline constexpr const char person_with_crown_medium_light_skin_tone[] = "🫅🏼"; + inline constexpr const char person_with_crown_tone3[] = "🫅🏽"; + inline constexpr const char person_with_crown_medium_skin_tone[] = "🫅🏽"; + inline constexpr const char person_with_crown_tone4[] = "🫅🏾"; + inline constexpr const char person_with_crown_medium_dark_skin_tone[] = "🫅🏾"; + inline constexpr const char person_with_crown_tone5[] = "🫅🏿"; + inline constexpr const char person_with_crown_dark_skin_tone[] = "🫅🏿"; + inline constexpr const char princess[] = "👸"; + inline constexpr const char princess_tone1[] = "👸🏻"; + inline constexpr const char princess_tone2[] = "👸🏼"; + inline constexpr const char princess_tone3[] = "👸🏽"; + inline constexpr const char princess_tone4[] = "👸🏾"; + inline constexpr const char princess_tone5[] = "👸🏿"; + inline constexpr const char prince[] = "🤴"; + inline constexpr const char prince_tone1[] = "🤴🏻"; + inline constexpr const char prince_tone2[] = "🤴🏼"; + inline constexpr const char prince_tone3[] = "🤴🏽"; + inline constexpr const char prince_tone4[] = "🤴🏾"; + inline constexpr const char prince_tone5[] = "🤴🏿"; + inline constexpr const char superhero[] = "🦸"; + inline constexpr const char superhero_tone1[] = "🦸🏻"; + inline constexpr const char superhero_light_skin_tone[] = "🦸🏻"; + inline constexpr const char superhero_tone2[] = "🦸🏼"; + inline constexpr const char superhero_medium_light_skin_tone[] = "🦸🏼"; + inline constexpr const char superhero_tone3[] = "🦸🏽"; + inline constexpr const char superhero_medium_skin_tone[] = "🦸🏽"; + inline constexpr const char superhero_tone4[] = "🦸🏾"; + inline constexpr const char superhero_medium_dark_skin_tone[] = "🦸🏾"; + inline constexpr const char superhero_tone5[] = "🦸🏿"; + inline constexpr const char superhero_dark_skin_tone[] = "🦸🏿"; + inline constexpr const char woman_superhero[] = "🦸‍♀️"; + inline constexpr const char woman_superhero_tone1[] = "🦸🏻‍♀️"; + inline constexpr const char woman_superhero_light_skin_tone[] = "🦸🏻‍♀️"; + inline constexpr const char woman_superhero_tone2[] = "🦸🏼‍♀️"; + inline constexpr const char woman_superhero_medium_light_skin_tone[] = "🦸🏼‍♀️"; + inline constexpr const char woman_superhero_tone3[] = "🦸🏽‍♀️"; + inline constexpr const char woman_superhero_medium_skin_tone[] = "🦸🏽‍♀️"; + inline constexpr const char woman_superhero_tone4[] = "🦸🏾‍♀️"; + inline constexpr const char woman_superhero_medium_dark_skin_tone[] = "🦸🏾‍♀️"; + inline constexpr const char woman_superhero_tone5[] = "🦸🏿‍♀️"; + inline constexpr const char woman_superhero_dark_skin_tone[] = "🦸🏿‍♀️"; + inline constexpr const char man_superhero[] = "🦸‍♂️"; + inline constexpr const char man_superhero_tone1[] = "🦸🏻‍♂️"; + inline constexpr const char man_superhero_light_skin_tone[] = "🦸🏻‍♂️"; + inline constexpr const char man_superhero_tone2[] = "🦸🏼‍♂️"; + inline constexpr const char man_superhero_medium_light_skin_tone[] = "🦸🏼‍♂️"; + inline constexpr const char man_superhero_tone3[] = "🦸🏽‍♂️"; + inline constexpr const char man_superhero_medium_skin_tone[] = "🦸🏽‍♂️"; + inline constexpr const char man_superhero_tone4[] = "🦸🏾‍♂️"; + inline constexpr const char man_superhero_medium_dark_skin_tone[] = "🦸🏾‍♂️"; + inline constexpr const char man_superhero_tone5[] = "🦸🏿‍♂️"; + inline constexpr const char man_superhero_dark_skin_tone[] = "🦸🏿‍♂️"; + inline constexpr const char supervillain[] = "🦹"; + inline constexpr const char supervillain_tone1[] = "🦹🏻"; + inline constexpr const char supervillain_light_skin_tone[] = "🦹🏻"; + inline constexpr const char supervillain_tone2[] = "🦹🏼"; + inline constexpr const char supervillain_medium_light_skin_tone[] = "🦹🏼"; + inline constexpr const char supervillain_tone3[] = "🦹🏽"; + inline constexpr const char supervillain_medium_skin_tone[] = "🦹🏽"; + inline constexpr const char supervillain_tone4[] = "🦹🏾"; + inline constexpr const char supervillain_medium_dark_skin_tone[] = "🦹🏾"; + inline constexpr const char supervillain_tone5[] = "🦹🏿"; + inline constexpr const char supervillain_dark_skin_tone[] = "🦹🏿"; + inline constexpr const char woman_supervillain[] = "🦹‍♀️"; + inline constexpr const char woman_supervillain_tone1[] = "🦹🏻‍♀️"; + inline constexpr const char woman_supervillain_light_skin_tone[] = "🦹🏻‍♀️"; + inline constexpr const char woman_supervillain_tone2[] = "🦹🏼‍♀️"; + inline constexpr const char woman_supervillain_medium_light_skin_tone[] = "🦹🏼‍♀️"; + inline constexpr const char woman_supervillain_tone3[] = "🦹🏽‍♀️"; + inline constexpr const char woman_supervillain_medium_skin_tone[] = "🦹🏽‍♀️"; + inline constexpr const char woman_supervillain_tone4[] = "🦹🏾‍♀️"; + inline constexpr const char woman_supervillain_medium_dark_skin_tone[] = "🦹🏾‍♀️"; + inline constexpr const char woman_supervillain_tone5[] = "🦹🏿‍♀️"; + inline constexpr const char woman_supervillain_dark_skin_tone[] = "🦹🏿‍♀️"; + inline constexpr const char man_supervillain[] = "🦹‍♂️"; + inline constexpr const char man_supervillain_tone1[] = "🦹🏻‍♂️"; + inline constexpr const char man_supervillain_light_skin_tone[] = "🦹🏻‍♂️"; + inline constexpr const char man_supervillain_tone2[] = "🦹🏼‍♂️"; + inline constexpr const char man_supervillain_medium_light_skin_tone[] = "🦹🏼‍♂️"; + inline constexpr const char man_supervillain_tone3[] = "🦹🏽‍♂️"; + inline constexpr const char man_supervillain_medium_skin_tone[] = "🦹🏽‍♂️"; + inline constexpr const char man_supervillain_tone4[] = "🦹🏾‍♂️"; + inline constexpr const char man_supervillain_medium_dark_skin_tone[] = "🦹🏾‍♂️"; + inline constexpr const char man_supervillain_tone5[] = "🦹🏿‍♂️"; + inline constexpr const char man_supervillain_dark_skin_tone[] = "🦹🏿‍♂️"; + inline constexpr const char ninja[] = "🥷"; + inline constexpr const char ninja_tone1[] = "🥷🏻"; + inline constexpr const char ninja_light_skin_tone[] = "🥷🏻"; + inline constexpr const char ninja_tone2[] = "🥷🏼"; + inline constexpr const char ninja_medium_light_skin_tone[] = "🥷🏼"; + inline constexpr const char ninja_tone3[] = "🥷🏽"; + inline constexpr const char ninja_medium_skin_tone[] = "🥷🏽"; + inline constexpr const char ninja_tone4[] = "🥷🏾"; + inline constexpr const char ninja_medium_dark_skin_tone[] = "🥷🏾"; + inline constexpr const char ninja_tone5[] = "🥷🏿"; + inline constexpr const char ninja_dark_skin_tone[] = "🥷🏿"; + inline constexpr const char mx_claus[] = "🧑‍🎄"; + inline constexpr const char mx_claus_tone1[] = "🧑🏻‍🎄"; + inline constexpr const char mx_claus_light_skin_tone[] = "🧑🏻‍🎄"; + inline constexpr const char mx_claus_tone2[] = "🧑🏼‍🎄"; + inline constexpr const char mx_claus_medium_light_skin_tone[] = "🧑🏼‍🎄"; + inline constexpr const char mx_claus_tone3[] = "🧑🏽‍🎄"; + inline constexpr const char mx_claus_medium_skin_tone[] = "🧑🏽‍🎄"; + inline constexpr const char mx_claus_tone4[] = "🧑🏾‍🎄"; + inline constexpr const char mx_claus_medium_dark_skin_tone[] = "🧑🏾‍🎄"; + inline constexpr const char mx_claus_tone5[] = "🧑🏿‍🎄"; + inline constexpr const char mx_claus_dark_skin_tone[] = "🧑🏿‍🎄"; + inline constexpr const char mrs_claus[] = "🤶"; + inline constexpr const char mother_christmas[] = "🤶"; + inline constexpr const char mrs_claus_tone1[] = "🤶🏻"; + inline constexpr const char mother_christmas_tone1[] = "🤶🏻"; + inline constexpr const char mrs_claus_tone2[] = "🤶🏼"; + inline constexpr const char mother_christmas_tone2[] = "🤶🏼"; + inline constexpr const char mrs_claus_tone3[] = "🤶🏽"; + inline constexpr const char mother_christmas_tone3[] = "🤶🏽"; + inline constexpr const char mrs_claus_tone4[] = "🤶🏾"; + inline constexpr const char mother_christmas_tone4[] = "🤶🏾"; + inline constexpr const char mrs_claus_tone5[] = "🤶🏿"; + inline constexpr const char mother_christmas_tone5[] = "🤶🏿"; + inline constexpr const char santa[] = "🎅"; + inline constexpr const char santa_claus[] = "🎅"; + inline constexpr const char santa_tone1[] = "🎅🏻"; + inline constexpr const char santa_tone2[] = "🎅🏼"; + inline constexpr const char santa_tone3[] = "🎅🏽"; + inline constexpr const char santa_tone4[] = "🎅🏾"; + inline constexpr const char santa_tone5[] = "🎅🏿"; + inline constexpr const char mage[] = "🧙"; + inline constexpr const char mage_tone1[] = "🧙🏻"; + inline constexpr const char mage_light_skin_tone[] = "🧙🏻"; + inline constexpr const char mage_tone2[] = "🧙🏼"; + inline constexpr const char mage_medium_light_skin_tone[] = "🧙🏼"; + inline constexpr const char mage_tone3[] = "🧙🏽"; + inline constexpr const char mage_medium_skin_tone[] = "🧙🏽"; + inline constexpr const char mage_tone4[] = "🧙🏾"; + inline constexpr const char mage_medium_dark_skin_tone[] = "🧙🏾"; + inline constexpr const char mage_tone5[] = "🧙🏿"; + inline constexpr const char mage_dark_skin_tone[] = "🧙🏿"; + inline constexpr const char woman_mage[] = "🧙‍♀️"; + inline constexpr const char woman_mage_tone1[] = "🧙🏻‍♀️"; + inline constexpr const char woman_mage_light_skin_tone[] = "🧙🏻‍♀️"; + inline constexpr const char woman_mage_tone2[] = "🧙🏼‍♀️"; + inline constexpr const char woman_mage_medium_light_skin_tone[] = "🧙🏼‍♀️"; + inline constexpr const char woman_mage_tone3[] = "🧙🏽‍♀️"; + inline constexpr const char woman_mage_medium_skin_tone[] = "🧙🏽‍♀️"; + inline constexpr const char woman_mage_tone4[] = "🧙🏾‍♀️"; + inline constexpr const char woman_mage_medium_dark_skin_tone[] = "🧙🏾‍♀️"; + inline constexpr const char woman_mage_tone5[] = "🧙🏿‍♀️"; + inline constexpr const char woman_mage_dark_skin_tone[] = "🧙🏿‍♀️"; + inline constexpr const char man_mage[] = "🧙‍♂️"; + inline constexpr const char man_mage_tone1[] = "🧙🏻‍♂️"; + inline constexpr const char man_mage_light_skin_tone[] = "🧙🏻‍♂️"; + inline constexpr const char man_mage_tone2[] = "🧙🏼‍♂️"; + inline constexpr const char man_mage_medium_light_skin_tone[] = "🧙🏼‍♂️"; + inline constexpr const char man_mage_tone3[] = "🧙🏽‍♂️"; + inline constexpr const char man_mage_medium_skin_tone[] = "🧙🏽‍♂️"; + inline constexpr const char man_mage_tone4[] = "🧙🏾‍♂️"; + inline constexpr const char man_mage_medium_dark_skin_tone[] = "🧙🏾‍♂️"; + inline constexpr const char man_mage_tone5[] = "🧙🏿‍♂️"; + inline constexpr const char man_mage_dark_skin_tone[] = "🧙🏿‍♂️"; + inline constexpr const char elf[] = "🧝"; + inline constexpr const char elf_tone1[] = "🧝🏻"; + inline constexpr const char elf_light_skin_tone[] = "🧝🏻"; + inline constexpr const char elf_tone2[] = "🧝🏼"; + inline constexpr const char elf_medium_light_skin_tone[] = "🧝🏼"; + inline constexpr const char elf_tone3[] = "🧝🏽"; + inline constexpr const char elf_medium_skin_tone[] = "🧝🏽"; + inline constexpr const char elf_tone4[] = "🧝🏾"; + inline constexpr const char elf_medium_dark_skin_tone[] = "🧝🏾"; + inline constexpr const char elf_tone5[] = "🧝🏿"; + inline constexpr const char elf_dark_skin_tone[] = "🧝🏿"; + inline constexpr const char woman_elf[] = "🧝‍♀️"; + inline constexpr const char woman_elf_tone1[] = "🧝🏻‍♀️"; + inline constexpr const char woman_elf_light_skin_tone[] = "🧝🏻‍♀️"; + inline constexpr const char woman_elf_tone2[] = "🧝🏼‍♀️"; + inline constexpr const char woman_elf_medium_light_skin_tone[] = "🧝🏼‍♀️"; + inline constexpr const char woman_elf_tone3[] = "🧝🏽‍♀️"; + inline constexpr const char woman_elf_medium_skin_tone[] = "🧝🏽‍♀️"; + inline constexpr const char woman_elf_tone4[] = "🧝🏾‍♀️"; + inline constexpr const char woman_elf_medium_dark_skin_tone[] = "🧝🏾‍♀️"; + inline constexpr const char woman_elf_tone5[] = "🧝🏿‍♀️"; + inline constexpr const char woman_elf_dark_skin_tone[] = "🧝🏿‍♀️"; + inline constexpr const char man_elf[] = "🧝‍♂️"; + inline constexpr const char man_elf_tone1[] = "🧝🏻‍♂️"; + inline constexpr const char man_elf_light_skin_tone[] = "🧝🏻‍♂️"; + inline constexpr const char man_elf_tone2[] = "🧝🏼‍♂️"; + inline constexpr const char man_elf_medium_light_skin_tone[] = "🧝🏼‍♂️"; + inline constexpr const char man_elf_tone3[] = "🧝🏽‍♂️"; + inline constexpr const char man_elf_medium_skin_tone[] = "🧝🏽‍♂️"; + inline constexpr const char man_elf_tone4[] = "🧝🏾‍♂️"; + inline constexpr const char man_elf_medium_dark_skin_tone[] = "🧝🏾‍♂️"; + inline constexpr const char man_elf_tone5[] = "🧝🏿‍♂️"; + inline constexpr const char man_elf_dark_skin_tone[] = "🧝🏿‍♂️"; + inline constexpr const char troll[] = "🧌"; + inline constexpr const char vampire[] = "🧛"; + inline constexpr const char vampire_tone1[] = "🧛🏻"; + inline constexpr const char vampire_light_skin_tone[] = "🧛🏻"; + inline constexpr const char vampire_tone2[] = "🧛🏼"; + inline constexpr const char vampire_medium_light_skin_tone[] = "🧛🏼"; + inline constexpr const char vampire_tone3[] = "🧛🏽"; + inline constexpr const char vampire_medium_skin_tone[] = "🧛🏽"; + inline constexpr const char vampire_tone4[] = "🧛🏾"; + inline constexpr const char vampire_medium_dark_skin_tone[] = "🧛🏾"; + inline constexpr const char vampire_tone5[] = "🧛🏿"; + inline constexpr const char vampire_dark_skin_tone[] = "🧛🏿"; + inline constexpr const char woman_vampire[] = "🧛‍♀️"; + inline constexpr const char woman_vampire_tone1[] = "🧛🏻‍♀️"; + inline constexpr const char woman_vampire_light_skin_tone[] = "🧛🏻‍♀️"; + inline constexpr const char woman_vampire_tone2[] = "🧛🏼‍♀️"; + inline constexpr const char woman_vampire_medium_light_skin_tone[] = "🧛🏼‍♀️"; + inline constexpr const char woman_vampire_tone3[] = "🧛🏽‍♀️"; + inline constexpr const char woman_vampire_medium_skin_tone[] = "🧛🏽‍♀️"; + inline constexpr const char woman_vampire_tone4[] = "🧛🏾‍♀️"; + inline constexpr const char woman_vampire_medium_dark_skin_tone[] = "🧛🏾‍♀️"; + inline constexpr const char woman_vampire_tone5[] = "🧛🏿‍♀️"; + inline constexpr const char woman_vampire_dark_skin_tone[] = "🧛🏿‍♀️"; + inline constexpr const char man_vampire[] = "🧛‍♂️"; + inline constexpr const char man_vampire_tone1[] = "🧛🏻‍♂️"; + inline constexpr const char man_vampire_light_skin_tone[] = "🧛🏻‍♂️"; + inline constexpr const char man_vampire_tone2[] = "🧛🏼‍♂️"; + inline constexpr const char man_vampire_medium_light_skin_tone[] = "🧛🏼‍♂️"; + inline constexpr const char man_vampire_tone3[] = "🧛🏽‍♂️"; + inline constexpr const char man_vampire_medium_skin_tone[] = "🧛🏽‍♂️"; + inline constexpr const char man_vampire_tone4[] = "🧛🏾‍♂️"; + inline constexpr const char man_vampire_medium_dark_skin_tone[] = "🧛🏾‍♂️"; + inline constexpr const char man_vampire_tone5[] = "🧛🏿‍♂️"; + inline constexpr const char man_vampire_dark_skin_tone[] = "🧛🏿‍♂️"; + inline constexpr const char zombie[] = "🧟"; + inline constexpr const char woman_zombie[] = "🧟‍♀️"; + inline constexpr const char man_zombie[] = "🧟‍♂️"; + inline constexpr const char genie[] = "🧞"; + inline constexpr const char woman_genie[] = "🧞‍♀️"; + inline constexpr const char man_genie[] = "🧞‍♂️"; + inline constexpr const char merperson[] = "🧜"; + inline constexpr const char merperson_tone1[] = "🧜🏻"; + inline constexpr const char merperson_light_skin_tone[] = "🧜🏻"; + inline constexpr const char merperson_tone2[] = "🧜🏼"; + inline constexpr const char merperson_medium_light_skin_tone[] = "🧜🏼"; + inline constexpr const char merperson_tone3[] = "🧜🏽"; + inline constexpr const char merperson_medium_skin_tone[] = "🧜🏽"; + inline constexpr const char merperson_tone4[] = "🧜🏾"; + inline constexpr const char merperson_medium_dark_skin_tone[] = "🧜🏾"; + inline constexpr const char merperson_tone5[] = "🧜🏿"; + inline constexpr const char merperson_dark_skin_tone[] = "🧜🏿"; + inline constexpr const char mermaid[] = "🧜‍♀️"; + inline constexpr const char mermaid_tone1[] = "🧜🏻‍♀️"; + inline constexpr const char mermaid_light_skin_tone[] = "🧜🏻‍♀️"; + inline constexpr const char mermaid_tone2[] = "🧜🏼‍♀️"; + inline constexpr const char mermaid_medium_light_skin_tone[] = "🧜🏼‍♀️"; + inline constexpr const char mermaid_tone3[] = "🧜🏽‍♀️"; + inline constexpr const char mermaid_medium_skin_tone[] = "🧜🏽‍♀️"; + inline constexpr const char mermaid_tone4[] = "🧜🏾‍♀️"; + inline constexpr const char mermaid_medium_dark_skin_tone[] = "🧜🏾‍♀️"; + inline constexpr const char mermaid_tone5[] = "🧜🏿‍♀️"; + inline constexpr const char mermaid_dark_skin_tone[] = "🧜🏿‍♀️"; + inline constexpr const char merman[] = "🧜‍♂️"; + inline constexpr const char merman_tone1[] = "🧜🏻‍♂️"; + inline constexpr const char merman_light_skin_tone[] = "🧜🏻‍♂️"; + inline constexpr const char merman_tone2[] = "🧜🏼‍♂️"; + inline constexpr const char merman_medium_light_skin_tone[] = "🧜🏼‍♂️"; + inline constexpr const char merman_tone3[] = "🧜🏽‍♂️"; + inline constexpr const char merman_medium_skin_tone[] = "🧜🏽‍♂️"; + inline constexpr const char merman_tone4[] = "🧜🏾‍♂️"; + inline constexpr const char merman_medium_dark_skin_tone[] = "🧜🏾‍♂️"; + inline constexpr const char merman_tone5[] = "🧜🏿‍♂️"; + inline constexpr const char merman_dark_skin_tone[] = "🧜🏿‍♂️"; + inline constexpr const char fairy[] = "🧚"; + inline constexpr const char fairy_tone1[] = "🧚🏻"; + inline constexpr const char fairy_light_skin_tone[] = "🧚🏻"; + inline constexpr const char fairy_tone2[] = "🧚🏼"; + inline constexpr const char fairy_medium_light_skin_tone[] = "🧚🏼"; + inline constexpr const char fairy_tone3[] = "🧚🏽"; + inline constexpr const char fairy_medium_skin_tone[] = "🧚🏽"; + inline constexpr const char fairy_tone4[] = "🧚🏾"; + inline constexpr const char fairy_medium_dark_skin_tone[] = "🧚🏾"; + inline constexpr const char fairy_tone5[] = "🧚🏿"; + inline constexpr const char fairy_dark_skin_tone[] = "🧚🏿"; + inline constexpr const char woman_fairy[] = "🧚‍♀️"; + inline constexpr const char woman_fairy_tone1[] = "🧚🏻‍♀️"; + inline constexpr const char woman_fairy_light_skin_tone[] = "🧚🏻‍♀️"; + inline constexpr const char woman_fairy_tone2[] = "🧚🏼‍♀️"; + inline constexpr const char woman_fairy_medium_light_skin_tone[] = "🧚🏼‍♀️"; + inline constexpr const char woman_fairy_tone3[] = "🧚🏽‍♀️"; + inline constexpr const char woman_fairy_medium_skin_tone[] = "🧚🏽‍♀️"; + inline constexpr const char woman_fairy_tone4[] = "🧚🏾‍♀️"; + inline constexpr const char woman_fairy_medium_dark_skin_tone[] = "🧚🏾‍♀️"; + inline constexpr const char woman_fairy_tone5[] = "🧚🏿‍♀️"; + inline constexpr const char woman_fairy_dark_skin_tone[] = "🧚🏿‍♀️"; + inline constexpr const char man_fairy[] = "🧚‍♂️"; + inline constexpr const char man_fairy_tone1[] = "🧚🏻‍♂️"; + inline constexpr const char man_fairy_light_skin_tone[] = "🧚🏻‍♂️"; + inline constexpr const char man_fairy_tone2[] = "🧚🏼‍♂️"; + inline constexpr const char man_fairy_medium_light_skin_tone[] = "🧚🏼‍♂️"; + inline constexpr const char man_fairy_tone3[] = "🧚🏽‍♂️"; + inline constexpr const char man_fairy_medium_skin_tone[] = "🧚🏽‍♂️"; + inline constexpr const char man_fairy_tone4[] = "🧚🏾‍♂️"; + inline constexpr const char man_fairy_medium_dark_skin_tone[] = "🧚🏾‍♂️"; + inline constexpr const char man_fairy_tone5[] = "🧚🏿‍♂️"; + inline constexpr const char man_fairy_dark_skin_tone[] = "🧚🏿‍♂️"; + inline constexpr const char angel[] = "👼"; + inline constexpr const char baby_angel[] = "👼"; + inline constexpr const char angel_tone1[] = "👼🏻"; + inline constexpr const char angel_tone2[] = "👼🏼"; + inline constexpr const char angel_tone3[] = "👼🏽"; + inline constexpr const char angel_tone4[] = "👼🏾"; + inline constexpr const char angel_tone5[] = "👼🏿"; + inline constexpr const char pregnant_person[] = "🫄"; + inline constexpr const char pregnant_person_tone1[] = "🫄🏻"; + inline constexpr const char pregnant_person_light_skin_tone[] = "🫄🏻"; + inline constexpr const char pregnant_person_tone2[] = "🫄🏼"; + inline constexpr const char pregnant_person_medium_light_skin_tone[] = "🫄🏼"; + inline constexpr const char pregnant_person_tone3[] = "🫄🏽"; + inline constexpr const char pregnant_person_medium_skin_tone[] = "🫄🏽"; + inline constexpr const char pregnant_person_tone4[] = "🫄🏾"; + inline constexpr const char pregnant_person_medium_dark_skin_tone[] = "🫄🏾"; + inline constexpr const char pregnant_person_tone5[] = "🫄🏿"; + inline constexpr const char pregnant_person_dark_skin_tone[] = "🫄🏿"; + inline constexpr const char pregnant_woman[] = "🤰"; + inline constexpr const char expecting_woman[] = "🤰"; + inline constexpr const char pregnant_woman_tone1[] = "🤰🏻"; + inline constexpr const char expecting_woman_tone1[] = "🤰🏻"; + inline constexpr const char pregnant_woman_tone2[] = "🤰🏼"; + inline constexpr const char expecting_woman_tone2[] = "🤰🏼"; + inline constexpr const char pregnant_woman_tone3[] = "🤰🏽"; + inline constexpr const char expecting_woman_tone3[] = "🤰🏽"; + inline constexpr const char pregnant_woman_tone4[] = "🤰🏾"; + inline constexpr const char expecting_woman_tone4[] = "🤰🏾"; + inline constexpr const char pregnant_woman_tone5[] = "🤰🏿"; + inline constexpr const char expecting_woman_tone5[] = "🤰🏿"; + inline constexpr const char pregnant_man[] = "🫃"; + inline constexpr const char pregnant_man_tone1[] = "🫃🏻"; + inline constexpr const char pregnant_man_light_skin_tone[] = "🫃🏻"; + inline constexpr const char pregnant_man_tone2[] = "🫃🏼"; + inline constexpr const char pregnant_man_medium_light_skin_tone[] = "🫃🏼"; + inline constexpr const char pregnant_man_tone3[] = "🫃🏽"; + inline constexpr const char pregnant_man_medium_skin_tone[] = "🫃🏽"; + inline constexpr const char pregnant_man_tone4[] = "🫃🏾"; + inline constexpr const char pregnant_man_medium_dark_skin_tone[] = "🫃🏾"; + inline constexpr const char pregnant_man_tone5[] = "🫃🏿"; + inline constexpr const char pregnant_man_dark_skin_tone[] = "🫃🏿"; + inline constexpr const char breast_feeding[] = "🤱"; + inline constexpr const char breast_feeding_tone1[] = "🤱🏻"; + inline constexpr const char breast_feeding_light_skin_tone[] = "🤱🏻"; + inline constexpr const char breast_feeding_tone2[] = "🤱🏼"; + inline constexpr const char breast_feeding_medium_light_skin_tone[] = "🤱🏼"; + inline constexpr const char breast_feeding_tone3[] = "🤱🏽"; + inline constexpr const char breast_feeding_medium_skin_tone[] = "🤱🏽"; + inline constexpr const char breast_feeding_tone4[] = "🤱🏾"; + inline constexpr const char breast_feeding_medium_dark_skin_tone[] = "🤱🏾"; + inline constexpr const char breast_feeding_tone5[] = "🤱🏿"; + inline constexpr const char breast_feeding_dark_skin_tone[] = "🤱🏿"; + inline constexpr const char person_feeding_baby[] = "🧑‍🍼"; + inline constexpr const char person_feeding_baby_tone1[] = "🧑🏻‍🍼"; + inline constexpr const char person_feeding_baby_light_skin_tone[] = "🧑🏻‍🍼"; + inline constexpr const char person_feeding_baby_tone2[] = "🧑🏼‍🍼"; + inline constexpr const char person_feeding_baby_medium_light_skin_tone[] = "🧑🏼‍🍼"; + inline constexpr const char person_feeding_baby_tone3[] = "🧑🏽‍🍼"; + inline constexpr const char person_feeding_baby_medium_skin_tone[] = "🧑🏽‍🍼"; + inline constexpr const char person_feeding_baby_tone4[] = "🧑🏾‍🍼"; + inline constexpr const char person_feeding_baby_medium_dark_skin_tone[] = "🧑🏾‍🍼"; + inline constexpr const char person_feeding_baby_tone5[] = "🧑🏿‍🍼"; + inline constexpr const char person_feeding_baby_dark_skin_tone[] = "🧑🏿‍🍼"; + inline constexpr const char woman_feeding_baby[] = "👩‍🍼"; + inline constexpr const char woman_feeding_baby_tone1[] = "👩🏻‍🍼"; + inline constexpr const char woman_feeding_baby_light_skin_tone[] = "👩🏻‍🍼"; + inline constexpr const char woman_feeding_baby_tone2[] = "👩🏼‍🍼"; + inline constexpr const char woman_feeding_baby_medium_light_skin_tone[] = "👩🏼‍🍼"; + inline constexpr const char woman_feeding_baby_tone3[] = "👩🏽‍🍼"; + inline constexpr const char woman_feeding_baby_medium_skin_tone[] = "👩🏽‍🍼"; + inline constexpr const char woman_feeding_baby_tone4[] = "👩🏾‍🍼"; + inline constexpr const char woman_feeding_baby_medium_dark_skin_tone[] = "👩🏾‍🍼"; + inline constexpr const char woman_feeding_baby_tone5[] = "👩🏿‍🍼"; + inline constexpr const char woman_feeding_baby_dark_skin_tone[] = "👩🏿‍🍼"; + inline constexpr const char man_feeding_baby[] = "👨‍🍼"; + inline constexpr const char man_feeding_baby_tone1[] = "👨🏻‍🍼"; + inline constexpr const char man_feeding_baby_light_skin_tone[] = "👨🏻‍🍼"; + inline constexpr const char man_feeding_baby_tone2[] = "👨🏼‍🍼"; + inline constexpr const char man_feeding_baby_medium_light_skin_tone[] = "👨🏼‍🍼"; + inline constexpr const char man_feeding_baby_tone3[] = "👨🏽‍🍼"; + inline constexpr const char man_feeding_baby_medium_skin_tone[] = "👨🏽‍🍼"; + inline constexpr const char man_feeding_baby_tone4[] = "👨🏾‍🍼"; + inline constexpr const char man_feeding_baby_medium_dark_skin_tone[] = "👨🏾‍🍼"; + inline constexpr const char man_feeding_baby_tone5[] = "👨🏿‍🍼"; + inline constexpr const char man_feeding_baby_dark_skin_tone[] = "👨🏿‍🍼"; + inline constexpr const char person_bowing[] = "🙇"; + inline constexpr const char bow[] = "🙇"; + inline constexpr const char person_bowing_tone1[] = "🙇🏻"; + inline constexpr const char bow_tone1[] = "🙇🏻"; + inline constexpr const char person_bowing_tone2[] = "🙇🏼"; + inline constexpr const char bow_tone2[] = "🙇🏼"; + inline constexpr const char person_bowing_tone3[] = "🙇🏽"; + inline constexpr const char bow_tone3[] = "🙇🏽"; + inline constexpr const char person_bowing_tone4[] = "🙇🏾"; + inline constexpr const char bow_tone4[] = "🙇🏾"; + inline constexpr const char person_bowing_tone5[] = "🙇🏿"; + inline constexpr const char bow_tone5[] = "🙇🏿"; + inline constexpr const char woman_bowing[] = "🙇‍♀️"; + inline constexpr const char woman_bowing_tone1[] = "🙇🏻‍♀️"; + inline constexpr const char woman_bowing_light_skin_tone[] = "🙇🏻‍♀️"; + inline constexpr const char woman_bowing_tone2[] = "🙇🏼‍♀️"; + inline constexpr const char woman_bowing_medium_light_skin_tone[] = "🙇🏼‍♀️"; + inline constexpr const char woman_bowing_tone3[] = "🙇🏽‍♀️"; + inline constexpr const char woman_bowing_medium_skin_tone[] = "🙇🏽‍♀️"; + inline constexpr const char woman_bowing_tone4[] = "🙇🏾‍♀️"; + inline constexpr const char woman_bowing_medium_dark_skin_tone[] = "🙇🏾‍♀️"; + inline constexpr const char woman_bowing_tone5[] = "🙇🏿‍♀️"; + inline constexpr const char woman_bowing_dark_skin_tone[] = "🙇🏿‍♀️"; + inline constexpr const char man_bowing[] = "🙇‍♂️"; + inline constexpr const char man_bowing_tone1[] = "🙇🏻‍♂️"; + inline constexpr const char man_bowing_light_skin_tone[] = "🙇🏻‍♂️"; + inline constexpr const char man_bowing_tone2[] = "🙇🏼‍♂️"; + inline constexpr const char man_bowing_medium_light_skin_tone[] = "🙇🏼‍♂️"; + inline constexpr const char man_bowing_tone3[] = "🙇🏽‍♂️"; + inline constexpr const char man_bowing_medium_skin_tone[] = "🙇🏽‍♂️"; + inline constexpr const char man_bowing_tone4[] = "🙇🏾‍♂️"; + inline constexpr const char man_bowing_medium_dark_skin_tone[] = "🙇🏾‍♂️"; + inline constexpr const char man_bowing_tone5[] = "🙇🏿‍♂️"; + inline constexpr const char man_bowing_dark_skin_tone[] = "🙇🏿‍♂️"; + inline constexpr const char person_tipping_hand[] = "💁"; + inline constexpr const char information_desk_person[] = "💁"; + inline constexpr const char person_tipping_hand_tone1[] = "💁🏻"; + inline constexpr const char information_desk_person_tone1[] = "💁🏻"; + inline constexpr const char person_tipping_hand_tone2[] = "💁🏼"; + inline constexpr const char information_desk_person_tone2[] = "💁🏼"; + inline constexpr const char person_tipping_hand_tone3[] = "💁🏽"; + inline constexpr const char information_desk_person_tone3[] = "💁🏽"; + inline constexpr const char person_tipping_hand_tone4[] = "💁🏾"; + inline constexpr const char information_desk_person_tone4[] = "💁🏾"; + inline constexpr const char person_tipping_hand_tone5[] = "💁🏿"; + inline constexpr const char information_desk_person_tone5[] = "💁🏿"; + inline constexpr const char woman_tipping_hand[] = "💁‍♀️"; + inline constexpr const char woman_tipping_hand_tone1[] = "💁🏻‍♀️"; + inline constexpr const char woman_tipping_hand_light_skin_tone[] = "💁🏻‍♀️"; + inline constexpr const char woman_tipping_hand_tone2[] = "💁🏼‍♀️"; + inline constexpr const char woman_tipping_hand_medium_light_skin_tone[] = "💁🏼‍♀️"; + inline constexpr const char woman_tipping_hand_tone3[] = "💁🏽‍♀️"; + inline constexpr const char woman_tipping_hand_medium_skin_tone[] = "💁🏽‍♀️"; + inline constexpr const char woman_tipping_hand_tone4[] = "💁🏾‍♀️"; + inline constexpr const char woman_tipping_hand_medium_dark_skin_tone[] = "💁🏾‍♀️"; + inline constexpr const char woman_tipping_hand_tone5[] = "💁🏿‍♀️"; + inline constexpr const char woman_tipping_hand_dark_skin_tone[] = "💁🏿‍♀️"; + inline constexpr const char man_tipping_hand[] = "💁‍♂️"; + inline constexpr const char man_tipping_hand_tone1[] = "💁🏻‍♂️"; + inline constexpr const char man_tipping_hand_light_skin_tone[] = "💁🏻‍♂️"; + inline constexpr const char man_tipping_hand_tone2[] = "💁🏼‍♂️"; + inline constexpr const char man_tipping_hand_medium_light_skin_tone[] = "💁🏼‍♂️"; + inline constexpr const char man_tipping_hand_tone3[] = "💁🏽‍♂️"; + inline constexpr const char man_tipping_hand_medium_skin_tone[] = "💁🏽‍♂️"; + inline constexpr const char man_tipping_hand_tone4[] = "💁🏾‍♂️"; + inline constexpr const char man_tipping_hand_medium_dark_skin_tone[] = "💁🏾‍♂️"; + inline constexpr const char man_tipping_hand_tone5[] = "💁🏿‍♂️"; + inline constexpr const char man_tipping_hand_dark_skin_tone[] = "💁🏿‍♂️"; + inline constexpr const char person_gesturing_no[] = "🙅"; + inline constexpr const char no_good[] = "🙅"; + inline constexpr const char person_gesturing_no_tone1[] = "🙅🏻"; + inline constexpr const char no_good_tone1[] = "🙅🏻"; + inline constexpr const char person_gesturing_no_tone2[] = "🙅🏼"; + inline constexpr const char no_good_tone2[] = "🙅🏼"; + inline constexpr const char person_gesturing_no_tone3[] = "🙅🏽"; + inline constexpr const char no_good_tone3[] = "🙅🏽"; + inline constexpr const char person_gesturing_no_tone4[] = "🙅🏾"; + inline constexpr const char no_good_tone4[] = "🙅🏾"; + inline constexpr const char person_gesturing_no_tone5[] = "🙅🏿"; + inline constexpr const char no_good_tone5[] = "🙅🏿"; + inline constexpr const char woman_gesturing_no[] = "🙅‍♀️"; + inline constexpr const char woman_gesturing_no_tone1[] = "🙅🏻‍♀️"; + inline constexpr const char woman_gesturing_no_light_skin_tone[] = "🙅🏻‍♀️"; + inline constexpr const char woman_gesturing_no_tone2[] = "🙅🏼‍♀️"; + inline constexpr const char woman_gesturing_no_medium_light_skin_tone[] = "🙅🏼‍♀️"; + inline constexpr const char woman_gesturing_no_tone3[] = "🙅🏽‍♀️"; + inline constexpr const char woman_gesturing_no_medium_skin_tone[] = "🙅🏽‍♀️"; + inline constexpr const char woman_gesturing_no_tone4[] = "🙅🏾‍♀️"; + inline constexpr const char woman_gesturing_no_medium_dark_skin_tone[] = "🙅🏾‍♀️"; + inline constexpr const char woman_gesturing_no_tone5[] = "🙅🏿‍♀️"; + inline constexpr const char woman_gesturing_no_dark_skin_tone[] = "🙅🏿‍♀️"; + inline constexpr const char man_gesturing_no[] = "🙅‍♂️"; + inline constexpr const char man_gesturing_no_tone1[] = "🙅🏻‍♂️"; + inline constexpr const char man_gesturing_no_light_skin_tone[] = "🙅🏻‍♂️"; + inline constexpr const char man_gesturing_no_tone2[] = "🙅🏼‍♂️"; + inline constexpr const char man_gesturing_no_medium_light_skin_tone[] = "🙅🏼‍♂️"; + inline constexpr const char man_gesturing_no_tone3[] = "🙅🏽‍♂️"; + inline constexpr const char man_gesturing_no_medium_skin_tone[] = "🙅🏽‍♂️"; + inline constexpr const char man_gesturing_no_tone4[] = "🙅🏾‍♂️"; + inline constexpr const char man_gesturing_no_medium_dark_skin_tone[] = "🙅🏾‍♂️"; + inline constexpr const char man_gesturing_no_tone5[] = "🙅🏿‍♂️"; + inline constexpr const char man_gesturing_no_dark_skin_tone[] = "🙅🏿‍♂️"; + inline constexpr const char person_gesturing_ok[] = "🙆"; + inline constexpr const char person_gesturing_ok_tone1[] = "🙆🏻"; + inline constexpr const char person_gesturing_ok_tone2[] = "🙆🏼"; + inline constexpr const char person_gesturing_ok_tone3[] = "🙆🏽"; + inline constexpr const char person_gesturing_ok_tone4[] = "🙆🏾"; + inline constexpr const char person_gesturing_ok_tone5[] = "🙆🏿"; + inline constexpr const char woman_gesturing_ok[] = "🙆‍♀️"; + inline constexpr const char woman_gesturing_ok_tone1[] = "🙆🏻‍♀️"; + inline constexpr const char woman_gesturing_ok_light_skin_tone[] = "🙆🏻‍♀️"; + inline constexpr const char woman_gesturing_ok_tone2[] = "🙆🏼‍♀️"; + inline constexpr const char woman_gesturing_ok_medium_light_skin_tone[] = "🙆🏼‍♀️"; + inline constexpr const char woman_gesturing_ok_tone3[] = "🙆🏽‍♀️"; + inline constexpr const char woman_gesturing_ok_medium_skin_tone[] = "🙆🏽‍♀️"; + inline constexpr const char woman_gesturing_ok_tone4[] = "🙆🏾‍♀️"; + inline constexpr const char woman_gesturing_ok_medium_dark_skin_tone[] = "🙆🏾‍♀️"; + inline constexpr const char woman_gesturing_ok_tone5[] = "🙆🏿‍♀️"; + inline constexpr const char woman_gesturing_ok_dark_skin_tone[] = "🙆🏿‍♀️"; + inline constexpr const char man_gesturing_ok[] = "🙆‍♂️"; + inline constexpr const char man_gesturing_ok_tone1[] = "🙆🏻‍♂️"; + inline constexpr const char man_gesturing_ok_light_skin_tone[] = "🙆🏻‍♂️"; + inline constexpr const char man_gesturing_ok_tone2[] = "🙆🏼‍♂️"; + inline constexpr const char man_gesturing_ok_medium_light_skin_tone[] = "🙆🏼‍♂️"; + inline constexpr const char man_gesturing_ok_tone3[] = "🙆🏽‍♂️"; + inline constexpr const char man_gesturing_ok_medium_skin_tone[] = "🙆🏽‍♂️"; + inline constexpr const char man_gesturing_ok_tone4[] = "🙆🏾‍♂️"; + inline constexpr const char man_gesturing_ok_medium_dark_skin_tone[] = "🙆🏾‍♂️"; + inline constexpr const char man_gesturing_ok_tone5[] = "🙆🏿‍♂️"; + inline constexpr const char man_gesturing_ok_dark_skin_tone[] = "🙆🏿‍♂️"; + inline constexpr const char person_raising_hand[] = "🙋"; + inline constexpr const char raising_hand[] = "🙋"; + inline constexpr const char person_raising_hand_tone1[] = "🙋🏻"; + inline constexpr const char raising_hand_tone1[] = "🙋🏻"; + inline constexpr const char person_raising_hand_tone2[] = "🙋🏼"; + inline constexpr const char raising_hand_tone2[] = "🙋🏼"; + inline constexpr const char person_raising_hand_tone3[] = "🙋🏽"; + inline constexpr const char raising_hand_tone3[] = "🙋🏽"; + inline constexpr const char person_raising_hand_tone4[] = "🙋🏾"; + inline constexpr const char raising_hand_tone4[] = "🙋🏾"; + inline constexpr const char person_raising_hand_tone5[] = "🙋🏿"; + inline constexpr const char raising_hand_tone5[] = "🙋🏿"; + inline constexpr const char woman_raising_hand[] = "🙋‍♀️"; + inline constexpr const char woman_raising_hand_tone1[] = "🙋🏻‍♀️"; + inline constexpr const char woman_raising_hand_light_skin_tone[] = "🙋🏻‍♀️"; + inline constexpr const char woman_raising_hand_tone2[] = "🙋🏼‍♀️"; + inline constexpr const char woman_raising_hand_medium_light_skin_tone[] = "🙋🏼‍♀️"; + inline constexpr const char woman_raising_hand_tone3[] = "🙋🏽‍♀️"; + inline constexpr const char woman_raising_hand_medium_skin_tone[] = "🙋🏽‍♀️"; + inline constexpr const char woman_raising_hand_tone4[] = "🙋🏾‍♀️"; + inline constexpr const char woman_raising_hand_medium_dark_skin_tone[] = "🙋🏾‍♀️"; + inline constexpr const char woman_raising_hand_tone5[] = "🙋🏿‍♀️"; + inline constexpr const char woman_raising_hand_dark_skin_tone[] = "🙋🏿‍♀️"; + inline constexpr const char man_raising_hand[] = "🙋‍♂️"; + inline constexpr const char man_raising_hand_tone1[] = "🙋🏻‍♂️"; + inline constexpr const char man_raising_hand_light_skin_tone[] = "🙋🏻‍♂️"; + inline constexpr const char man_raising_hand_tone2[] = "🙋🏼‍♂️"; + inline constexpr const char man_raising_hand_medium_light_skin_tone[] = "🙋🏼‍♂️"; + inline constexpr const char man_raising_hand_tone3[] = "🙋🏽‍♂️"; + inline constexpr const char man_raising_hand_medium_skin_tone[] = "🙋🏽‍♂️"; + inline constexpr const char man_raising_hand_tone4[] = "🙋🏾‍♂️"; + inline constexpr const char man_raising_hand_medium_dark_skin_tone[] = "🙋🏾‍♂️"; + inline constexpr const char man_raising_hand_tone5[] = "🙋🏿‍♂️"; + inline constexpr const char man_raising_hand_dark_skin_tone[] = "🙋🏿‍♂️"; + inline constexpr const char deaf_person[] = "🧏"; + inline constexpr const char deaf_person_tone1[] = "🧏🏻"; + inline constexpr const char deaf_person_light_skin_tone[] = "🧏🏻"; + inline constexpr const char deaf_person_tone2[] = "🧏🏼"; + inline constexpr const char deaf_person_medium_light_skin_tone[] = "🧏🏼"; + inline constexpr const char deaf_person_tone3[] = "🧏🏽"; + inline constexpr const char deaf_person_medium_skin_tone[] = "🧏🏽"; + inline constexpr const char deaf_person_tone4[] = "🧏🏾"; + inline constexpr const char deaf_person_medium_dark_skin_tone[] = "🧏🏾"; + inline constexpr const char deaf_person_tone5[] = "🧏🏿"; + inline constexpr const char deaf_person_dark_skin_tone[] = "🧏🏿"; + inline constexpr const char deaf_woman[] = "🧏‍♀️"; + inline constexpr const char deaf_woman_tone1[] = "🧏🏻‍♀️"; + inline constexpr const char deaf_woman_light_skin_tone[] = "🧏🏻‍♀️"; + inline constexpr const char deaf_woman_tone2[] = "🧏🏼‍♀️"; + inline constexpr const char deaf_woman_medium_light_skin_tone[] = "🧏🏼‍♀️"; + inline constexpr const char deaf_woman_tone3[] = "🧏🏽‍♀️"; + inline constexpr const char deaf_woman_medium_skin_tone[] = "🧏🏽‍♀️"; + inline constexpr const char deaf_woman_tone4[] = "🧏🏾‍♀️"; + inline constexpr const char deaf_woman_medium_dark_skin_tone[] = "🧏🏾‍♀️"; + inline constexpr const char deaf_woman_tone5[] = "🧏🏿‍♀️"; + inline constexpr const char deaf_woman_dark_skin_tone[] = "🧏🏿‍♀️"; + inline constexpr const char deaf_man[] = "🧏‍♂️"; + inline constexpr const char deaf_man_tone1[] = "🧏🏻‍♂️"; + inline constexpr const char deaf_man_light_skin_tone[] = "🧏🏻‍♂️"; + inline constexpr const char deaf_man_tone2[] = "🧏🏼‍♂️"; + inline constexpr const char deaf_man_medium_light_skin_tone[] = "🧏🏼‍♂️"; + inline constexpr const char deaf_man_tone3[] = "🧏🏽‍♂️"; + inline constexpr const char deaf_man_medium_skin_tone[] = "🧏🏽‍♂️"; + inline constexpr const char deaf_man_tone4[] = "🧏🏾‍♂️"; + inline constexpr const char deaf_man_medium_dark_skin_tone[] = "🧏🏾‍♂️"; + inline constexpr const char deaf_man_tone5[] = "🧏🏿‍♂️"; + inline constexpr const char deaf_man_dark_skin_tone[] = "🧏🏿‍♂️"; + inline constexpr const char person_facepalming[] = "🤦"; + inline constexpr const char face_palm[] = "🤦"; + inline constexpr const char facepalm[] = "🤦"; + inline constexpr const char person_facepalming_tone1[] = "🤦🏻"; + inline constexpr const char face_palm_tone1[] = "🤦🏻"; + inline constexpr const char facepalm_tone1[] = "🤦🏻"; + inline constexpr const char person_facepalming_tone2[] = "🤦🏼"; + inline constexpr const char face_palm_tone2[] = "🤦🏼"; + inline constexpr const char facepalm_tone2[] = "🤦🏼"; + inline constexpr const char person_facepalming_tone3[] = "🤦🏽"; + inline constexpr const char face_palm_tone3[] = "🤦🏽"; + inline constexpr const char facepalm_tone3[] = "🤦🏽"; + inline constexpr const char person_facepalming_tone4[] = "🤦🏾"; + inline constexpr const char face_palm_tone4[] = "🤦🏾"; + inline constexpr const char facepalm_tone4[] = "🤦🏾"; + inline constexpr const char person_facepalming_tone5[] = "🤦🏿"; + inline constexpr const char face_palm_tone5[] = "🤦🏿"; + inline constexpr const char facepalm_tone5[] = "🤦🏿"; + inline constexpr const char woman_facepalming[] = "🤦‍♀️"; + inline constexpr const char woman_facepalming_tone1[] = "🤦🏻‍♀️"; + inline constexpr const char woman_facepalming_light_skin_tone[] = "🤦🏻‍♀️"; + inline constexpr const char woman_facepalming_tone2[] = "🤦🏼‍♀️"; + inline constexpr const char woman_facepalming_medium_light_skin_tone[] = "🤦🏼‍♀️"; + inline constexpr const char woman_facepalming_tone3[] = "🤦🏽‍♀️"; + inline constexpr const char woman_facepalming_medium_skin_tone[] = "🤦🏽‍♀️"; + inline constexpr const char woman_facepalming_tone4[] = "🤦🏾‍♀️"; + inline constexpr const char woman_facepalming_medium_dark_skin_tone[] = "🤦🏾‍♀️"; + inline constexpr const char woman_facepalming_tone5[] = "🤦🏿‍♀️"; + inline constexpr const char woman_facepalming_dark_skin_tone[] = "🤦🏿‍♀️"; + inline constexpr const char man_facepalming[] = "🤦‍♂️"; + inline constexpr const char man_facepalming_tone1[] = "🤦🏻‍♂️"; + inline constexpr const char man_facepalming_light_skin_tone[] = "🤦🏻‍♂️"; + inline constexpr const char man_facepalming_tone2[] = "🤦🏼‍♂️"; + inline constexpr const char man_facepalming_medium_light_skin_tone[] = "🤦🏼‍♂️"; + inline constexpr const char man_facepalming_tone3[] = "🤦🏽‍♂️"; + inline constexpr const char man_facepalming_medium_skin_tone[] = "🤦🏽‍♂️"; + inline constexpr const char man_facepalming_tone4[] = "🤦🏾‍♂️"; + inline constexpr const char man_facepalming_medium_dark_skin_tone[] = "🤦🏾‍♂️"; + inline constexpr const char man_facepalming_tone5[] = "🤦🏿‍♂️"; + inline constexpr const char man_facepalming_dark_skin_tone[] = "🤦🏿‍♂️"; + inline constexpr const char person_shrugging[] = "🤷"; + inline constexpr const char shrug[] = "🤷"; + inline constexpr const char person_shrugging_tone1[] = "🤷🏻"; + inline constexpr const char shrug_tone1[] = "🤷🏻"; + inline constexpr const char person_shrugging_tone2[] = "🤷🏼"; + inline constexpr const char shrug_tone2[] = "🤷🏼"; + inline constexpr const char person_shrugging_tone3[] = "🤷🏽"; + inline constexpr const char shrug_tone3[] = "🤷🏽"; + inline constexpr const char person_shrugging_tone4[] = "🤷🏾"; + inline constexpr const char shrug_tone4[] = "🤷🏾"; + inline constexpr const char person_shrugging_tone5[] = "🤷🏿"; + inline constexpr const char shrug_tone5[] = "🤷🏿"; + inline constexpr const char woman_shrugging[] = "🤷‍♀️"; + inline constexpr const char woman_shrugging_tone1[] = "🤷🏻‍♀️"; + inline constexpr const char woman_shrugging_light_skin_tone[] = "🤷🏻‍♀️"; + inline constexpr const char woman_shrugging_tone2[] = "🤷🏼‍♀️"; + inline constexpr const char woman_shrugging_medium_light_skin_tone[] = "🤷🏼‍♀️"; + inline constexpr const char woman_shrugging_tone3[] = "🤷🏽‍♀️"; + inline constexpr const char woman_shrugging_medium_skin_tone[] = "🤷🏽‍♀️"; + inline constexpr const char woman_shrugging_tone4[] = "🤷🏾‍♀️"; + inline constexpr const char woman_shrugging_medium_dark_skin_tone[] = "🤷🏾‍♀️"; + inline constexpr const char woman_shrugging_tone5[] = "🤷🏿‍♀️"; + inline constexpr const char woman_shrugging_dark_skin_tone[] = "🤷🏿‍♀️"; + inline constexpr const char man_shrugging[] = "🤷‍♂️"; + inline constexpr const char man_shrugging_tone1[] = "🤷🏻‍♂️"; + inline constexpr const char man_shrugging_light_skin_tone[] = "🤷🏻‍♂️"; + inline constexpr const char man_shrugging_tone2[] = "🤷🏼‍♂️"; + inline constexpr const char man_shrugging_medium_light_skin_tone[] = "🤷🏼‍♂️"; + inline constexpr const char man_shrugging_tone3[] = "🤷🏽‍♂️"; + inline constexpr const char man_shrugging_medium_skin_tone[] = "🤷🏽‍♂️"; + inline constexpr const char man_shrugging_tone4[] = "🤷🏾‍♂️"; + inline constexpr const char man_shrugging_medium_dark_skin_tone[] = "🤷🏾‍♂️"; + inline constexpr const char man_shrugging_tone5[] = "🤷🏿‍♂️"; + inline constexpr const char man_shrugging_dark_skin_tone[] = "🤷🏿‍♂️"; + inline constexpr const char person_pouting[] = "🙎"; + inline constexpr const char person_with_pouting_face[] = "🙎"; + inline constexpr const char person_pouting_tone1[] = "🙎🏻"; + inline constexpr const char person_with_pouting_face_tone1[] = "🙎🏻"; + inline constexpr const char person_pouting_tone2[] = "🙎🏼"; + inline constexpr const char person_with_pouting_face_tone2[] = "🙎🏼"; + inline constexpr const char person_pouting_tone3[] = "🙎🏽"; + inline constexpr const char person_with_pouting_face_tone3[] = "🙎🏽"; + inline constexpr const char person_pouting_tone4[] = "🙎🏾"; + inline constexpr const char person_with_pouting_face_tone4[] = "🙎🏾"; + inline constexpr const char person_pouting_tone5[] = "🙎🏿"; + inline constexpr const char person_with_pouting_face_tone5[] = "🙎🏿"; + inline constexpr const char woman_pouting[] = "🙎‍♀️"; + inline constexpr const char woman_pouting_tone1[] = "🙎🏻‍♀️"; + inline constexpr const char woman_pouting_light_skin_tone[] = "🙎🏻‍♀️"; + inline constexpr const char woman_pouting_tone2[] = "🙎🏼‍♀️"; + inline constexpr const char woman_pouting_medium_light_skin_tone[] = "🙎🏼‍♀️"; + inline constexpr const char woman_pouting_tone3[] = "🙎🏽‍♀️"; + inline constexpr const char woman_pouting_medium_skin_tone[] = "🙎🏽‍♀️"; + inline constexpr const char woman_pouting_tone4[] = "🙎🏾‍♀️"; + inline constexpr const char woman_pouting_medium_dark_skin_tone[] = "🙎🏾‍♀️"; + inline constexpr const char woman_pouting_tone5[] = "🙎🏿‍♀️"; + inline constexpr const char woman_pouting_dark_skin_tone[] = "🙎🏿‍♀️"; + inline constexpr const char man_pouting[] = "🙎‍♂️"; + inline constexpr const char man_pouting_tone1[] = "🙎🏻‍♂️"; + inline constexpr const char man_pouting_light_skin_tone[] = "🙎🏻‍♂️"; + inline constexpr const char man_pouting_tone2[] = "🙎🏼‍♂️"; + inline constexpr const char man_pouting_medium_light_skin_tone[] = "🙎🏼‍♂️"; + inline constexpr const char man_pouting_tone3[] = "🙎🏽‍♂️"; + inline constexpr const char man_pouting_medium_skin_tone[] = "🙎🏽‍♂️"; + inline constexpr const char man_pouting_tone4[] = "🙎🏾‍♂️"; + inline constexpr const char man_pouting_medium_dark_skin_tone[] = "🙎🏾‍♂️"; + inline constexpr const char man_pouting_tone5[] = "🙎🏿‍♂️"; + inline constexpr const char man_pouting_dark_skin_tone[] = "🙎🏿‍♂️"; + inline constexpr const char person_frowning[] = "🙍"; + inline constexpr const char person_frowning_tone1[] = "🙍🏻"; + inline constexpr const char person_frowning_tone2[] = "🙍🏼"; + inline constexpr const char person_frowning_tone3[] = "🙍🏽"; + inline constexpr const char person_frowning_tone4[] = "🙍🏾"; + inline constexpr const char person_frowning_tone5[] = "🙍🏿"; + inline constexpr const char woman_frowning[] = "🙍‍♀️"; + inline constexpr const char woman_frowning_tone1[] = "🙍🏻‍♀️"; + inline constexpr const char woman_frowning_light_skin_tone[] = "🙍🏻‍♀️"; + inline constexpr const char woman_frowning_tone2[] = "🙍🏼‍♀️"; + inline constexpr const char woman_frowning_medium_light_skin_tone[] = "🙍🏼‍♀️"; + inline constexpr const char woman_frowning_tone3[] = "🙍🏽‍♀️"; + inline constexpr const char woman_frowning_medium_skin_tone[] = "🙍🏽‍♀️"; + inline constexpr const char woman_frowning_tone4[] = "🙍🏾‍♀️"; + inline constexpr const char woman_frowning_medium_dark_skin_tone[] = "🙍🏾‍♀️"; + inline constexpr const char woman_frowning_tone5[] = "🙍🏿‍♀️"; + inline constexpr const char woman_frowning_dark_skin_tone[] = "🙍🏿‍♀️"; + inline constexpr const char man_frowning[] = "🙍‍♂️"; + inline constexpr const char man_frowning_tone1[] = "🙍🏻‍♂️"; + inline constexpr const char man_frowning_light_skin_tone[] = "🙍🏻‍♂️"; + inline constexpr const char man_frowning_tone2[] = "🙍🏼‍♂️"; + inline constexpr const char man_frowning_medium_light_skin_tone[] = "🙍🏼‍♂️"; + inline constexpr const char man_frowning_tone3[] = "🙍🏽‍♂️"; + inline constexpr const char man_frowning_medium_skin_tone[] = "🙍🏽‍♂️"; + inline constexpr const char man_frowning_tone4[] = "🙍🏾‍♂️"; + inline constexpr const char man_frowning_medium_dark_skin_tone[] = "🙍🏾‍♂️"; + inline constexpr const char man_frowning_tone5[] = "🙍🏿‍♂️"; + inline constexpr const char man_frowning_dark_skin_tone[] = "🙍🏿‍♂️"; + inline constexpr const char person_getting_haircut[] = "💇"; + inline constexpr const char haircut[] = "💇"; + inline constexpr const char person_getting_haircut_tone1[] = "💇🏻"; + inline constexpr const char haircut_tone1[] = "💇🏻"; + inline constexpr const char person_getting_haircut_tone2[] = "💇🏼"; + inline constexpr const char haircut_tone2[] = "💇🏼"; + inline constexpr const char person_getting_haircut_tone3[] = "💇🏽"; + inline constexpr const char haircut_tone3[] = "💇🏽"; + inline constexpr const char person_getting_haircut_tone4[] = "💇🏾"; + inline constexpr const char haircut_tone4[] = "💇🏾"; + inline constexpr const char person_getting_haircut_tone5[] = "💇🏿"; + inline constexpr const char haircut_tone5[] = "💇🏿"; + inline constexpr const char woman_getting_haircut[] = "💇‍♀️"; + inline constexpr const char woman_getting_haircut_tone1[] = "💇🏻‍♀️"; + inline constexpr const char woman_getting_haircut_light_skin_tone[] = "💇🏻‍♀️"; + inline constexpr const char woman_getting_haircut_tone2[] = "💇🏼‍♀️"; + inline constexpr const char woman_getting_haircut_medium_light_skin_tone[] = "💇🏼‍♀️"; + inline constexpr const char woman_getting_haircut_tone3[] = "💇🏽‍♀️"; + inline constexpr const char woman_getting_haircut_medium_skin_tone[] = "💇🏽‍♀️"; + inline constexpr const char woman_getting_haircut_tone4[] = "💇🏾‍♀️"; + inline constexpr const char woman_getting_haircut_medium_dark_skin_tone[] = "💇🏾‍♀️"; + inline constexpr const char woman_getting_haircut_tone5[] = "💇🏿‍♀️"; + inline constexpr const char woman_getting_haircut_dark_skin_tone[] = "💇🏿‍♀️"; + inline constexpr const char man_getting_haircut[] = "💇‍♂️"; + inline constexpr const char man_getting_haircut_tone1[] = "💇🏻‍♂️"; + inline constexpr const char man_getting_haircut_light_skin_tone[] = "💇🏻‍♂️"; + inline constexpr const char man_getting_haircut_tone2[] = "💇🏼‍♂️"; + inline constexpr const char man_getting_haircut_medium_light_skin_tone[] = "💇🏼‍♂️"; + inline constexpr const char man_getting_haircut_tone3[] = "💇🏽‍♂️"; + inline constexpr const char man_getting_haircut_medium_skin_tone[] = "💇🏽‍♂️"; + inline constexpr const char man_getting_haircut_tone4[] = "💇🏾‍♂️"; + inline constexpr const char man_getting_haircut_medium_dark_skin_tone[] = "💇🏾‍♂️"; + inline constexpr const char man_getting_haircut_tone5[] = "💇🏿‍♂️"; + inline constexpr const char man_getting_haircut_dark_skin_tone[] = "💇🏿‍♂️"; + inline constexpr const char person_getting_massage[] = "💆"; + inline constexpr const char massage[] = "💆"; + inline constexpr const char person_getting_massage_tone1[] = "💆🏻"; + inline constexpr const char massage_tone1[] = "💆🏻"; + inline constexpr const char person_getting_massage_tone2[] = "💆🏼"; + inline constexpr const char massage_tone2[] = "💆🏼"; + inline constexpr const char person_getting_massage_tone3[] = "💆🏽"; + inline constexpr const char massage_tone3[] = "💆🏽"; + inline constexpr const char person_getting_massage_tone4[] = "💆🏾"; + inline constexpr const char massage_tone4[] = "💆🏾"; + inline constexpr const char person_getting_massage_tone5[] = "💆🏿"; + inline constexpr const char massage_tone5[] = "💆🏿"; + inline constexpr const char woman_getting_face_massage[] = "💆‍♀️"; + inline constexpr const char woman_getting_face_massage_tone1[] = "💆🏻‍♀️"; + inline constexpr const char woman_getting_face_massage_light_skin_tone[] = "💆🏻‍♀️"; + inline constexpr const char woman_getting_face_massage_tone2[] = "💆🏼‍♀️"; + inline constexpr const char woman_getting_face_massage_medium_light_skin_tone[] = "💆🏼‍♀️"; + inline constexpr const char woman_getting_face_massage_tone3[] = "💆🏽‍♀️"; + inline constexpr const char woman_getting_face_massage_medium_skin_tone[] = "💆🏽‍♀️"; + inline constexpr const char woman_getting_face_massage_tone4[] = "💆🏾‍♀️"; + inline constexpr const char woman_getting_face_massage_medium_dark_skin_tone[] = "💆🏾‍♀️"; + inline constexpr const char woman_getting_face_massage_tone5[] = "💆🏿‍♀️"; + inline constexpr const char woman_getting_face_massage_dark_skin_tone[] = "💆🏿‍♀️"; + inline constexpr const char man_getting_face_massage[] = "💆‍♂️"; + inline constexpr const char man_getting_face_massage_tone1[] = "💆🏻‍♂️"; + inline constexpr const char man_getting_face_massage_light_skin_tone[] = "💆🏻‍♂️"; + inline constexpr const char man_getting_face_massage_tone2[] = "💆🏼‍♂️"; + inline constexpr const char man_getting_face_massage_medium_light_skin_tone[] = "💆🏼‍♂️"; + inline constexpr const char man_getting_face_massage_tone3[] = "💆🏽‍♂️"; + inline constexpr const char man_getting_face_massage_medium_skin_tone[] = "💆🏽‍♂️"; + inline constexpr const char man_getting_face_massage_tone4[] = "💆🏾‍♂️"; + inline constexpr const char man_getting_face_massage_medium_dark_skin_tone[] = "💆🏾‍♂️"; + inline constexpr const char man_getting_face_massage_tone5[] = "💆🏿‍♂️"; + inline constexpr const char man_getting_face_massage_dark_skin_tone[] = "💆🏿‍♂️"; + inline constexpr const char person_in_steamy_room[] = "🧖"; + inline constexpr const char person_in_steamy_room_tone1[] = "🧖🏻"; + inline constexpr const char person_in_steamy_room_light_skin_tone[] = "🧖🏻"; + inline constexpr const char person_in_steamy_room_tone2[] = "🧖🏼"; + inline constexpr const char person_in_steamy_room_medium_light_skin_tone[] = "🧖🏼"; + inline constexpr const char person_in_steamy_room_tone3[] = "🧖🏽"; + inline constexpr const char person_in_steamy_room_medium_skin_tone[] = "🧖🏽"; + inline constexpr const char person_in_steamy_room_tone4[] = "🧖🏾"; + inline constexpr const char person_in_steamy_room_medium_dark_skin_tone[] = "🧖🏾"; + inline constexpr const char person_in_steamy_room_tone5[] = "🧖🏿"; + inline constexpr const char person_in_steamy_room_dark_skin_tone[] = "🧖🏿"; + inline constexpr const char woman_in_steamy_room[] = "🧖‍♀️"; + inline constexpr const char woman_in_steamy_room_tone1[] = "🧖🏻‍♀️"; + inline constexpr const char woman_in_steamy_room_light_skin_tone[] = "🧖🏻‍♀️"; + inline constexpr const char woman_in_steamy_room_tone2[] = "🧖🏼‍♀️"; + inline constexpr const char woman_in_steamy_room_medium_light_skin_tone[] = "🧖🏼‍♀️"; + inline constexpr const char woman_in_steamy_room_tone3[] = "🧖🏽‍♀️"; + inline constexpr const char woman_in_steamy_room_medium_skin_tone[] = "🧖🏽‍♀️"; + inline constexpr const char woman_in_steamy_room_tone4[] = "🧖🏾‍♀️"; + inline constexpr const char woman_in_steamy_room_medium_dark_skin_tone[] = "🧖🏾‍♀️"; + inline constexpr const char woman_in_steamy_room_tone5[] = "🧖🏿‍♀️"; + inline constexpr const char woman_in_steamy_room_dark_skin_tone[] = "🧖🏿‍♀️"; + inline constexpr const char man_in_steamy_room[] = "🧖‍♂️"; + inline constexpr const char man_in_steamy_room_tone1[] = "🧖🏻‍♂️"; + inline constexpr const char man_in_steamy_room_light_skin_tone[] = "🧖🏻‍♂️"; + inline constexpr const char man_in_steamy_room_tone2[] = "🧖🏼‍♂️"; + inline constexpr const char man_in_steamy_room_medium_light_skin_tone[] = "🧖🏼‍♂️"; + inline constexpr const char man_in_steamy_room_tone3[] = "🧖🏽‍♂️"; + inline constexpr const char man_in_steamy_room_medium_skin_tone[] = "🧖🏽‍♂️"; + inline constexpr const char man_in_steamy_room_tone4[] = "🧖🏾‍♂️"; + inline constexpr const char man_in_steamy_room_medium_dark_skin_tone[] = "🧖🏾‍♂️"; + inline constexpr const char man_in_steamy_room_tone5[] = "🧖🏿‍♂️"; + inline constexpr const char man_in_steamy_room_dark_skin_tone[] = "🧖🏿‍♂️"; + inline constexpr const char nail_care[] = "💅"; + inline constexpr const char nail_polish[] = "💅"; + inline constexpr const char nail_care_tone1[] = "💅🏻"; + inline constexpr const char nail_care_tone2[] = "💅🏼"; + inline constexpr const char nail_care_tone3[] = "💅🏽"; + inline constexpr const char nail_care_tone4[] = "💅🏾"; + inline constexpr const char nail_care_tone5[] = "💅🏿"; + inline constexpr const char selfie[] = "🤳"; + inline constexpr const char selfie_tone1[] = "🤳🏻"; + inline constexpr const char selfie_tone2[] = "🤳🏼"; + inline constexpr const char selfie_tone3[] = "🤳🏽"; + inline constexpr const char selfie_tone4[] = "🤳🏾"; + inline constexpr const char selfie_tone5[] = "🤳🏿"; + inline constexpr const char dancer[] = "💃"; + inline constexpr const char woman_dancing[] = "💃"; + inline constexpr const char dancer_tone1[] = "💃🏻"; + inline constexpr const char dancer_tone2[] = "💃🏼"; + inline constexpr const char dancer_tone3[] = "💃🏽"; + inline constexpr const char dancer_tone4[] = "💃🏾"; + inline constexpr const char dancer_tone5[] = "💃🏿"; + inline constexpr const char man_dancing[] = "🕺"; + inline constexpr const char male_dancer[] = "🕺"; + inline constexpr const char man_dancing_tone1[] = "🕺🏻"; + inline constexpr const char male_dancer_tone1[] = "🕺🏻"; + inline constexpr const char man_dancing_tone2[] = "🕺🏼"; + inline constexpr const char male_dancer_tone2[] = "🕺🏼"; + inline constexpr const char man_dancing_tone3[] = "🕺🏽"; + inline constexpr const char male_dancer_tone3[] = "🕺🏽"; + inline constexpr const char man_dancing_tone5[] = "🕺🏿"; + inline constexpr const char male_dancer_tone5[] = "🕺🏿"; + inline constexpr const char man_dancing_tone4[] = "🕺🏾"; + inline constexpr const char male_dancer_tone4[] = "🕺🏾"; + inline constexpr const char people_with_bunny_ears_partying[] = "👯"; + inline constexpr const char dancers[] = "👯"; + inline constexpr const char women_with_bunny_ears_partying[] = "👯‍♀️"; + inline constexpr const char men_with_bunny_ears_partying[] = "👯‍♂️"; + inline constexpr const char levitate[] = "🕴️"; + inline constexpr const char man_in_business_suit_levitating[] = "🕴️"; + inline constexpr const char levitate_tone1[] = "🕴🏻"; + inline constexpr const char man_in_business_suit_levitating_tone1[] = "🕴🏻"; + inline constexpr const char man_in_business_suit_levitating_light_skin_tone[] = "🕴🏻"; + inline constexpr const char levitate_tone2[] = "🕴🏼"; + inline constexpr const char man_in_business_suit_levitating_tone2[] = "🕴🏼"; + inline constexpr const char man_in_business_suit_levitating_medium_light_skin_tone[] = "🕴🏼"; + inline constexpr const char levitate_tone3[] = "🕴🏽"; + inline constexpr const char man_in_business_suit_levitating_tone3[] = "🕴🏽"; + inline constexpr const char man_in_business_suit_levitating_medium_skin_tone[] = "🕴🏽"; + inline constexpr const char levitate_tone4[] = "🕴🏾"; + inline constexpr const char man_in_business_suit_levitating_tone4[] = "🕴🏾"; + inline constexpr const char man_in_business_suit_levitating_medium_dark_skin_tone[] = "🕴🏾"; + inline constexpr const char levitate_tone5[] = "🕴🏿"; + inline constexpr const char man_in_business_suit_levitating_tone5[] = "🕴🏿"; + inline constexpr const char man_in_business_suit_levitating_dark_skin_tone[] = "🕴🏿"; + inline constexpr const char person_in_manual_wheelchair[] = "🧑‍🦽"; + inline constexpr const char person_in_manual_wheelchair_tone1[] = "🧑🏻‍🦽"; + inline constexpr const char person_in_manual_wheelchair_light_skin_tone[] = "🧑🏻‍🦽"; + inline constexpr const char person_in_manual_wheelchair_tone2[] = "🧑🏼‍🦽"; + inline constexpr const char person_in_manual_wheelchair_medium_light_skin_tone[] = "🧑🏼‍🦽"; + inline constexpr const char person_in_manual_wheelchair_tone3[] = "🧑🏽‍🦽"; + inline constexpr const char person_in_manual_wheelchair_medium_skin_tone[] = "🧑🏽‍🦽"; + inline constexpr const char person_in_manual_wheelchair_tone4[] = "🧑🏾‍🦽"; + inline constexpr const char person_in_manual_wheelchair_medium_dark_skin_tone[] = "🧑🏾‍🦽"; + inline constexpr const char person_in_manual_wheelchair_tone5[] = "🧑🏿‍🦽"; + inline constexpr const char person_in_manual_wheelchair_dark_skin_tone[] = "🧑🏿‍🦽"; + inline constexpr const char woman_in_manual_wheelchair[] = "👩‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_tone1[] = "👩🏻‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_light_skin_tone[] = "👩🏻‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_tone2[] = "👩🏼‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_medium_light_skin_tone[] = "👩🏼‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_tone3[] = "👩🏽‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_medium_skin_tone[] = "👩🏽‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_tone4[] = "👩🏾‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_medium_dark_skin_tone[] = "👩🏾‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_tone5[] = "👩🏿‍🦽"; + inline constexpr const char woman_in_manual_wheelchair_dark_skin_tone[] = "👩🏿‍🦽"; + inline constexpr const char man_in_manual_wheelchair[] = "👨‍🦽"; + inline constexpr const char man_in_manual_wheelchair_tone1[] = "👨🏻‍🦽"; + inline constexpr const char man_in_manual_wheelchair_light_skin_tone[] = "👨🏻‍🦽"; + inline constexpr const char man_in_manual_wheelchair_tone2[] = "👨🏼‍🦽"; + inline constexpr const char man_in_manual_wheelchair_medium_light_skin_tone[] = "👨🏼‍🦽"; + inline constexpr const char man_in_manual_wheelchair_tone3[] = "👨🏽‍🦽"; + inline constexpr const char man_in_manual_wheelchair_medium_skin_tone[] = "👨🏽‍🦽"; + inline constexpr const char man_in_manual_wheelchair_tone4[] = "👨🏾‍🦽"; + inline constexpr const char man_in_manual_wheelchair_medium_dark_skin_tone[] = "👨🏾‍🦽"; + inline constexpr const char man_in_manual_wheelchair_tone5[] = "👨🏿‍🦽"; + inline constexpr const char man_in_manual_wheelchair_dark_skin_tone[] = "👨🏿‍🦽"; + inline constexpr const char person_in_motorized_wheelchair[] = "🧑‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_tone1[] = "🧑🏻‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_light_skin_tone[] = "🧑🏻‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_tone2[] = "🧑🏼‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_medium_light_skin_tone[] = "🧑🏼‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_tone3[] = "🧑🏽‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_medium_skin_tone[] = "🧑🏽‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_tone4[] = "🧑🏾‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_medium_dark_skin_tone[] = "🧑🏾‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_tone5[] = "🧑🏿‍🦼"; + inline constexpr const char person_in_motorized_wheelchair_dark_skin_tone[] = "🧑🏿‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair[] = "👩‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_tone1[] = "👩🏻‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_light_skin_tone[] = "👩🏻‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_tone2[] = "👩🏼‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_medium_light_skin_tone[] = "👩🏼‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_tone3[] = "👩🏽‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_medium_skin_tone[] = "👩🏽‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_tone4[] = "👩🏾‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_medium_dark_skin_tone[] = "👩🏾‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_tone5[] = "👩🏿‍🦼"; + inline constexpr const char woman_in_motorized_wheelchair_dark_skin_tone[] = "👩🏿‍🦼"; + inline constexpr const char man_in_motorized_wheelchair[] = "👨‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_tone1[] = "👨🏻‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_light_skin_tone[] = "👨🏻‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_tone2[] = "👨🏼‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_medium_light_skin_tone[] = "👨🏼‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_tone3[] = "👨🏽‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_medium_skin_tone[] = "👨🏽‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_tone4[] = "👨🏾‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_medium_dark_skin_tone[] = "👨🏾‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_tone5[] = "👨🏿‍🦼"; + inline constexpr const char man_in_motorized_wheelchair_dark_skin_tone[] = "👨🏿‍🦼"; + inline constexpr const char person_walking[] = "🚶"; + inline constexpr const char walking[] = "🚶"; + inline constexpr const char person_walking_tone1[] = "🚶🏻"; + inline constexpr const char walking_tone1[] = "🚶🏻"; + inline constexpr const char person_walking_tone2[] = "🚶🏼"; + inline constexpr const char walking_tone2[] = "🚶🏼"; + inline constexpr const char person_walking_tone3[] = "🚶🏽"; + inline constexpr const char walking_tone3[] = "🚶🏽"; + inline constexpr const char person_walking_tone4[] = "🚶🏾"; + inline constexpr const char walking_tone4[] = "🚶🏾"; + inline constexpr const char person_walking_tone5[] = "🚶🏿"; + inline constexpr const char walking_tone5[] = "🚶🏿"; + inline constexpr const char woman_walking[] = "🚶‍♀️"; + inline constexpr const char woman_walking_tone1[] = "🚶🏻‍♀️"; + inline constexpr const char woman_walking_light_skin_tone[] = "🚶🏻‍♀️"; + inline constexpr const char woman_walking_tone2[] = "🚶🏼‍♀️"; + inline constexpr const char woman_walking_medium_light_skin_tone[] = "🚶🏼‍♀️"; + inline constexpr const char woman_walking_tone3[] = "🚶🏽‍♀️"; + inline constexpr const char woman_walking_medium_skin_tone[] = "🚶🏽‍♀️"; + inline constexpr const char woman_walking_tone4[] = "🚶🏾‍♀️"; + inline constexpr const char woman_walking_medium_dark_skin_tone[] = "🚶🏾‍♀️"; + inline constexpr const char woman_walking_tone5[] = "🚶🏿‍♀️"; + inline constexpr const char woman_walking_dark_skin_tone[] = "🚶🏿‍♀️"; + inline constexpr const char man_walking[] = "🚶‍♂️"; + inline constexpr const char man_walking_tone1[] = "🚶🏻‍♂️"; + inline constexpr const char man_walking_light_skin_tone[] = "🚶🏻‍♂️"; + inline constexpr const char man_walking_tone2[] = "🚶🏼‍♂️"; + inline constexpr const char man_walking_medium_light_skin_tone[] = "🚶🏼‍♂️"; + inline constexpr const char man_walking_tone3[] = "🚶🏽‍♂️"; + inline constexpr const char man_walking_medium_skin_tone[] = "🚶🏽‍♂️"; + inline constexpr const char man_walking_tone4[] = "🚶🏾‍♂️"; + inline constexpr const char man_walking_medium_dark_skin_tone[] = "🚶🏾‍♂️"; + inline constexpr const char man_walking_tone5[] = "🚶🏿‍♂️"; + inline constexpr const char man_walking_dark_skin_tone[] = "🚶🏿‍♂️"; + inline constexpr const char person_with_probing_cane[] = "🧑‍🦯"; + inline constexpr const char person_with_probing_cane_tone1[] = "🧑🏻‍🦯"; + inline constexpr const char person_with_probing_cane_light_skin_tone[] = "🧑🏻‍🦯"; + inline constexpr const char person_with_probing_cane_tone2[] = "🧑🏼‍🦯"; + inline constexpr const char person_with_probing_cane_medium_light_skin_tone[] = "🧑🏼‍🦯"; + inline constexpr const char person_with_probing_cane_tone3[] = "🧑🏽‍🦯"; + inline constexpr const char person_with_probing_cane_medium_skin_tone[] = "🧑🏽‍🦯"; + inline constexpr const char person_with_probing_cane_tone4[] = "🧑🏾‍🦯"; + inline constexpr const char person_with_probing_cane_medium_dark_skin_tone[] = "🧑🏾‍🦯"; + inline constexpr const char person_with_probing_cane_tone5[] = "🧑🏿‍🦯"; + inline constexpr const char person_with_probing_cane_dark_skin_tone[] = "🧑🏿‍🦯"; + inline constexpr const char woman_with_probing_cane[] = "👩‍🦯"; + inline constexpr const char woman_with_probing_cane_tone1[] = "👩🏻‍🦯"; + inline constexpr const char woman_with_probing_cane_light_skin_tone[] = "👩🏻‍🦯"; + inline constexpr const char woman_with_probing_cane_tone2[] = "👩🏼‍🦯"; + inline constexpr const char woman_with_probing_cane_medium_light_skin_tone[] = "👩🏼‍🦯"; + inline constexpr const char woman_with_probing_cane_tone3[] = "👩🏽‍🦯"; + inline constexpr const char woman_with_probing_cane_medium_skin_tone[] = "👩🏽‍🦯"; + inline constexpr const char woman_with_probing_cane_tone4[] = "👩🏾‍🦯"; + inline constexpr const char woman_with_probing_cane_medium_dark_skin_tone[] = "👩🏾‍🦯"; + inline constexpr const char woman_with_probing_cane_tone5[] = "👩🏿‍🦯"; + inline constexpr const char woman_with_probing_cane_dark_skin_tone[] = "👩🏿‍🦯"; + inline constexpr const char man_with_probing_cane[] = "👨‍🦯"; + inline constexpr const char man_with_probing_cane_tone1[] = "👨🏻‍🦯"; + inline constexpr const char man_with_probing_cane_light_skin_tone[] = "👨🏻‍🦯"; + inline constexpr const char man_with_probing_cane_tone2[] = "👨🏼‍🦯"; + inline constexpr const char man_with_probing_cane_medium_light_skin_tone[] = "👨🏼‍🦯"; + inline constexpr const char man_with_probing_cane_tone3[] = "👨🏽‍🦯"; + inline constexpr const char man_with_probing_cane_medium_skin_tone[] = "👨🏽‍🦯"; + inline constexpr const char man_with_probing_cane_tone4[] = "👨🏾‍🦯"; + inline constexpr const char man_with_probing_cane_medium_dark_skin_tone[] = "👨🏾‍🦯"; + inline constexpr const char man_with_probing_cane_tone5[] = "👨🏿‍🦯"; + inline constexpr const char man_with_probing_cane_dark_skin_tone[] = "👨🏿‍🦯"; + inline constexpr const char person_kneeling[] = "🧎"; + inline constexpr const char person_kneeling_tone1[] = "🧎🏻"; + inline constexpr const char person_kneeling_light_skin_tone[] = "🧎🏻"; + inline constexpr const char person_kneeling_tone2[] = "🧎🏼"; + inline constexpr const char person_kneeling_medium_light_skin_tone[] = "🧎🏼"; + inline constexpr const char person_kneeling_tone3[] = "🧎🏽"; + inline constexpr const char person_kneeling_medium_skin_tone[] = "🧎🏽"; + inline constexpr const char person_kneeling_tone4[] = "🧎🏾"; + inline constexpr const char person_kneeling_medium_dark_skin_tone[] = "🧎🏾"; + inline constexpr const char person_kneeling_tone5[] = "🧎🏿"; + inline constexpr const char person_kneeling_dark_skin_tone[] = "🧎🏿"; + inline constexpr const char woman_kneeling[] = "🧎‍♀️"; + inline constexpr const char woman_kneeling_tone1[] = "🧎🏻‍♀️"; + inline constexpr const char woman_kneeling_light_skin_tone[] = "🧎🏻‍♀️"; + inline constexpr const char woman_kneeling_tone2[] = "🧎🏼‍♀️"; + inline constexpr const char woman_kneeling_medium_light_skin_tone[] = "🧎🏼‍♀️"; + inline constexpr const char woman_kneeling_tone3[] = "🧎🏽‍♀️"; + inline constexpr const char woman_kneeling_medium_skin_tone[] = "🧎🏽‍♀️"; + inline constexpr const char woman_kneeling_tone4[] = "🧎🏾‍♀️"; + inline constexpr const char woman_kneeling_medium_dark_skin_tone[] = "🧎🏾‍♀️"; + inline constexpr const char woman_kneeling_tone5[] = "🧎🏿‍♀️"; + inline constexpr const char woman_kneeling_dark_skin_tone[] = "🧎🏿‍♀️"; + inline constexpr const char man_kneeling[] = "🧎‍♂️"; + inline constexpr const char man_kneeling_tone1[] = "🧎🏻‍♂️"; + inline constexpr const char man_kneeling_light_skin_tone[] = "🧎🏻‍♂️"; + inline constexpr const char man_kneeling_tone2[] = "🧎🏼‍♂️"; + inline constexpr const char man_kneeling_medium_light_skin_tone[] = "🧎🏼‍♂️"; + inline constexpr const char man_kneeling_tone3[] = "🧎🏽‍♂️"; + inline constexpr const char man_kneeling_medium_skin_tone[] = "🧎🏽‍♂️"; + inline constexpr const char man_kneeling_tone4[] = "🧎🏾‍♂️"; + inline constexpr const char man_kneeling_medium_dark_skin_tone[] = "🧎🏾‍♂️"; + inline constexpr const char man_kneeling_tone5[] = "🧎🏿‍♂️"; + inline constexpr const char man_kneeling_dark_skin_tone[] = "🧎🏿‍♂️"; + inline constexpr const char person_running[] = "🏃"; + inline constexpr const char runner[] = "🏃"; + inline constexpr const char person_running_tone1[] = "🏃🏻"; + inline constexpr const char runner_tone1[] = "🏃🏻"; + inline constexpr const char person_running_tone2[] = "🏃🏼"; + inline constexpr const char runner_tone2[] = "🏃🏼"; + inline constexpr const char person_running_tone3[] = "🏃🏽"; + inline constexpr const char runner_tone3[] = "🏃🏽"; + inline constexpr const char person_running_tone4[] = "🏃🏾"; + inline constexpr const char runner_tone4[] = "🏃🏾"; + inline constexpr const char person_running_tone5[] = "🏃🏿"; + inline constexpr const char runner_tone5[] = "🏃🏿"; + inline constexpr const char woman_running[] = "🏃‍♀️"; + inline constexpr const char woman_running_tone1[] = "🏃🏻‍♀️"; + inline constexpr const char woman_running_light_skin_tone[] = "🏃🏻‍♀️"; + inline constexpr const char woman_running_tone2[] = "🏃🏼‍♀️"; + inline constexpr const char woman_running_medium_light_skin_tone[] = "🏃🏼‍♀️"; + inline constexpr const char woman_running_tone3[] = "🏃🏽‍♀️"; + inline constexpr const char woman_running_medium_skin_tone[] = "🏃🏽‍♀️"; + inline constexpr const char woman_running_tone4[] = "🏃🏾‍♀️"; + inline constexpr const char woman_running_medium_dark_skin_tone[] = "🏃🏾‍♀️"; + inline constexpr const char woman_running_tone5[] = "🏃🏿‍♀️"; + inline constexpr const char woman_running_dark_skin_tone[] = "🏃🏿‍♀️"; + inline constexpr const char man_running[] = "🏃‍♂️"; + inline constexpr const char man_running_tone1[] = "🏃🏻‍♂️"; + inline constexpr const char man_running_light_skin_tone[] = "🏃🏻‍♂️"; + inline constexpr const char man_running_tone2[] = "🏃🏼‍♂️"; + inline constexpr const char man_running_medium_light_skin_tone[] = "🏃🏼‍♂️"; + inline constexpr const char man_running_tone3[] = "🏃🏽‍♂️"; + inline constexpr const char man_running_medium_skin_tone[] = "🏃🏽‍♂️"; + inline constexpr const char man_running_tone4[] = "🏃🏾‍♂️"; + inline constexpr const char man_running_medium_dark_skin_tone[] = "🏃🏾‍♂️"; + inline constexpr const char man_running_tone5[] = "🏃🏿‍♂️"; + inline constexpr const char man_running_dark_skin_tone[] = "🏃🏿‍♂️"; + inline constexpr const char person_standing[] = "🧍"; + inline constexpr const char person_standing_tone1[] = "🧍🏻"; + inline constexpr const char person_standing_light_skin_tone[] = "🧍🏻"; + inline constexpr const char person_standing_tone2[] = "🧍🏼"; + inline constexpr const char person_standing_medium_light_skin_tone[] = "🧍🏼"; + inline constexpr const char person_standing_tone3[] = "🧍🏽"; + inline constexpr const char person_standing_medium_skin_tone[] = "🧍🏽"; + inline constexpr const char person_standing_tone4[] = "🧍🏾"; + inline constexpr const char person_standing_medium_dark_skin_tone[] = "🧍🏾"; + inline constexpr const char person_standing_tone5[] = "🧍🏿"; + inline constexpr const char person_standing_dark_skin_tone[] = "🧍🏿"; + inline constexpr const char woman_standing[] = "🧍‍♀️"; + inline constexpr const char woman_standing_tone1[] = "🧍🏻‍♀️"; + inline constexpr const char woman_standing_light_skin_tone[] = "🧍🏻‍♀️"; + inline constexpr const char woman_standing_tone2[] = "🧍🏼‍♀️"; + inline constexpr const char woman_standing_medium_light_skin_tone[] = "🧍🏼‍♀️"; + inline constexpr const char woman_standing_tone3[] = "🧍🏽‍♀️"; + inline constexpr const char woman_standing_medium_skin_tone[] = "🧍🏽‍♀️"; + inline constexpr const char woman_standing_tone4[] = "🧍🏾‍♀️"; + inline constexpr const char woman_standing_medium_dark_skin_tone[] = "🧍🏾‍♀️"; + inline constexpr const char woman_standing_tone5[] = "🧍🏿‍♀️"; + inline constexpr const char woman_standing_dark_skin_tone[] = "🧍🏿‍♀️"; + inline constexpr const char man_standing[] = "🧍‍♂️"; + inline constexpr const char man_standing_tone1[] = "🧍🏻‍♂️"; + inline constexpr const char man_standing_light_skin_tone[] = "🧍🏻‍♂️"; + inline constexpr const char man_standing_tone2[] = "🧍🏼‍♂️"; + inline constexpr const char man_standing_medium_light_skin_tone[] = "🧍🏼‍♂️"; + inline constexpr const char man_standing_tone3[] = "🧍🏽‍♂️"; + inline constexpr const char man_standing_medium_skin_tone[] = "🧍🏽‍♂️"; + inline constexpr const char man_standing_tone4[] = "🧍🏾‍♂️"; + inline constexpr const char man_standing_medium_dark_skin_tone[] = "🧍🏾‍♂️"; + inline constexpr const char man_standing_tone5[] = "🧍🏿‍♂️"; + inline constexpr const char man_standing_dark_skin_tone[] = "🧍🏿‍♂️"; + inline constexpr const char people_holding_hands[] = "🧑‍🤝‍🧑"; + inline constexpr const char people_holding_hands_tone1[] = "🧑🏻‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_light_skin_tone[] = "🧑🏻‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_tone1_tone2[] = "🧑🏻‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_light_skin_tone_medium_light_skin_tone[] = "🧑🏻‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_tone1_tone3[] = "🧑🏻‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_light_skin_tone_medium_skin_tone[] = "🧑🏻‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_tone1_tone4[] = "🧑🏻‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "🧑🏻‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone1_tone5[] = "🧑🏻‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_light_skin_tone_dark_skin_tone[] = "🧑🏻‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_tone2_tone1[] = "🧑🏼‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_light_skin_tone[] = "🧑🏼‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_tone2[] = "🧑🏼‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_medium_light_skin_tone[] = "🧑🏼‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_tone2_tone3[] = "🧑🏼‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "🧑🏼‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_tone2_tone4[] = "🧑🏼‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "🧑🏼‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone2_tone5[] = "🧑🏼‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "🧑🏼‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_tone3_tone1[] = "🧑🏽‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_medium_skin_tone_light_skin_tone[] = "🧑🏽‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_tone3_tone2[] = "🧑🏽‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "🧑🏽‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_tone3[] = "🧑🏽‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_medium_skin_tone[] = "🧑🏽‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_tone3_tone4[] = "🧑🏽‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "🧑🏽‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone3_tone5[] = "🧑🏽‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_medium_skin_tone_dark_skin_tone[] = "🧑🏽‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_tone4_tone1[] = "🧑🏾‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "🧑🏾‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_tone4_tone2[] = "🧑🏾‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "🧑🏾‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_tone4_tone3[] = "🧑🏾‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "🧑🏾‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_tone4[] = "🧑🏾‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone[] = "🧑🏾‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone4_tone5[] = "🧑🏾‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "🧑🏾‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_tone5_tone1[] = "🧑🏿‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_dark_skin_tone_light_skin_tone[] = "🧑🏿‍🤝‍🧑🏻"; + inline constexpr const char people_holding_hands_tone5_tone2[] = "🧑🏿‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "🧑🏿‍🤝‍🧑🏼"; + inline constexpr const char people_holding_hands_tone5_tone3[] = "🧑🏿‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_dark_skin_tone_medium_skin_tone[] = "🧑🏿‍🤝‍🧑🏽"; + inline constexpr const char people_holding_hands_tone5_tone4[] = "🧑🏿‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "🧑🏿‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone5[] = "🧑🏿‍🤝‍🧑🏿"; + inline constexpr const char people_holding_hands_dark_skin_tone[] = "🧑🏿‍🤝‍🧑🏿"; + inline constexpr const char couple[] = "👫"; + inline constexpr const char woman_and_man_holding_hands_tone1[] = "👫🏻"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone[] = "👫🏻"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone2[] = "👩🏻‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone[] = "👩🏻‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone3[] = "👩🏻‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_medium_skin_tone[] = "👩🏻‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone4[] = "👩🏻‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "👩🏻‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone5[] = "👩🏻‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_dark_skin_tone[] = "👩🏻‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone1[] = "👩🏼‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone[] = "👩🏼‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_tone2[] = "👫🏼"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone[] = "👫🏼"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone3[] = "👩🏼‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "👩🏼‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone4[] = "👩🏼‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "👩🏼‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone5[] = "👩🏼‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "👩🏼‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone1[] = "👩🏽‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_light_skin_tone[] = "👩🏽‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone2[] = "👩🏽‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "👩🏽‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_tone3[] = "👫🏽"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone[] = "👫🏽"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone4[] = "👩🏽‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "👩🏽‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone5[] = "👩🏽‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone[] = "👩🏽‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone1[] = "👩🏾‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone2[] = "👩🏾‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone3[] = "👩🏾‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_tone4[] = "👫🏾"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone[] = "👫🏾"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone5[] = "👩🏾‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍🤝‍👨🏿"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone1[] = "👩🏿‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_light_skin_tone[] = "👩🏿‍🤝‍👨🏻"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone2[] = "👩🏿‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "👩🏿‍🤝‍👨🏼"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone3[] = "👩🏿‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone[] = "👩🏿‍🤝‍👨🏽"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone4[] = "👩🏿‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "👩🏿‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone5[] = "👫🏿"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone[] = "👫🏿"; + inline constexpr const char two_women_holding_hands[] = "👭"; + inline constexpr const char women_holding_hands_tone1[] = "👭🏻"; + inline constexpr const char women_holding_hands_light_skin_tone[] = "👭🏻"; + inline constexpr const char women_holding_hands_tone1_tone2[] = "👩🏻‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_light_skin_tone_medium_light_skin_tone[] = "👩🏻‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_tone1_tone3[] = "👩🏻‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_light_skin_tone_medium_skin_tone[] = "👩🏻‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_tone1_tone4[] = "👩🏻‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "👩🏻‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone1_tone5[] = "👩🏻‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_light_skin_tone_dark_skin_tone[] = "👩🏻‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_tone2_tone1[] = "👩🏼‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_light_skin_tone[] = "👩🏼‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_tone2[] = "👭🏼"; + inline constexpr const char women_holding_hands_medium_light_skin_tone[] = "👭🏼"; + inline constexpr const char women_holding_hands_tone2_tone3[] = "👩🏼‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "👩🏼‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_tone2_tone4[] = "👩🏼‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "👩🏼‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone2_tone5[] = "👩🏼‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "👩🏼‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_tone3_tone1[] = "👩🏽‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_medium_skin_tone_light_skin_tone[] = "👩🏽‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_tone3_tone2[] = "👩🏽‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "👩🏽‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_tone3[] = "👭🏽"; + inline constexpr const char women_holding_hands_medium_skin_tone[] = "👭🏽"; + inline constexpr const char women_holding_hands_tone3_tone4[] = "👩🏽‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "👩🏽‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone3_tone5[] = "👩🏽‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_medium_skin_tone_dark_skin_tone[] = "👩🏽‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_tone4_tone1[] = "👩🏾‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_tone4_tone2[] = "👩🏾‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_tone4_tone3[] = "👩🏾‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_tone4[] = "👭🏾"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone[] = "👭🏾"; + inline constexpr const char women_holding_hands_tone4_tone5[] = "👩🏾‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍🤝‍👩🏿"; + inline constexpr const char women_holding_hands_tone5_tone1[] = "👩🏿‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_dark_skin_tone_light_skin_tone[] = "👩🏿‍🤝‍👩🏻"; + inline constexpr const char women_holding_hands_tone5_tone2[] = "👩🏿‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "👩🏿‍🤝‍👩🏼"; + inline constexpr const char women_holding_hands_tone5_tone3[] = "👩🏿‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_dark_skin_tone_medium_skin_tone[] = "👩🏿‍🤝‍👩🏽"; + inline constexpr const char women_holding_hands_tone5_tone4[] = "👩🏿‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "👩🏿‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone5[] = "👭🏿"; + inline constexpr const char women_holding_hands_dark_skin_tone[] = "👭🏿"; + inline constexpr const char two_men_holding_hands[] = "👬"; + inline constexpr const char men_holding_hands_tone1[] = "👬🏻"; + inline constexpr const char men_holding_hands_light_skin_tone[] = "👬🏻"; + inline constexpr const char men_holding_hands_tone1_tone2[] = "👨🏻‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_light_skin_tone_medium_light_skin_tone[] = "👨🏻‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_tone1_tone3[] = "👨🏻‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_light_skin_tone_medium_skin_tone[] = "👨🏻‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_tone1_tone4[] = "👨🏻‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "👨🏻‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone1_tone5[] = "👨🏻‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_light_skin_tone_dark_skin_tone[] = "👨🏻‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_tone2_tone1[] = "👨🏼‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_light_skin_tone[] = "👨🏼‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_tone2[] = "👬🏼"; + inline constexpr const char men_holding_hands_medium_light_skin_tone[] = "👬🏼"; + inline constexpr const char men_holding_hands_tone2_tone3[] = "👨🏼‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "👨🏼‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_tone2_tone4[] = "👨🏼‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "👨🏼‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone2_tone5[] = "👨🏼‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "👨🏼‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_tone3_tone1[] = "👨🏽‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_medium_skin_tone_light_skin_tone[] = "👨🏽‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_tone3_tone2[] = "👨🏽‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "👨🏽‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_tone3[] = "👬🏽"; + inline constexpr const char men_holding_hands_medium_skin_tone[] = "👬🏽"; + inline constexpr const char men_holding_hands_tone3_tone4[] = "👨🏽‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "👨🏽‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone3_tone5[] = "👨🏽‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_medium_skin_tone_dark_skin_tone[] = "👨🏽‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_tone4_tone1[] = "👨🏾‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "👨🏾‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_tone4_tone2[] = "👨🏾‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "👨🏾‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_tone4_tone3[] = "👨🏾‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "👨🏾‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_tone4[] = "👬🏾"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone[] = "👬🏾"; + inline constexpr const char men_holding_hands_tone4_tone5[] = "👨🏾‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "👨🏾‍🤝‍👨🏿"; + inline constexpr const char men_holding_hands_tone5_tone1[] = "👨🏿‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_dark_skin_tone_light_skin_tone[] = "👨🏿‍🤝‍👨🏻"; + inline constexpr const char men_holding_hands_tone5_tone2[] = "👨🏿‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "👨🏿‍🤝‍👨🏼"; + inline constexpr const char men_holding_hands_tone5_tone3[] = "👨🏿‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_dark_skin_tone_medium_skin_tone[] = "👨🏿‍🤝‍👨🏽"; + inline constexpr const char men_holding_hands_tone5_tone4[] = "👨🏿‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "👨🏿‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone5[] = "👬🏿"; + inline constexpr const char men_holding_hands_dark_skin_tone[] = "👬🏿"; + inline constexpr const char couple_with_heart[] = "💑"; + inline constexpr const char couple_with_heart_tone1[] = "💑🏻"; + inline constexpr const char couple_with_heart_light_skin_tone[] = "💑🏻"; + inline constexpr const char couple_with_heart_person_person_tone1_tone2[] = "🧑🏻‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_medium_light_skin_tone[] = "🧑🏻‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_tone1_tone3[] = "🧑🏻‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_medium_skin_tone[] = "🧑🏻‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_tone1_tone4[] = "🧑🏻‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_medium_dark_skin_tone[] = "🧑🏻‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_tone1_tone5[] = "🧑🏻‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_dark_skin_tone[] = "🧑🏻‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_tone2_tone1[] = "🧑🏼‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_light_skin_tone[] = "🧑🏼‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_tone2[] = "💑🏼"; + inline constexpr const char couple_with_heart_medium_light_skin_tone[] = "💑🏼"; + inline constexpr const char couple_with_heart_person_person_tone2_tone3[] = "🧑🏼‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_medium_skin_tone[] = "🧑🏼‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_tone2_tone4[] = "🧑🏼‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_medium_dark_skin_tone[] = "🧑🏼‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_tone2_tone5[] = "🧑🏼‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_dark_skin_tone[] = "🧑🏼‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_tone3_tone1[] = "🧑🏽‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_light_skin_tone[] = "🧑🏽‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_tone3_tone2[] = "🧑🏽‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_medium_light_skin_tone[] = "🧑🏽‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_tone3[] = "💑🏽"; + inline constexpr const char couple_with_heart_medium_skin_tone[] = "💑🏽"; + inline constexpr const char couple_with_heart_person_person_tone3_tone4[] = "🧑🏽‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_medium_dark_skin_tone[] = "🧑🏽‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_tone3_tone5[] = "🧑🏽‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_dark_skin_tone[] = "🧑🏽‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_tone4_tone1[] = "🧑🏾‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_light_skin_tone[] = "🧑🏾‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_tone4_tone2[] = "🧑🏾‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_medium_light_skin_tone[] = "🧑🏾‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_tone4_tone3[] = "🧑🏾‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_medium_skin_tone[] = "🧑🏾‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_tone4[] = "💑🏾"; + inline constexpr const char couple_with_heart_medium_dark_skin_tone[] = "💑🏾"; + inline constexpr const char couple_with_heart_person_person_tone4_tone5[] = "🧑🏾‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_dark_skin_tone[] = "🧑🏾‍❤️‍🧑🏿"; + inline constexpr const char couple_with_heart_person_person_tone5_tone1[] = "🧑🏿‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_light_skin_tone[] = "🧑🏿‍❤️‍🧑🏻"; + inline constexpr const char couple_with_heart_person_person_tone5_tone2[] = "🧑🏿‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_medium_light_skin_tone[] = "🧑🏿‍❤️‍🧑🏼"; + inline constexpr const char couple_with_heart_person_person_tone5_tone3[] = "🧑🏿‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_medium_skin_tone[] = "🧑🏿‍❤️‍🧑🏽"; + inline constexpr const char couple_with_heart_person_person_tone5_tone4[] = "🧑🏿‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_medium_dark_skin_tone[] = "🧑🏿‍❤️‍🧑🏾"; + inline constexpr const char couple_with_heart_tone5[] = "💑🏿"; + inline constexpr const char couple_with_heart_dark_skin_tone[] = "💑🏿"; + inline constexpr const char couple_with_heart_woman_man[] = "👩‍❤️‍👨"; + inline constexpr const char couple_with_heart_woman_man_tone1[] = "👩🏻‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone[] = "👩🏻‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone2[] = "👩🏻‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_medium_light_skin_tone[] = "👩🏻‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone3[] = "👩🏻‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_medium_skin_tone[] = "👩🏻‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone4[] = "👩🏻‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_medium_dark_skin_tone[] = "👩🏻‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone5[] = "👩🏻‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_dark_skin_tone[] = "👩🏻‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone1[] = "👩🏼‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_light_skin_tone[] = "👩🏼‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_tone2[] = "👩🏼‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone[] = "👩🏼‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone3[] = "👩🏼‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_medium_skin_tone[] = "👩🏼‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone4[] = "👩🏼‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👩🏼‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone5[] = "👩🏼‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_dark_skin_tone[] = "👩🏼‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone1[] = "👩🏽‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_light_skin_tone[] = "👩🏽‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone2[] = "👩🏽‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_medium_light_skin_tone[] = "👩🏽‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_tone3[] = "👩🏽‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone[] = "👩🏽‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone4[] = "👩🏽‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_medium_dark_skin_tone[] = "👩🏽‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone5[] = "👩🏽‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_dark_skin_tone[] = "👩🏽‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone1[] = "👩🏾‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone2[] = "👩🏾‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone3[] = "👩🏾‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_tone4[] = "👩🏾‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone[] = "👩🏾‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone5[] = "👩🏾‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone1[] = "👩🏿‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_light_skin_tone[] = "👩🏿‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone2[] = "👩🏿‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_medium_light_skin_tone[] = "👩🏿‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone3[] = "👩🏿‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_medium_skin_tone[] = "👩🏿‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone4[] = "👩🏿‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_medium_dark_skin_tone[] = "👩🏿‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_woman_man_tone5[] = "👩🏿‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone[] = "👩🏿‍❤️‍👨🏿"; + inline constexpr const char couple_ww[] = "👩‍❤️‍👩"; + inline constexpr const char couple_with_heart_ww[] = "👩‍❤️‍👩"; + inline constexpr const char couple_with_heart_woman_woman_tone1[] = "👩🏻‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone[] = "👩🏻‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone2[] = "👩🏻‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_medium_light_skin_tone[] = "👩🏻‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone3[] = "👩🏻‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_medium_skin_tone[] = "👩🏻‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone4[] = "👩🏻‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_medium_dark_skin_tone[] = "👩🏻‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone5[] = "👩🏻‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_dark_skin_tone[] = "👩🏻‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone1[] = "👩🏼‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_light_skin_tone[] = "👩🏼‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_tone2[] = "👩🏼‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone[] = "👩🏼‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone3[] = "👩🏼‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_medium_skin_tone[] = "👩🏼‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone4[] = "👩🏼‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_medium_dark_skin_tone[] = "👩🏼‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone5[] = "👩🏼‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_dark_skin_tone[] = "👩🏼‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone1[] = "👩🏽‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_light_skin_tone[] = "👩🏽‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone2[] = "👩🏽‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_medium_light_skin_tone[] = "👩🏽‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_tone3[] = "👩🏽‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone[] = "👩🏽‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone4[] = "👩🏽‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_medium_dark_skin_tone[] = "👩🏽‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone5[] = "👩🏽‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_dark_skin_tone[] = "👩🏽‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone1[] = "👩🏾‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone2[] = "👩🏾‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone3[] = "👩🏾‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_tone4[] = "👩🏾‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone[] = "👩🏾‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone5[] = "👩🏾‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone1[] = "👩🏿‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_light_skin_tone[] = "👩🏿‍❤️‍👩🏻"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone2[] = "👩🏿‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_medium_light_skin_tone[] = "👩🏿‍❤️‍👩🏼"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone3[] = "👩🏿‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_medium_skin_tone[] = "👩🏿‍❤️‍👩🏽"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone4[] = "👩🏿‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_medium_dark_skin_tone[] = "👩🏿‍❤️‍👩🏾"; + inline constexpr const char couple_with_heart_woman_woman_tone5[] = "👩🏿‍❤️‍👩🏿"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone[] = "👩🏿‍❤️‍👩🏿"; + inline constexpr const char couple_mm[] = "👨‍❤️‍👨"; + inline constexpr const char couple_with_heart_mm[] = "👨‍❤️‍👨"; + inline constexpr const char couple_with_heart_man_man_tone1[] = "👨🏻‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone[] = "👨🏻‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_tone1_tone2[] = "👨🏻‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_medium_light_skin_tone[] = "👨🏻‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_tone1_tone3[] = "👨🏻‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_medium_skin_tone[] = "👨🏻‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_tone1_tone4[] = "👨🏻‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_medium_dark_skin_tone[] = "👨🏻‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_tone1_tone5[] = "👨🏻‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_dark_skin_tone[] = "👨🏻‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_tone2_tone1[] = "👨🏼‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_light_skin_tone[] = "👨🏼‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_tone2[] = "👨🏼‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone[] = "👨🏼‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_tone2_tone3[] = "👨🏼‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_medium_skin_tone[] = "👨🏼‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_tone2_tone4[] = "👨🏼‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👨🏼‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_tone2_tone5[] = "👨🏼‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_dark_skin_tone[] = "👨🏼‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_tone3_tone1[] = "👨🏽‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_light_skin_tone[] = "👨🏽‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_tone3_tone2[] = "👨🏽‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_medium_light_skin_tone[] = "👨🏽‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_tone3[] = "👨🏽‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone[] = "👨🏽‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_tone3_tone4[] = "👨🏽‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_medium_dark_skin_tone[] = "👨🏽‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_tone3_tone5[] = "👨🏽‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_dark_skin_tone[] = "👨🏽‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_tone4_tone1[] = "👨🏾‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_light_skin_tone[] = "👨🏾‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_tone4_tone2[] = "👨🏾‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👨🏾‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_tone4_tone3[] = "👨🏾‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_medium_skin_tone[] = "👨🏾‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_tone4[] = "👨🏾‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone[] = "👨🏾‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_tone4_tone5[] = "👨🏾‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_dark_skin_tone[] = "👨🏾‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_tone5_tone1[] = "👨🏿‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_light_skin_tone[] = "👨🏿‍❤️‍👨🏻"; + inline constexpr const char couple_with_heart_man_man_tone5_tone2[] = "👨🏿‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_medium_light_skin_tone[] = "👨🏿‍❤️‍👨🏼"; + inline constexpr const char couple_with_heart_man_man_tone5_tone3[] = "👨🏿‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_medium_skin_tone[] = "👨🏿‍❤️‍👨🏽"; + inline constexpr const char couple_with_heart_man_man_tone5_tone4[] = "👨🏿‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_medium_dark_skin_tone[] = "👨🏿‍❤️‍👨🏾"; + inline constexpr const char couple_with_heart_man_man_tone5[] = "👨🏿‍❤️‍👨🏿"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone[] = "👨🏿‍❤️‍👨🏿"; + inline constexpr const char couplekiss[] = "💏"; + inline constexpr const char kiss_tone1[] = "💏🏻"; + inline constexpr const char kiss_light_skin_tone[] = "💏🏻"; + inline constexpr const char kiss_person_person_tone1_tone2[] = "🧑🏻‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_light_skin_tone_medium_light_skin_tone[] = "🧑🏻‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_tone1_tone3[] = "🧑🏻‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_light_skin_tone_medium_skin_tone[] = "🧑🏻‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_tone1_tone4[] = "🧑🏻‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_light_skin_tone_medium_dark_skin_tone[] = "🧑🏻‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_tone1_tone5[] = "🧑🏻‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_light_skin_tone_dark_skin_tone[] = "🧑🏻‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_tone2_tone1[] = "🧑🏼‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_light_skin_tone[] = "🧑🏼‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_tone2[] = "💏🏼"; + inline constexpr const char kiss_medium_light_skin_tone[] = "💏🏼"; + inline constexpr const char kiss_person_person_tone2_tone3[] = "🧑🏼‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_medium_skin_tone[] = "🧑🏼‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_tone2_tone4[] = "🧑🏼‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_medium_dark_skin_tone[] = "🧑🏼‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_tone2_tone5[] = "🧑🏼‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_dark_skin_tone[] = "🧑🏼‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_tone3_tone1[] = "🧑🏽‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_medium_skin_tone_light_skin_tone[] = "🧑🏽‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_tone3_tone2[] = "🧑🏽‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_medium_skin_tone_medium_light_skin_tone[] = "🧑🏽‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_tone3[] = "💏🏽"; + inline constexpr const char kiss_medium_skin_tone[] = "💏🏽"; + inline constexpr const char kiss_person_person_tone3_tone4[] = "🧑🏽‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_medium_skin_tone_medium_dark_skin_tone[] = "🧑🏽‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_tone3_tone5[] = "🧑🏽‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_medium_skin_tone_dark_skin_tone[] = "🧑🏽‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_tone4_tone1[] = "🧑🏾‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_light_skin_tone[] = "🧑🏾‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_tone4_tone2[] = "🧑🏾‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_medium_light_skin_tone[] = "🧑🏾‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_tone4_tone3[] = "🧑🏾‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_medium_skin_tone[] = "🧑🏾‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_tone4[] = "💏🏾"; + inline constexpr const char kiss_medium_dark_skin_tone[] = "💏🏾"; + inline constexpr const char kiss_person_person_tone4_tone5[] = "🧑🏾‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_dark_skin_tone[] = "🧑🏾‍❤️‍💋‍🧑🏿"; + inline constexpr const char kiss_person_person_tone5_tone1[] = "🧑🏿‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_dark_skin_tone_light_skin_tone[] = "🧑🏿‍❤️‍💋‍🧑🏻"; + inline constexpr const char kiss_person_person_tone5_tone2[] = "🧑🏿‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_dark_skin_tone_medium_light_skin_tone[] = "🧑🏿‍❤️‍💋‍🧑🏼"; + inline constexpr const char kiss_person_person_tone5_tone3[] = "🧑🏿‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_dark_skin_tone_medium_skin_tone[] = "🧑🏿‍❤️‍💋‍🧑🏽"; + inline constexpr const char kiss_person_person_tone5_tone4[] = "🧑🏿‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_person_person_dark_skin_tone_medium_dark_skin_tone[] = "🧑🏿‍❤️‍💋‍🧑🏾"; + inline constexpr const char kiss_tone5[] = "💏🏿"; + inline constexpr const char kiss_dark_skin_tone[] = "💏🏿"; + inline constexpr const char kiss_woman_man[] = "👩‍❤️‍💋‍👨"; + inline constexpr const char kiss_woman_man_tone1[] = "👩🏻‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_light_skin_tone[] = "👩🏻‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_tone1_tone2[] = "👩🏻‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_light_skin_tone_medium_light_skin_tone[] = "👩🏻‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_tone1_tone3[] = "👩🏻‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_light_skin_tone_medium_skin_tone[] = "👩🏻‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_tone1_tone4[] = "👩🏻‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_light_skin_tone_medium_dark_skin_tone[] = "👩🏻‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_tone1_tone5[] = "👩🏻‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_light_skin_tone_dark_skin_tone[] = "👩🏻‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_tone2_tone1[] = "👩🏼‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_light_skin_tone[] = "👩🏼‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_tone2[] = "👩🏼‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone[] = "👩🏼‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_tone2_tone3[] = "👩🏼‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_medium_skin_tone[] = "👩🏼‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_tone2_tone4[] = "👩🏼‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👩🏼‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_tone2_tone5[] = "👩🏼‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_dark_skin_tone[] = "👩🏼‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_tone3_tone1[] = "👩🏽‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_medium_skin_tone_light_skin_tone[] = "👩🏽‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_tone3_tone2[] = "👩🏽‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_medium_skin_tone_medium_light_skin_tone[] = "👩🏽‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_tone3[] = "👩🏽‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_medium_skin_tone[] = "👩🏽‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_tone3_tone4[] = "👩🏽‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_medium_skin_tone_medium_dark_skin_tone[] = "👩🏽‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_tone3_tone5[] = "👩🏽‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_medium_skin_tone_dark_skin_tone[] = "👩🏽‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_tone4_tone1[] = "👩🏾‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_tone4_tone2[] = "👩🏾‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_tone4_tone3[] = "👩🏾‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_tone4[] = "👩🏾‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone[] = "👩🏾‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_tone4_tone5[] = "👩🏾‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_tone5_tone1[] = "👩🏿‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_dark_skin_tone_light_skin_tone[] = "👩🏿‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_woman_man_tone5_tone2[] = "👩🏿‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_dark_skin_tone_medium_light_skin_tone[] = "👩🏿‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_woman_man_tone5_tone3[] = "👩🏿‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_dark_skin_tone_medium_skin_tone[] = "👩🏿‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_woman_man_tone5_tone4[] = "👩🏿‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_dark_skin_tone_medium_dark_skin_tone[] = "👩🏿‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_woman_man_tone5[] = "👩🏿‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_woman_man_dark_skin_tone[] = "👩🏿‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_ww[] = "👩‍❤️‍💋‍👩"; + inline constexpr const char couplekiss_ww[] = "👩‍❤️‍💋‍👩"; + inline constexpr const char kiss_woman_woman_tone1[] = "👩🏻‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_light_skin_tone[] = "👩🏻‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_tone1_tone2[] = "👩🏻‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_light_skin_tone_medium_light_skin_tone[] = "👩🏻‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_tone1_tone3[] = "👩🏻‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_light_skin_tone_medium_skin_tone[] = "👩🏻‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_tone1_tone4[] = "👩🏻‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_light_skin_tone_medium_dark_skin_tone[] = "👩🏻‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_tone1_tone5[] = "👩🏻‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_light_skin_tone_dark_skin_tone[] = "👩🏻‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_tone2_tone1[] = "👩🏼‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_light_skin_tone[] = "👩🏼‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_tone2[] = "👩🏼‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone[] = "👩🏼‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_tone2_tone3[] = "👩🏼‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_medium_skin_tone[] = "👩🏼‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_tone2_tone4[] = "👩🏼‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_medium_dark_skin_tone[] = "👩🏼‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_tone2_tone5[] = "👩🏼‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_dark_skin_tone[] = "👩🏼‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_tone3_tone1[] = "👩🏽‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_light_skin_tone[] = "👩🏽‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_tone3_tone2[] = "👩🏽‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_medium_light_skin_tone[] = "👩🏽‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_tone3[] = "👩🏽‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_medium_skin_tone[] = "👩🏽‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_tone3_tone4[] = "👩🏽‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_medium_dark_skin_tone[] = "👩🏽‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_tone3_tone5[] = "👩🏽‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_dark_skin_tone[] = "👩🏽‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_tone4_tone1[] = "👩🏾‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_tone4_tone2[] = "👩🏾‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_tone4_tone3[] = "👩🏾‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_tone4[] = "👩🏾‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone[] = "👩🏾‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_tone4_tone5[] = "👩🏾‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_tone5_tone1[] = "👩🏿‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_light_skin_tone[] = "👩🏿‍❤️‍💋‍👩🏻"; + inline constexpr const char kiss_woman_woman_tone5_tone2[] = "👩🏿‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_medium_light_skin_tone[] = "👩🏿‍❤️‍💋‍👩🏼"; + inline constexpr const char kiss_woman_woman_tone5_tone3[] = "👩🏿‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_medium_skin_tone[] = "👩🏿‍❤️‍💋‍👩🏽"; + inline constexpr const char kiss_woman_woman_tone5_tone4[] = "👩🏿‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_medium_dark_skin_tone[] = "👩🏿‍❤️‍💋‍👩🏾"; + inline constexpr const char kiss_woman_woman_tone5[] = "👩🏿‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_woman_woman_dark_skin_tone[] = "👩🏿‍❤️‍💋‍👩🏿"; + inline constexpr const char kiss_mm[] = "👨‍❤️‍💋‍👨"; + inline constexpr const char couplekiss_mm[] = "👨‍❤️‍💋‍👨"; + inline constexpr const char kiss_man_man[] = "👨‍❤️‍💋‍👨"; + inline constexpr const char kiss_man_man_tone1[] = "👨🏻‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_light_skin_tone[] = "👨🏻‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_tone1_tone2[] = "👨🏻‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_light_skin_tone_medium_light_skin_tone[] = "👨🏻‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_tone1_tone3[] = "👨🏻‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_light_skin_tone_medium_skin_tone[] = "👨🏻‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_tone1_tone4[] = "👨🏻‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_light_skin_tone_medium_dark_skin_tone[] = "👨🏻‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_tone1_tone5[] = "👨🏻‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_light_skin_tone_dark_skin_tone[] = "👨🏻‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_tone2_tone1[] = "👨🏼‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_light_skin_tone[] = "👨🏼‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_tone2[] = "👨🏼‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_medium_light_skin_tone[] = "👨🏼‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_tone2_tone3[] = "👨🏼‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_medium_skin_tone[] = "👨🏼‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_tone2_tone4[] = "👨🏼‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👨🏼‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_tone2_tone5[] = "👨🏼‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_dark_skin_tone[] = "👨🏼‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_tone3_tone1[] = "👨🏽‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_medium_skin_tone_light_skin_tone[] = "👨🏽‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_tone3_tone2[] = "👨🏽‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_medium_skin_tone_medium_light_skin_tone[] = "👨🏽‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_tone3[] = "👨🏽‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_medium_skin_tone[] = "👨🏽‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_tone3_tone4[] = "👨🏽‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_medium_skin_tone_medium_dark_skin_tone[] = "👨🏽‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_tone3_tone5[] = "👨🏽‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_medium_skin_tone_dark_skin_tone[] = "👨🏽‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_tone4_tone1[] = "👨🏾‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_light_skin_tone[] = "👨🏾‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_tone4_tone2[] = "👨🏾‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👨🏾‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_tone4_tone3[] = "👨🏾‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_medium_skin_tone[] = "👨🏾‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_tone4[] = "👨🏾‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone[] = "👨🏾‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_tone4_tone5[] = "👨🏾‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_dark_skin_tone[] = "👨🏾‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_tone5_tone1[] = "👨🏿‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_dark_skin_tone_light_skin_tone[] = "👨🏿‍❤️‍💋‍👨🏻"; + inline constexpr const char kiss_man_man_tone5_tone2[] = "👨🏿‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_dark_skin_tone_medium_light_skin_tone[] = "👨🏿‍❤️‍💋‍👨🏼"; + inline constexpr const char kiss_man_man_tone5_tone3[] = "👨🏿‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_dark_skin_tone_medium_skin_tone[] = "👨🏿‍❤️‍💋‍👨🏽"; + inline constexpr const char kiss_man_man_tone5_tone4[] = "👨🏿‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_dark_skin_tone_medium_dark_skin_tone[] = "👨🏿‍❤️‍💋‍👨🏾"; + inline constexpr const char kiss_man_man_tone5[] = "👨🏿‍❤️‍💋‍👨🏿"; + inline constexpr const char kiss_man_man_dark_skin_tone[] = "👨🏿‍❤️‍💋‍👨🏿"; + inline constexpr const char family[] = "👪"; + inline constexpr const char family_man_woman_boy[] = "👨‍👩‍👦"; + inline constexpr const char family_mwg[] = "👨‍👩‍👧"; + inline constexpr const char family_mwgb[] = "👨‍👩‍👧‍👦"; + inline constexpr const char family_mwbb[] = "👨‍👩‍👦‍👦"; + inline constexpr const char family_mwgg[] = "👨‍👩‍👧‍👧"; + inline constexpr const char family_wwb[] = "👩‍👩‍👦"; + inline constexpr const char family_wwg[] = "👩‍👩‍👧"; + inline constexpr const char family_wwgb[] = "👩‍👩‍👧‍👦"; + inline constexpr const char family_wwbb[] = "👩‍👩‍👦‍👦"; + inline constexpr const char family_wwgg[] = "👩‍👩‍👧‍👧"; + inline constexpr const char family_mmb[] = "👨‍👨‍👦"; + inline constexpr const char family_mmg[] = "👨‍👨‍👧"; + inline constexpr const char family_mmgb[] = "👨‍👨‍👧‍👦"; + inline constexpr const char family_mmbb[] = "👨‍👨‍👦‍👦"; + inline constexpr const char family_mmgg[] = "👨‍👨‍👧‍👧"; + inline constexpr const char family_woman_boy[] = "👩‍👦"; + inline constexpr const char family_woman_girl[] = "👩‍👧"; + inline constexpr const char family_woman_girl_boy[] = "👩‍👧‍👦"; + inline constexpr const char family_woman_boy_boy[] = "👩‍👦‍👦"; + inline constexpr const char family_woman_girl_girl[] = "👩‍👧‍👧"; + inline constexpr const char family_man_boy[] = "👨‍👦"; + inline constexpr const char family_man_girl[] = "👨‍👧"; + inline constexpr const char family_man_girl_boy[] = "👨‍👧‍👦"; + inline constexpr const char family_man_boy_boy[] = "👨‍👦‍👦"; + inline constexpr const char family_man_girl_girl[] = "👨‍👧‍👧"; + inline constexpr const char knot[] = "🪢"; + inline constexpr const char yarn[] = "🧶"; + inline constexpr const char thread[] = "🧵"; + inline constexpr const char sewing_needle[] = "🪡"; + inline constexpr const char coat[] = "🧥"; + inline constexpr const char lab_coat[] = "🥼"; + inline constexpr const char safety_vest[] = "🦺"; + inline constexpr const char womans_clothes[] = "👚"; + inline constexpr const char shirt[] = "👕"; + inline constexpr const char t_shirt[] = "👕"; + inline constexpr const char jeans[] = "👖"; + inline constexpr const char briefs[] = "🩲"; + inline constexpr const char shorts[] = "🩳"; + inline constexpr const char necktie[] = "👔"; + inline constexpr const char dress[] = "👗"; + inline constexpr const char bikini[] = "👙"; + inline constexpr const char one_piece_swimsuit[] = "🩱"; + inline constexpr const char kimono[] = "👘"; + inline constexpr const char sari[] = "🥻"; + inline constexpr const char thong_sandal[] = "🩴"; + inline constexpr const char womans_flat_shoe[] = "🥿"; + inline constexpr const char flat_shoe[] = "🥿"; + inline constexpr const char high_heel[] = "👠"; + inline constexpr const char sandal[] = "👡"; + inline constexpr const char womans_sandal[] = "👡"; + inline constexpr const char boot[] = "👢"; + inline constexpr const char womans_boot[] = "👢"; + inline constexpr const char mans_shoe[] = "👞"; + inline constexpr const char athletic_shoe[] = "👟"; + inline constexpr const char running_shoe[] = "👟"; + inline constexpr const char hiking_boot[] = "🥾"; + inline constexpr const char socks[] = "🧦"; + inline constexpr const char gloves[] = "🧤"; + inline constexpr const char scarf[] = "🧣"; + inline constexpr const char tophat[] = "🎩"; + inline constexpr const char top_hat[] = "🎩"; + inline constexpr const char billed_cap[] = "🧢"; + inline constexpr const char womans_hat[] = "👒"; + inline constexpr const char mortar_board[] = "🎓"; + inline constexpr const char helmet_with_cross[] = "⛑️"; + inline constexpr const char helmet_with_white_cross[] = "⛑️"; + inline constexpr const char military_helmet[] = "🪖"; + inline constexpr const char crown[] = "👑"; + inline constexpr const char ring[] = "💍"; + inline constexpr const char pouch[] = "👝"; + inline constexpr const char clutch_bag[] = "👝"; + inline constexpr const char purse[] = "👛"; + inline constexpr const char handbag[] = "👜"; + inline constexpr const char briefcase[] = "💼"; + inline constexpr const char school_satchel[] = "🎒"; + inline constexpr const char backpack[] = "🎒"; + inline constexpr const char luggage[] = "🧳"; + inline constexpr const char eyeglasses[] = "👓"; + inline constexpr const char glasses[] = "👓"; + inline constexpr const char dark_sunglasses[] = "🕶️"; + inline constexpr const char goggles[] = "🥽"; + inline constexpr const char closed_umbrella[] = "🌂"; + inline constexpr const char pink_heart[] = "🩷"; + inline constexpr const char heart[] = "❤️"; + inline constexpr const char red_heart[] = "❤️"; + inline constexpr const char orange_heart[] = "🧡"; + inline constexpr const char yellow_heart[] = "💛"; + inline constexpr const char green_heart[] = "💚"; + inline constexpr const char light_blue_heart[] = "🩵"; + inline constexpr const char blue_heart[] = "💙"; + inline constexpr const char purple_heart[] = "💜"; + inline constexpr const char black_heart[] = "🖤"; + inline constexpr const char grey_heart[] = "🩶"; + inline constexpr const char white_heart[] = "🤍"; + inline constexpr const char brown_heart[] = "🤎"; + inline constexpr const char broken_heart[] = "💔"; + inline constexpr const char heart_exclamation[] = "❣️"; + inline constexpr const char heavy_heart_exclamation_mark_ornament[] = "❣️"; + inline constexpr const char two_hearts[] = "💕"; + inline constexpr const char revolving_hearts[] = "💞"; + inline constexpr const char heartbeat[] = "💓"; + inline constexpr const char beating_heart[] = "💓"; + inline constexpr const char heartpulse[] = "💗"; + inline constexpr const char growing_heart[] = "💗"; + inline constexpr const char sparkling_heart[] = "💖"; + inline constexpr const char cupid[] = "💘"; + inline constexpr const char gift_heart[] = "💝"; + inline constexpr const char mending_heart[] = "❤️‍🩹"; + inline constexpr const char heart_on_fire[] = "❤️‍🔥"; + inline constexpr const char heart_decoration[] = "💟"; + inline constexpr const char peace[] = "☮️"; + inline constexpr const char peace_symbol[] = "☮️"; + inline constexpr const char cross[] = "✝️"; + inline constexpr const char latin_cross[] = "✝️"; + inline constexpr const char star_and_crescent[] = "☪️"; + inline constexpr const char om_symbol[] = "🕉️"; + inline constexpr const char wheel_of_dharma[] = "☸️"; + inline constexpr const char khanda[] = "🪯"; + inline constexpr const char star_of_david[] = "✡️"; + inline constexpr const char six_pointed_star[] = "🔯"; + inline constexpr const char menorah[] = "🕎"; + inline constexpr const char yin_yang[] = "☯️"; + inline constexpr const char orthodox_cross[] = "☦️"; + inline constexpr const char place_of_worship[] = "🛐"; + inline constexpr const char worship_symbol[] = "🛐"; + inline constexpr const char ophiuchus[] = "⛎"; + inline constexpr const char aries[] = "♈"; + inline constexpr const char taurus[] = "♉"; + inline constexpr const char gemini[] = "♊"; + inline constexpr const char cancer[] = "♋"; + inline constexpr const char leo[] = "♌"; + inline constexpr const char virgo[] = "♍"; + inline constexpr const char libra[] = "♎"; + inline constexpr const char scorpius[] = "♏"; + inline constexpr const char scorpio[] = "♏"; + inline constexpr const char sagittarius[] = "♐"; + inline constexpr const char capricorn[] = "♑"; + inline constexpr const char aquarius[] = "♒"; + inline constexpr const char pisces[] = "♓"; + inline constexpr const char id[] = "🆔"; + inline constexpr const char atom[] = "⚛️"; + inline constexpr const char atom_symbol[] = "⚛️"; + inline constexpr const char accept[] = "🉑"; + inline constexpr const char radioactive[] = "☢️"; + inline constexpr const char radioactive_sign[] = "☢️"; + inline constexpr const char biohazard[] = "☣️"; + inline constexpr const char biohazard_sign[] = "☣️"; + inline constexpr const char mobile_phone_off[] = "📴"; + inline constexpr const char vibration_mode[] = "📳"; + inline constexpr const char u6709[] = "🈶"; + inline constexpr const char u7121[] = "🈚"; + inline constexpr const char u7533[] = "🈸"; + inline constexpr const char u55b6[] = "🈺"; + inline constexpr const char u6708[] = "🈷️"; + inline constexpr const char eight_pointed_black_star[] = "✴️"; + inline constexpr const char vs[] = "🆚"; + inline constexpr const char white_flower[] = "💮"; + inline constexpr const char ideograph_advantage[] = "🉐"; + inline constexpr const char secret[] = "㊙️"; + inline constexpr const char congratulations[] = "㊗️"; + inline constexpr const char u5408[] = "🈴"; + inline constexpr const char u6e80[] = "🈵"; + inline constexpr const char u5272[] = "🈹"; + inline constexpr const char u7981[] = "🈲"; + inline constexpr const char a[] = "🅰️"; + inline constexpr const char b[] = "🅱️"; + inline constexpr const char ab[] = "🆎"; + inline constexpr const char cl[] = "🆑"; + inline constexpr const char o2[] = "🅾️"; + inline constexpr const char sos[] = "🆘"; + inline constexpr const char x[] = "❌"; + inline constexpr const char cross_mark[] = "❌"; + inline constexpr const char o[] = "⭕"; + inline constexpr const char octagonal_sign[] = "🛑"; + inline constexpr const char stop_sign[] = "🛑"; + inline constexpr const char no_entry[] = "⛔"; + inline constexpr const char name_badge[] = "📛"; + inline constexpr const char no_entry_sign[] = "🚫"; + inline constexpr const char prohibited[] = "🚫"; + inline constexpr const char anger[] = "💢"; + inline constexpr const char hotsprings[] = "♨️"; + inline constexpr const char hot_springs[] = "♨️"; + inline constexpr const char no_pedestrians[] = "🚷"; + inline constexpr const char do_not_litter[] = "🚯"; + inline constexpr const char no_littering[] = "🚯"; + inline constexpr const char no_bicycles[] = "🚳"; + inline constexpr const char non_potable_water[] = "🚱"; + inline constexpr const char underage[] = "🔞"; + inline constexpr const char no_mobile_phones[] = "📵"; + inline constexpr const char no_smoking[] = "🚭"; + inline constexpr const char exclamation[] = "❗"; + inline constexpr const char grey_exclamation[] = "❕"; + inline constexpr const char question[] = "❓"; + inline constexpr const char question_mark[] = "❓"; + inline constexpr const char grey_question[] = "❔"; + inline constexpr const char bangbang[] = "‼️"; + inline constexpr const char interrobang[] = "⁉️"; + inline constexpr const char low_brightness[] = "🔅"; + inline constexpr const char high_brightness[] = "🔆"; + inline constexpr const char part_alternation_mark[] = "〽️"; + inline constexpr const char warning[] = "⚠️"; + inline constexpr const char children_crossing[] = "🚸"; + inline constexpr const char trident[] = "🔱"; + inline constexpr const char fleur_de_lis[] = "⚜️"; + inline constexpr const char beginner[] = "🔰"; + inline constexpr const char recycle[] = "♻️"; + inline constexpr const char white_check_mark[] = "✅"; + inline constexpr const char u6307[] = "🈯"; + inline constexpr const char chart[] = "💹"; + inline constexpr const char sparkle[] = "❇️"; + inline constexpr const char eight_spoked_asterisk[] = "✳️"; + inline constexpr const char negative_squared_cross_mark[] = "❎"; + inline constexpr const char globe_with_meridians[] = "🌐"; + inline constexpr const char diamond_shape_with_a_dot_inside[] = "💠"; + inline constexpr const char m[] = "Ⓜ️"; + inline constexpr const char circled_m[] = "Ⓜ️"; + inline constexpr const char cyclone[] = "🌀"; + inline constexpr const char zzz[] = "💤"; + inline constexpr const char atm[] = "🏧"; + inline constexpr const char wc[] = "🚾"; + inline constexpr const char water_closet[] = "🚾"; + inline constexpr const char wheelchair[] = "♿"; + inline constexpr const char parking[] = "🅿️"; + inline constexpr const char elevator[] = "🛗"; + inline constexpr const char u7a7a[] = "🈳"; + inline constexpr const char sa[] = "🈂️"; + inline constexpr const char passport_control[] = "🛂"; + inline constexpr const char customs[] = "🛃"; + inline constexpr const char baggage_claim[] = "🛄"; + inline constexpr const char left_luggage[] = "🛅"; + inline constexpr const char wireless[] = "🛜"; + inline constexpr const char mens[] = "🚹"; + inline constexpr const char mens_room[] = "🚹"; + inline constexpr const char womens[] = "🚺"; + inline constexpr const char womens_room[] = "🚺"; + inline constexpr const char baby_symbol[] = "🚼"; + inline constexpr const char restroom[] = "🚻"; + inline constexpr const char put_litter_in_its_place[] = "🚮"; + inline constexpr const char cinema[] = "🎦"; + inline constexpr const char signal_strength[] = "📶"; + inline constexpr const char antenna_bars[] = "📶"; + inline constexpr const char koko[] = "🈁"; + inline constexpr const char symbols[] = "🔣"; + inline constexpr const char input_symbols[] = "🔣"; + inline constexpr const char information_source[] = "ℹ️"; + inline constexpr const char information[] = "ℹ️"; + inline constexpr const char abc[] = "🔤"; + inline constexpr const char abcd[] = "🔡"; + inline constexpr const char capital_abcd[] = "🔠"; + inline constexpr const char ng[] = "🆖"; + inline constexpr const char ok[] = "🆗"; + inline constexpr const char up[] = "🆙"; + inline constexpr const char cool[] = "🆒"; + inline constexpr const char _new[] = "🆕"; + inline constexpr const char free[] = "🆓"; + inline constexpr const char zero[] = "0️⃣"; + inline constexpr const char one[] = "1️⃣"; + inline constexpr const char two[] = "2️⃣"; + inline constexpr const char three[] = "3️⃣"; + inline constexpr const char four[] = "4️⃣"; + inline constexpr const char five[] = "5️⃣"; + inline constexpr const char six[] = "6️⃣"; + inline constexpr const char seven[] = "7️⃣"; + inline constexpr const char eight[] = "8️⃣"; + inline constexpr const char nine[] = "9️⃣"; + inline constexpr const char keycap_ten[] = "🔟"; + inline constexpr const char input_numbers[] = "🔢"; + inline constexpr const char hash[] = "#️⃣"; + inline constexpr const char asterisk[] = "*️⃣"; + inline constexpr const char keycap_asterisk[] = "*️⃣"; + inline constexpr const char eject[] = "⏏️"; + inline constexpr const char eject_symbol[] = "⏏️"; + inline constexpr const char arrow_forward[] = "▶️"; + inline constexpr const char pause_button[] = "⏸️"; + inline constexpr const char double_vertical_bar[] = "⏸️"; + inline constexpr const char play_pause[] = "⏯️"; + inline constexpr const char stop_button[] = "⏹️"; + inline constexpr const char record_button[] = "⏺️"; + inline constexpr const char track_next[] = "⏭️"; + inline constexpr const char next_track[] = "⏭️"; + inline constexpr const char track_previous[] = "⏮️"; + inline constexpr const char previous_track[] = "⏮️"; + inline constexpr const char fast_forward[] = "⏩"; + inline constexpr const char rewind[] = "⏪"; + inline constexpr const char arrow_double_up[] = "⏫"; + inline constexpr const char arrow_double_down[] = "⏬"; + inline constexpr const char arrow_backward[] = "◀️"; + inline constexpr const char arrow_up_small[] = "🔼"; + inline constexpr const char arrow_down_small[] = "🔽"; + inline constexpr const char arrow_right[] = "➡️"; + inline constexpr const char right_arrow[] = "➡️"; + inline constexpr const char arrow_left[] = "⬅️"; + inline constexpr const char left_arrow[] = "⬅️"; + inline constexpr const char arrow_up[] = "⬆️"; + inline constexpr const char up_arrow[] = "⬆️"; + inline constexpr const char arrow_down[] = "⬇️"; + inline constexpr const char down_arrow[] = "⬇️"; + inline constexpr const char arrow_upper_right[] = "↗️"; + inline constexpr const char arrow_lower_right[] = "↘️"; + inline constexpr const char arrow_lower_left[] = "↙️"; + inline constexpr const char arrow_upper_left[] = "↖️"; + inline constexpr const char up_left_arrow[] = "↖️"; + inline constexpr const char arrow_up_down[] = "↕️"; + inline constexpr const char up_down_arrow[] = "↕️"; + inline constexpr const char left_right_arrow[] = "↔️"; + inline constexpr const char arrow_right_hook[] = "↪️"; + inline constexpr const char leftwards_arrow_with_hook[] = "↩️"; + inline constexpr const char arrow_heading_up[] = "⤴️"; + inline constexpr const char arrow_heading_down[] = "⤵️"; + inline constexpr const char twisted_rightwards_arrows[] = "🔀"; + inline constexpr const char repeat[] = "🔁"; + inline constexpr const char repeat_one[] = "🔂"; + inline constexpr const char arrows_counterclockwise[] = "🔄"; + inline constexpr const char arrows_clockwise[] = "🔃"; + inline constexpr const char musical_note[] = "🎵"; + inline constexpr const char notes[] = "🎶"; + inline constexpr const char musical_notes[] = "🎶"; + inline constexpr const char heavy_plus_sign[] = "➕"; + inline constexpr const char heavy_minus_sign[] = "➖"; + inline constexpr const char heavy_division_sign[] = "➗"; + inline constexpr const char heavy_multiplication_x[] = "✖️"; + inline constexpr const char heavy_equals_sign[] = "🟰"; + inline constexpr const char infinity[] = "♾️"; + inline constexpr const char heavy_dollar_sign[] = "💲"; + inline constexpr const char currency_exchange[] = "💱"; + inline constexpr const char tm[] = "™️"; + inline constexpr const char trade_mark[] = "™️"; + inline constexpr const char copyright[] = "©️"; + inline constexpr const char registered[] = "®️"; + inline constexpr const char wavy_dash[] = "〰️"; + inline constexpr const char curly_loop[] = "➰"; + inline constexpr const char loop[] = "➿"; + inline constexpr const char end[] = "🔚"; + inline constexpr const char end_arrow[] = "🔚"; + inline constexpr const char back[] = "🔙"; + inline constexpr const char back_arrow[] = "🔙"; + inline constexpr const char on[] = "🔛"; + inline constexpr const char on_arrow[] = "🔛"; + inline constexpr const char top[] = "🔝"; + inline constexpr const char top_arrow[] = "🔝"; + inline constexpr const char soon[] = "🔜"; + inline constexpr const char soon_arrow[] = "🔜"; + inline constexpr const char heavy_check_mark[] = "✔️"; + inline constexpr const char check_mark[] = "✔️"; + inline constexpr const char ballot_box_with_check[] = "☑️"; + inline constexpr const char radio_button[] = "🔘"; + inline constexpr const char white_circle[] = "⚪"; + inline constexpr const char black_circle[] = "⚫"; + inline constexpr const char red_circle[] = "🔴"; + inline constexpr const char blue_circle[] = "🔵"; + inline constexpr const char brown_circle[] = "🟤"; + inline constexpr const char purple_circle[] = "🟣"; + inline constexpr const char green_circle[] = "🟢"; + inline constexpr const char yellow_circle[] = "🟡"; + inline constexpr const char orange_circle[] = "🟠"; + inline constexpr const char small_red_triangle[] = "🔺"; + inline constexpr const char small_red_triangle_down[] = "🔻"; + inline constexpr const char small_orange_diamond[] = "🔸"; + inline constexpr const char small_blue_diamond[] = "🔹"; + inline constexpr const char large_orange_diamond[] = "🔶"; + inline constexpr const char large_blue_diamond[] = "🔷"; + inline constexpr const char white_square_button[] = "🔳"; + inline constexpr const char black_square_button[] = "🔲"; + inline constexpr const char black_small_square[] = "▪️"; + inline constexpr const char white_small_square[] = "▫️"; + inline constexpr const char black_medium_small_square[] = "◾"; + inline constexpr const char white_medium_small_square[] = "◽"; + inline constexpr const char black_medium_square[] = "◼️"; + inline constexpr const char white_medium_square[] = "◻️"; + inline constexpr const char black_large_square[] = "⬛"; + inline constexpr const char white_large_square[] = "⬜"; + inline constexpr const char orange_square[] = "🟧"; + inline constexpr const char blue_square[] = "🟦"; + inline constexpr const char red_square[] = "🟥"; + inline constexpr const char brown_square[] = "🟫"; + inline constexpr const char purple_square[] = "🟪"; + inline constexpr const char green_square[] = "🟩"; + inline constexpr const char yellow_square[] = "🟨"; + inline constexpr const char speaker[] = "🔈"; + inline constexpr const char mute[] = "🔇"; + inline constexpr const char muted_speaker[] = "🔇"; + inline constexpr const char sound[] = "🔉"; + inline constexpr const char loud_sound[] = "🔊"; + inline constexpr const char bell[] = "🔔"; + inline constexpr const char no_bell[] = "🔕"; + inline constexpr const char mega[] = "📣"; + inline constexpr const char megaphone[] = "📣"; + inline constexpr const char loudspeaker[] = "📢"; + inline constexpr const char speech_left[] = "🗨️"; + inline constexpr const char left_speech_bubble[] = "🗨️"; + inline constexpr const char eye_in_speech_bubble[] = "👁‍🗨"; + inline constexpr const char speech_balloon[] = "💬"; + inline constexpr const char thought_balloon[] = "💭"; + inline constexpr const char anger_right[] = "🗯️"; + inline constexpr const char right_anger_bubble[] = "🗯️"; + inline constexpr const char spades[] = "♠️"; + inline constexpr const char spade_suit[] = "♠️"; + inline constexpr const char clubs[] = "♣️"; + inline constexpr const char club_suit[] = "♣️"; + inline constexpr const char hearts[] = "♥️"; + inline constexpr const char heart_suit[] = "♥️"; + inline constexpr const char diamonds[] = "♦️"; + inline constexpr const char diamond_suit[] = "♦️"; + inline constexpr const char black_joker[] = "🃏"; + inline constexpr const char joker[] = "🃏"; + inline constexpr const char flower_playing_cards[] = "🎴"; + inline constexpr const char mahjong[] = "🀄"; + inline constexpr const char clock1[] = "🕐"; + inline constexpr const char one_oclock[] = "🕐"; + inline constexpr const char clock2[] = "🕑"; + inline constexpr const char two_oclock[] = "🕑"; + inline constexpr const char clock3[] = "🕒"; + inline constexpr const char three_oclock[] = "🕒"; + inline constexpr const char clock4[] = "🕓"; + inline constexpr const char four_oclock[] = "🕓"; + inline constexpr const char clock5[] = "🕔"; + inline constexpr const char five_oclock[] = "🕔"; + inline constexpr const char clock6[] = "🕕"; + inline constexpr const char six_oclock[] = "🕕"; + inline constexpr const char clock7[] = "🕖"; + inline constexpr const char seven_oclock[] = "🕖"; + inline constexpr const char clock8[] = "🕗"; + inline constexpr const char eight_oclock[] = "🕗"; + inline constexpr const char clock9[] = "🕘"; + inline constexpr const char nine_oclock[] = "🕘"; + inline constexpr const char clock10[] = "🕙"; + inline constexpr const char ten_oclock[] = "🕙"; + inline constexpr const char clock11[] = "🕚"; + inline constexpr const char eleven_oclock[] = "🕚"; + inline constexpr const char clock12[] = "🕛"; + inline constexpr const char twelve_oclock[] = "🕛"; + inline constexpr const char clock130[] = "🕜"; + inline constexpr const char one_thirty[] = "🕜"; + inline constexpr const char clock230[] = "🕝"; + inline constexpr const char two_thirty[] = "🕝"; + inline constexpr const char clock330[] = "🕞"; + inline constexpr const char three_thirty[] = "🕞"; + inline constexpr const char clock430[] = "🕟"; + inline constexpr const char four_thirty[] = "🕟"; + inline constexpr const char clock530[] = "🕠"; + inline constexpr const char five_thirty[] = "🕠"; + inline constexpr const char clock630[] = "🕡"; + inline constexpr const char six_thirty[] = "🕡"; + inline constexpr const char clock730[] = "🕢"; + inline constexpr const char seven_thirty[] = "🕢"; + inline constexpr const char clock830[] = "🕣"; + inline constexpr const char eight_thirty[] = "🕣"; + inline constexpr const char clock930[] = "🕤"; + inline constexpr const char nine_thirty[] = "🕤"; + inline constexpr const char clock1030[] = "🕥"; + inline constexpr const char ten_thirty[] = "🕥"; + inline constexpr const char clock1130[] = "🕦"; + inline constexpr const char eleven_thirty[] = "🕦"; + inline constexpr const char clock1230[] = "🕧"; + inline constexpr const char twelve_thirty[] = "🕧"; + inline constexpr const char female_sign[] = "♀️"; + inline constexpr const char male_sign[] = "♂️"; + inline constexpr const char transgender_symbol[] = "⚧"; + inline constexpr const char medical_symbol[] = "⚕️"; + inline constexpr const char regional_indicator_z[] = "🇿"; + inline constexpr const char regional_indicator_y[] = "🇾"; + inline constexpr const char regional_indicator_x[] = "🇽"; + inline constexpr const char regional_indicator_w[] = "🇼"; + inline constexpr const char regional_indicator_v[] = "🇻"; + inline constexpr const char regional_indicator_u[] = "🇺"; + inline constexpr const char regional_indicator_t[] = "🇹"; + inline constexpr const char regional_indicator_s[] = "🇸"; + inline constexpr const char regional_indicator_r[] = "🇷"; + inline constexpr const char regional_indicator_q[] = "🇶"; + inline constexpr const char regional_indicator_p[] = "🇵"; + inline constexpr const char regional_indicator_o[] = "🇴"; + inline constexpr const char regional_indicator_n[] = "🇳"; + inline constexpr const char regional_indicator_m[] = "🇲"; + inline constexpr const char regional_indicator_l[] = "🇱"; + inline constexpr const char regional_indicator_k[] = "🇰"; + inline constexpr const char regional_indicator_j[] = "🇯"; + inline constexpr const char regional_indicator_i[] = "🇮"; + inline constexpr const char regional_indicator_h[] = "🇭"; + inline constexpr const char regional_indicator_g[] = "🇬"; + inline constexpr const char regional_indicator_f[] = "🇫"; + inline constexpr const char regional_indicator_e[] = "🇪"; + inline constexpr const char regional_indicator_d[] = "🇩"; + inline constexpr const char regional_indicator_c[] = "🇨"; + inline constexpr const char regional_indicator_b[] = "🇧"; + inline constexpr const char regional_indicator_a[] = "🇦"; + inline constexpr const char red_car[] = "🚗"; + inline constexpr const char automobile[] = "🚗"; + inline constexpr const char taxi[] = "🚕"; + inline constexpr const char blue_car[] = "🚙"; + inline constexpr const char pickup_truck[] = "🛻"; + inline constexpr const char minibus[] = "🚐"; + inline constexpr const char bus[] = "🚌"; + inline constexpr const char trolleybus[] = "🚎"; + inline constexpr const char race_car[] = "🏎️"; + inline constexpr const char racing_car[] = "🏎️"; + inline constexpr const char police_car[] = "🚓"; + inline constexpr const char ambulance[] = "🚑"; + inline constexpr const char fire_engine[] = "🚒"; + inline constexpr const char truck[] = "🚚"; + inline constexpr const char articulated_lorry[] = "🚛"; + inline constexpr const char tractor[] = "🚜"; + inline constexpr const char probing_cane[] = "🦯"; + inline constexpr const char manual_wheelchair[] = "🦽"; + inline constexpr const char motorized_wheelchair[] = "🦼"; + inline constexpr const char crutch[] = "🩼"; + inline constexpr const char scooter[] = "🛴"; + inline constexpr const char kick_scooter[] = "🛴"; + inline constexpr const char bike[] = "🚲"; + inline constexpr const char bicycle[] = "🚲"; + inline constexpr const char motor_scooter[] = "🛵"; + inline constexpr const char motorbike[] = "🛵"; + inline constexpr const char motorcycle[] = "🏍️"; + inline constexpr const char racing_motorcycle[] = "🏍️"; + inline constexpr const char auto_rickshaw[] = "🛺"; + inline constexpr const char wheel[] = "🛞"; + inline constexpr const char rotating_light[] = "🚨"; + inline constexpr const char oncoming_police_car[] = "🚔"; + inline constexpr const char oncoming_bus[] = "🚍"; + inline constexpr const char oncoming_automobile[] = "🚘"; + inline constexpr const char oncoming_taxi[] = "🚖"; + inline constexpr const char aerial_tramway[] = "🚡"; + inline constexpr const char mountain_cableway[] = "🚠"; + inline constexpr const char suspension_railway[] = "🚟"; + inline constexpr const char railway_car[] = "🚃"; + inline constexpr const char train[] = "🚋"; + inline constexpr const char tram_car[] = "🚋"; + inline constexpr const char mountain_railway[] = "🚞"; + inline constexpr const char monorail[] = "🚝"; + inline constexpr const char bullettrain_side[] = "🚄"; + inline constexpr const char bullettrain_front[] = "🚅"; + inline constexpr const char bullet_train[] = "🚅"; + inline constexpr const char light_rail[] = "🚈"; + inline constexpr const char steam_locomotive[] = "🚂"; + inline constexpr const char locomotive[] = "🚂"; + inline constexpr const char train2[] = "🚆"; + inline constexpr const char metro[] = "🚇"; + inline constexpr const char tram[] = "🚊"; + inline constexpr const char station[] = "🚉"; + inline constexpr const char airplane[] = "✈️"; + inline constexpr const char airplane_departure[] = "🛫"; + inline constexpr const char airplane_arriving[] = "🛬"; + inline constexpr const char airplane_small[] = "🛩️"; + inline constexpr const char small_airplane[] = "🛩️"; + inline constexpr const char seat[] = "💺"; + inline constexpr const char satellite_orbital[] = "🛰️"; + inline constexpr const char rocket[] = "🚀"; + inline constexpr const char flying_saucer[] = "🛸"; + inline constexpr const char helicopter[] = "🚁"; + inline constexpr const char canoe[] = "🛶"; + inline constexpr const char kayak[] = "🛶"; + inline constexpr const char sailboat[] = "⛵"; + inline constexpr const char speedboat[] = "🚤"; + inline constexpr const char motorboat[] = "🛥️"; + inline constexpr const char motor_boat[] = "🛥️"; + inline constexpr const char cruise_ship[] = "🛳️"; + inline constexpr const char passenger_ship[] = "🛳️"; + inline constexpr const char ferry[] = "⛴️"; + inline constexpr const char ship[] = "🚢"; + inline constexpr const char ring_buoy[] = "🛟"; + inline constexpr const char anchor[] = "⚓"; + inline constexpr const char hook[] = "🪝"; + inline constexpr const char fuelpump[] = "⛽"; + inline constexpr const char fuel_pump[] = "⛽"; + inline constexpr const char construction[] = "🚧"; + inline constexpr const char vertical_traffic_light[] = "🚦"; + inline constexpr const char traffic_light[] = "🚥"; + inline constexpr const char busstop[] = "🚏"; + inline constexpr const char bus_stop[] = "🚏"; + inline constexpr const char map[] = "🗺️"; + inline constexpr const char world_map[] = "🗺️"; + inline constexpr const char moyai[] = "🗿"; + inline constexpr const char moai[] = "🗿"; + inline constexpr const char statue_of_liberty[] = "🗽"; + inline constexpr const char tokyo_tower[] = "🗼"; + inline constexpr const char european_castle[] = "🏰"; + inline constexpr const char castle[] = "🏰"; + inline constexpr const char japanese_castle[] = "🏯"; + inline constexpr const char stadium[] = "🏟️"; + inline constexpr const char ferris_wheel[] = "🎡"; + inline constexpr const char roller_coaster[] = "🎢"; + inline constexpr const char carousel_horse[] = "🎠"; + inline constexpr const char fountain[] = "⛲"; + inline constexpr const char beach_umbrella[] = "⛱️"; + inline constexpr const char umbrella_on_ground[] = "⛱️"; + inline constexpr const char beach[] = "🏖️"; + inline constexpr const char beach_with_umbrella[] = "🏖️"; + inline constexpr const char island[] = "🏝️"; + inline constexpr const char desert_island[] = "🏝️"; + inline constexpr const char desert[] = "🏜️"; + inline constexpr const char volcano[] = "🌋"; + inline constexpr const char mountain[] = "⛰️"; + inline constexpr const char mountain_snow[] = "🏔️"; + inline constexpr const char snow_capped_mountain[] = "🏔️"; + inline constexpr const char mount_fuji[] = "🗻"; + inline constexpr const char camping[] = "🏕️"; + inline constexpr const char tent[] = "⛺"; + inline constexpr const char house[] = "🏠"; + inline constexpr const char house_with_garden[] = "🏡"; + inline constexpr const char homes[] = "🏘️"; + inline constexpr const char house_buildings[] = "🏘️"; + inline constexpr const char houses[] = "🏘️"; + inline constexpr const char house_abandoned[] = "🏚️"; + inline constexpr const char derelict_house_building[] = "🏚️"; + inline constexpr const char hut[] = "🛖"; + inline constexpr const char construction_site[] = "🏗️"; + inline constexpr const char building_construction[] = "🏗️"; + inline constexpr const char factory[] = "🏭"; + inline constexpr const char office[] = "🏢"; + inline constexpr const char department_store[] = "🏬"; + inline constexpr const char post_office[] = "🏣"; + inline constexpr const char european_post_office[] = "🏤"; + inline constexpr const char hospital[] = "🏥"; + inline constexpr const char bank[] = "🏦"; + inline constexpr const char hotel[] = "🏨"; + inline constexpr const char convenience_store[] = "🏪"; + inline constexpr const char school[] = "🏫"; + inline constexpr const char love_hotel[] = "🏩"; + inline constexpr const char wedding[] = "💒"; + inline constexpr const char classical_building[] = "🏛️"; + inline constexpr const char church[] = "⛪"; + inline constexpr const char mosque[] = "🕌"; + inline constexpr const char synagogue[] = "🕍"; + inline constexpr const char hindu_temple[] = "🛕"; + inline constexpr const char kaaba[] = "🕋"; + inline constexpr const char shinto_shrine[] = "⛩️"; + inline constexpr const char railway_track[] = "🛤️"; + inline constexpr const char railroad_track[] = "🛤️"; + inline constexpr const char motorway[] = "🛣️"; + inline constexpr const char japan[] = "🗾"; + inline constexpr const char map_of_japan[] = "🗾"; + inline constexpr const char rice_scene[] = "🎑"; + inline constexpr const char park[] = "🏞️"; + inline constexpr const char national_park[] = "🏞️"; + inline constexpr const char sunrise[] = "🌅"; + inline constexpr const char sunrise_over_mountains[] = "🌄"; + inline constexpr const char stars[] = "🌠"; + inline constexpr const char shooting_star[] = "🌠"; + inline constexpr const char sparkler[] = "🎇"; + inline constexpr const char fireworks[] = "🎆"; + inline constexpr const char city_sunset[] = "🌇"; + inline constexpr const char city_sunrise[] = "🌇"; + inline constexpr const char sunset[] = "🌇"; + inline constexpr const char city_dusk[] = "🌆"; + inline constexpr const char cityscape[] = "🏙️"; + inline constexpr const char night_with_stars[] = "🌃"; + inline constexpr const char milky_way[] = "🌌"; + inline constexpr const char bridge_at_night[] = "🌉"; + inline constexpr const char foggy[] = "🌁"; +} +}; diff --git a/3rdParty/dpp/user.h b/3rdParty/dpp/user.h index c582f443c3..4d111ebc26 100644 --- a/3rdParty/dpp/user.h +++ b/3rdParty/dpp/user.h @@ -20,7 +20,7 @@ ************************************************************************************/ #pragma once #include -#include +#include #include #include #include @@ -28,75 +28,193 @@ namespace dpp { +constexpr uint32_t MAX_AVATAR_SIZE = 10240 * 1000; // 10240KB. + /** * @brief Various bitmask flags used to represent information about a dpp::user */ enum user_flags : uint32_t { - /// User is a bot - u_bot = 0b00000000000000000000001, - /// User is a system user (Clyde!) - u_system = 0b00000000000000000000010, - /// User has multi-factor authentication enabled - u_mfa_enabled = 0b00000000000000000000100, - /// User is verified (verified email address) - u_verified = 0b00000000000000000001000, - /// User has full nitro - u_nitro_full = 0b00000000000000000010000, - /// User has nitro classic - u_nitro_classic = 0b00000000000000000100000, - /// User is discord staff - u_discord_employee = 0b00000000000000001000000, - /// User owns a partnered server - u_partnered_owner = 0b00000000000000010000000, - /// User is a member of hypesquad events - u_hypesquad_events = 0b00000000000000100000000, - /// User has BugHunter level 1 - u_bughunter_1 = 0b00000000000001000000000, - /// User is a member of House Bravery - u_house_bravery = 0b00000000000010000000000, - /// User is a member of House Brilliance - u_house_brilliance = 0b00000000000100000000000, - /// User is a member of House Balance - u_house_balance = 0b00000000001000000000000, - /// User is an early supporter - u_early_supporter = 0b00000000010000000000000, - /// User is a team user - u_team_user = 0b00000000100000000000000, - /// User is has Bug Hunter level 2 - u_bughunter_2 = 0b00000001000000000000000, - /// User is a verified bot - u_verified_bot = 0b00000010000000000000000, - /// User has the Early Verified Bot Developer badge - u_verified_bot_dev = 0b00000100000000000000000, - /// User's icon is animated - u_animated_icon = 0b00001000000000000000000, - /// User is a certified moderator - u_certified_moderator = 0b00010000000000000000000, - /// User is a bot using HTTP interactions (shows online even when not connected to a websocket) - u_bot_http_interactions = 0b00100000000000000000000, - /// User has nitro basic - u_nitro_basic = 0b01000000000000000000000, - /// User has the active developer badge - u_active_developer = 0b10000000000000000000000, + /** + * @brief User is a bot. + */ + u_bot = 0b00000000000000000000000000000001, + + /** + * @brief User is a system user (Clyde!). + */ + u_system = 0b00000000000000000000000000000010, + + /** + * @brief User has multi-factor authentication enabled. + */ + u_mfa_enabled = 0b00000000000000000000000000000100, + + /** + * @brief User is verified (verified email address). + */ + u_verified = 0b00000000000000000000000000001000, + + /** + * @brief User has full nitro. + */ + u_nitro_full = 0b00000000000000000000000000010000, + + /** + * @brief User has nitro classic. + */ + u_nitro_classic = 0b00000000000000000000000000100000, + + /** + * @brief User is discord staff. + */ + u_discord_employee = 0b00000000000000000000000001000000, + + /** + * @brief User owns a partnered server. + */ + u_partnered_owner = 0b00000000000000000000000010000000, + + /** + * @brief User is a member of hypesquad events. + */ + u_hypesquad_events = 0b00000000000000000000000100000000, + + /** + * @brief User has BugHunter level 1. + */ + u_bughunter_1 = 0b00000000000000000000001000000000, + + /** + * @brief User is a member of House Bravery. + */ + u_house_bravery = 0b00000000000000000000010000000000, + + /** + * @brief User is a member of House Brilliance. + */ + u_house_brilliance = 0b00000000000000000000100000000000, + + /** + * @brief User is a member of House Balance. + */ + u_house_balance = 0b00000000000000000001000000000000, + + /** + * @brief User is an early supporter. + */ + u_early_supporter = 0b00000000000000000010000000000000, + + /** + * @brief User is a team user. + */ + u_team_user = 0b00000000000000000100000000000000, + + /** + * @brief User is has Bug Hunter level 2. + */ + u_bughunter_2 = 0b00000000000000001000000000000000, + + /** + * @brief User is a verified bot. + */ + u_verified_bot = 0b00000000000000010000000000000000, + + /** + * @brief User has the Early Verified Bot Developer badge. + */ + u_verified_bot_dev = 0b00000000000000100000000000000000, + + /** + * @brief User's icon is animated. + */ + u_animated_icon = 0b00000000000001000000000000000000, + + /** + * @brief User is a certified moderator. + */ + u_certified_moderator = 0b00000000000010000000000000000000, + + /** + * @brief User is a bot using HTTP interactions. + * + * @note shows online even when not connected to a websocket. + */ + u_bot_http_interactions = 0b00000000000100000000000000000000, + + /** + * @brief User has nitro basic. + */ + u_nitro_basic = 0b00000000001000000000000000000000, + + /** + * @brief User has the active developer badge. + */ + u_active_developer = 0b00000000010000000000000000000000, + + /** + * @brief User's banner is animated. + */ + u_animated_banner = 0b00000000100000000000000000000000, }; /** * @brief Represents a user on discord. May or may not be a member of a dpp::guild. */ -class DPP_EXPORT user : public managed, public json_interface { +class DPP_EXPORT user : public managed, public json_interface { +protected: + friend struct json_interface; + + /** Fill this record from json. + * @param j The json to fill this record from + * @return Reference to self + */ + user& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Convert to JSON + * + * @param with_id include ID in output + * @return json JSON output + */ + virtual json to_json_impl(bool with_id = true) const; + public: - /** Discord username */ + /** + * @brief Discord username. + */ std::string username; - /** Avatar hash */ + + /** + * @brief Global display name. + */ + std::string global_name; + + /** + * @brief Avatar hash. + */ utility::iconhash avatar; - /** Flags built from a bitmask of values in dpp::user_flags */ + + /** + * @brief Avatar decoration hash. + */ + utility::iconhash avatar_decoration; + + /** + * @brief Flags built from a bitmask of values in dpp::user_flags. + */ uint32_t flags; - /** Discriminator (aka tag), 4 digits usually displayed with leading zeroes. + + /** + * @brief Discriminator (aka tag), 4 digits usually displayed with leading zeroes. * - * @note To print the discriminator with leading zeroes, use format_username() + * @note To print the discriminator with leading zeroes, use format_username(). + * 0 for users that have migrated to the new username format. */ uint16_t discriminator; - /** Reference count of how many guilds this user is in */ + + /** + * @brief Reference count of how many guilds this user is in. + */ uint8_t refcount; /** @@ -107,36 +225,45 @@ class DPP_EXPORT user : public managed, public json_interface { /** * @brief Destroy the user object */ - virtual ~user(); + virtual ~user() = default; /** - * @brief Create a mentionable user. + * @brief Create a mentionable user. * @param id The ID of the user. * @return std::string The formatted mention of the user. - */ + */ static std::string get_mention(const snowflake& id); - /** Fill this record from json. - * @param j The json to fill this record from - * @return Reference to self - */ - user& fill_from_json(nlohmann::json* j); + /** + * @brief Get the avatar url of the user + * + * @note If the user doesn't have an avatar, the default user avatar url is returned which is always in `png` format! + * + * @param size The size of the avatar in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized avatar is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string avatar url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_avatar_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; /** - * @brief Convert to JSON string - * - * @param with_id include ID in output - * @return std::string JSON output + * @brief Get the default avatar url of the user. This is calculated by the discriminator. + * + * @return std::string avatar url or an empty string, if the discriminator is empty */ - virtual std::string build_json(bool with_id = true) const; + std::string get_default_avatar_url() const; /** - * @brief Get the avatar url of the user object + * @brief Get the avatar decoration url of the user if they have one, otherwise returns an empty string. * - * @param size The size of the avatar in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized avatar is returned. - * @return std::string avatar url. If the user doesn't have an avatar, the default user avatar url is returned + * @param size The size of the avatar decoration in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized avatar decoration is returned. + * @return std::string avatar url or an empty string */ - std::string get_avatar_url(uint16_t size = 0) const; + std::string get_avatar_decoration_url(uint16_t size = 0) const; /** * @brief Return a ping/mention for the user @@ -145,36 +272,48 @@ class DPP_EXPORT user : public managed, public json_interface { */ std::string get_mention() const; + /** + * @brief Returns URL to user + * + * @return string of URL to user + */ + std::string get_url() const; + /** * @brief Return true if user has the active Developer badge * * @return true if has active developer */ bool is_active_developer() const; + /** * @brief User is a bot * * @return True if the user is a bot */ bool is_bot() const; + /** * @brief User is a system user (Clyde) * * @return true if user is a system user */ bool is_system() const; + /** * @brief User has multi-factor authentication enabled * * @return true if multi-factor is enabled */ bool is_mfa_enabled() const; + /** * @brief Return true if user has verified account * * @return true if verified */ bool is_verified() const; + /** * @brief Return true if user has full nitro. * This is mutually exclusive with full nitro. @@ -182,6 +321,7 @@ class DPP_EXPORT user : public managed, public json_interface { * @return true if user has full nitro */ bool has_nitro_full() const; + /** * @brief Return true if user has nitro classic. * This is mutually exclusive with nitro classic. @@ -189,6 +329,7 @@ class DPP_EXPORT user : public managed, public json_interface { * @return true if user has nitro classic */ bool has_nitro_classic() const; + /** * @brief Return true if user has nitro basic. * This is mutually exclusive with nitro basic. @@ -196,84 +337,98 @@ class DPP_EXPORT user : public managed, public json_interface { * @return true if user has nitro basic */ bool has_nitro_basic() const; + /** * @brief Return true if user is a discord employee * * @return true if user is discord staff */ bool is_discord_employee() const; + /** * @brief Return true if user owns a partnered server * * @return true if user has partnered server */ bool is_partnered_owner() const; + /** * @brief Return true if user has hypesquad events * * @return true if has hypesquad events */ bool has_hypesquad_events() const; + /** * @brief Return true if user has the bughunter level 1 badge * * @return true if has bughunter level 1 */ bool is_bughunter_1() const; + /** * @brief Return true if user is in house bravery * * @return true if in house bravery */ bool is_house_bravery() const; + /** * @brief Return true if user is in house brilliance * * @return true if in house brilliance */ bool is_house_brilliance() const; + /** * @brief Return true if user is in house balance * * @return true if in house brilliance */ bool is_house_balance() const; + /** * @brief Return true if user is an early supporter * * @return true if early supporter */ bool is_early_supporter() const; + /** * @brief Return true if user is a team user * * @return true if a team user */ bool is_team_user() const; + /** * @brief Return true if user has the bughunter level 2 badge * * @return true if has bughunter level 2 */ bool is_bughunter_2() const; + /** * @brief Return true if user has the verified bot badge * * @return true if verified bot */ bool is_verified_bot() const; + /** * @brief Return true if user is an early verified bot developer * * @return true if verified bot developer */ bool is_verified_bot_dev() const; + /** * @brief Return true if user is a certified moderator * * @return true if certified moderator */ bool is_certified_moderator() const; + /** * @brief Return true if user is a bot which exclusively uses HTTP interactions. * Bots using HTTP interactions are always considered online even when not connected to a websocket. @@ -281,6 +436,7 @@ class DPP_EXPORT user : public managed, public json_interface { * @return true if is a http interactions only bot */ bool is_bot_http_interactions() const; + /** * @brief Return true if user has an animated icon * @@ -289,9 +445,12 @@ class DPP_EXPORT user : public managed, public json_interface { bool has_animated_icon() const; /** - * @brief Format a username into user#discriminator + * @brief Format a username into user\#discriminator * * For example Brain#0001 + * + * @note This will, most often, return something like Brain#0000 due to discriminators slowly being removed. + * Some accounts, along with most bots, still have discriminators, so they will still show as Bot#1234. * * @return Formatted username and discriminator */ @@ -304,44 +463,96 @@ class DPP_EXPORT user : public managed, public json_interface { * which bots do not normally have. */ class DPP_EXPORT user_identified : public user, public json_interface { -public: - std::string locale; //!< Optional: the user's chosen language option identify - std::string email; //!< Optional: the user's email email (may be empty) - utility::iconhash banner; //!< Optional: the user's banner hash identify (may be empty) - uint32_t accent_color; //!< Optional: the user's banner color encoded as an integer representation of hexadecimal color code identify (may be empty) - bool verified; //!< Optional: whether the email on this account has been verified email - +protected: + friend struct json_interface; + /** Fill this record from json. * @param j The json to fill this record from * @return Reference to self */ - user_identified& fill_from_json(nlohmann::json* j); + user_identified& fill_from_json_impl(nlohmann::json* j); /** - * @brief Convert to JSON string - * + * @brief Convert to JSON + * * @param with_id include ID in output - * @return std::string JSON output + * @return json JSON output + */ + virtual json to_json_impl(bool with_id = true) const; + +public: + /** + * @brief Optional: The user's chosen language option identify. + */ + std::string locale; + + /** + * @brief Optional: The user's email. + * + * @note This may be empty. + */ + std::string email; + + /** + * @brief Optional: The user's banner hash identify. + * + * @note This may be empty. + */ + utility::iconhash banner; + + /** + * @brief Optional: The user's banner color encoded as an integer representation of hexadecimal color code identify. + * + * @note This may be empty. */ - virtual std::string build_json(bool with_id = true) const; + uint32_t accent_color; + + + /** + * @brief Optional: Whether the email on this account has been verified email. + */ + bool verified; /** * @brief Construct a new user identified object */ user_identified(); + /** + * @brief Construct a new user identified object from a user object + * + * @param u user object + */ + user_identified(const user& u); + /** * @brief Destroy the user identified object */ - virtual ~user_identified(); + virtual ~user_identified() = default; + + using json_interface::fill_from_json; + using json_interface::build_json; + using json_interface::to_json; /** - * @brief Get the user identified's banner url if they have one, otherwise returns an empty string + * @brief Return true if user has an animated banner * - * @param size The size of the banner in pixels. It can be any power of two between 16 and 4096. if not specified, the default sized banner is returned. - * @return std::string banner url or empty string + * @return true if banner is animated (gif) */ - std::string get_banner_url(uint16_t size = 0) const; + bool has_animated_banner() const; + + /** + * @brief Get the user identified's banner url if they have one, otherwise returns an empty string + * + * @param size The size of the banner in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized banner is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string banner url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_banner_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; }; @@ -365,7 +576,9 @@ void from_json(const nlohmann::json& j, user& u); */ void from_json(const nlohmann::json& j, user_identified& u); -/** A group of users */ +/** + * @brief A group of users. + */ typedef std::unordered_map user_map; -}; +} diff --git a/3rdParty/dpp/utility.h b/3rdParty/dpp/utility.h index a4c17f6051..f7c9fbecd5 100644 --- a/3rdParty/dpp/utility.h +++ b/3rdParty/dpp/utility.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -27,461 +28,1042 @@ #include #include #include +#include +#include +#include +#include /** * @brief The main namespace for D++ functions, classes and types */ namespace dpp { + +enum sticker_format : uint8_t; + +/** + * @brief Macro that expands to static_asserts checking sizeof and alignof are equal between two types. + */ +#define DPP_CHECK_ABI_COMPAT(a, b) \ +static_assert(sizeof(a) == sizeof(b), #a " and " #b " must be the same size for ABI compatibility"); \ +static_assert(alignof(a) == alignof(b), #a " and " #b " must be the same alignment for ABI compatibility"); \ + +/** + * @brief Utility helper functions, generally for logging, running programs, time/date manipulation, etc + */ +namespace utility { + +/** + * @brief Helper function to easily create discord's cdn endpoint urls. + * @warning **For internal use only!** + * + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param allowed_formats A vector of supported formats for the endpoint from the discord docs + * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) + * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string + * @param size the image size which will be appended as a querystring to the url. + * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) + * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. + * In this case, the `format`-parameter is only used for non-animated images. + * @param is_animated Whether the image is actually animated or not + * @return std::string endpoint url or empty string + */ +std::string DPP_EXPORT cdn_endpoint_url(const std::vector &allowed_formats, const std::string &path_without_extension, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); + +/** + * @brief Helper function to easily create discord's cdn endpoint urls. + * @warning **For internal use only!** + * + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param allowed_formats A vector of supported formats for the endpoint from the discord docs + * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) + * @param hash The hash (optional). If not empty, it will be prefixed with `a_` for animated images (`is_animated`=true) + * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string + * @param size the image size which will be appended as a querystring to the url. + * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) + * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. + * In this case, the `format`-parameter is only used for non-animated images. + * @param is_animated Whether the image is actually animated or not + * @return std::string endpoint url or empty string + */ +std::string DPP_EXPORT cdn_endpoint_url_hash(const std::vector &allowed_formats, const std::string &path_without_extension, const std::string &hash, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); + +/** + * @brief Helper function to easily create discord's cdn endpoint urls (specialised for stickers) + * @warning **For internal use only!** + * + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param sticker_id The sticker ID. Returns empty string if 0 + * @param format The format type + * @return std::string endpoint url or empty string + */ +std::string DPP_EXPORT cdn_endpoint_url_sticker(snowflake sticker_id, sticker_format format); + +/** + * @brief Supported AVX instruction set type for audio mixing + */ +enum avx_type_t : uint8_t { + /** + * @brief No AVX Support + */ + avx_none, + + /** + * @brief AVX support + */ + avx_1, + + /** + * @brief AVX2 support + */ + avx_2, + + /** + * @brief AVX512 support + */ + avx_512, +}; + +/** + * @brief Timestamp formats for dpp::utility::timestamp() + * + * @note These values are the actual character values specified by the Discord API + * and should not be changed unless the Discord API changes the specification! + * They have been sorted into numerical order of their ASCII value to keep C++ happy. + */ +enum time_format : uint8_t { + /** + * @brief "20 April 2021" - Long Date + */ + tf_long_date = 'D', + + /** + * @brief "Tuesday, 20 April 2021 16:20" - Long Date/Time + */ + tf_long_datetime = 'F', + + /** + * @brief "2 months ago" - Relative Time + */ + tf_relative_time = 'R', + + /** + * @brief "16:20:30" - Long Time + */ + tf_long_time = 'T', + + /** + * @brief "20/04/2021" - Short Date + */ + tf_short_date = 'd', + + /** + * @brief "20 April 2021 16:20" - Short Date/Time + */ + tf_short_datetime = 'f', + + /** + * @brief "16:20" - Short Time + */ + tf_short_time = 't', +}; + +/** + * @brief Guild navigation types for dpp::utility::guild_navigation() + */ +enum guild_navigation_type { + /** + * @brief _Customize_ tab with the server's dpp::onboarding_prompt + */ + gnt_customize, + + /** + * @brief "16:20" _Browse Channels_ tab + */ + gnt_browse, + + /** + * @brief Server Guide + */ + gnt_guide, +}; + +/** + * @brief The base URL for CDN content such as profile pictures and guild icons. + */ +inline const std::string cdn_host = "https://cdn.discordapp.com"; + +/** + * @brief The base URL for message/user/channel links. + */ +inline const std::string url_host = "https://discord.com"; + +/** + * @brief Callback for the results of a command executed via dpp::utility::exec + */ +typedef std::function cmd_result_t; + +/** + * @brief Run a commandline program asynchronously. The command line program + * is spawned in a separate std::thread, and when complete, its output from + * stdout is passed to the callback function in its string parameter. For example: + * ```cpp + * dpp::utility::exec("/bin/ls", {"-al"}, [](const std::string& output) { + * std::cout << "Output of 'ls -al': " << output << "\n"; + * }); + * ``` + * + * @param cmd The command to run. + * @param parameters Command line parameters. Each will be escaped using `std::quoted`. + * @param callback The callback to call on completion. + */ +void DPP_EXPORT exec(const std::string& cmd, std::vector parameters = {}, cmd_result_t callback = {}); + +/** + * @brief Return a mentionable timestamp (used in a message). These timestamps will display the given timestamp in the user's timezone and locale. + * + * @param ts Time stamp to convert + * @param tf Format of timestamp using dpp::utility::time_format + * @return std::string The formatted timestamp + */ +std::string DPP_EXPORT timestamp(time_t ts, time_format tf = tf_short_datetime); + +/** + * @brief Create a mentionable guild navigation (used in a message). + * + * @param guild_id The guild ID + * @param gnt Guild navigation type using dpp::utility::guild_navigation_type + * @return std::string The formatted timestamp + */ +std::string DPP_EXPORT guild_navigation(const snowflake guild_id, guild_navigation_type gnt); + +/** + * @brief Returns current date and time + * + * @return std::string Current date and time in "Y-m-d H:M:S" format + */ +std::string DPP_EXPORT current_date_time(); +/** + * @brief Convert a dpp::loglevel enum value to a string + * + * @param in log level to convert + * @return std::string string form of log level + */ +std::string DPP_EXPORT loglevel(dpp::loglevel in); + +/** + * @brief Store a 128 bit icon hash (profile picture, server icon etc) + * as a 128 bit binary value made of two uint64_t. + * Has a constructor to build one from a string, and a method to fetch + * the value back in string form. + */ +struct DPP_EXPORT iconhash { + /** + * @brief High 64 bits. + */ + uint64_t first; + + /** + * @brief Low 64 bits. + */ + uint64_t second; + + /** + * @brief Construct a new iconhash object + * @param _first Leftmost portion of the hash value + * @param _second Rightmost portion of the hash value + */ + iconhash(uint64_t _first = 0, uint64_t _second = 0) noexcept; + + /** + * @brief Construct a new iconhash object + * + * @param hash String hash to construct from. + * Must contain a 32 character hex string. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + iconhash(const std::string &hash); + + /** + * @brief Assign from std::string + * + * @param assignment string to assign from. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + iconhash& operator=(const std::string &assignment); + + /** + * @brief Check if one iconhash is equal to another + * + * @param other other iconhash to compare + * @return True if the iconhash objects match + */ + bool operator==(const iconhash& other) const noexcept; + + /** + * @brief Change value of iconhash object + * + * @param hash String hash to change to. + * Must contain a 32 character hex string. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + void set(const std::string &hash); + + /** + * @brief Convert iconhash back to 32 character + * string value. + * + * @return std::string Hash value + */ + std::string to_string() const; +}; + +/** + * @brief Image to be received or sent to API calls. + * + * This class is carefully crafted to be 16 bytes, + * this is why we use a ptr + 4 byte size instead of a vector. + * We want this class to be substitutable with iconhash in data structures. + */ +struct DPP_EXPORT image_data { + /** + * @brief Data in bytes of the image. + */ + std::unique_ptr data = nullptr; + + /** + * @brief Size of the data in bytes. + */ + uint32_t size = 0; + + /** + * @brief Type of the image. + * + * @see image_type + */ + image_type type = {}; + + /** + * @brief Construct an empty image. + */ + image_data() = default; + + /** + * @brief Copy an image. + * + * @param rhs Image to copy + */ + image_data(const image_data& rhs); + + /** + * @brief Move an image. + * + * @param rhs Image to copy + */ + image_data(image_data&& rhs) noexcept = default; + + /** + * @brief Construct from string buffer + * + * @param format Image format + * @param bytes Data in a string + * @see image_type + */ + image_data(image_type format, std::string_view bytes); + + /** + * @brief Construct from byte buffer + * + * @param format Image format + * @param bytes Data of the image + * @param byte_size Image size in bytes + * @see image_type + */ + image_data(image_type format, const std::byte* bytes, uint32_t byte_size); + + /** + * @brief Copy an image data. + * + * @param rhs Image to copy + * @return self for chaining + */ + image_data& operator=(const image_data& rhs); + + /** + * @brief Move an image data. + * + * @param rhs Image to move from + * @return self for chaining + */ + image_data& operator=(image_data&& rhs) noexcept = default; + + /** + * @brief Set image data. + * + * @param format Image format + * @param bytes Data of the image + */ + void set(image_type format, std::string_view bytes); + + /** + * @brief Set image data. + * + * @param format Format of the image + * @param bytes Data of the image + * @param byte_size Image size in bytes + */ + void set(image_type format, const std::byte* bytes, uint32_t byte_size); + + /** + * @brief Encode to base64. + * + * @return std::string New string with the image data encoded in base64 + */ + std::string base64_encode() const; + + /** + * @brief Get the file extension. + * + * Alias for \ref file_extension + * @return std::string File extension e.g. `.png` + */ + std::string get_file_extension() const; + + /** + * @brief Get the mime type. + * + * Alias for \ref mime_type + * @return std::string File mime type e.g. "image/png" + */ + std::string get_mime_type() const; + + /** + * @brief Check if this is an empty image. + * + * @return bool Whether the image is empty or not + */ + bool empty() const noexcept; + + /** + * @brief Build a data URI scheme suitable for sending to Discord + * + * @see https://discord.com/developers/docs/reference#image-data + * @return The data URI scheme as a json or null if empty + */ + json to_nullable_json() const; +}; + +/** + * @brief Wrapper class around a variant for either iconhash or image, + * for API objects that have one or the other (generally iconhash when receiving, + * image when uploading an image) + */ +struct icon { + /** + * @brief Iconhash received or image data for upload. + */ + std::variant hash_or_data; + + /** + * @brief Assign to iconhash. + * + * @param hash Iconhash + */ + icon& operator=(const iconhash& hash); + + /** + * @brief Assign to iconhash. + * + * @param hash Iconhash + */ + icon& operator=(iconhash&& hash) noexcept; + + /** + * @brief Assign to image. + * + * @param img Image + */ + icon& operator=(const image_data& img); + + /** + * @brief Assign to image. + * + * @param img Image + */ + icon& operator=(image_data&& img) noexcept; + + /** + * @brief Check whether this icon is stored as an iconhash + * + * @see iconhash + * @return bool Whether this icon is stored as an iconhash + */ + bool is_iconhash() const; + + /** + * @brief Get as icon hash. + * + * @warning The behavior is undefined if `is_iconhash() == false` + * @return iconhash& This iconhash + */ + iconhash& as_iconhash() &; + /** - * @brief Utility helper functions, generally for logging, running programs, time/date manipulation, etc - */ - namespace utility { - - /** - * @brief Timestamp formats for dpp::utility::timestamp() - * - * @note These values are the actual character values specified by the Discord API - * and should not be changed unless the Discord API changes the specification! - * They have been sorted into numerical order of their ASCII value to keep C++ happy. - */ - enum time_format : uint8_t { - /// "20 April 2021" - Long Date - tf_long_date = 'D', - /// "Tuesday, 20 April 2021 16:20" - Long Date/Time - tf_long_datetime = 'F', - /// "2 months ago" - Relative Time - tf_relative_time = 'R', - /// "16:20:30" - Long Time - tf_long_time = 'T', - /// "20/04/2021" - Short Date - tf_short_date = 'd', - /// "20 April 2021 16:20" - Short Date/Time - tf_short_datetime = 'f', - /// "16:20" - Short Time - tf_short_time = 't', - }; - - /** - * @brief The base URL for CDN content such as profile pictures and guild icons. - */ - const std::string cdn_host = "https://cdn.discordapp.com"; - - /** - * @brief Callback for the results of a command executed via dpp::utility::exec - */ - typedef std::function cmd_result_t; - - /** - * @brief Run a commandline program asynchronously. The command line program - * is spawned in a separate std::thread, and when complete, its output from - * stdout is passed to the callback function in its string parameter. For example - * ``` - * dpp::utility::exec("/bin/ls", {"-al"}, [](const std::string& output) { - * std::cout << "Output of 'ls -al': " << output << "\n"; - * }); - * ``` - * - * @param cmd The command to run. - * @param parameters Command line parameters. Each will be escaped using `std::quoted`. - * @param callback The callback to call on completion. - */ - void DPP_EXPORT exec(const std::string& cmd, std::vector parameters = {}, cmd_result_t callback = {}); - - /** - * @brief Return a mentionable timestamp (used in a message). These timestamps will display the given timestamp in the user's timezone and locale. - * - * @param ts Time stamp to convert - * @param tf Format of timestamp using dpp::utility::time_format - * @return std::string The formatted timestamp - */ - std::string DPP_EXPORT timestamp(time_t ts, time_format tf = tf_short_datetime); - - /** - * @brief Returns current date and time - * - * @return std::string Current date and time in "Y-m-d H:M:S" format - */ - std::string DPP_EXPORT current_date_time(); - /** - * @brief Convert a dpp::loglevel enum value to a string - * - * @param in log level to convert - * @return std::string string form of log level - */ - std::string DPP_EXPORT loglevel(dpp::loglevel in); - - /** - * @brief Store a 128 bit icon hash (profile picture, server icon etc) - * as a 128 bit binary value made of two uint64_t. - * Has a constructor to build one from a string, and a method to fetch - * the value back in string form. - */ - struct DPP_EXPORT iconhash { - - uint64_t first; //!< High 64 bits - uint64_t second; //!< Low 64 bits - - /** - * @brief Construct a new iconhash object - * @param _first Leftmost portion of the hash value - * @param _second Rightmost portion of the hash value - */ - iconhash(uint64_t _first = 0, uint64_t _second = 0); - - /** - * @brief Construct a new iconhash object - */ - iconhash(const iconhash&); - - /** - * @brief Destroy the iconhash object - */ - ~iconhash(); - - /** - * @brief Construct a new iconhash object - * - * @param hash String hash to construct from. - * Must contain a 32 character hex string. - * - * @throws std::length_error if the provided - * string is not exactly 32 characters long. - */ - iconhash(const std::string &hash); - - /** - * @brief Assign from std::string - * - * @param assignment string to assign from. - * - * @throws std::length_error if the provided - * string is not exactly 32 characters long. - */ - iconhash& operator=(const std::string &assignment); - - /** - * @brief Check if one iconhash is equal to another - * - * @param other other iconhash to compare - * @return True if the iconhash objects match - */ - bool operator==(const iconhash& other) const; - - /** - * @brief Change value of iconhash object - * - * @param hash String hash to change to. - * Must contain a 32 character hex string. - * - * @throws std::length_error if the provided - * string is not exactly 32 characters long. - */ - void set(const std::string &hash); - - /** - * @brief Convert iconhash back to 32 character - * string value. - * - * @return std::string Hash value - */ - std::string to_string() const; - }; - - /** - * @brief Return the current time with fractions of seconds. - * This is a unix epoch time with the fractional seconds part - * after the decimal place. - * - * @return double time with fractional seconds - */ - double DPP_EXPORT time_f(); - - /** - * @brief Returns true if D++ was built with voice support - * - * @return bool True if voice support is compiled in (libsodium/libopus) - */ - bool DPP_EXPORT has_voice(); - - /** - * @brief Convert a byte count to display value - * - * @param c number of bytes - * @return std::string display value suffixed with M, G, T where necessary - */ - std::string DPP_EXPORT bytes(uint64_t c); - - /** - * @brief A class used to represent an uptime in hours, minutes, - * seconds and days, with helper functions to convert from time_t - * and display as a string. - */ - struct DPP_EXPORT uptime { - uint16_t days; //!< Number of days - uint8_t hours; //!< Number of hours - uint8_t mins; //!< Number of minutes - uint8_t secs; //!< Number of seconds - - /** - * @brief Construct a new uptime object - */ - uptime(); - - /** - * @brief Construct a new uptime object - * - * @param diff A time_t to initialise the object from - */ - uptime(time_t diff); - - /** - * @brief Construct a new uptime object - * - * @param diff A time_t to initialise the object from - */ - uptime(double diff); - - /** - * @brief Get uptime as string - * - * @return std::string Uptime as string - */ - std::string to_string() const; - - /** - * @brief Get uptime as seconds - * - * @return uint64_t Uptime as seconds - */ - uint64_t to_secs() const; - - /** - * @brief Get uptime as milliseconds - * - * @return uint64_t Uptime as milliseconds - */ - uint64_t to_msecs() const; - }; - - /** - * @brief Convert doubles to RGB for sending in embeds - * - * @param red red value, between 0 and 1 inclusive - * @param green green value, between 0 and 1 inclusive - * @param blue blue value, between 0 and 1 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT rgb(double red, double green, double blue); - - /** - * @brief Convert ints to RGB for sending in embeds - * - * @param red red value, between 0 and 255 inclusive - * @param green green value, between 0 and 255 inclusive - * @param blue blue value, between 0 and 255 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT rgb(int red, int green, int blue); - - /** - * @brief Convert doubles to CMYK for sending in embeds - * - * @param c cyan value, between 0 and 1 inclusive - * @param m magenta value, between 0 and 1 inclusive - * @param y yellow value, between 0 and 1 inclusive - * @param k key (black) value, between 0 and 1 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT cmyk(double c, double m, double y, double k); - - /** - * @brief Convert ints to CMYK for sending in embeds - * - * @param c cyan value, between 0 and 255 inclusive - * @param m magenta value, between 0 and 255 inclusive - * @param y yellow value, between 0 and 255 inclusive - * @param k key (black) value, between 0 and 255 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT cmyk(int c, int m, int y, int k); - - /** - * @brief Output hex values of a section of memory for debugging - * - * @param data The start of the data to display - * @param length The length of data to display - */ - std::string DPP_EXPORT debug_dump(uint8_t* data, size_t length); - - /** - * @brief Returns the length of a UTF-8 string in codepoints - * - * @param str string to count length of - * @return size_t length of string (0 for invalid utf8) - */ - size_t DPP_EXPORT utf8len(const std::string &str); - - /** - * @brief Return substring of a UTF-8 encoded string in codepoints - * - * @param str string to return substring from - * @param start start codepoint offset - * @param length length in codepoints - * @return std::string Substring in UTF-8 or empty string if invalid UTF-8 passed in - */ - std::string DPP_EXPORT utf8substr(const std::string& str, std::string::size_type start, std::string::size_type length); - - /** - * @brief Read a whole file into a std::string. - * Be sure you have enough memory to read the file, if you are reading a large file. - * @note Be aware this function can block! If you are regularly reading large files, consider caching them. - * @param filename The path to the file to read - * @return std::string The file contents - * @throw dpp::exception on failure to read the entire file - */ - std::string DPP_EXPORT read_file(const std::string& filename); - - /** - * @brief Validate a string value - * In the event the length of the string is less than _min, then an exception of type dpp:length_exception - * will be thrown. If the string is longer than _max UTF8 codepoints it will be truncated to fit. - * - * @param value The value to validate - * @param _min Minimum length - * @param _max Maximum length - * @param exception_message Exception message to throw if value length < _min - * @return std::string Validated string, truncated if necessary. - * @throw dpp::length_exception if value UTF8 length < _min - */ - std::string DPP_EXPORT validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message); - - /** - * @brief Return the url parameter for an avatar size, or empty if the size is 0 - * - * @param size size to generate url parameter for - * @return std::string url parameter - */ - std::string DPP_EXPORT avatar_size(uint32_t size); - - /** - * @brief Split (tokenize) a string into a vector, using the given separators - * - * @param in Input string - * @param sep Separator characters - * @return std::vector Tokenized strings - */ - std::vector DPP_EXPORT tokenize(std::string const &in, const char* sep = "\r\n"); - - /** - * @brief Create a bot invite - * - * @param bot_id Bot ID - * @param permissions Permission bitmask of the bot to invite - * @param scopes Scopes to use - * @return Invite URL - */ - std::string DPP_EXPORT bot_invite_url(const snowflake bot_id, const uint64_t permissions = 0, const std::vector& scopes = {"bot", "applications.commands"}); - - /** - * @brief Escapes Discord's markdown sequences in a string - * - * @param text Text to escape - * @param escape_code_blocks If set to false, then code blocks are not escaped. - * This means that you can still use a code block, and the text within will be left as-is. - * If set to true, code blocks will also be escaped so that ` symbol may be used as a normal - * character. - * @return std::string The text with the markdown special characters escaped with a backslash - */ - std::string DPP_EXPORT markdown_escape(const std::string& text, bool escape_code_blocks = false); - - /** - * @brief Encodes a url parameter similar to [php urlencode()](https://www.php.net/manual/en/function.urlencode.php) - * - * @param value String to encode - * @return std::string URL encoded string - */ - std::string DPP_EXPORT url_encode(const std::string &value); - - /** - * @brief Create a mentionable slashcommand (used in a message). - * @param command_id The ID of the slashcommand - * @param command_name The command name - * @param subcommand Optional: The subcommand name (for mentioning a subcommand) - * @return std::string The formatted mention - */ - std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand = ""); - - /** - * @brief Create a mentionable slashcommand (used in a message). - * @param command_id The ID of the slashcommand - * @param command_name The command name - * @param subcommand_group The subcommand group name - * @param subcommand The subcommand name - * @return std::string The formatted mention of the slashcommand with its subcommand - */ - std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand); - - /** - * @brief Create a mentionable user. - * @param id The ID of the user. - * @return std::string The formatted mention of the user. - */ - std::string DPP_EXPORT user_mention(const snowflake& id); - - /** - * @brief Create a mentionable channel. - * @param id The ID of the channel. - * @return std::string The formatted mention of the channel. - */ - std::string DPP_EXPORT channel_mention(const snowflake& id); - - /** - * @brief Create a mentionable emoji - * @param name The name of the emoji. - * @param id The ID of the emoji. - * @param is_animated is emoji animated. - * @return std::string The formatted mention of the emoji. - */ - std::string DPP_EXPORT emoji_mention(const std::string& name, const snowflake& id, bool is_animated = false); - - /** - * @brief Create a mentionable role. - * @param id The ID of the role. - * @return std::string The formatted mention of the role. - */ - std::string DPP_EXPORT role_mention(const snowflake& id); - - /** - * @brief Returns the library's version string - * - * @return std::string version - */ - std::string DPP_EXPORT version(); - - /** - * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. - * Entries with empty key names or values are omitted. - * - * @param parameters parameters to create a url query string for - * @return std::string A correctly encoded url query string - */ - std::string DPP_EXPORT make_url_parameters(const std::map& parameters); - - /** - * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. - * Entries with empty key names or zero values are omitted. - * - * @param parameters parameters to create a url query string for - * @return std::string A correctly encoded url query string - */ - std::string DPP_EXPORT make_url_parameters(const std::map& parameters); - - /** - * @brief Set the name of the current thread for debugging and statistical reporting - * - * @param name New name to set - */ - void DPP_EXPORT set_thread_name(const std::string& name); - - }; + * @brief Get as icon hash. + * + * @warning The behavior is undefined if `is_iconhash() == false` + * @return iconhash& This iconhash + */ + const iconhash& as_iconhash() const&; + + /** + * @brief Get as icon hash. + * + * @warning The behavior is undefined if `is_iconhash() == false` + * @return iconhash& This iconhash + */ + iconhash&& as_iconhash() &&; + + /** + * @brief Check whether this icon is stored as an image + * + * @see image_data + * @return bool Whether this icon is stored as an image + */ + bool is_image_data() const; + + /** + * @brief Get as image data. + * + * @warning The behavior is undefined if `is_image_data() == false` + * @return image_data& This image + */ + image_data& as_image_data() &; + + /** + * @brief Get as image. + * + * @warning The behavior is undefined if `is_image_data() == false` + * @return image_data& This image + */ + const image_data& as_image_data() const&; + + /** + * @brief Get as image. + * + * @warning The behavior is undefined if `is_image_data() == false` + * @return image_data& This image + */ + image_data&& as_image_data() &&; +}; + +/** + * @brief Return the current time with fractions of seconds. + * This is a unix epoch time with the fractional seconds part + * after the decimal place. + * + * @return double time with fractional seconds + */ +double DPP_EXPORT time_f(); + +/** + * @brief Returns true if D++ was built with voice support + * + * @return bool True if voice support is compiled in (libopus) + */ +bool DPP_EXPORT has_voice(); + +/** + * @brief Returns an enum value indicating which AVX instruction + * set is used for mixing received voice data, if any + * + * @return avx_type_t AVX type + */ +avx_type_t DPP_EXPORT voice_avx(); + +/** + * @brief Returns true if D++ was built with coroutine support + * + * @return bool True if coroutines are supported + */ +bool DPP_EXPORT is_coro_enabled(); + +/** + * @brief Convert a byte count to display value + * + * @param c number of bytes + * @return std::string display value suffixed with M, G, T where necessary + */ +std::string DPP_EXPORT bytes(uint64_t c); + +/** + * @brief A class used to represent an uptime in hours, minutes, + * seconds and days, with helper functions to convert from time_t + * and display as a string. + */ +struct DPP_EXPORT uptime { + /** + * @brief Number of days. + */ + uint16_t days; + + /** + * @brief Number of hours. + */ + uint8_t hours; + + /** + * @brief Number of minutes. + */ + uint8_t mins; + + /** + * @brief Number of seconds. + */ + uint8_t secs; + + /** + * @brief Construct a new uptime object + */ + uptime(); + + /** + * @brief Construct a new uptime object + * + * @param diff A time_t to initialise the object from + */ + uptime(time_t diff); + + /** + * @brief Construct a new uptime object + * + * @param diff A time_t to initialise the object from + */ + uptime(double diff); + + /** + * @brief Get uptime as string + * + * @return std::string Uptime as string + */ + std::string to_string() const; + + /** + * @brief Get uptime as seconds + * + * @return uint64_t Uptime as seconds + */ + uint64_t to_secs() const; + + /** + * @brief Get uptime as milliseconds + * + * @return uint64_t Uptime as milliseconds + */ + uint64_t to_msecs() const; +}; + +/** + * @brief Convert doubles to RGB for sending in embeds + * + * @param red red value, between 0 and 1 inclusive + * @param green green value, between 0 and 1 inclusive + * @param blue blue value, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT rgb(double red, double green, double blue); + +/** + * @brief Convert ints to RGB for sending in embeds + * + * @param red red value, between 0 and 255 inclusive + * @param green green value, between 0 and 255 inclusive + * @param blue blue value, between 0 and 255 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT rgb(int red, int green, int blue); + +/** + * @brief Convert doubles to CMYK for sending in embeds + * + * @param c cyan value, between 0 and 1 inclusive + * @param m magenta value, between 0 and 1 inclusive + * @param y yellow value, between 0 and 1 inclusive + * @param k key (black) value, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT cmyk(double c, double m, double y, double k); + +/** + * @brief Convert ints to CMYK for sending in embeds + * + * @param c cyan value, between 0 and 255 inclusive + * @param m magenta value, between 0 and 255 inclusive + * @param y yellow value, between 0 and 255 inclusive + * @param k key (black) value, between 0 and 255 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT cmyk(int c, int m, int y, int k); + +/** + * @brief Convert doubles to HSL for sending in embeds + * + * @param h hue value, between 0 and 1 inclusive + * @param s saturation value in percentage, between 0 and 1 inclusive + * @param l lightness value in percentage, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT hsl(double h, double s, double l); + +/** + * @brief Convert ints to HSL for sending in embeds + * + * @param h hue value, between 0 and 360 inclusive + * @param s saturation value in percentage, between 0 and 100 inclusive + * @param l lightness value in percentage, between 0 and 100 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT hsl(int h, int s, int l); + +/** + * @brief Output hex values of a section of memory for debugging + * + * @param data The start of the data to display + * @param length The length of data to display + */ +std::string DPP_EXPORT debug_dump(const uint8_t* data, size_t length); + +/** + * @brief Returns the length of a UTF-8 string in codepoints. + * @note Result is unspecified for strings that are not valid UTF-8. + * + * @param str string to count length of + * @return size_t Length of string + */ +size_t DPP_EXPORT utf8len(std::string_view str); + +/** + * @brief Return subview of a UTF-8 encoded string in codepoints. + * @note You must ensure that the resulting view is not used after the lifetime of the viewed string has ended. + * @note Result is unspecified for strings that are not valid UTF-8. + * + * @param str string to return substring from + * @param start start codepoint offset + * @param length length in codepoints + * @return std::string_view The requested subview + */ +std::string_view DPP_EXPORT utf8subview(std::string_view str, size_t start, size_t length); + +/** + * @brief Return substring of a UTF-8 encoded string in codepoints. + * @note Result is unspecified for strings that are not valid UTF-8. + * + * @param str string to return substring from + * @param start start codepoint offset + * @param length length in codepoints + * @return std::string The requested substring + */ +std::string DPP_EXPORT utf8substr(std::string_view str, size_t start, size_t length); + +/** + * @brief Read a whole file into a std::string. + * @note This function can take a while if this is a large file, causing events to not reply and also taking up a lot of memory. Make sure you have enough memory, and use dpp::interaction_create_t::thinking in events where you call this function on big files. + * @warning Be aware this function can block! If you are regularly reading large files, consider caching them. + * @param filename The path to the file to read + * @return std::string The file contents + * @throw dpp::file_exception on failure to read the entire file + */ +std::string DPP_EXPORT read_file(const std::string& filename); + +/** + * @brief Validate a string value + * In the event the length of the string is less than _min, then an exception of type dpp:length_exception + * will be thrown. If the string is longer than _max UTF8 codepoints it will be truncated to fit. + * + * @param value The value to validate + * @param _min Minimum length + * @param _max Maximum length + * @param exception_message Exception message to throw if value length < _min + * @return std::string Validated string, truncated if necessary. + * @throw dpp::length_exception if value UTF8 length < _min + */ +std::string DPP_EXPORT validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message); + +/** + * @brief Get the url query parameter for the cdn endpoint. Internally used to build url getters. + * + * @param size size to generate url parameter for. Must be any power of two between 16 and 4096 (inclusive) or it'll return an empty string. + * @return std::string url query parameter e.g. `?size=128`, or an empty string + */ +std::string DPP_EXPORT avatar_size(uint32_t size); + +/** + * @brief Split (tokenize) a string into a vector, using the given separators + * + * @param in Input string + * @param sep Separator characters + * @return std::vector Tokenized strings + */ +std::vector DPP_EXPORT tokenize(std::string const &in, const char* sep = "\r\n"); + +/** + * @brief Create a bot invite + * + * @param bot_id Bot ID + * @param permissions Permission bitmask of the bot to invite + * @param scopes Scopes to use + * @return Invite URL + */ +std::string DPP_EXPORT bot_invite_url(const snowflake bot_id, const uint64_t permissions = 0, const std::vector& scopes = {"bot", "applications.commands"}); + +/** + * @brief Escapes Discord's markdown sequences in a string + * + * @param text Text to escape + * @param escape_code_blocks If set to false, then code blocks are not escaped. + * This means that you can still use a code block, and the text within will be left as-is. + * If set to true, code blocks will also be escaped so that ` symbol may be used as a normal + * character. + * @return std::string The text with the markdown special characters escaped with a backslash + */ +std::string DPP_EXPORT markdown_escape(const std::string& text, bool escape_code_blocks = false); + +/** + * @brief Encodes a url parameter similar to [php urlencode()](https://www.php.net/manual/en/function.urlencode.php) + * + * @param value String to encode + * @return std::string URL encoded string + */ +std::string DPP_EXPORT url_encode(const std::string &value); + +/** + * @brief Create a mentionable slashcommand (used in a message). + * @param command_id The ID of the slashcommand + * @param command_name The command name + * @param subcommand Optional: The subcommand name (for mentioning a subcommand) + * @return std::string The formatted mention + */ +std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand = ""); + +/** + * @brief Create a mentionable slashcommand (used in a message). + * @param command_id The ID of the slashcommand + * @param command_name The command name + * @param subcommand_group The subcommand group name + * @param subcommand The subcommand name + * @return std::string The formatted mention of the slashcommand with its subcommand + */ +std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand); + +/** + * @brief Create a mentionable user. + * @param id The ID of the user. + * @return std::string The formatted mention of the user. + */ +std::string DPP_EXPORT user_mention(const snowflake& id); + +/** +* @brief Create a mentionable channel. +* @param id The ID of the channel. +* @return std::string The formatted mention of the channel. +*/ +std::string DPP_EXPORT channel_mention(const snowflake& id); + +/** +* @brief Create a mentionable emoji +* @param name The name of the emoji. +* @param id The ID of the emoji. +* @param is_animated is emoji animated. +* @return std::string The formatted mention of the emoji. +*/ +std::string DPP_EXPORT emoji_mention(std::string_view name, snowflake id, bool is_animated = false); + +/** +* @brief Create a mentionable role. +* @param id The ID of the role. +* @return std::string The formatted mention of the role. +*/ +std::string DPP_EXPORT role_mention(const snowflake& id); + +/** +* @brief Create a URL for message. +* @param guild_id The ID of the guild where message is written. +* @param channel_id The ID of the channel where message is written. +* @param message_id The ID of the message. +* @return std::string The URL to message or empty string if any of ids is 0. +*/ +std::string DPP_EXPORT message_url(const snowflake& guild_id, const snowflake& channel_id, const snowflake& message_id); + +/** +* @brief Create a URL for message. +* @param guild_id The ID of the guild where channel is located. +* @param channel_id The ID of the channel. +* @return std::string The URL to message or empty string if any of ids is 0. +*/ +std::string DPP_EXPORT channel_url(const snowflake& guild_id, const snowflake& channel_id); + +/** +* @brief Create a URL for message. +* @param guild_id The ID of the guild where thread is located. +* @param thread_id The ID of the thread. +* @return std::string The URL to message or empty string if any of ids is 0. +*/ +std::string DPP_EXPORT thread_url(const snowflake& guild_id, const snowflake& thread_id); + +/** +* @brief Create a URL for message. +* @param user_id The ID of the guild where thread is located. +* @return std::string The URL to message or empty string if id is 0. +*/ +std::string DPP_EXPORT user_url(const snowflake& user_id); + + + +#ifdef _DOXYGEN_ +/** + * @brief Get the mime type for an image type. + * @param type Image type + * @return std::string The mime type for this image type + */ +std::string DPP_EXPORT mime_type(image_type type); + +/** + * @brief Get the mime type for a sticker format. + * @param format Sticker format + * @return std::string The mime type for this sticker format + */ +std::string DPP_EXPORT mime_type(sticker_format format); + +/** + * @brief Get the file extension for an image type. + * @param type Image type + * @return std::string The file extension (e.g. ".png") for this image type + */ +std::string DPP_EXPORT file_extension(image_type type); + +/** + * @brief Get the file extension for a sticker format. + * @param format Sticker format + * @return std::string The file extension (e.g. ".png") for this sticker format + */ +std::string DPP_EXPORT file_extension(sticker_format format); +#else +/** + * @brief Get the mime type for an image type. + * @param type Image type + * @return std::string The mime type for this image type + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T type); + +/** + * @brief Get the mime type for a sticker format. + * @param format Sticker format + * @return std::string The mime type for this sticker format + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T format); + +/** + * @brief Get the file extension for an image type. + * @param type Image type + * @return std::string The file extension (e.g. ".png") for this image type + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T type); + +/** + * @brief Get the file extension for a sticker format. + * @param format Sticker format + * @return std::string The file extension (e.g. ".png") for this sticker format + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T format); +#endif + +/** + * @brief Returns the library's version string + * + * @return std::string version + */ +std::string DPP_EXPORT version(); + +/** + * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. + * Entries with empty key names or values are omitted. + * + * @param parameters parameters to create a url query string for + * @return std::string A correctly encoded url query string + */ +std::string DPP_EXPORT make_url_parameters(const std::map& parameters); + +/** + * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. + * Entries with empty key names or zero values are omitted. + * + * @param parameters parameters to create a url query string for + * @return std::string A correctly encoded url query string + */ +std::string DPP_EXPORT make_url_parameters(const std::map& parameters); + +/** + * @brief Set the name of the current thread for debugging and statistical reporting + * + * @param name New name to set + */ +void DPP_EXPORT set_thread_name(const std::string& name); + +#ifdef __cpp_concepts // if c++20 +/** + * @brief Concept satisfied if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ +template +concept callable_returns = std::convertible_to, R>; + +/** + * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @deprecated In C++20 mode, prefer using the concept `callable_returns`. + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ +template +inline constexpr bool callable_returns_v = callable_returns; +#else +/** + * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ +template +inline constexpr bool callable_returns_v = std::is_convertible_v, R>; +#endif + +/** + * @brief Utility struct that has the same size and alignment as another but does nothing. Useful for ABI compatibility. + * + * @tparam T Type to mimic + */ +template +struct alignas(T) dummy { + /** @brief Array of bytes with a size mimicking T */ + std::array data; }; + +} // namespace utility +} diff --git a/3rdParty/dpp/version.h b/3rdParty/dpp/version.h index dbc615b2f7..a687326dfb 100644 --- a/3rdParty/dpp/version.h +++ b/3rdParty/dpp/version.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -20,10 +21,10 @@ ************************************************************************************/ #pragma once -#if !defined(DPP_VERSION_LONG) -#define DPP_VERSION_LONG 0x00100022 -#define DPP_VERSION_SHORT 100022 -#define DPP_VERSION_TEXT "D++ 10.0.22 (01-Nov-2022)" +#ifndef DPP_VERSION_LONG +#define DPP_VERSION_LONG 0x00100035 +#define DPP_VERSION_SHORT 100035 +#define DPP_VERSION_TEXT "D++ 10.0.35 (29-Oct-2024)" #define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) #define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) diff --git a/3rdParty/dpp/voiceregion.h b/3rdParty/dpp/voiceregion.h index 39c88057aa..ce14995854 100644 --- a/3rdParty/dpp/voiceregion.h +++ b/3rdParty/dpp/voiceregion.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -21,7 +22,7 @@ #pragma once #include #include -#include +#include #include namespace dpp { @@ -30,16 +31,45 @@ namespace dpp { * @brief Flags related to a voice region */ enum voiceregion_flags { - v_optimal = 0x00000001, - v_deprecated = 0x00000010, - v_custom = 0x00000100, - v_vip = 0x00001000 + /** + * @brief The closest (optimal) voice region. + */ + v_optimal = 0x00000001, + + /** + * @brief A Deprecated voice region (avoid switching to these). + */ + v_deprecated = 0x00000010, + + /** + * @brief A custom voice region (used for events/etc). + */ + v_custom = 0x00000100 }; /** * @brief Represents a voice region on discord */ class DPP_EXPORT voiceregion : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return voiceregion& Reference to self + */ + voiceregion& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build a json for this object + * + * @param with_id Add ID to output + * @return json JSON string + */ + virtual json to_json_impl(bool with_id = false) const; + public: /** * @brief Voice server ID @@ -66,22 +96,6 @@ class DPP_EXPORT voiceregion : public json_interface { */ virtual ~voiceregion() = default; - /** - * @brief Fill object properties from JSON - * - * @param j JSON to fill from - * @return voiceregion& Reference to self - */ - voiceregion& fill_from_json(nlohmann::json* j); - - /** - * @brief Build a json string for this object - * - * @param with_id Add ID to output - * @return std::string JSON string - */ - virtual std::string build_json(bool with_id = false) const; - /** * @brief True if is the optimal voice server * @@ -102,13 +116,6 @@ class DPP_EXPORT voiceregion : public json_interface { * @return true if custom */ bool is_custom() const; - - /** - * @brief True if is a VIP voice server - * - * @return true if VIP - */ - bool is_vip() const; }; /** @@ -116,4 +123,4 @@ class DPP_EXPORT voiceregion : public json_interface { */ typedef std::unordered_map voiceregion_map; -}; +} diff --git a/3rdParty/dpp/voicestate.h b/3rdParty/dpp/voicestate.h index 82eebbe378..8a685d297b 100644 --- a/3rdParty/dpp/voicestate.h +++ b/3rdParty/dpp/voicestate.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -21,7 +22,7 @@ #pragma once #include #include -#include +#include #include #include @@ -31,13 +32,40 @@ namespace dpp { * @brief Bit mask flags relating to voice states */ enum voicestate_flags { - vs_deaf = 0b00000001, //!< Deafened by the server - vs_mute = 0b00000010, //!< Muted by the server - vs_self_mute = 0b00000100, //!< Locally Muted - vs_self_deaf = 0b00001000, //!< Locally deafened - vs_self_stream = 0b00010000, //!< Whether this user is streaming using "Go Live" - vs_self_video = 0b00100000, //!< Whether this user's camera is enabled - vs_suppress = 0b01000000 //!< Whether this user's permission to speak is denied + /** + * @brief Deafened by the server. + */ + vs_deaf = 0b00000001, + + /** + * @brief Muted by the server. + */ + vs_mute = 0b00000010, + + /** + * @brief Locally Muted. + */ + vs_self_mute = 0b00000100, + + /** + * @brief Locally deafened. + */ + vs_self_deaf = 0b00001000, + + /** + * @brief Whether this user is streaming using "Go Live". + */ + vs_self_stream = 0b00010000, + + /** + * @brief Whether this user's camera is enabled. + */ + vs_self_video = 0b00100000, + + /** + * @brief Whether this user's permission to speak is denied. + */ + vs_suppress = 0b01000000 }; /** @@ -46,14 +74,56 @@ enum voicestate_flags { * or via dpp::channel::get_voice_members */ class DPP_EXPORT voicestate : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill voicestate object from json data + * + * @param j JSON data to fill from + * @return voicestate& Reference to self + */ + voicestate& fill_from_json_impl(nlohmann::json* j); + public: - class discord_client* shard; //!< Owning shard - snowflake guild_id; //!< Optional: the guild id this voice state is for - snowflake channel_id; //!< the channel id this user is connected to (may be empty) - snowflake user_id; //!< the user id this voice state is for - std::string session_id; //!< the session id for this voice state - uint8_t flags; //!< Voice state flags (see dpp::voicestate_flags) - time_t request_to_speak; //!< The time at which the user requested to speak, or 0 + /** + * @brief Owning shard. + */ + class discord_client* shard; + + /** + * @brief Optional: The guild id this voice state is for. + */ + snowflake guild_id{0}; + + /** + * @brief The channel id this user is connected to. + * + * @note This may be empty. + */ + snowflake channel_id{0}; + + /** + * @brief The user id this voice state is for. + */ + snowflake user_id{0}; + + /** + * @brief The session id for this voice state. + */ + std::string session_id; + + /** + * @brief Voice state flags from dpp::voicestate_flags. + */ + uint8_t flags{0}; + + /** + * @brief The time at which the user requested to speak. + * + * @note If the user never requested to speak, this is 0. + */ + time_t request_to_speak{0}; /** * @brief Construct a new voicestate object @@ -66,45 +136,44 @@ class DPP_EXPORT voicestate : public json_interface { virtual ~voicestate() = default; /** - * @brief Fill voicestate object from json data - * - * @param j JSON data to fill from - * @return voicestate& Reference to self + * @brief Return true if the user is deafened by the server. */ - voicestate& fill_from_json(nlohmann::json* j); + bool is_deaf() const; /** - * @brief Build json representation of the object - * - * @param with_id Add ID to output - * @return std::string JSON string + * @brief Return true if the user is muted by the server. */ - virtual std::string build_json(bool with_id = false) const; - - /// Return true if the user is deafened by the server - bool is_deaf() const; - - /// Return true if the user is muted by the server bool is_mute() const; - /// Return true if user muted themselves + /** + * @brief Return true if user muted themselves. + */ bool is_self_mute() const; - /// Return true if user deafened themselves + /** + * @brief Return true if user deafened themselves. + */ bool is_self_deaf() const; - /// Return true if the user is streaming using "Go Live" + /** + * @brief Return true if the user is streaming using "Go Live". + */ bool self_stream() const; - /// Return true if the user's camera is enabled + /** + * @brief Return true if the user's camera is enabled. + */ bool self_video() const; - /// Return true if user is suppressed. - /// "HELP HELP I'M BEING SUPPRESSED!" + /** + * @brief Return true if user is suppressed. + * + * "HELP HELP I'M BEING SUPPRESSED!" + */ bool is_suppressed() const; }; /** A container of voicestates */ typedef std::unordered_map voicestate_map; -}; +} diff --git a/3rdParty/dpp/webhook.h b/3rdParty/dpp/webhook.h index 003f0b6cf8..92b07ee21d 100644 --- a/3rdParty/dpp/webhook.h +++ b/3rdParty/dpp/webhook.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -23,7 +24,10 @@ #include #include #include -#include +#include +#include +#include +#include #include #include @@ -33,74 +37,156 @@ namespace dpp { * @brief Defines types of webhook */ enum webhook_type { - w_incoming = 1, //!< Incoming webhook - w_channel_follower = 2 //!< Channel following webhook + /** + * @brief Incoming webhook. + */ + w_incoming = 1, + + /** + * @brief Channel following webhook. + */ + w_channel_follower = 2, + + /** + * @brief Application webhooks for interactions. + */ + w_application = 3 }; /** * @brief Represents a discord webhook */ -class DPP_EXPORT webhook : public managed, public json_interface { +class DPP_EXPORT webhook : public managed, public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill in object from json data + * + * @param j JSON data + * @return webhook& Reference to self + */ + webhook& fill_from_json_impl(nlohmann::json* j); + + /** + * @brief Build JSON string from object + * + * @param with_id Include the ID of the webhook in the json + * @return std::string JSON encoded object + */ + virtual json to_json_impl(bool with_id = false) const; + public: - uint8_t type; //!< the type of the webhook - snowflake guild_id; //!< Optional: the guild id this webhook is for - snowflake channel_id; //!< the channel id this webhook is for - snowflake user_id; //!< Optional: the user this webhook was created by (not returned when getting a webhook with its token) - std::string name; //!< the default name of the webhook (may be empty) - std::string avatar; //!< the default avatar of the webhook (may be empty) - std::string token; //!< Optional: the secure token of the webhook (returned for Incoming Webhooks) - snowflake application_id; //!< the bot/OAuth2 application that created this webhook (may be empty) - std::string* image_data; //!< base64 encoded image data if uploading a new image + /** + * @brief Type of the webhook from dpp::webhook_type. + */ + uint8_t type; /** - * @brief Construct a new webhook object + * @brief The guild id this webhook is for. + * + * @note This field is optional, and may also be empty. */ - webhook(); + snowflake guild_id; /** - * @brief Construct a new webhook object using the Webhook URL provided by Discord + * @brief The channel id this webhook is for. * - * @param webhook_url a fully qualified web address of an existing webhook + * @note This may be empty. */ - webhook(const std::string& webhook_url); + snowflake channel_id; /** - * @brief Construct a new webhook object using the webhook ID and the webhook token + * @brief The user this webhook was created by. * - * @param webhook_id id taken from a link of an existing webhook - * @param webhook_token token taken from a link of an existing webhook + * @note This field is optional. + * @warning This is not returned when getting a webhook with its token! */ - webhook(const snowflake webhook_id, const std::string& webhook_token); + user user_obj; /** - * @brief Destroy the webhook object + * @brief The default name of the webhook. + * + * @note This may be empty. */ - ~webhook(); + std::string name; /** - * @brief Fill in object from json data - * - * @param j JSON data - * @return webhook& Reference to self + * @brief The default avatar of the webhook. + * + * @note This may be empty. */ - webhook& fill_from_json(nlohmann::json* j); + utility::iconhash avatar; /** - * @brief Build JSON string from object - * - * @param with_id Include the ID of the webhook in the json - * @return std::string JSON encoded object + * @brief The secure token of the webhook (returned for Incoming Webhooks). + * + * @note This field is optional. + */ + std::string token; + + /** + * @brief The bot/OAuth2 application that created this webhook. + * + * @note This may be empty. + */ + snowflake application_id; + + /** + * @brief The guild of the channel that this webhook is following (only for Channel Follower Webhooks). + * + * @warning This will be absent if the webhook creator has since lost access to the guild where the followed channel resides! + */ + guild source_guild; + + /** + * @brief The channel that this webhook is following (only for Channel Follower Webhooks). + * + * @warning This will be absent if the webhook creator has since lost access to the guild where the followed channel resides! + */ + channel source_channel; + + /** + * @brief The url used for executing the webhook (returned by the webhooks OAuth2 flow). + */ + std::string url; + + /** + * @brief base64 encoded image data if uploading a new image. + * + * @warning You should only ever read data from here. If you want to set the data, use dpp::webhook::load_image. */ - virtual std::string build_json(bool with_id = false) const; + std::string image_data; + + /** + * @brief Construct a new webhook object + */ + webhook(); + + /** + * @brief Construct a new webhook object using the Webhook URL provided by Discord + * + * @param webhook_url a fully qualified web address of an existing webhook + * @throw logic_exception if the webhook url could not be parsed + */ + webhook(const std::string& webhook_url); + + /** + * @brief Construct a new webhook object using the webhook ID and the webhook token + * + * @param webhook_id id taken from a link of an existing webhook + * @param webhook_token token taken from a link of an existing webhook + */ + webhook(const snowflake webhook_id, const std::string& webhook_token); /** * @brief Base64 encode image data and allocate it to image_data * * @param image_blob Binary image data - * @param type Image type + * @param type Image type. It can be one of `i_gif`, `i_jpg` or `i_png`. * @param is_base64_encoded True if the image data is already base64 encoded * @return webhook& Reference to self - * @throw dpp::exception Image data is larger than the maximum size of 256 kilobytes + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes */ webhook& load_image(const std::string &image_blob, const image_type type, bool is_base64_encoded = false); }; @@ -110,4 +196,4 @@ class DPP_EXPORT webhook : public managed, public json_interface { */ typedef std::unordered_map webhook_map; -}; +} diff --git a/3rdParty/dpp/win32_safe_warnings.h b/3rdParty/dpp/win32_safe_warnings.h index c4a09d21ea..2ab24d2bd5 100644 --- a/3rdParty/dpp/win32_safe_warnings.h +++ b/3rdParty/dpp/win32_safe_warnings.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * diff --git a/3rdParty/dpp/wsclient.h b/3rdParty/dpp/wsclient.h index 6c1b919046..44b753fe42 100644 --- a/3rdParty/dpp/wsclient.h +++ b/3rdParty/dpp/wsclient.h @@ -2,6 +2,7 @@ * * D++, A Lightweight C++ library for Discord * + * SPDX-License-Identifier: Apache-2.0 * Copyright 2021 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * @@ -22,8 +23,6 @@ #include #include #include -#include -#include #include namespace dpp { @@ -36,6 +35,7 @@ enum websocket_protocol_t : uint8_t { * @brief JSON data, text, UTF-8 character set */ ws_json = 0, + /** * @brief Erlang Term Format (ETF) binary protocol */ @@ -62,21 +62,47 @@ enum ws_state : uint8_t { /** * @brief Low-level websocket opcodes for frames */ -enum ws_opcode : uint8_t -{ - OP_CONTINUATION = 0x00, //!< Continuation - OP_TEXT = 0x01, //!< Text frame - OP_BINARY = 0x02, //!< Binary frame - OP_CLOSE = 0x08, //!< Close notification with close code - OP_PING = 0x09, //!< Low level ping - OP_PONG = 0x0a //!< Low level pong +enum ws_opcode : uint8_t { + /** + * @brief Continuation. + */ + OP_CONTINUATION = 0x00, + + /** + * @brief Text frame. + */ + OP_TEXT = 0x01, + + /** + * @brief Binary frame. + */ + OP_BINARY = 0x02, + + /** + * @brief Close notification with close code. + */ + OP_CLOSE = 0x08, + + /** + * @brief Low level ping. + */ + OP_PING = 0x09, + + /** + * @brief Low level pong. + */ + OP_PONG = 0x0a, + + /** + * @brief Automatic selection of type + */ + OP_AUTO = 0xff, }; /** * @brief Implements a websocket client based on the SSL client */ -class DPP_EXPORT websocket_client : public ssl_client -{ +class DPP_EXPORT websocket_client : public ssl_client { /** * @brief Connection key used in the HTTP headers */ @@ -107,7 +133,7 @@ class DPP_EXPORT websocket_client : public ssl_client * @param buffer The buffer to operate on. Will modify the string removing completed items from the head of the queue * @return true if a complete header has been received */ - bool parseheader(std::string &buffer); + bool parseheader(std::string& buffer); /** * @brief Unpack a frame and pass completed frames up the stack. @@ -116,7 +142,7 @@ class DPP_EXPORT websocket_client : public ssl_client * @param first True if is the first element (reserved for future use) * @return true if a complete frame has been received */ - bool unpack(std::string &buffer, uint32_t offset, bool first = true); + bool unpack(std::string& buffer, uint32_t offset, bool first = true); /** * @brief Fill a header for outbound messages @@ -128,11 +154,10 @@ class DPP_EXPORT websocket_client : public ssl_client size_t fill_header(unsigned char* outbuf, size_t sendlength, ws_opcode opcode); /** - * @brief Handle ping and pong requests. - * @param ping True if this is a ping, false if it is a pong - * @param payload The ping payload, to be returned as-is for a ping + * @brief Handle ping requests. + * @param payload The ping payload, to be returned as-is for a pong */ - void handle_ping_pong(bool ping, const std::string &payload); + void handle_ping(const std::string& payload); protected: @@ -145,7 +170,7 @@ class DPP_EXPORT websocket_client : public ssl_client * @brief Get websocket state * @return websocket state */ - ws_state get_state(); + [[nodiscard]] ws_state get_state() const; public: @@ -158,41 +183,44 @@ class DPP_EXPORT websocket_client : public ssl_client * @note Voice websockets only support OP_TEXT, and other websockets must be * OP_BINARY if you are going to send ETF. */ - websocket_client(const std::string &hostname, const std::string &port = "443", const std::string &urlpath = "", ws_opcode opcode = OP_BINARY); + websocket_client(const std::string& hostname, const std::string& port = "443", const std::string& urlpath = "", ws_opcode opcode = OP_BINARY); /** * @brief Destroy the websocket client object */ - virtual ~websocket_client(); + virtual ~websocket_client() = default; /** * @brief Write to websocket. Encapsulates data in frames if the status is CONNECTED. * @param data The data to send. + * @param _opcode The opcode of the data to send, either binary or text. The default + * is to use the socket's opcode as set in the constructor. */ - virtual void write(const std::string &data); + virtual void write(const std::string_view data, ws_opcode _opcode = OP_AUTO); /** * @brief Processes incoming frames from the SSL socket input buffer. * @param buffer The buffer contents. Can modify this value removing the head elements when processed. */ - virtual bool handle_buffer(std::string &buffer); + virtual bool handle_buffer(std::string& buffer); /** * @brief Close websocket */ - virtual void close(); + virtual void close(); /** * @brief Receives raw frame content only without headers - * + * * @param buffer The buffer contents + * @param opcode Frame type, e.g. OP_TEXT, OP_BINARY * @return True if the frame was successfully handled. False if no valid frame is in the buffer. */ - virtual bool handle_frame(const std::string &buffer); + virtual bool handle_frame(const std::string& buffer, ws_opcode opcode); /** * @brief Called upon error frame. - * + * * @param errorcode The error code from the websocket server */ virtual void error(uint32_t errorcode); @@ -209,4 +237,4 @@ class DPP_EXPORT websocket_client : public ssl_client void send_close_packet(); }; -}; \ No newline at end of file +} diff --git a/3rdPartyBinaries/dpp.dll b/3rdPartyBinaries/dpp.dll deleted file mode 100644 index c53f63be66..0000000000 Binary files a/3rdPartyBinaries/dpp.dll and /dev/null differ diff --git a/3rdPartyBinaries/dpp.lib b/3rdPartyBinaries/dpp.lib index d83210bd47..ce860826b4 100644 Binary files a/3rdPartyBinaries/dpp.lib and b/3rdPartyBinaries/dpp.lib differ diff --git a/3rdPartyBinaries/dpp_debug.dll b/3rdPartyBinaries/dpp_debug.dll new file mode 100644 index 0000000000..d4f1f1f3c7 Binary files /dev/null and b/3rdPartyBinaries/dpp_debug.dll differ diff --git a/3rdPartyBinaries/dpp_release.dll b/3rdPartyBinaries/dpp_release.dll new file mode 100644 index 0000000000..e00f2c8a3a Binary files /dev/null and b/3rdPartyBinaries/dpp_release.dll differ diff --git a/3rdPartyBinaries/dppd.lib b/3rdPartyBinaries/dppd.lib deleted file mode 100644 index 4a41c40394..0000000000 Binary files a/3rdPartyBinaries/dppd.lib and /dev/null differ diff --git a/3rdPartyBinaries/libcrypto-1_1-x64.dll b/3rdPartyBinaries/libcrypto-1_1-x64.dll index 8a06122706..19a42c9d29 100644 Binary files a/3rdPartyBinaries/libcrypto-1_1-x64.dll and b/3rdPartyBinaries/libcrypto-1_1-x64.dll differ diff --git a/3rdPartyBinaries/libssl-1_1-x64.dll b/3rdPartyBinaries/libssl-1_1-x64.dll index 139496c89f..b484de93ff 100644 Binary files a/3rdPartyBinaries/libssl-1_1-x64.dll and b/3rdPartyBinaries/libssl-1_1-x64.dll differ diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 3870f6d30d..07cdcd9115 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -2519,12 +2519,7 @@ if (WIN32) add_library(dpp_lib IMPORTED UNKNOWN) set_target_properties(dpp_lib PROPERTIES - IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dpp.lib - IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dppd.lib - MAP_IMPORTED_CONFIG_DEBUG DEBUG - MAP_IMPORTED_CONFIG_RELEASE RELEASE - MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE - MAP_IMPORTED_CONFIG_MINSIZEREL RELEASE + IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../3rdPartyBinaries/dpp.lib ) add_library(discord_lib IMPORTED UNKNOWN)