diff --git a/3rdParty/dpp/appcommand.h b/3rdParty/dpp/appcommand.h index 5f458b9c12..2861b652c3 100644 --- a/3rdParty/dpp/appcommand.h +++ b/3rdParty/dpp/appcommand.h @@ -19,6 +19,7 @@ * ************************************************************************************/ #pragma once +#include #include #include #include @@ -39,9 +40,13 @@ namespace dpp { * This value represents that maximum. interaction_response::add_autocomplete_choice does not allow * adding more than this number of elements to the vector. */ -#ifndef AUTOCOMPLETE_MAX_CHOICES - #define AUTOCOMPLETE_MAX_CHOICES 25 -#endif +inline constexpr size_t AUTOCOMPLETE_MAX_CHOICES = 25; + +/** + * @brief Discords default attachment size limit is 10MiB, and will be used in case + * an interaction does not contain its attachment size limit. + */ +inline constexpr uint32_t DEFAULT_ATTACHMENT_SIZE_LIMIT = 10 * 1024 * 1024; /** * @brief Represents command option types. @@ -443,6 +448,9 @@ enum interaction_response_type { * @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, }; @@ -601,6 +609,36 @@ struct DPP_EXPORT interaction_modal_response : public interaction_response, publ */ interaction_modal_response(const std::string& _custom_id, const std::string& _title, const std::vector _components = {}); + /** + * @brief Copy constructor + * + * @param other The object to copy from + */ + interaction_modal_response(const interaction_modal_response& other) = default; + + /** + * @brief Copy assignment operator + * + * @param other The object to copy from + * @return interaction_modal_response& reference to self + */ + interaction_modal_response& operator=(const interaction_modal_response& other) = default; + + /** + * @brief Move constructor + * + * @param other The object to move from + */ + interaction_modal_response(interaction_modal_response&& other) noexcept = default; + + /** + * @brief Move assignment operator + * + * @param other The object to move from + * @return interaction_modal_response& reference to self + */ + interaction_modal_response& operator=(interaction_modal_response&& other) noexcept = default; + /** * @brief Set the custom id * @@ -770,6 +808,26 @@ enum interaction_type { 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 */ @@ -918,7 +976,7 @@ class DPP_EXPORT interaction : public managed, public json_interface authorizing_integration_owners; + + /** + * @brief Context where the interaction was triggered from + */ + std::optional context; + /** * @brief ID of the application this interaction is for. */ @@ -1029,6 +1097,11 @@ class DPP_EXPORT interaction : 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; @@ -1487,7 +1595,8 @@ class DPP_EXPORT slashcommand : public managed, public json_interface contexts); + /** * @brief Adds a permission to the command * @@ -1588,4 +1705,4 @@ typedef std::unordered_map slashcommand_map; */ typedef std::unordered_map guild_command_permissions_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/application.h b/3rdParty/dpp/application.h index b789b13c28..5029dca4ea 100644 --- a/3rdParty/dpp/application.h +++ b/3rdParty/dpp/application.h @@ -21,6 +21,7 @@ ************************************************************************************/ #pragma once +#include #include #include #include @@ -30,6 +31,8 @@ #include #include #include +#include +#include namespace dpp { @@ -209,6 +212,31 @@ class DPP_EXPORT app_team { snowflake owner_user_id; }; +/** + * @brief Status indicating whether event webhooks are enabled or disabled for an application. + */ +enum application_event_webhook_status: uint8_t { + /** + * @brief Webhook events are disabled by developer + */ + ews_disabled = 1, + /** + * @brief Webhook events are enabled by developer + */ + ews_enabled = 2, + /** + * @brief Webhook events are disabled by Discord, usually due to inactivity + */ + ews_disabled_by_discord = 3, +}; + +/** + * @brief Configuration object for an app installation + */ +struct DPP_EXPORT integration_configuration { + application_install_params oauth2_install_params; +}; + /** * @brief The application class represents details of a bot application */ @@ -325,6 +353,11 @@ class DPP_EXPORT application : public managed, public json_interface event_webhooks_types; + + /** + * If webhook events are enabled for the app. + */ + application_event_webhook_status event_webhooks_status; + /** * @brief Up to 5 tags describing the content and functionality of the application. */ @@ -352,6 +400,11 @@ class DPP_EXPORT application : public managed, public json_interface integration_types_config; + /** * @brief The application's default custom authorization link, if enabled. */ @@ -467,4 +520,4 @@ class DPP_EXPORT application : public managed, public json_interface application_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/auditlog.h b/3rdParty/dpp/auditlog.h index 0c3dca2e62..2b2c517906 100644 --- a/3rdParty/dpp/auditlog.h +++ b/3rdParty/dpp/auditlog.h @@ -478,4 +478,4 @@ class DPP_EXPORT auditlog : public json_interface { virtual ~auditlog() = default; }; -} // namespace dpp +} diff --git a/3rdParty/dpp/automod.h b/3rdParty/dpp/automod.h index 9cd7498bcc..a16c92a5fa 100644 --- a/3rdParty/dpp/automod.h +++ b/3rdParty/dpp/automod.h @@ -400,4 +400,4 @@ class DPP_EXPORT automod_rule : public managed, public json_interface automod_rule_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/ban.h b/3rdParty/dpp/ban.h index af56027002..e8c6cc4f5d 100644 --- a/3rdParty/dpp/ban.h +++ b/3rdParty/dpp/ban.h @@ -66,4 +66,4 @@ class DPP_EXPORT ban : public json_interface { */ typedef std::unordered_map ban_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/bignum.h b/3rdParty/dpp/bignum.h new file mode 100644 index 0000000000..dda2975d32 --- /dev/null +++ b/3rdParty/dpp/bignum.h @@ -0,0 +1,119 @@ +/************************************************************************************ + * + * 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 Forward declaration of structure which implements proper destructor for unique_ptr as type is incomplete in header + * custom deleter defined where openssl_bignum is complete + */ + struct DPP_EXPORT bn_deleter { + + /** + * @brief Deletes an `openssl_bignum` instance. + * + * This operator is invoked automatically by `std::unique_ptr` when the + * managed object needs to be destroyed. + * + * @param p Pointer to the `openssl_bignum` instance to delete. + */ + void operator()(openssl_bignum* p) const noexcept; + }; + + /** + * @brief Internal opaque struct to contain OpenSSL things + */ + std::unique_ptr ssl_bn; + +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 37577acd00..f1d519c107 100644 --- a/3rdParty/dpp/cache.h +++ b/3rdParty/dpp/cache.h @@ -250,7 +250,7 @@ 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)); } }; @@ -270,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); -} // namespace dpp +} diff --git a/3rdParty/dpp/channel.h b/3rdParty/dpp/channel.h index 841e5fe877..b2ee201786 100644 --- a/3rdParty/dpp/channel.h +++ b/3rdParty/dpp/channel.h @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -243,38 +242,6 @@ 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 { - /** - * @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 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 @@ -301,42 +268,6 @@ enum auto_archive_duration_t : uint8_t { arc_1_week = 4, }; -/** - * @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; - - /** - * @brief Any user-thread settings, currently only used for notifications. - */ - uint32_t flags; -}; - /** * @brief Represents a tag that is able to be applied to a thread in a forum or media channel */ @@ -401,11 +332,6 @@ struct DPP_EXPORT forum_tag : public managed, public json_interface { forum_tag& set_name(const std::string& name); }; -/** - * @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 A definition of a discord channel. * There are one of these for every channel type except threads. Threads are @@ -429,6 +355,8 @@ class DPP_EXPORT channel : public managed, public json_interface { */ virtual json to_json_impl(bool with_id = false) const; + static constexpr uint16_t CHANNEL_TYPE_MASK = 0b0000000000001111; + public: /** * @brief Channel name (1-100 characters). @@ -705,6 +633,13 @@ class DPP_EXPORT channel : public managed, public json_interface { * @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 * + * **Example:** + * + * ```cpp + * channel.add_permission_overwrite(388499352297406481, dpp::ot_role, dpp::p_manage_channels | dpp::p_manage_messages, 0); + * // Allows p_manage_channels and p_manage_messages permissions for the provided role. + * ``` + * * @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); @@ -716,6 +651,13 @@ class DPP_EXPORT channel : public managed, public json_interface { * @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 * + * **Example:** + * + * ```cpp + * channel.set_permission_overwrite(388499352297406481, dpp::ot_role, dpp::p_manage_channels | dpp::p_manage_messages, 0); + * // Sets the allowed permissions to p_manage_channels and p_manage_messages and removes all denied permission flags for the provided role. + * ``` + * * @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 @@ -937,111 +879,6 @@ class DPP_EXPORT channel : public managed, public json_interface { }; -/** @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; - - /** - * @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; - - /** - * @brief Approximate count of members in a thread (stops counting at 50) - */ - 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 - * - * @return true if private thread - */ - bool is_private_thread() const; - - /** - * @brief Destroy the thread object - */ - virtual ~thread() = default; -}; - - -/** - * @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 * @@ -1055,30 +892,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; - -/** - * @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; - -} // namespace dpp +} diff --git a/3rdParty/dpp/cluster.h b/3rdParty/dpp/cluster.h index 314739423b..6fe24b0a12 100644 --- a/3rdParty/dpp/cluster.h +++ b/3rdParty/dpp/cluster.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -48,9 +48,18 @@ #include #include #include +#include namespace dpp { +/** + * @brief Pass this value into the constructor of dpp::cluster for the shard count to create a cluster with no shards. + * A cluster with no shards does not connect to a websocket, but can still use the event loop to dispatch HTTPS API + * requests to Discord. This is useful for bots that do not need to receive websocket events as it will save a lot of + * resources. + */ +constexpr uint32_t NO_SHARDS = ~0U; + /** * @brief Types of startup for cluster::start() */ @@ -124,26 +133,85 @@ class DPP_EXPORT cluster { shard_list shards; /** - * @brief List of all active registered timers + * @brief List of shards waiting for reconnection + */ + reconnect_list reconnections; + + /** + * @brief Ephemeral list of deleted timer ids */ - timer_reg_t timer_list; + timers_deleted_t deleted_timers; /** - * @brief List of timers by time + * @brief Priority queue of of timers by time */ timer_next_t next_timer; /** - * @brief Tick active timers + * @brief Mutex to work with named_commands and synchronize read write access */ - void tick_timers(); + std::shared_mutex named_commands_mutex; /** - * @brief Reschedule a timer for its next tick - * - * @param t Timer to reschedule + * @brief Mutex for protection of shards list + */ + mutable std::shared_mutex shards_mutex; + + /** + * @brief Typedef for slashcommand handler type + */ + using slashcommand_handler_t = std::function; + +#ifndef DPP_NO_CORO + /** + * @brief Typedef for coroutines based slashcommand handler type */ - void timer_reschedule(timer_t* t); + 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 Thread pool + */ + std::unique_ptr pool{nullptr}; + + /** + * @brief Used to spawn the socket engine into its own thread if + * the cluster is started with dpp::st_return. It is unused otherwise. + */ + std::thread engine_thread; + + /** + * @brief Protection mutex for timers + */ + std::mutex timer_guard; + + /** + * @brief Webhook server if enabled + */ + struct discord_webhook_server* webhook_server{nullptr}; + + /** + * @brief Mark a shard as requiring reconnection. + * Destructs the old shard in 5 seconds and creates a new one attempting to resume. + * + * @param shard_id Shard ID + */ + void add_reconnect(uint32_t shard_id); + public: /** * @brief Current bot token for all shards on this cluster and all commands sent via HTTP @@ -198,9 +266,33 @@ class DPP_EXPORT cluster { websocket_protocol_t ws_mode; /** - * @brief Condition variable notified when the cluster is terminating. + * @brief Atomic bool to set to true when the cluster is terminating. + * + * D++ itself does not set this value, it is for library users to set if they want + * the cluster to terminate outside of a flow where they may have simple access to + * destruct the cluster object. */ - std::condition_variable terminating; + std::atomic_bool terminating{false}; + + /** + * @brief The time (in seconds) that a request is allowed to take. + */ + uint16_t request_timeout = 60; + + /** + * @brief Socket engine instance + */ + std::unique_ptr socketengine; + + /** + * @brief Constructor for creating a cluster without a token. + * A cluster created without a token has no shards, and just runs the event loop. You can use this to make asynchronous + * HTTP requests via e.g. dpp::cluster::request without having to connect to a websocket to receive shard events. + * @param pool_threads The number of threads to allocate for the thread pool. This defaults to half your system concurrency and if set to a number less than 4, will default to 4. + * All callbacks and events are placed into the thread pool. The bigger you make this pool (but generally no bigger than your number of cores), the more your bot will scale. + * @throw dpp::exception Thrown on windows, if WinSock fails to initialise, or on any other system if a dpp::request_queue fails to construct + */ + explicit cluster(uint32_t pool_threads = std::thread::hardware_concurrency() / 2); /** * @brief Constructor for creating a cluster. All but the token are optional. @@ -212,11 +304,34 @@ class DPP_EXPORT cluster { * @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 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(). + * @param pool_threads The number of threads to allocate for the thread pool. This defaults to half your system concurrency and if set to a number less than 4, will default to 4. + * All callbacks and events are placed into the thread pool. The bigger you make this pool (but generally no bigger than your number of cores), the more your bot will scale. * @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 = cache_policy::cpol_default, 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 pool_threads = std::thread::hardware_concurrency() / 2); + + /** + * @brief Create a webhook server for receiving interactions + * @note This should be considered mutually exclusive with delivery of interaction events via shards. + * @param discord_public_key Public key for the application from the application dashboard page + * @param address address to bind to, use "0.0.0.0" to bind to all local addresses + * @param port port to bind to. You should generally use a port > 1024. + * @param ssl_private_key Private key PEM file for HTTPS/SSL. If empty, a plaintext server is created + * @param ssl_public_key Public key PEM file for HTTPS/SSL. If empty, a plaintext server is created + */ + cluster& enable_webhook_server(const std::string& discord_public_key, const std::string_view address, uint16_t port, const std::string& ssl_private_key = "", const std::string& ssl_public_key = ""); + + /** + * @brief Place some arbitrary work into the thread pool for execution when time permits. + * + * Work units are fetched into threads on the thread pool from the queue in order of priority, + * lowest numeric values first. Low numeric values should be reserved for API replies from Discord, + * guild creation events, etc. + * + * @param priority Priority of the work unit + * @param task Task to queue + */ + void queue_work(int priority, work_unit task); /** * @brief dpp::cluster is non-copyable @@ -273,6 +388,11 @@ class DPP_EXPORT cluster { */ cluster& set_websocket_protocol(websocket_protocol_t mode); + /** + * @brief Tick active timers + */ + void tick_timers(); + /** * @brief Set the audit log reason for the next REST call to be made. * This is set per-thread, so you must ensure that if you call this method, your request that @@ -325,7 +445,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 +466,33 @@ class DPP_EXPORT cluster { */ timer start_timer(timer_callback_t on_tick, uint64_t frequency, timer_callback_t on_stop = {}); +#ifndef DPP_NO_CORO + /** + * @brief Start a coroutine timer. Every `frequency` seconds, the callback is called. + * + * @param on_tick The callback lambda to call for this timer when ticked + * @param on_stop The callback lambda to call for this timer when it is stopped + * @param frequency How often to tick the timer in seconds + * @return timer A handle to the timer, used to remove that timer later + */ + template T, std::invocable U = std::function> + requires (dpp::awaitable_type::type>) + timer start_timer(T&& on_tick, uint64_t frequency, U&& on_stop = {}) { + std::function ticker = [fun = std::forward(on_tick)](timer t) mutable -> dpp::job { + co_await std::invoke(fun, t); + }; + std::function stopper; + if constexpr (dpp::awaitable_type::type>) { + stopper = [fun = std::forward(on_stop)](timer t) mutable -> dpp::job { + co_await std::invoke(fun, t); + }; + } else { + stopper = std::forward(on_stop); + } + return start_timer(std::move(ticker), frequency, std::move(stopper)); + } +#endif + /** * @brief Stop a ticking timer * @@ -355,7 +502,7 @@ class DPP_EXPORT cluster { */ bool stop_timer(timer t); -#ifdef DPP_CORO +#ifndef DPP_NO_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 * @@ -396,7 +543,7 @@ class DPP_EXPORT cluster { * * @param return_after If true the bot will return to your program after starting shards, if false this function will never return. */ - void start(bool return_after = true); + void start(start_type return_after = st_wait); /** * @brief Set the presence for all shards on the cluster @@ -411,17 +558,91 @@ class DPP_EXPORT cluster { * @param id Shard ID * @return discord_client* shard, or null */ - discord_client* get_shard(uint32_t id); + discord_client* get_shard(uint32_t id) const; /** * @brief Get the list of shards * - * @return shard_list& Reference to map of shards for this cluster + * @return shard_list map of shards for this cluster + */ + shard_list get_shards() const; + + /** + * @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. */ - const shard_list& get_shards(); + 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 + */ + template + std::enable_if_t, 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; + } + + /** + * @brief Get the number of currently active HTTP(S) requests active in the cluster. + * This total includes all in-flight API requests and calls to dpp::cluster::request(). + * Note that once a request is passed to the thread pool it is no longer counted here. + * @return Total active request count + */ + size_t active_requests(); + +#ifndef DPP_NO_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 + requires (utility::callable_returns, const slashcommand_t&>) + bool register_command(const std::string& name, F&& handler) { + std::unique_lock lk(named_commands_mutex); + auto [_, inserted] = named_commands.try_emplace(name, std::in_place_type, 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 channel effect send event + * + * @see https://discord.com/developers/docs/events/gateway-events#voice-channel-effect-send + * @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_channel_effect_send_t&, and returns void. + */ + event_router_t on_voice_channel_effect_send; + /** * @brief on voice state update event * @@ -431,7 +652,16 @@ class DPP_EXPORT cluster { */ 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 * @@ -461,6 +691,14 @@ class DPP_EXPORT cluster { */ event_router_t on_log; + /** + * @brief Called when a file descriptor is removed from the socket engine + * + * @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 socket_close_t&, and returns void. + */ + event_router_t on_socket_close; + /** * @brief on guild join request delete. * Triggered when a user declines the membership screening questionnaire for a guild. @@ -992,6 +1230,24 @@ class DPP_EXPORT cluster { */ 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. * @@ -1205,7 +1461,7 @@ class DPP_EXPORT cluster { /** * @brief Called when packets are sent from the voice buffer. * The voice buffer contains packets that are already encoded with Opus and encrypted - * with Sodium, and merged into packets by the repacketizer, which is done in the + * 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. @@ -1218,17 +1474,6 @@ class DPP_EXPORT cluster { event_router_t on_voice_buffer_send; - /** - * @brief Called when a user is talking on a voice channel. - * - * @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_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, @@ -1368,11 +1613,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 filemimetypes List of mime types for each file 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 = {}, const std::vector& filemimetypes = {}); + 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. @@ -1421,7 +1664,7 @@ class DPP_EXPORT cluster { 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 + * @brief Create a followup message for an interaction * * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response * @param token Token for the interaction webhook @@ -1429,10 +1672,10 @@ class DPP_EXPORT cluster { * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). */ - void 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 + * @brief Edit original followup message for an interaction * This is an alias for cluster::interaction_response_edit * @see cluster::interaction_response_edit * @@ -1454,7 +1697,7 @@ class DPP_EXPORT cluster { void interaction_followup_delete(const std::string &token, command_completion_event_t callback = utility::log_error()); /** - * @brief Edit followup message to a slash command + * @brief Edit followup message for an interaction * 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 @@ -1466,7 +1709,7 @@ class DPP_EXPORT cluster { void interaction_followup_edit(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); /** - * @brief Get the followup message to a slash command + * @brief Get the followup message for an interaction * * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message * @param token Token for the interaction webhook @@ -1477,7 +1720,7 @@ class DPP_EXPORT cluster { 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 + * @brief Get the original followup message for an interaction * This is an alias for cluster::interaction_response_get_original * @see cluster::interaction_response_get_original * @@ -1771,6 +2014,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. * @@ -1941,6 +2193,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 * @@ -2009,8 +2309,8 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param c Channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions bitmask - * @param deny deny permissions bitmask + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @param member true if the overwrite_id is a user id, false if it is a channel id * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). @@ -2024,8 +2324,8 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @param channel_id ID of the channel to set permissions for * @param overwrite_id Overwrite to change (a user or role ID) - * @param allow allow permissions bitmask - * @param deny deny permissions bitmask + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @param member true if the overwrite_id is a user id, false if it is a channel id * @param callback Function to call when the API call completes. * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). @@ -2089,10 +2389,21 @@ class DPP_EXPORT cluster { * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages * @param channel_id Channel ID to get pins for * @param callback Function to call when the API call completes. - * On success the callback will contain a dpp::message_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::message_pin_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 channel_pins_get(snowflake channel_id, command_completion_event_t callback); + /** + * @brief Get a channel's pins + * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages + * @param channel_id Channel ID to get pins for + * @param before Get messages pinned before this timestamp. + * @param limit Max number of pins to return (1-50). Defaults to 50 if not set. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message_pin_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 channel_pins_get(snowflake channel_id, std::optional before, std::optional limit, command_completion_event_t callback); + /** * @brief Adds a recipient to a Group DM using their access token * @see https://discord.com/developers/docs/resources/channel#group-dm-add-recipient @@ -2391,6 +2702,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 * @@ -2487,7 +2811,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. @@ -2631,6 +2955,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 * @@ -2784,7 +3157,7 @@ class DPP_EXPORT cluster { * @brief Get the guild's onboarding configuration * * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding - * @param o The onboarding object + * @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(). */ @@ -3141,11 +3514,16 @@ class DPP_EXPORT cluster { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * @see https://discord.com/developers/docs/resources/guild#modify-current-member * @param guild_id Guild ID to change on - * @param nickname New nickname, or empty string to clear nickname + * @param nickname New nickname, or empty string to clear nickname. + * @param banner_blob New banner, or empty string to clear banner. + * @param banner_type Type of image for new banner. + * @param avatar_blob New avatar, or empty string to clear avatar. + * @param avatar_type Type of image for new avatar. + * @param bio New bio, or empty string to clear bio * @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_current_member_edit(snowflake guild_id, const std::string &nickname, command_completion_event_t callback = utility::log_error()); + void guild_current_member_edit(snowflake guild_id, const std::string& nickname, const std::string& banner_blob, const image_type banner_type, const std::string& avatar_blob, const image_type avatar_type, const std::string& bio, command_completion_event_t callback = utility::log_error()); /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). @@ -3166,19 +3544,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. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @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::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 @@ -3423,7 +3806,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(). @@ -3432,7 +3815,7 @@ class DPP_EXPORT cluster { /** * @brief Get a list of available sticker packs - * @see https://discord.com/developers/docs/resources/sticker#list-nitro-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(). */ @@ -3582,6 +3965,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 * @@ -3605,6 +3998,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 * @@ -3694,6 +4098,16 @@ class DPP_EXPORT cluster { */ 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. @@ -3705,11 +4119,21 @@ class DPP_EXPORT cluster { */ void skus_get(command_completion_event_t callback = utility::log_error()); -#include -#ifdef DPP_CORO -#include + /** + * @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()); + +#ifndef DPP_NO_CORO + #include #endif }; -} // namespace dpp +} diff --git a/3rdParty/dpp/cluster_coro_calls.h b/3rdParty/dpp/cluster_coro_calls.h index 15840b932a..cd7b131586 100644 --- a/3rdParty/dpp/cluster_coro_calls.h +++ b/3rdParty/dpp/cluster_coro_calls.h @@ -280,7 +280,7 @@ [[nodiscard]] async co_interaction_response_get_original(const std::string &token); /** - * @brief Create a followup message to a slash command + * @brief Create a followup message for an interaction * * @see dpp::cluster::interaction_followup_create * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response @@ -292,7 +292,7 @@ [[nodiscard]] async co_interaction_followup_create(const std::string &token, const message &m); /** - * @brief Edit original followup message to a slash command + * @brief Edit original followup message for an interaction * This is an alias for cluster::interaction_response_edit * @see dpp::cluster::interaction_followup_edit_original * @see cluster::interaction_response_edit @@ -316,7 +316,7 @@ [[nodiscard]] async co_interaction_followup_delete(const std::string &token); /** - * @brief Edit followup message to a slash command + * @brief Edit followup message for an interaction * 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 @@ -329,7 +329,7 @@ [[nodiscard]] async co_interaction_followup_edit(const std::string &token, const message &m); /** - * @brief Get the followup message to a slash command + * @brief Get the followup message for an interaction * * @see dpp::cluster::interaction_followup_get * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message @@ -341,7 +341,7 @@ [[nodiscard]] async co_interaction_followup_get(const std::string &token, snowflake message_id); /** - * @brief Get the original followup message to a slash command + * @brief Get the original followup message for an interaction * This is an alias for cluster::interaction_response_get_original * @see dpp::cluster::interaction_followup_get_original * @see cluster::interaction_response_get_original @@ -450,8 +450,8 @@ * @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 allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @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 @@ -466,8 +466,8 @@ * @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 allow Bitmask of allowed permissions (refer to enum dpp::permissions) + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) * @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 @@ -576,6 +576,18 @@ */ [[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 * @see dpp::cluster::create_dm_channel @@ -696,6 +708,60 @@ */ [[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. * @@ -739,6 +805,17 @@ */ [[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 * @see dpp::cluster::get_gateway_bot @@ -758,11 +835,16 @@ * @see dpp::cluster::guild_current_member_edit * @see https://discord.com/developers/docs/resources/guild#modify-current-member * @param guild_id Guild ID to change on - * @param nickname New nickname, or empty string to clear nickname + * @param nickname New nickname, or empty string to clear nickname. + * @param banner_blob New banner, or empty string to clear banner. + * @param banner_type Type of image for new banner. + * @param avatar_blob New avatar, or empty string to clear avatar. + * @param avatar_type Type of image for new avatar. + * @param bio New bio, or empty string to clear bio * @return confirmation returned object on completion * \memberof dpp::cluster */ -[[nodiscard]] async co_guild_current_member_edit(snowflake guild_id, const std::string &nickname); +[[nodiscard]] async co_guild_current_member_edit(snowflake guild_id, const std::string& nickname, const std::string& banner_blob, const image_type banner_type, const std::string& avatar_blob, const image_type avatar_type, const std::string& bio); /** * @brief Get the audit log for a guild @@ -1051,7 +1133,7 @@ * * @see dpp::cluster::guild_get_onboarding * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding - * @param o The onboarding object + * @param guild_id The guild to pull the onboarding configuration from. * @return onboarding returned object on completion * \memberof dpp::cluster */ @@ -1224,6 +1306,20 @@ */ [[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 * @@ -1502,6 +1598,15 @@ */ [[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 * @@ -1586,16 +1691,80 @@ */ [[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 * @see dpp::cluster::channel_pins_get * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages * @param channel_id Channel ID to get pins for - * @return message_map returned object on completion + * @return message_pin_map returned object on completion * \memberof dpp::cluster */ [[nodiscard]] async co_channel_pins_get(snowflake channel_id); +/** + * @brief Get a channel's pins + * @see dpp::cluster::channel_pins_get + * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages + * @param channel_id Channel ID to get pins for + * @param before Get messages pinned before this timestamp. + * @param limit Max number of pins to return (1-50). Defaults to 50 if not set. + * @return message_pin_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_pins_get(snowflake channel_id, std::optional before, std::optional limit); + /** * @brief Create a role on a guild * @@ -1673,7 +1842,7 @@ * @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 + * @return application_role_connection_metadata_list returned object on completion * \memberof dpp::cluster */ [[nodiscard]] async co_application_role_connection_get(snowflake application_id); @@ -1685,7 +1854,7 @@ * @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 + * @return application_role_connection_metadata_list returned object on completion * @note An application can have a maximum of 5 metadata records. * \memberof dpp::cluster */ @@ -1857,7 +2026,7 @@ /** * @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 @@ -1877,7 +2046,7 @@ /** * @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 */ @@ -2148,20 +2317,25 @@ [[nodiscard]] async co_thread_get(snowflake thread_id); /** - * @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 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. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @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::length_exception Image data is larger than the maximum size of 256 kilobytes * \memberof dpp::cluster */ -[[nodiscard]] async co_current_user_edit(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png); +[[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 @@ -2210,6 +2384,17 @@ */ [[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 * @@ -2234,6 +2419,18 @@ */ [[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). * This call requires the oauth2 `connections` scope and cannot be executed @@ -2468,5 +2665,5 @@ /* End of auto-generated definitions */ -[[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 = {}); +[[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"); diff --git a/3rdParty/dpp/cluster_sync_calls.h b/3rdParty/dpp/cluster_sync_calls.h deleted file mode 100644 index d1e067c579..0000000000 --- a/3rdParty/dpp/cluster_sync_calls.h +++ /dev/null @@ -1,3037 +0,0 @@ -/************************************************************************************ - * - * 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. - * - ************************************************************************************/ - - -/* Auto @generated by buildtools/make_sync_struct.php. - * - * DO NOT EDIT BY HAND! - * - * To re-generate this header file re-run the script! - */ -/** - * @brief Create/overwrite global slash commands. - * Any existing global slash commands will be deleted and replaced with these. - * - * @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. - * 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 - * @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); - -/** - * @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 - * @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_delete_sync(); - -/** - * @brief Create a global slash command (a bot can have a maximum of 100 of these). - * - * @see dpp::cluster::global_command_create - * @see https://discord.com/developers/docs/interactions/application-commands#create-global-application-command - * @param s Slash command to create - * @return slashcommand 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. - */ -slashcommand global_command_create_sync(const slashcommand &s); - -/** - * @brief Get a global slash command - * - * @see dpp::cluster::global_command_get - * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-command - * @param id The ID of the slash command - * @return slashcommand 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. - */ -slashcommand global_command_get_sync(snowflake id); - -/** - * @brief Delete a global slash command (a bot can have a maximum of 100 of these) - * - * @see dpp::cluster::global_command_delete - * @see https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command - * @param id Slash command to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation global_command_delete_sync(snowflake id); - -/** - * @brief Edit a global slash command (a bot can have a maximum of 100 of these) - * - * @see dpp::cluster::global_command_edit - * @see https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command - * @param s Slash command to change - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation global_command_edit_sync(const slashcommand &s); - -/** - * @brief Get the application's global slash commands - * - * @see dpp::cluster::global_commands_get - * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-commands - * @return slashcommand_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -slashcommand_map global_commands_get_sync(); - -/** - * @brief Create/overwrite guild slash commands. - * Any existing guild slash commands on this guild will be deleted and replaced with these. - * - * @see dpp::cluster::guild_bulk_command_create - * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands - * @param commands Vector of slash commands to create/update. - * New guild commands will be available in the guild immediately. If the command did not already exist, it will count toward daily application command create limits. - * @param guild_id Guild ID to create/update the slash commands in - * @return slashcommand_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -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 - * @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_delete_sync(snowflake guild_id); - -/** - * @brief Get all slash command permissions of a guild - * - * @see dpp::cluster::guild_commands_get_permissions - * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions - * @param guild_id Guild ID to get the slash commands permissions for - * @return guild_command_permissions_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -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 - * - * @note You can only add up to 10 permission overwrites for a command - * - * @see dpp::cluster::guild_bulk_command_edit_permissions - * @see https://discord.com/developers/docs/interactions/application-commands#batch-edit-application-command-permissions - * @warning The endpoint will overwrite all existing permissions for all commands of the application in a guild, including slash commands, user commands, and message commands. Meaning that if you forgot to pass a slash command, the permissions of it might be removed. - * @param commands A vector of slash commands to edit/overwrite the permissions for - * @param guild_id Guild ID to edit permissions of the slash commands in - * @return guild_command_permissions_map returned object on completion - * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * 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); - -/** - * @brief Create a slash command local to a guild - * - * @see dpp::cluster::guild_command_create - * @see https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command - * @note Creating a command with the same name as an existing command for your application will overwrite the old command. - * @param s Slash command to create - * @param guild_id Guild ID to create the slash command in - * @return slashcommand 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. - */ -slashcommand guild_command_create_sync(const slashcommand &s, snowflake guild_id); - -/** - * @brief Delete a slash command local to a guild - * - * @see dpp::cluster::guild_command_delete - * @see https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command - * @param id Slash command to delete - * @param guild_id Guild ID to delete the slash command in - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_command_delete_sync(snowflake id, snowflake guild_id); - -/** - * @brief Edit slash command permissions of a guild - * - * @see dpp::cluster::guild_command_edit_permissions - * @see https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions - * @note You can only add up to 10 permission overwrites for a command - * @param s Slash command to edit the permissions for - * @param guild_id Guild ID to edit the slash command in - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_command_edit_permissions_sync(const slashcommand &s, snowflake guild_id); - -/** - * @brief Get a slash command of a guild - * - * @see dpp::cluster::guild_command_get - * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command - * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions - * @param id The ID of the slash command - * @param guild_id Guild ID to get the slash command from - * @return slashcommand 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. - */ -slashcommand guild_command_get_sync(snowflake id, snowflake guild_id); - -/** - * @brief Get the permissions for a slash command of a guild - * - * @see dpp::cluster::guild_command_get_permissions - * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions - * @param id The ID of the slash command to get the permissions for - * @param guild_id Guild ID to get the permissions of - * @return guild_command_permissions 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. - */ -guild_command_permissions guild_command_get_permissions_sync(snowflake id, snowflake guild_id); - -/** - * @brief Edit a slash command local to a guild - * - * @see dpp::cluster::guild_command_edit - * @see https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command - * @param s Slash command to edit - * @param guild_id Guild ID to edit the slash command in - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_command_edit_sync(const slashcommand &s, snowflake guild_id); - -/** - * @brief Get the application's slash commands for a guild - * - * @see dpp::cluster::guild_commands_get - * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands - * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions - * @param guild_id Guild ID to get the slash commands for - * @return slashcommand_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -slashcommand_map guild_commands_get_sync(snowflake guild_id); - -/** - * @brief Respond to a slash command - * - * @see dpp::cluster::interaction_response_create - * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response - * @param interaction_id Interaction id to respond to - * @param token Token for the interaction webhook - * @param r Response to send - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation interaction_response_create_sync(snowflake interaction_id, const std::string &token, const interaction_response &r); - -/** - * @brief Edit response to a slash command - * - * @see dpp::cluster::interaction_response_edit - * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response - * @param token Token for the interaction webhook - * @param m Message to send - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation 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 - * @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_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 - * @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); - -/** - * @brief Edit original followup message to a slash command - * This is an alias for cluster::interaction_response_edit - * @see dpp::cluster::interaction_followup_edit_original - * @see cluster::interaction_response_edit - * - * @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 - * @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); - -/** - * @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 - * @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); - -/** - * @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 - * @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); - -/** - * @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 - * @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); - -/** - * @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 - * @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_original_sync(const std::string &token); - -/** - * @brief Get all auto moderation rules for a guild - * - * @param guild_id Guild id of the auto moderation rule - * @return automod_rule_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -automod_rule_map automod_rules_get_sync(snowflake guild_id); - -/** - * @brief Get a single auto moderation rule - * - * @param guild_id Guild id of the auto moderation rule - * @param rule_id Rule id to retrieve - * @return automod_rule returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -automod_rule automod_rule_get_sync(snowflake guild_id, snowflake rule_id); - -/** - * @brief Create an auto moderation rule - * - * @param guild_id Guild id of the auto moderation rule - * @param r Auto moderation rule to create - * @return automod_rule returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -automod_rule automod_rule_create_sync(snowflake guild_id, const automod_rule& r); - -/** - * @brief Edit an auto moderation rule - * - * @param guild_id Guild id of the auto moderation rule - * @param r Auto moderation rule to edit. The rule's id must be set. - * @return automod_rule returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -automod_rule automod_rule_edit_sync(snowflake guild_id, const automod_rule& r); - -/** - * @brief Delete an auto moderation rule - * - * @param guild_id Guild id of the auto moderation rule - * @param rule_id Auto moderation rule id to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation automod_rule_delete_sync(snowflake guild_id, snowflake rule_id); - -/** - * @brief Create a channel - * - * Create a new channel object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, - * only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible - * for guild administrators. Returns the new channel object on success. Fires a `Channel Create Gateway` event. - * - * All parameters to this endpoint are optional excluding `name` - * - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::channel_create - * @see https://discord.com/developers/docs/resources/channel#create-channel - * @param c Channel to create - * @return channel 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. - */ -channel channel_create_sync(const class channel &c); - -/** - * @brief Remove a permission from a channel - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::channel_delete_permission - * @see https://discord.com/developers/docs/resources/channel#delete-channel-permission - * @param c Channel to remove permission from - * @param overwrite_id Overwrite to remove, user or channel ID - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation channel_delete_permission_sync(const class channel &c, snowflake overwrite_id); - -/** - * @brief Delete a channel - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::channel_delete - * @see https://discord.com/developers/docs/resources/channel#deleteclose-channel - * @param channel_id Channel id to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation 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 - * @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_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 - * @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_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 - * - * Modify the positions of a set of channel objects for the guild. - * Requires `MANAGE_CHANNELS` permission. Fires multiple `Channel Update Gateway` events. - * Only channels to be modified are required. - * - * @see dpp::cluster::channel_edit_positions - * @see https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions - * @param c Channel to change the position for - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation channel_edit_positions_sync(const std::vector &c); - -/** - * @brief Edit a channel - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::channel_edit - * @see https://discord.com/developers/docs/resources/channel#modify-channel - * @param c Channel to edit/update - * @return channel 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. - */ -channel channel_edit_sync(const class channel &c); - -/** - * @brief Follow an announcement (news) channel - * @see dpp::cluster::channel_follow_news - * @see https://discord.com/developers/docs/resources/channel#follow-news-channel - * @param c Channel id to follow - * @param target_channel_id Channel to subscribe the channel to - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation channel_follow_news_sync(const class channel &c, snowflake target_channel_id); - -/** - * @brief Get a channel - * - * @see dpp::cluster::channel_get - * @see https://discord.com/developers/docs/resources/channel#get-channel - * @param c Channel ID to retrieve - * @return channel 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. - */ -channel channel_get_sync(snowflake c); - -/** - * @brief Create invite for a channel - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::channel_invite_create - * @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 invite 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. - */ -invite channel_invite_create_sync(const class channel &c, const class invite &i); - -/** - * @brief Get invites for a channel - * - * @see dpp::cluster::channel_invites_get - * @see https://discord.com/developers/docs/resources/invite#get-invites - * @param c Channel to get invites for - * @return invite_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -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 - * @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_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 - * @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_typing_sync(snowflake cid); - -/** - * @brief Get all channels for a guild - * - * @see dpp::cluster::channels_get - * @see https://discord.com/developers/docs/resources/channel#get-channels - * @param guild_id Guild ID to retrieve channels for - * @return channel_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -channel_map channels_get_sync(snowflake guild_id); - -/** - * @brief Create a dm channel - * @see dpp::cluster::create_dm_channel - * @see https://discord.com/developers/docs/resources/user#create-dm - * @param user_id User ID to create DM channel with - * @return channel 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. - */ -channel create_dm_channel_sync(snowflake user_id); - -/** - * @brief Get current user DM channels - * - * @return channel_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -channel_map current_user_get_dms_sync(); - -/** - * @brief Create a direct message, also create the channel for the direct message if needed - * - * @see dpp::cluster::direct_message_create - * @see https://discord.com/developers/docs/resources/user#create-dm - * @see dpp::cluster::direct_message_create - * @see https://discord.com/developers/docs/resources/channel#create-message - * @param user_id User ID of user to send message to - * @param m Message object - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message direct_message_create_sync(snowflake user_id, const message &m); - -/** - * @brief Adds a recipient to a Group DM using their access token - * @see dpp::cluster::gdm_add - * @see https://discord.com/developers/docs/resources/channel#group-dm-add-recipient - * @param channel_id Channel id to add group DM recipients to - * @param user_id User ID to add - * @param access_token Access token from OAuth2 - * @param nick Nickname of user to apply to the chat - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation 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 - * @see dpp::cluster::gdm_remove - * @see https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient - * @param channel_id Channel ID of group DM - * @param user_id User ID to remove from group DM - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation gdm_remove_sync(snowflake channel_id, snowflake user_id); - -/** - * @brief Create single emoji. - * You must ensure that the emoji passed contained image data using the emoji::load_image() method. - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * - * @see dpp::cluster::guild_emoji_create - * @see https://discord.com/developers/docs/resources/emoji#create-guild-emoji - * @param guild_id Guild ID to create emoji om - * @param newemoji Emoji to create - * @return emoji 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. - */ -emoji guild_emoji_create_sync(snowflake guild_id, const class emoji& newemoji); - -/** - * @brief Delete a guild emoji - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * - * @see dpp::cluster::guild_emoji_delete - * @see https://discord.com/developers/docs/resources/emoji#delete-guild-emoji - * @param guild_id Guild ID to delete emoji on - * @param emoji_id Emoji ID to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation 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#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 - * @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); - -/** - * @brief Get a single emoji - * - * @see dpp::cluster::guild_emoji_get - * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji - * @param guild_id Guild ID to get emoji for - * @param emoji_id Emoji ID to get - * @return emoji 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. - */ -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#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 - * @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); - -/** - * @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 - * @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. - */ -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 - * @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. - */ -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 - * @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 entitlement_test_delete_sync(snowflake entitlement_id); - -/** - * @brief Get the gateway information for the bot using the token - * @see dpp::cluster::get_gateway_bot - * @see https://discord.com/developers/docs/topics/gateway#get-gateway-bot - * @return gateway 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. - */ -gateway get_gateway_bot_sync(); - -/** - * @brief Modify current member - * - * Modifies the current member in a guild. - * Fires a `Guild Member Update` Gateway event. - * - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::guild_current_member_edit - * @see https://discord.com/developers/docs/resources/guild#modify-current-member - * @param guild_id Guild ID to change on - * @param nickname New nickname, or empty string to clear nickname - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_current_member_edit_sync(snowflake guild_id, const std::string &nickname); - -/** - * @brief Get the audit log for a guild - * - * @see dpp::cluster::guild_auditlog_get - * @see https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log - * @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 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 - * @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, snowflake after, uint32_t limit); - -/** - * @brief Add guild ban - * - * Create a guild ban, and optionally delete previous messages sent by the banned user. - * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. - * @see dpp::cluster::guild_ban_add - * @see https://discord.com/developers/docs/resources/guild#create-guild-ban - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to add ban to - * @param user_id User ID to ban - * @param delete_message_seconds How many seconds to delete messages for, between 0 and 604800 (7 days). Defaults to 0 - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0); - -/** - * @brief Delete guild ban - * - * Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. - * Fires a Guild Ban Remove Gateway event. - * @see dpp::cluster::guild_ban_delete - * @see https://discord.com/developers/docs/resources/guild#remove-guild-ban - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild to delete ban from - * @param user_id User ID to delete ban for - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_ban_delete_sync(snowflake guild_id, snowflake user_id); - -/** - * @brief Create a guild - * - * Create a new guild. Returns a guild object on success. `Fires a Guild Create Gateway` event. - * - * When using the roles parameter, the first member of the array is used to change properties of the guild's everyone role. - * If you are trying to bootstrap a guild with additional roles, keep this in mind. The required id field within each role object is an - * integer placeholder, and will be replaced by the API upon consumption. Its purpose is to allow you to overwrite a role's permissions - * in a channel when also passing in channels with the channels array. - * When using the channels parameter, the position field is ignored, and none of the default channels are created. The id field within - * each channel object may be set to an integer placeholder, and will be replaced by the API upon consumption. Its purpose is to - * allow you to create `GUILD_CATEGORY` channels by setting the `parent_id` field on any children to the category's id field. - * Category channels must be listed before any children. - * - * @see dpp::cluster::guild_create - * @see https://discord.com/developers/docs/resources/guild#create-guild - * @note The region field is deprecated and is replaced by channel.rtc_region. This endpoint can be used only by bots in less than 10 guilds. - * @param g Guild to create - * @return guild 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. - */ -guild guild_create_sync(const class guild &g); - -/** - * @brief Delete a guild - * - * Delete a guild permanently. User must be owner. Fires a `Guild Delete Gateway` event. - * - * @see dpp::cluster::guild_delete - * @see https://discord.com/developers/docs/resources/guild#delete-guild - * @param guild_id Guild ID to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_delete_sync(snowflake guild_id); - -/** - * @brief Delete guild integration - * - * Delete the attached integration object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. - * Requires the `MANAGE_GUILD` permission. Fires a Guild Integrations Update Gateway event. - * - * @see dpp::cluster::guild_delete_integration - * @see https://discord.com/developers/docs/resources/guild#delete-guild-integration - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to delete integration for - * @param integration_id Integration ID to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_delete_integration_sync(snowflake guild_id, snowflake integration_id); - -/** - * @brief Edit a guild - * - * Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated guild object on success. - * Fires a `Guild Update Gateway` event. - * - * @see dpp::cluster::guild_edit - * @see https://discord.com/developers/docs/resources/guild#modify-guild - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param g Guild to edit - * @return guild 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. - */ -guild guild_edit_sync(const class guild &g); - -/** - * @brief Edit guild widget - * - * Requires the `MANAGE_GUILD` permission. - * - * @see dpp::cluster::guild_edit_widget - * @see https://discord.com/developers/docs/resources/guild#modify-guild-widget - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to edit widget for - * @param gw New guild widget information - * @return guild_widget 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. - */ -guild_widget guild_edit_widget_sync(snowflake guild_id, const class guild_widget &gw); - -/** - * @brief Get single guild ban - * - * Requires the `BAN_MEMBERS` permission. - * @see dpp::cluster::guild_get_ban - * @see https://discord.com/developers/docs/resources/guild#get-guild-ban - * @param guild_id Guild ID to get ban for - * @param user_id User ID of ban to retrieve - * @return ban 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. - */ -ban guild_get_ban_sync(snowflake guild_id, snowflake user_id); - -/** - * @brief Get guild ban list - * - * Requires the `BAN_MEMBERS` permission. - * @see dpp::cluster::guild_get_bans - * @see https://discord.com/developers/docs/resources/guild#get-guild-bans - * @note Provide a user ID to `before` and `after` for pagination. Users will always be returned in ascending order by the user ID. If both before and after are provided, only before is respected. - * @param guild_id Guild ID to get bans for - * @param before If non-zero, all bans for user ids before this user id will be returned up to the limit - * @param after if non-zero, all bans for user ids after this user id will be returned up to the limit - * @param limit the maximum number of bans to retrieve in this call up to a maximum of 1000 - * @return ban_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -ban_map guild_get_bans_sync(snowflake guild_id, snowflake before, snowflake after, snowflake limit); - - -guild guild_get_sync(snowflake guild_id); - -/** - * @brief Get guild integrations - * - * Requires the `MANAGE_GUILD` permission. - * - * @see dpp::cluster::guild_get_integrations - * @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 - * @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); - - -guild guild_get_preview_sync(snowflake guild_id); - -/** - * @brief Get guild vanity url, if enabled - * - * Returns a partial dpp::invite object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. code will be null if a vanity url for the guild is not set. - * @see dpp::cluster::guild_get_vanity - * @see https://discord.com/developers/docs/resources/guild#get-guild-vanity-url - * @param guild_id Guild to get vanity URL for - * @return invite 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. - */ -invite guild_get_vanity_sync(snowflake guild_id); - -/** - * @brief Get guild widget - * - * Requires the `MANAGE_GUILD` permission. - * - * @see dpp::cluster::guild_get_widget - * @see https://discord.com/developers/docs/resources/guild#get-guild-widget - * @param guild_id Guild ID to get widget for - * @return guild_widget 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. - */ -guild_widget guild_get_widget_sync(snowflake guild_id); - -/** - * @brief Modify guild integration - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * - * @see dpp::cluster::guild_modify_integration - * @see https://discord.com/developers/docs/resources/guild#modify-guild-integration - * @param guild_id Guild ID to modify integration for - * @param i Integration to modify - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_modify_integration_sync(snowflake guild_id, const class integration &i); - -/** - * @brief Get prune counts - * - * Returns a prune object indicating the number of members that would be removed in a prune operation. Requires the `KICK_MEMBERS` - * permission. By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the - * include_roles parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional - * roles will not. - * - * @see dpp::cluster::guild_get_prune_counts - * @see https://discord.com/developers/docs/resources/guild#get-guild-prune-count - * @param guild_id Guild ID to count for pruning - * @param pruneinfo Pruning info - * @return prune 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. - */ -prune guild_get_prune_counts_sync(snowflake guild_id, const struct prune& pruneinfo); - -/** - * @brief Begin guild prune - * - * Begin a prune operation. Requires the `KICK_MEMBERS` permission. Returns a prune object indicating the number of members - * that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to false, forcing - * 'pruned' to 0. Fires multiple `Guild Member Remove` Gateway events. - * By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` - * parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. - * - * @see dpp::cluster::guild_begin_prune - * @see https://discord.com/developers/docs/resources/guild#begin-guild-prune - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to prune - * @param pruneinfo Pruning info - * @return prune 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. - */ -prune guild_begin_prune_sync(snowflake guild_id, const struct prune& pruneinfo); - -/** - * @brief Change current user nickname - * - * Modifies the nickname of the current user in a guild. - * Fires a `Guild Member Update` Gateway event. - * - * @deprecated Deprecated in favor of Modify Current Member. Will be replaced by dpp::cluster::guild_current_member_edit - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::guild_set_nickname - * @see https://discord.com/developers/docs/resources/guild#modify-current-user-nick - * @param guild_id Guild ID to change nickname on - * @param nickname New nickname, or empty string to clear nickname - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_set_nickname_sync(snowflake guild_id, const std::string &nickname); - -/** - * @brief Sync guild integration - * - * @see dpp::cluster::guild_sync_integration - * @see https://discord.com/developers/docs/resources/guild#sync-guild-integration - * @param guild_id Guild ID to sync integration on - * @param integration_id Integration ID to synchronise - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_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 o The onboarding object - * @return onboarding 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. - */ -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 - * @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. - */ -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 - * @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::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 - * @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::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. - * - * Adds a user to the guild, provided you have a valid oauth2 access token for the user with the guilds.join scope. - * Returns the guild_member, which is defaulted if the user is already a member of the guild. Fires a `Guild Member Add` Gateway event. - * - * For guilds with Membership Screening enabled, this endpoint will default to adding new members as pending in the guild member object. - * Members that are pending will have to complete membership screening before they become full members that can talk. - * - * @note All parameters to this endpoint except for access_token are optional. - * The bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission. - * @see dpp::cluster::guild_add_member - * @see https://discord.com/developers/docs/resources/guild#add-guild-member - * @param gm Guild member to add - * @param access_token Access token from Oauth2 scope - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_add_member_sync(const guild_member& gm, const std::string &access_token); - -/** - * @brief Edit the properties of an existing guild member - * - * Modify attributes of a guild member. Returns the guild_member. Fires a `Guild Member Update` Gateway event. - * To remove a timeout, set the `communication_disabled_until` to a non-zero time in the past, e.g. 1. - * When moving members to channels, the API user must have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. - * For moving and disconnecting users from voice, use dpp::cluster::guild_member_move. - * @see dpp::cluster::guild_edit_member - * @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 gm Guild member to edit - * @return guild_member 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. - */ -guild_member guild_edit_member_sync(const guild_member& gm); - -/** - * @brief Get a guild member - * @see dpp::cluster::guild_get_member - * @see https://discord.com/developers/docs/resources/guild#get-guild-member - * @param guild_id Guild ID to get member for - * @param user_id User ID of member to get - * @return guild_member 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. - */ -guild_member guild_get_member_sync(snowflake guild_id, snowflake user_id); - -/** - * @brief Get all guild members - * - * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. - * @see dpp::cluster::guild_get_members - * @see https://discord.com/developers/docs/resources/guild#get-guild-members - * @param guild_id Guild ID to get all members for - * @param limit max number of members to return (1-1000) - * @param after the highest user id in the previous page - * @return guild_member_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -guild_member_map guild_get_members_sync(snowflake guild_id, uint16_t limit, snowflake after); - -/** - * @brief Add role to guild member - * - * Adds a role to a guild member. Requires the `MANAGE_ROLES` permission. - * Fires a `Guild Member Update` Gateway event. - * @see dpp::cluster::guild_member_add_role - * @see https://discord.com/developers/docs/resources/guild#add-guild-member-role - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to add a role to - * @param user_id User ID to add role to - * @param role_id Role ID to add to the user - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_member_add_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); - -/** - * @brief Remove (kick) a guild member - * - * Remove a member from a guild. Requires `KICK_MEMBERS` permission. - * Fires a `Guild Member Remove` Gateway event. - * @see dpp::cluster::guild_member_delete - * @see https://discord.com/developers/docs/resources/guild#remove-guild-member - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @deprecated Replaced by dpp::cluster::guild_member_kick - * @param guild_id Guild ID to kick member from - * @param user_id User ID to kick - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_member_delete_sync(snowflake guild_id, snowflake user_id); - -/** - * @brief Remove (kick) a guild member - * - * Remove a member from a guild. Requires `KICK_MEMBERS` permission. - * Fires a `Guild Member Remove` Gateway event. - * @see dpp::cluster::guild_member_kick - * @see https://discord.com/developers/docs/resources/guild#remove-guild-member - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to kick member from - * @param user_id User ID to kick - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_member_kick_sync(snowflake guild_id, snowflake user_id); - -/** - * @brief Set the timeout of a guild member - * - * Fires a `Guild Member Update` Gateway event. - * @see dpp::cluster::guild_member_timeout - * @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 timeout the member in - * @param user_id User ID to set the timeout for - * @param communication_disabled_until The timestamp when the user's timeout will expire (up to 28 days in the future). Set to 0 to remove the timeout - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_member_timeout_sync(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); - -/** - * @brief Remove role from guild member - * - * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. - * Fires a `Guild Member Update` Gateway event. - * @see dpp::cluster::guild_member_delete_role - * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to remove role from user on - * @param user_id User ID to remove role from - * @param role_id Role to remove - * @return confirmation returned object on completion - * @deprecated Use dpp::cluster::guild_member_remove_role instead - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * 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); - -/** - * @brief Remove role from guild member - * - * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. - * Fires a `Guild Member Update` Gateway event. - * @see dpp::cluster::guild_member_remove_role - * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to remove role from user on - * @param user_id User ID to remove role from - * @param role_id Role to remove - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_member_remove_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); - -/** - * @brief Moves the guild member to a other voice channel, if member is connected to one. - * Set the `channel_id` to `0` to disconnect the user. - * - * Fires a `Guild Member Update` Gateway event. - * @note When moving members to channels, the API user __must__ have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::guild_member_move - * @see https://discord.com/developers/docs/resources/guild#modify-guild-member - * @param channel_id Id of the channel to which the user is used. Set to `0` to disconnect the user - * @param guild_id Guild id to which the user is connected - * @param user_id User id, who should be moved - * @return guild_member 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. - */ -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. - * - * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. - * @see dpp::cluster::guild_search_members - * @see https://discord.com/developers/docs/resources/guild#search-guild-members - * @param guild_id Guild ID to search in - * @param query Query string to match username(s) and nickname(s) against - * @param limit max number of members to return (1-1000) - * @return guild_member_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -guild_member_map guild_search_members_sync(snowflake guild_id, const std::string& query, uint16_t limit); - -/** - * @brief Get guild invites - * - * Returns a list of invite objects (with invite metadata) for the guild. Requires the `MANAGE_GUILD` permission. - * - * @see dpp::cluster::guild_get_invites - * @see https://discord.com/developers/docs/resources/guild#get-guild-invites - * @param guild_id Guild ID to get invites for - * @return invite_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -invite_map guild_get_invites_sync(snowflake guild_id); - - -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 - * @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 invite_get_sync(const std::string &invite_code); - -/** - * @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 - * @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_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 - * @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_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 - * - * @see dpp::cluster::message_create - * @see https://discord.com/developers/docs/resources/channel#create-message - * @param m Message to send - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message message_create_sync(const struct message &m); - -/** - * @brief Crosspost a message. The callback function is called when the message has been sent - * - * @see dpp::cluster::message_crosspost - * @see https://discord.com/developers/docs/resources/channel#crosspost-message - * @param message_id Message to crosspost - * @param channel_id Channel ID to crosspost from - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message 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 - * @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_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 - * @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_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 - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * - * @note If any message provided older than 2 weeks or any duplicate message ID, it will fail. - * - * @see dpp::cluster::message_delete_bulk - * @see https://discord.com/developers/docs/resources/channel#bulk-delete-messages - * @param message_ids List of message IDs to delete (at least 2 and at most 100 message IDs) - * @param channel_id Channel to delete from - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation 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 - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * - * @see dpp::cluster::message_delete - * @see https://discord.com/developers/docs/resources/channel#delete-message - * @param message_id Message ID to delete - * @param channel_id Channel to delete from - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation 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 - * @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_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 - * @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_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 - * @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_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 - * @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_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 - * @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_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 - * @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_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 - * - * @see dpp::cluster::message_edit - * @see https://discord.com/developers/docs/resources/channel#edit-message - * @param m Message to edit - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message message_edit_sync(const struct message &m); - -/** - * @brief Get a message - * - * @see dpp::cluster::message_get - * @see https://discord.com/developers/docs/resources/channel#get-channel-message - * @param message_id Message ID - * @param channel_id Channel ID - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message 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 - * @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_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 - * @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 message_get_reactions_sync(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit); - -/** - * @brief Pin a message - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::message_pin - * @see https://discord.com/developers/docs/resources/channel#pin-message - * @param channel_id Channel id to pin message on - * @param message_id Message id to pin message on - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation message_pin_sync(snowflake channel_id, snowflake message_id); - -/** - * @brief Get multiple messages. - * - * This function will attempt to fetch as many messages as possible using multiple API calls if needed. - * - * @see dpp::cluster::messages_get - * @see https://discord.com/developers/docs/resources/channel#get-channel-messages - * @param channel_id Channel ID to retrieve messages for - * @param around Messages should be retrieved around this ID if this is set to non-zero - * @param before Messages before this ID should be retrieved if this is set to non-zero - * @param after Messages after this ID should be retrieved if this is set to non-zero - * @param limit This number of messages maximum should be returned, up to a maximum of 100. - * @return message_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message_map messages_get_sync(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); - -/** - * @brief Unpin a message - * @see dpp::cluster::message_unpin - * @see https://discord.com/developers/docs/resources/channel#unpin-message - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param channel_id Channel id to unpin message on - * @param message_id Message id to unpin message on - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation message_unpin_sync(snowflake channel_id, snowflake message_id); - -/** - * @brief Get a channel's pins - * @see dpp::cluster::channel_pins_get - * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages - * @param channel_id Channel ID to get pins for - * @return message_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message_map channel_pins_get_sync(snowflake channel_id); - -/** - * @brief Create a role on a guild - * - * Create a new role for the guild. Requires the `MANAGE_ROLES` permission. Returns the new role object on success. - * Fires a `Guild Role Create` Gateway event. - * - * @see dpp::cluster::role_create - * @see https://discord.com/developers/docs/resources/guild#create-guild-role - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param r Role to create (guild ID is encapsulated in the role object) - * @return role 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. - */ -role role_create_sync(const class role &r); - -/** - * @brief Delete a role - * - * Requires the `MANAGE_ROLES` permission. Fires a `Guild Role Delete` Gateway event. - * - * @see dpp::cluster::role_delete - * @see https://discord.com/developers/docs/resources/guild#delete-guild-role - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to delete the role on - * @param role_id Role ID to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation role_delete_sync(snowflake guild_id, snowflake role_id); - -/** - * @brief Edit a role on a guild - * - * Requires the `MANAGE_ROLES` permission. Returns the updated role on success. Fires a `Guild Role Update` Gateway event. - * - * @see dpp::cluster::role_edit - * @see https://discord.com/developers/docs/resources/guild#modify-guild-role - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param r Role to edit - * @return role 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. - */ -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. - * - * Modify the positions of a set of role objects for the guild. Requires the `MANAGE_ROLES` permission. - * Fires multiple `Guild Role Update` Gateway events. - * - * @see dpp::cluster::roles_edit_position - * @see https://discord.com/developers/docs/resources/guild#modify-guild-role-positions - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @param guild_id Guild ID to change the roles position on - * @param roles Vector of roles to change the positions of - * @return role_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -role_map roles_edit_position_sync(snowflake guild_id, const std::vector &roles); - -/** - * @brief Get a role for a guild - * - * @see dpp::cluster::roles_get - * @see https://discord.com/developers/docs/resources/guild#get-guild-roles - * @param guild_id Guild ID to get role for - * @return role_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -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 - * @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 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 - * @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 application_role_connection_update_sync(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 - * @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. - */ -application_role_connection user_application_role_connection_get_sync(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 - * @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. - */ -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 - * @see dpp::cluster::guild_events_get - * @see https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild - * @param guild_id Guild to get events for - * @return scheduled_event_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -scheduled_event_map guild_events_get_sync(snowflake guild_id); - -/** - * @brief Create a scheduled event on a guild - * - * @see dpp::cluster::guild_event_create - * @see https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event - * @param event Event to create (guild ID must be populated) - * @return scheduled_event 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. - */ -scheduled_event guild_event_create_sync(const scheduled_event& event); - -/** - * @brief Delete a scheduled event from a guild - * - * @see dpp::cluster::guild_event_delete - * @see https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event - * @param event_id Event ID to delete - * @param guild_id Guild ID of event to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_event_delete_sync(snowflake event_id, snowflake guild_id); - -/** - * @brief Edit/modify a scheduled event on a guild - * - * @see dpp::cluster::guild_event_edit - * @see https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event - * @param event Event to create (event ID and guild ID must be populated) - * @return scheduled_event 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. - */ -scheduled_event guild_event_edit_sync(const scheduled_event& event); - -/** - * @brief Get a scheduled event for a guild - * - * @see dpp::cluster::guild_event_get - * @see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event - * @param guild_id Guild to get event for - * @param event_id Event ID to get - * @return scheduled_event 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. - */ -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 - * @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. - */ -sku_map skus_get_sync(); - - -stage_instance stage_instance_create_sync(const stage_instance& si); - -/** - * @brief Get the stage instance associated with the channel id, if it exists. - * @see dpp::cluster::stage_instance_get - * @see https://discord.com/developers/docs/resources/stage-instance#get-stage-instance - * @param channel_id ID of the associated channel - * @return stage_instance 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. - */ -stage_instance stage_instance_get_sync(const snowflake channel_id); - - -stage_instance stage_instance_edit_sync(const stage_instance& si); - -/** - * @brief Delete a stage instance. - * @see dpp::cluster::stage_instance_delete - * @see https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance - * @param channel_id ID of the associated channel - * @return confirmation returned object on completion - * @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 - * @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); - -/** - * @brief Create a sticker in a guild - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::guild_sticker_create - * @see https://discord.com/developers/docs/resources/sticker#create-guild-sticker - * @param s Sticker to create. Must have its guild ID set. - * @return sticker 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. - */ -sticker guild_sticker_create_sync(const sticker &s); - -/** - * @brief Delete a sticker from a guild - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::guild_sticker_delete - * @see https://discord.com/developers/docs/resources/sticker#delete-guild-sticker - * @param sticker_id sticker ID to delete - * @param guild_id guild ID to delete from - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_sticker_delete_sync(snowflake sticker_id, snowflake guild_id); - -/** - * @brief Get a guild sticker - * @see dpp::cluster::guild_sticker_get - * @see https://discord.com/developers/docs/resources/sticker#get-guild-sticker - * @param id Id of sticker to get. - * @param guild_id Guild ID of the guild where the sticker is - * @return sticker 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. - */ -sticker guild_sticker_get_sync(snowflake id, snowflake guild_id); - -/** - * @brief Modify a sticker in a guild - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::guild_sticker_modify - * @see https://discord.com/developers/docs/resources/sticker#modify-guild-sticker - * @param s Sticker to modify. Must have its guild ID and sticker ID set. - * @return sticker 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. - */ -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 - * @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 - * @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); - -/** - * @brief Get a nitro sticker - * @see dpp::cluster::nitro_sticker_get - * @see https://discord.com/developers/docs/resources/sticker#get-sticker - * @param id Id of sticker to get. - * @return sticker 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. - */ -sticker nitro_sticker_get_sync(snowflake id); - -/** - * @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 - * @return sticker_pack_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -sticker_pack_map sticker_packs_get_sync(); - -/** - * @brief Create a new guild based on a template. - * @note This endpoint can be used only by bots in less than 10 guilds. - * @see dpp::cluster::guild_create_from_template - * @see https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template - * @param code Template code to create guild from - * @param name Guild name to create - * @return guild 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. - */ -guild guild_create_from_template_sync(const std::string &code, const std::string &name); - -/** - * @brief Creates a template for the guild - * - * @see dpp::cluster::guild_template_create - * @see https://discord.com/developers/docs/resources/guild-template#create-guild-template - * @param guild_id Guild to create template from - * @param name Template name to create - * @param description Description of template to create - * @return dtemplate 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. - */ -dtemplate guild_template_create_sync(snowflake guild_id, const std::string &name, const std::string &description); - -/** - * @brief Deletes the template - * - * @see dpp::cluster::guild_template_delete - * @see https://discord.com/developers/docs/resources/guild-template#delete-guild-template - * @param guild_id Guild ID of template to delete - * @param code Template code to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation guild_template_delete_sync(snowflake guild_id, const std::string &code); - -/** - * @brief Modifies the template's metadata. - * - * @see dpp::cluster::guild_template_modify - * @see https://discord.com/developers/docs/resources/guild-template#modify-guild-template - * @param guild_id Guild ID of template to modify - * @param code Template code to modify - * @param name New name of template - * @param description New description of template - * @return dtemplate 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. - */ -dtemplate guild_template_modify_sync(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); - -/** - * @brief Get guild templates - * - * @see dpp::cluster::guild_templates_get - * @see https://discord.com/developers/docs/resources/guild-template#get-guild-templates - * @param guild_id Guild ID to get templates for - * @return dtemplate_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -dtemplate_map guild_templates_get_sync(snowflake guild_id); - -/** - * @brief Syncs the template to the guild's current state. - * - * @see dpp::cluster::guild_template_sync - * @see https://discord.com/developers/docs/resources/guild-template#sync-guild-template - * @param guild_id Guild to synchronise template for - * @param code Code of template to synchronise - * @return dtemplate 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. - */ -dtemplate guild_template_sync_sync(snowflake guild_id, const std::string &code); - -/** - * @brief Get a template - * @see dpp::cluster::template_get - * @see https://discord.com/developers/docs/resources/guild-template#get-guild-template - * @param code Template code - * @return dtemplate 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. - */ -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/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 - * @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); - -/** - * @brief Leave a thread - * @see dpp::cluster::current_user_leave_thread - * @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 - * @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); - -/** - * @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/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 - * @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. - */ -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/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 - * @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); - -/** - * @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/resources/channel#list-private-archived-threads - * @param channel_id Channel to get public archived threads for - * @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 - * @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); - -/** - * @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/resources/channel#list-public-archived-threads - * @param channel_id Channel to get public archived threads for - * @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 - * @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); - -/** - * @brief Get a thread member - * @see dpp::cluster::thread_member_get - * @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 - * @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); - -/** - * @brief Get members of a thread - * @see dpp::cluster::thread_members_get - * @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 - * @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); - -/** - * @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 - * @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. - */ -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/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) - * @param thread_type Type of thread - CHANNEL_PUBLIC_THREAD, CHANNEL_ANNOUNCEMENT_THREAD, CHANNEL_PRIVATE_THREAD - * @param invitable whether non-moderators can add other non-moderators to a thread; only available when creating a private thread - * @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 - * @return thread 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. - */ -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 - * @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_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/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 - * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) - * @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 - * @return thread 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. - */ -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/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 - * @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); - -/** - * @brief Remove a member from a thread - * @see dpp::cluster::thread_member_remove - * @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 - * @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); - -/** - * @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 - * @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_get_sync(snowflake thread_id); - -/** - * @brief Edit current (bot) user - * - * 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. It can be one of `i_gif`, `i_jpg` or `i_png`. - * @return user returned object on completion - * @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 - * @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); - -/** - * @brief Get current (bot) application - * - * @see dpp::cluster::current_application_get - * @see https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information - * @return application 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. - */ -application current_application_get_sync(); - -/** - * @brief Get current (bot) user - * - * @see dpp::cluster::current_user_get - * @see https://discord.com/developers/docs/resources/user#get-current-user - * @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`. - * \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. - */ -user_identified current_user_get_sync(); - -/** - * @brief Set the bot's voice state on a stage channel - * - * **Caveats** - * - * There are currently several caveats for this endpoint: - * - * - `channel_id` must currently point to a stage channel. - * - current user must already have joined `channel_id`. - * - You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself. - * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. - * - You are able to set `request_to_speak_timestamp` to any present or future time. - * - * @see dpp::cluster::current_user_set_voice_state - * @see https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state - * @param guild_id Guild to set voice state on - * @param channel_id Stage channel to set voice state on - * @return confirmation returned object on completion - * @param suppress True if the user's audio should be suppressed, false if it should not - * @param request_to_speak_timestamp The time at which we requested to speak, or 0 to clear the request. The time set here must be the current time or in the future. - * @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 - * @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); - -/** - * @brief Set a user's voice state on a stage channel - * - * **Caveats** - * - * There are currently several caveats for this endpoint: - * - * - `channel_id` must currently point to a stage channel. - * - User must already have joined `channel_id`. - * - You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently) - * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. - * - When suppressed, the user will have their `request_to_speak_timestamp` removed. - * - * @see dpp::cluster::user_set_voice_state - * @see https://discord.com/developers/docs/resources/guild#modify-user-voice-state - * @param user_id The user to set the voice state of - * @param guild_id Guild to set voice state on - * @param channel_id Stage channel to set voice state on - * @return confirmation returned object on completion - * @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 - * @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); - -/** - * @brief Get current user's connections (linked accounts, e.g. steam, xbox). - * This call requires the oauth2 `connections` scope and cannot be executed - * against a bot token. - * @see dpp::cluster::current_user_connections_get - * @see https://discord.com/developers/docs/resources/user#get-user-connections - * @return connection_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -connection_map current_user_connections_get_sync(); - -/** - * @brief Get current (bot) user guilds - * @see dpp::cluster::current_user_get_guilds - * @see https://discord.com/developers/docs/resources/user#get-current-user-guilds - * @return guild_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -guild_map current_user_get_guilds_sync(); - -/** - * @brief Leave a guild - * @see dpp::cluster::current_user_leave_guild - * @see https://discord.com/developers/docs/resources/user#leave-guild - * @param guild_id Guild ID to leave - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation current_user_leave_guild_sync(snowflake guild_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 - * @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 unless you want something special from `dpp::user_identified` or you've turned off caching, you have no need to call this. - * 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 - * @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); - -/** - * @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 - * @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_cached_sync(snowflake user_id); - -/** - * @brief Get all voice regions - * @see dpp::cluster::get_voice_regions - * @see https://discord.com/developers/docs/resources/voice#list-voice-regions - * @return voiceregion_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -voiceregion_map get_voice_regions_sync(); - -/** - * @brief Get guild voice regions. - * - * Voice regions per guild are somewhat deprecated in preference of per-channel voice regions. - * Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when - * the guild is VIP-enabled. - * - * @see dpp::cluster::guild_get_voice_regions - * @see https://discord.com/developers/docs/resources/guild#get-guild-voice-regions - * @param guild_id Guild ID to get voice regions for - * @return voiceregion_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -voiceregion_map guild_get_voice_regions_sync(snowflake guild_id); - - -webhook create_webhook_sync(const class webhook &wh); - -/** - * @brief Delete a webhook - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::delete_webhook - * @see https://discord.com/developers/docs/resources/webhook#delete-webhook - * @param webhook_id Webhook ID to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation delete_webhook_sync(snowflake webhook_id); - -/** - * @brief Delete webhook message - * - * @see dpp::cluster::delete_webhook_message - * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message - * @param wh Webhook to delete message for - * @param message_id Message ID to delete - * @param thread_id ID of the thread the message is in - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); - -/** - * @brief Delete webhook with token - * @see dpp::cluster::delete_webhook_with_token - * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token - * @param webhook_id Webhook ID to delete - * @param token Token of webhook to delete - * @return confirmation returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -confirmation delete_webhook_with_token_sync(snowflake webhook_id, const std::string &token); - -/** - * @brief Edit webhook - * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. - * @see dpp::cluster::edit_webhook - * @see https://discord.com/developers/docs/resources/webhook#modify-webhook - * @param wh Webhook to edit - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook edit_webhook_sync(const class webhook& wh); - -/** - * @brief Edit webhook message - * - * When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on - * the new content. The allowed_mentions field of the edit request controls how this happens. If there is no explicit - * allowed_mentions in the edit request, the content will be parsed with default allowances, that is, without regard to - * whether or not an allowed_mentions was present in the request that originally created the message. - * - * @see dpp::cluster::edit_webhook_message - * @see https://discord.com/developers/docs/resources/webhook#edit-webhook-message - * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. - * @param wh Webhook to edit message for - * @param m New message - * @param thread_id ID of the thread the message is in - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message edit_webhook_message_sync(const class webhook &wh, const struct message &m, snowflake thread_id = 0); - -/** - * @brief Edit webhook with token (token is encapsulated in the webhook object) - * @see dpp::cluster::edit_webhook_with_token - * @see https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token - * @param wh Webhook to edit (should include token) - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook edit_webhook_with_token_sync(const class webhook& wh); - -/** - * @brief Execute webhook - * - * @see dpp::cluster::execute_webhook - * @see https://discord.com/developers/docs/resources/webhook#execute-webhook - * @param wh Webhook to execute - * @param m Message to send - * @param wait waits for server confirmation of message send before response, and returns the created message body - * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived - * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) - * @return message returned object on completion - * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = ""); - -/** - * @brief Get channel webhooks - * @see dpp::cluster::get_channel_webhooks - * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks - * @param channel_id Channel ID to get webhooks for - * @return webhook_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook_map get_channel_webhooks_sync(snowflake channel_id); - -/** - * @brief Get guild webhooks - * @see dpp::cluster::get_guild_webhooks - * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks - * @param guild_id Guild ID to get webhooks for - * @return webhook_map returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook_map get_guild_webhooks_sync(snowflake guild_id); - -/** - * @brief Get webhook - * @see dpp::cluster::get_webhook - * @see https://discord.com/developers/docs/resources/webhook#get-webhook - * @param webhook_id Webhook ID to get - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook get_webhook_sync(snowflake webhook_id); - -/** - * @brief Get webhook message - * - * @see dpp::cluster::get_webhook_message - * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message - * @param wh Webhook to get the original message for - * @param message_id The message ID - * @param thread_id ID of the thread the message is in - * @return message returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -message get_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); - -/** - * @brief Get webhook using token - * @see dpp::cluster::get_webhook_with_token - * @see https://discord.com/developers/docs/resources/webhook#get-webhook-with-token - * @param webhook_id Webhook ID to retrieve - * @param token Token of webhook - * @return webhook returned object on completion - * \memberof dpp::cluster - * @throw dpp::rest_exception upon failure to execute REST function - * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. - * Avoid direct use of this function inside an event handler. - */ -webhook get_webhook_with_token_sync(snowflake webhook_id, const std::string &token); - - -/* End of auto-generated definitions */ diff --git a/3rdParty/dpp/collector.h b/3rdParty/dpp/collector.h index bf3ebb9262..48aa5982fb 100644 --- a/3rdParty/dpp/collector.h +++ b/3rdParty/dpp/collector.h @@ -166,27 +166,27 @@ class collected_reaction : public managed { /** * @brief Reacting user. */ - user react_user; + user react_user{}; /** * @brief Reacting guild. */ - guild* react_guild{}; + guild react_guild{}; /** * @brief Reacting guild member. */ - guild_member react_member; + guild_member react_member{}; /** * @brief Reacting channel. */ - channel* react_channel{}; + channel react_channel{}; /** * @brief Reacted emoji. */ - emoji react_emoji; + emoji react_emoji{}; /** * @brief Optional: ID of the user who authored the message which was reacted to. @@ -351,7 +351,7 @@ class channel_collector : public channel_collector_t { * @param element element to filter * @return Returned item to add to the list, or nullptr to skip adding this element */ - virtual const dpp::channel* filter(const dpp::channel_create_t& element) { return element.created; } + virtual const dpp::channel* filter(const dpp::channel_create_t& element) { return &element.created; } /** * @brief Destroy the channel collector object @@ -425,7 +425,7 @@ class role_collector : public role_collector_t { * @param element element to filter * @return Returned item to add to the list, or nullptr to skip adding this element */ - virtual const dpp::role* filter(const dpp::guild_role_create_t& element) { return element.created; } + virtual const dpp::role* filter(const dpp::guild_role_create_t& element) { return &element.created; } /** * @brief Destroy the role collector object @@ -470,4 +470,4 @@ class scheduled_event_collector : public scheduled_event_collector_t { virtual ~scheduled_event_collector() = default; }; -} // namespace dpp +} diff --git a/3rdParty/dpp/colors.h b/3rdParty/dpp/colors.h index e7d8833991..2f2112438f 100644 --- a/3rdParty/dpp/colors.h +++ b/3rdParty/dpp/colors.h @@ -31,7 +31,7 @@ namespace dpp { * @brief predefined color constants. */ namespace colors { - const uint32_t + static constexpr uint32_t white = 0xFFFFFF, discord_white = 0xFFFFFE, light_gray = 0xC0C0C0, diff --git a/3rdParty/dpp/commandhandler.h b/3rdParty/dpp/commandhandler.h index 8a38bbcfa1..a244c18c25 100644 --- a/3rdParty/dpp/commandhandler.h +++ b/3rdParty/dpp/commandhandler.h @@ -253,7 +253,7 @@ struct DPP_EXPORT command_info_t { * functions. * @deprecated commandhandler and message commands are deprecated and dpp::slashcommand is encouraged as a replacement. */ -class DPP_EXPORT commandhandler { +class DPP_EXPORT DPP_DEPRECATED("commandhandler should not be used. Please consider using dpp::cluster::register_command instead.") commandhandler { private: /** * @brief List of guild commands to bulk register @@ -425,4 +425,4 @@ class DPP_EXPORT commandhandler { }; -} // namespace dpp +} diff --git a/3rdParty/dpp/compat.h b/3rdParty/dpp/compat.h new file mode 100644 index 0000000000..8416ce28fe --- /dev/null +++ b/3rdParty/dpp/compat.h @@ -0,0 +1,43 @@ +/************************************************************************************ +* +* 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 + +#ifdef _WIN32 + #include + #include + #include + namespace dpp::compat { + using pollfd = WSAPOLLFD; + inline int poll(pollfd* fds, ULONG nfds, int timeout) { + return WSAPoll(fds, nfds, timeout); + } + } // namespace dpp::compat + #pragma comment(lib, "ws2_32") +#else + #include + #include + #include + #include + namespace dpp::compat { + using ::pollfd; + using ::poll; + } // namespace dpp::compat +#endif diff --git a/3rdParty/dpp/coro.h b/3rdParty/dpp/coro.h index 9836dea5f1..bc9c140d2b 100644 --- a/3rdParty/dpp/coro.h +++ b/3rdParty/dpp/coro.h @@ -21,6 +21,7 @@ #pragma once +#include "coro/awaitable.h" #include "coro/async.h" #include "coro/coroutine.h" #include "coro/job.h" diff --git a/3rdParty/dpp/coro/async.h b/3rdParty/dpp/coro/async.h index 7067fd6bc4..5e2a09a127 100644 --- a/3rdParty/dpp/coro/async.h +++ b/3rdParty/dpp/coro/async.h @@ -22,19 +22,20 @@ #include +#include + namespace dpp { -struct async_dummy { - int* dummy_shared_state = nullptr; +struct async_dummy : awaitable_dummy { + std::shared_ptr dummy_shared_state = nullptr; }; } -#ifdef DPP_CORO +#ifndef DPP_NO_CORO #include "coro.h" -#include #include #include #include @@ -45,341 +46,44 @@ namespace dpp { namespace detail { -/** - * @brief Empty struct used for overload resolution. - */ -struct empty_tag_t{}; - namespace async { /** - * @brief Represents the step an std::async is at. - */ -enum class state_t { - /** - * @brief Request was sent but not co_await-ed. handle is nullptr, result_storage is not constructed. - */ - sent, - - /** - * @brief Request was co_await-ed. handle is valid, result_storage is not constructed. - */ - waiting, - - /** - * @brief Request was completed. handle is unknown, result_storage is valid. - */ - done, - - /** - * @brief Request was never co_await-ed. - */ - dangling -}; - -/** - * @brief State of the async and its callback. - * - * Defined outside of dpp::async because this seems to work better with Intellisense. + * @brief Shared state of the async and its callback, to be used across threads. */ template -struct async_callback_data { +struct callback { /** - * @brief Number of references to this callback state. + * @brief Promise object to set the result into */ - std::atomic ref_count{1}; + std::shared_ptr> promise{nullptr}; /** - * @brief State of the awaitable and the API callback + * @brief Call operator, sets the value in the promise and notifies any awaiter + * + * @param v Callback value */ - std::atomic state = state_t::sent; - - /** - * @brief The stored result of the API call, stored as an array of bytes to directly construct in place - */ - alignas(R) std::array result_storage; - - /** - * @brief Handle to the coroutine co_await-ing on this API call - * - * @see std::coroutine_handle - */ - std_coroutine::coroutine_handle<> coro_handle = nullptr; - - /** - * @brief Convenience function to construct the result in the storage and initialize its lifetime - * - * @warning This is only a convenience function, ONLY CALL THIS IN THE CALLBACK, before setting state to done. - */ - template - void construct_result(Ts&&... ts) { - // Standard-compliant type punning yay - std::construct_at(reinterpret_cast(result_storage.data()), std::forward(ts)...); + template + void operator()(const U& v) const requires (std::convertible_to) { + promise->set_value(v); } - - /** - * @brief Destructor. - * - * Also destroys the result if present. - */ - ~async_callback_data() { - if (state.load() == state_t::done) { - std::destroy_at(reinterpret_cast(result_storage.data())); - } - } -}; - -/** - * @brief Base class of dpp::async. - * - * @warning This class should not be used directly by a user, use dpp::async instead. - * @note This class contains all the functions used internally by co_await. It is intentionally opaque and a private base of dpp::async so a user cannot call await_suspend and await_resume directly. - */ -template -class async_base { - /** - * @brief Ref-counted callback, contains the callback logic and manages the lifetime of the callback data over multiple threads. - */ - struct shared_callback { - /** - * @brief Self-managed ref-counted pointer to the state data - */ - async_callback_data *state = new async_callback_data; - - /** - * @brief Callback function. - * - * Constructs the callback data, and if the coroutine was awaiting, resume it - * @param cback The result of the API call. - * @tparam V Forwarding reference convertible to R - */ - template V> - void operator()(V &&cback) const { - state->construct_result(std::forward(cback)); - if (auto previous_state = state->state.exchange(state_t::done); previous_state == state_t::waiting) { - state->coro_handle.resume(); - } - } - - /** - * @brief Main constructor, allocates a new callback_state object. - */ - shared_callback() = default; - - /** - * @brief Empty constructor, holds no state. - */ - explicit shared_callback(detail::empty_tag_t) noexcept : state{nullptr} {} - - /** - * @brief Copy constructor. Takes shared ownership of the callback state, increasing the reference count. - */ - shared_callback(const shared_callback &other) noexcept { - this->operator=(other); - } - - /** - * @brief Move constructor. Transfers ownership from another object, leaving intact the reference count. The other object releases the callback state. - */ - shared_callback(shared_callback &&other) noexcept { - this->operator=(std::move(other)); - } - - /** - * @brief Destructor. Releases the held reference and destroys if no other references exist. - */ - ~shared_callback() { - if (!state) { // Moved-from object - return; - } - - auto count = state->ref_count.fetch_sub(1); - if (count == 0) { - delete state; - } - } - - /** - * @brief Copy assignment. Takes shared ownership of the callback state, increasing the reference count. - */ - shared_callback &operator=(const shared_callback &other) noexcept { - state = other.state; - ++state->ref_count; - return *this; - } - - /** - * @brief Move assignment. Transfers ownership from another object, leaving intact the reference count. The other object releases the callback state. - */ - shared_callback &operator=(shared_callback &&other) noexcept { - state = std::exchange(other.state, nullptr); - return *this; - } - - /** - * @brief Function called by the async when it is destroyed when it was never co_awaited, signals to the callback to abort. - */ - void set_dangling() noexcept { - if (!state) { // moved-from object - return; - } - state->state.store(state_t::dangling); - } - - bool done(std::memory_order order = std::memory_order_seq_cst) const noexcept { - return (state->state.load(order) == state_t::done); - } - - /** - * @brief Convenience function to get the shared callback state's result. - * - * @warning It is UB to call this on a callback whose state is anything else but state_t::done. - */ - R &get_result() noexcept { - assert(state && done()); - return (*reinterpret_cast(state->result_storage.data())); - } - - /** - * @brief Convenience function to get the shared callback state's result. - * - * @warning It is UB to call this on a callback whose state is anything else but state_t::done. - */ - const R &get_result() const noexcept { - assert(state && done()); - return (*reinterpret_cast(state->result_storage.data())); - } - }; - + /** - * @brief Shared state of the async and its callback, to be used across threads. - */ - shared_callback api_callback{nullptr}; - -public: - /** - * @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. + * @brief Call operator, sets the value in the promise and notifies any awaiter * - * @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 + * @param v Callback value */ - template -#ifndef _DOXYGEN_ - requires std::invocable> -#endif - explicit async_base(Obj &&obj, Fun &&fun, Args&&... args) : api_callback{} { - std::invoke(std::forward(fun), std::forward(obj), std::forward(args)..., api_callback); + template + void operator()(U&& v) const requires (std::convertible_to) { + promise->set_value(std::move(v)); } - + /** - * @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 + * @brief Call operator, sets the value in the promise and notifies any awaiter */ - template -#ifndef _DOXYGEN_ - requires std::invocable> -#endif - explicit async_base(Fun &&fun, Args&&... args) : api_callback{} { - std::invoke(std::forward(fun), std::forward(args)..., api_callback); - } - - /** - * @brief Construct an empty async. Using `co_await` on an empty async is undefined behavior. - */ - async_base() noexcept : api_callback{detail::empty_tag_t{}} {} - - /** - * @brief Destructor. If any callback is pending it will be aborted. - */ - ~async_base() { - api_callback.set_dangling(); - } - - /** - * @brief Copy constructor is disabled - */ - async_base(const async_base &) = delete; - - /** - * @brief Move constructor - * - * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. - * - * @remark Using the moved-from async after this function is undefined behavior. - * @param other The async object to move the data from. - */ - async_base(async_base &&other) noexcept = default; - - /** - * @brief Copy assignment is disabled - */ - async_base &operator=(const async_base &) = delete; - - /** - * @brief Move assignment operator. - * - * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. - * - * @remark Using the moved-from async after this function is undefined behavior. - * @param other The async object to move the data from - */ - async_base &operator=(async_base &&other) noexcept = default; - - /** - * @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 of the API call or not - */ - [[nodiscard]] bool await_ready() const noexcept { - return api_callback.done(); - } - - /** - * @brief Second function called by the standard library when the object is co-awaited, if await_ready returned false. - * - * Checks again for the presence of the result, if absent, signals to suspend and keep track of the calling coroutine for the callback to resume. - * - * @remark Do not call this manually, use the co_await keyword instead. - * @param caller The handle to the coroutine co_await-ing and being suspended - */ - [[nodiscard]] bool await_suspend(detail::std_coroutine::coroutine_handle<> caller) noexcept { - auto sent = state_t::sent; - api_callback.state->coro_handle = caller; - return api_callback.state->state.compare_exchange_strong(sent, state_t::waiting); // true (suspend) if `sent` was replaced with `waiting` -- false (resume) if the value was not `sent` (`done` is the only other option) - } - - /** - * @brief Function called by the standard library when the async is resumed. Its return value is what the whole co_await expression evaluates to - * - * @remark Do not call this manually, use the co_await keyword instead. - * @return The result of the API call as an lvalue reference. - */ - R& await_resume() & noexcept { - return api_callback.get_result(); - } - - - /** - * @brief Function called by the standard library when the async is resumed. Its return value is what the whole co_await expression evaluates to - * - * @remark Do not call this manually, use the co_await keyword instead. - * @return The result of the API call as a const lvalue reference. - */ - const R& await_resume() const& noexcept { - return api_callback.get_result(); - } - - /** - * @brief Function called by the standard library when the async is resumed. Its return value is what the whole co_await expression evaluates to - * - * @remark Do not call this manually, use the co_await keyword instead. - * @return The result of the API call as an rvalue reference. - */ - R&& await_resume() && noexcept { - return std::move(api_callback.get_result()); + void operator()() const requires (std::is_void_v) + { + promise->set_value(); } }; @@ -400,19 +104,26 @@ struct confirmation_callback_t; * @tparam R The return type of the API call. Defaults to confirmation_callback_t */ template -class async : private detail::async::async_base { +class async : public awaitable { /** - * @brief Internal use only base class. It serves to prevent await_suspend and await_resume from being used directly. - * - * @warning For internal use only, do not use. - * @see operator co_await() + * @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 */ - friend class detail::async::async_base; + explicit async(std::shared_ptr> &&promise) : awaitable{promise.get()}, api_callback{std::move(promise)} {} public: - using detail::async::async_base::async_base; // use async_base's constructors. unfortunately on clang this doesn't include the templated ones so we have to delegate below - using detail::async::async_base::operator=; // use async_base's assignment operator - using detail::async::async_base::await_ready; // expose await_ready as 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. @@ -425,7 +136,9 @@ class async : private detail::async::async_base { #ifndef _DOXYGEN_ requires std::invocable> #endif - explicit async(Obj &&obj, Fun &&fun, Args&&... args) : detail::async::async_base{std::forward(obj), std::forward(fun), std::forward(args)...} {} + 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. @@ -437,87 +150,40 @@ class async : private detail::async::async_base { #ifndef _DOXYGEN_ requires std::invocable> #endif - explicit async(Fun &&fun, Args&&... args) : detail::async::async_base{std::forward(fun), std::forward(args)...} {} - -#ifdef _DOXYGEN_ // :) - /** - * @brief Construct an empty async. Using `co_await` on an empty async is undefined behavior. - */ - async() noexcept; - - /** - * @brief Destructor. If any callback is pending it will be aborted. - */ - ~async(); - - /** - * @brief Copy constructor is disabled - */ - async(const async &); - - /** - * @brief Move constructor - * - * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. - * - * @remark Using the moved-from async after this function is undefined behavior. - * @param other The async object to move the data from. - */ - async(async &&other) noexcept = default; + explicit async(Fun &&fun, Args&&... args) : async{std::make_shared>()} { + std::invoke(std::forward(fun), std::forward(args)..., api_callback); + } /** - * @brief Copy assignment is disabled + * @brief Copy constructor is disabled. */ - async &operator=(const async &) = delete; + async(const async&) = delete; /** - * @brief Move assignment operator. - * - * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. - * - * @remark Using the moved-from async after this function is undefined behavior. - * @param other The async object to move the data from + * @brief Move constructor, moves the awaitable async object */ - async &operator=(async &&other) noexcept = default; - + async(async&&) = default; + /** - * @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 of the API call or not + * @brief Copy assignment operator is disabled. */ - [[nodiscard]] bool await_ready() const noexcept; -#endif - - /** - * @brief Suspend the caller until the request completes. - * - * @return On resumption, this expression evaluates to the result object of type R, as a reference. - */ - [[nodiscard]] auto& operator co_await() & noexcept { - return static_cast&>(*this); - } - + async& operator=(const async&) = delete; + /** - * @brief Suspend the caller until the request completes. - * - * @return On resumption, this expression evaluates to the result object of type R, as a const reference. + * @brief Move assignment operator, moves the awaitable async object */ - [[nodiscard]] const auto& operator co_await() const & noexcept { - return static_cast const&>(*this); - } + async& operator=(async&&) = default; /** - * @brief Suspend the caller until the request completes. - * - * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. + * @brief Destructor, signals to the callback that the async object is gone and shouldn't be notified of the result */ - [[nodiscard]] auto&& operator co_await() && noexcept { - return static_cast&&>(*this); + ~async() { + this->abandon(); } }; DPP_CHECK_ABI_COMPAT(async<>, async_dummy); -} // namespace dpp +} -#endif /* DPP_CORO */ +#endif /* DPP_NO_CORO */ diff --git a/3rdParty/dpp/coro/awaitable.h b/3rdParty/dpp/coro/awaitable.h new file mode 100644 index 0000000000..99c21bab62 --- /dev/null +++ b/3rdParty/dpp/coro/awaitable.h @@ -0,0 +1,735 @@ +/************************************************************************************ + * + * 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; +}; + +} + +#ifndef DPP_NO_CORO + +#include + +// Do not include as coro.h includes or depending on clang version +#include +#include +#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(static_cast(~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_NO_CORO */ diff --git a/3rdParty/dpp/coro/coro.h b/3rdParty/dpp/coro/coro.h index 4136f16de8..a6e0f881fa 100644 --- a/3rdParty/dpp/coro/coro.h +++ b/3rdParty/dpp/coro/coro.h @@ -19,9 +19,12 @@ * ************************************************************************************/ -#ifdef DPP_CORO #pragma once +#include + +#ifndef DPP_NO_CORO + #if (defined(_LIBCPP_VERSION) and !defined(__cpp_impl_coroutine)) // if libc++ experimental implementation (LLVM < 14) # define STDCORO_EXPERIMENTAL_HEADER # define STDCORO_EXPERIMENTAL_NAMESPACE @@ -114,13 +117,14 @@ decltype(auto) co_await_resolve(T&& expr) noexcept { } #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 -bool has_co_await_member; +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)` @@ -128,7 +132,7 @@ bool has_co_await_member; * @note This is actually a C++20 concept but Doxygen doesn't do well with them */ template -bool has_free_co_await; +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. @@ -136,7 +140,15 @@ bool has_free_co_await; * @note This is actually a C++20 concept but Doxygen doesn't do well with them */ template -bool has_await_members; +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` @@ -144,6 +156,7 @@ bool has_await_members; * This function is conditionally noexcept, if the returned expression also is. */ decltype(auto) co_await_resolve(auto&& expr) {} + #endif /** @@ -154,6 +167,12 @@ using awaitable_result = decltype(co_await_resolve(std::declval()).await_resu } // 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 @@ -182,5 +201,5 @@ inline int coro_alloc_count = 0; } // namespace dpp -#endif /* DPP_CORO */ +#endif /* DPP_NO_CORO */ diff --git a/3rdParty/dpp/coro/coroutine.h b/3rdParty/dpp/coro/coroutine.h index fd0a6d7bc1..d92ad09eaf 100644 --- a/3rdParty/dpp/coro/coroutine.h +++ b/3rdParty/dpp/coro/coroutine.h @@ -30,9 +30,10 @@ struct coroutine_dummy { } -#ifdef DPP_CORO +#ifndef DPP_NO_CORO -#include "coro.h" +#include +#include #include #include @@ -46,7 +47,7 @@ namespace detail { namespace coroutine { - template +template struct promise_t; template @@ -55,184 +56,95 @@ template */ using handle_t = std_coroutine::coroutine_handle>; +} // namespace coroutine + +} // namespace detail + /** - * @brief Base class of dpp::coroutine. + * @class coroutine coroutine.h coro/coroutine.h + * @brief Base type for a coroutine, starts on co_await. * - * @warn This class should not be used directly by a user, use dpp::coroutine instead. - * @note This class contains all the functions used internally by co_await. It is intentionally opaque and a private base of dpp::coroutine so a user cannot call await_suspend and await_resume directly. + * @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 coroutine_base { -protected: +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 promise_t; + friend struct detail::coroutine::promise_t; /** * @brief Coroutine handle. */ detail::coroutine::handle_t handle{nullptr}; -private: /** * @brief Construct from a handle. Internal use only. */ - coroutine_base(detail::coroutine::handle_t h) : handle{h} {} - -public: - /** - * @brief Default constructor, creates an empty coroutine. - */ - coroutine_base() = default; - - /** - * @brief Copy constructor is disabled - */ - coroutine_base(const coroutine_base &) = delete; + coroutine(detail::coroutine::handle_t h) : handle{h} {} - /** - * @brief Move constructor, grabs another coroutine's handle - * - * @param other Coroutine to move the handle from - */ - coroutine_base(coroutine_base &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + struct awaiter { + /** + * @brief Reference to the coroutine object being awaited. + */ + coroutine &coro; - /** - * @brief Destructor, destroys the handle. - */ - ~coroutine_base() { - if (handle) { - handle.destroy(); + /** + * @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 Copy assignment is disabled - */ - coroutine_base &operator=(const coroutine_base &) = delete; - - /** - * @brief Move assignment, grabs another coroutine's handle - * - * @param other Coroutine to move the handle from - */ - coroutine_base &operator=(coroutine_base &&other) noexcept { - handle = std::exchange(other.handle, nullptr); - return *this; - } - /** - * @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 (!handle) { - throw dpp::logic_exception("cannot co_await an empty coroutine"); + /** + * @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; } - return 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]] handle_t await_suspend(detail::std_coroutine::coroutine_handle caller) noexcept { - handle.promise().parent = caller; - return handle; - } - /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @remark Do not call this manually, use the co_await keyword instead. - * @throw Throws any exception thrown or uncaught by the coroutine - * @return R The result of the coroutine. It is given to the caller as a result to `co_await` - */ - decltype(auto) await_resume() & { - return static_cast &>(*this).await_resume_impl(); - } - - /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @remark Do not call this manually, use the co_await keyword instead. - * @throw Throws any exception thrown or uncaught by the coroutine - * @return R The result of the coroutine. It is given to the caller as a result to `co_await` - */ - [[nodiscard]] decltype(auto) await_resume() const & { - return static_cast const&>(*this).await_resume_impl(); - } - - /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @remark Do not call this manually, use the co_await keyword instead. - * @throw Throws any exception thrown or uncaught by the coroutine - * @return R The result of the coroutine. It is given to the caller as a result to `co_await` - */ - [[nodiscard]] decltype(auto) await_resume() && { - return static_cast &&>(*this).await_resume_impl(); - } -}; - -} // namespace coroutine - -} // namespace detail + /** + * @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 + } + } + }; -/** - * @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 coroutine : private detail::coroutine::coroutine_base { +public: /** - * @brief Internal use only base class containing common logic between coroutine and coroutine. It also serves to prevent await_suspend and await_resume from being used directly. - * - * @warning For internal use only, do not use. - * @see operator co_await() + * @brief The type of the result produced by this coroutine. */ - friend class detail::coroutine::coroutine_base; - - [[nodiscard]] R& await_resume_impl() & { - detail::coroutine::promise_t &promise = this->handle.promise(); - if (promise.exception) { - std::rethrow_exception(promise.exception); - } - return *promise.result; - } - - [[nodiscard]] const R& await_resume_impl() const & { - detail::coroutine::promise_t &promise = this->handle.promise(); - if (promise.exception) { - std::rethrow_exception(promise.exception); - } - return *promise.result; - } - - [[nodiscard]] R&& await_resume_impl() && { - detail::coroutine::promise_t &promise = this->handle.promise(); - if (promise.exception) { - std::rethrow_exception(promise.exception); - } - return *std::move(promise.result); - } + using result_type = R; -public: -#ifdef _DOXYGEN_ // :)))) /** * @brief Default constructor, creates an empty coroutine. */ @@ -248,12 +160,16 @@ class coroutine : private detail::coroutine::coroutine_base { * * @param other Coroutine to move the handle from */ - coroutine(coroutine &&other) noexcept; + coroutine(coroutine &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} /** * @brief Destructor, destroys the handle. */ - ~coroutine(); + ~coroutine() { + if (handle) { + handle.destroy(); + } + } /** * @brief Copy assignment is disabled @@ -265,106 +181,15 @@ class coroutine : private detail::coroutine::coroutine_base { * * @param other Coroutine to move the handle from */ - coroutine &operator=(coroutine &&other) noexcept; - - /** - * @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; -#else - using detail::coroutine::coroutine_base::coroutine_base; // use coroutine_base's constructors - using detail::coroutine::coroutine_base::operator=; // use coroutine_base's assignment operators - using detail::coroutine::coroutine_base::await_ready; // expose await_ready as public -#endif - - /** - * @brief Suspend the caller until the coroutine completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as a reference. - */ - [[nodiscard]] auto& operator co_await() & noexcept { - return static_cast&>(*this); - } - - /** - * @brief Suspend the caller until the coroutine completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as a const reference. - */ - [[nodiscard]] const auto& operator co_await() const & noexcept { - return static_cast const&>(*this); - } - - /** - * @brief Suspend the caller until the coroutine completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. - */ - [[nodiscard]] auto&& operator co_await() && noexcept { - return static_cast&&>(*this); - } -}; - -#ifndef _DOXYGEN_ // don't generate this on doxygen because `using` doesn't work and 2 copies of coroutine_base's docs is enough -/** - * @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 the D++ 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 coroutine : private detail::coroutine::coroutine_base { - /** - * @brief Base class has friend access for CRTP downcast - */ - friend class detail::coroutine::coroutine_base; - - void await_resume_impl() const; - -public: - using detail::coroutine::coroutine_base::coroutine_base; // use coroutine_base's constructors - using detail::coroutine::coroutine_base::operator=; // use coroutine_base's assignment operators - using detail::coroutine::coroutine_base::await_ready; // expose await_ready as public - - /** - * @brief Suspend the current coroutine until the coroutine completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as a reference. - */ - [[nodiscard]] auto& operator co_await() & noexcept { - return static_cast&>(*this); - } - - /** - * @brief Suspend the current coroutine until the coroutine completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as a const reference. - */ - [[nodiscard]] const auto& operator co_await() const & noexcept { - return static_cast const &>(*this); + coroutine &operator=(coroutine &&other) noexcept { + handle = std::exchange(other.handle, nullptr); + return *this; } - - /** - * @brief Suspend the current coroutine until the coroutine completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. - */ - [[nodiscard]] auto&& operator co_await() && noexcept { - return static_cast&&>(*this); + + [[nodiscard]] auto operator co_await() { + return awaiter{*this}; } }; -#endif /* _DOXYGEN_ */ namespace detail::coroutine { template @@ -565,18 +390,10 @@ namespace detail::coroutine { } // namespace detail -#ifndef _DOXYGEN_ -inline void coroutine::await_resume_impl() const { - if (handle.promise().exception) { - std::rethrow_exception(handle.promise().exception); - } -} -#endif /* _DOXYGEN_ */ - DPP_CHECK_ABI_COMPAT(coroutine, coroutine_dummy) DPP_CHECK_ABI_COMPAT(coroutine, coroutine_dummy) -} // namespace dpp +} /** * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. @@ -586,4 +403,4 @@ struct dpp::detail::std_coroutine::coroutine_traits, Args...> using promise_type = dpp::detail::coroutine::promise_t; }; -#endif /* DPP_CORO */ +#endif /* DPP_NO_CORO */ diff --git a/3rdParty/dpp/coro/job.h b/3rdParty/dpp/coro/job.h index 6bed5f312e..eb9290cc90 100644 --- a/3rdParty/dpp/coro/job.h +++ b/3rdParty/dpp/coro/job.h @@ -29,7 +29,7 @@ struct job_dummy { } -#ifdef DPP_CORO +#ifndef DPP_NO_CORO #include "coro.h" @@ -142,4 +142,4 @@ struct dpp::detail::std_coroutine::coroutine_traits { using promise_type = dpp::detail::job::promise; }; -#endif /* DPP_CORO */ +#endif /* DPP_NO_CORO */ diff --git a/3rdParty/dpp/coro/task.h b/3rdParty/dpp/coro/task.h index 83cdee8d52..2a4ad35155 100644 --- a/3rdParty/dpp/coro/task.h +++ b/3rdParty/dpp/coro/task.h @@ -21,18 +21,19 @@ #pragma once #include +#include namespace dpp { -struct task_dummy { +struct task_dummy : awaitable_dummy { int* handle_dummy = nullptr; }; } -#ifdef DPP_CORO +#ifndef DPP_NO_CORO -#include "coro.h" +#include #include #include @@ -51,28 +52,6 @@ namespace detail { /* Internal cogwheels for dpp::task */ namespace task { -/** - * @brief State of a task - */ -enum class state_t { - /** - * @brief Task was started but never co_await-ed - */ - started, - /** - * @brief Task was co_await-ed and is pending completion - */ - awaited, - /** - * @brief Task is completed - */ - done, - /** - * @brief Task is still running but the actual dpp::task object is destroyed - */ - dangling -}; - /** * @brief A @ref dpp::task "task"'s promise_t type, with special logic for handling nested tasks. * @@ -97,176 +76,6 @@ struct final_awaiter; template using handle_t = std_coroutine::coroutine_handle>; -/** - * @brief Base class of @ref dpp::task. - * - * @warning This class should not be used directly by a user, use @ref dpp::task instead. - * @note This class contains all the functions used internally by co_await. It is intentionally opaque and a private base of @ref dpp::task so a user cannot call await_suspend() and await_resume() directly. - */ -template -class task_base { -protected: - /** - * @brief The coroutine handle of this task. - */ - handle_t handle; - - /** - * @brief Promise type of this coroutine. For internal use only, do not use. - */ - friend struct promise_t; - -private: - /** - * @brief Construct from a coroutine handle. Internal use only - */ - explicit task_base(handle_t handle_) : handle(handle_) {} - -public: - /** - * @brief Default constructor, creates a task not bound to a coroutine. - */ - task_base() = default; - - /** - * @brief Copy constructor is disabled - */ - task_base(const task_base &) = delete; - - /** - * @brief Move constructor, grabs another task's coroutine handle - * - * @param other Task to move the handle from - */ - task_base(task_base &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} - - /** - * @brief Destructor. - * - * Destroys the handle. - * @warning The coroutine must be finished before this is called, otherwise it runs the risk of being resumed after it is destroyed, resuming in use-after-free undefined behavior. - */ - ~task_base() { - if (handle) { - promise_t &promise = handle.promise(); - state_t previous_state = promise.state.exchange(state_t::dangling); - - if (previous_state == state_t::done) { - handle.destroy(); - } - else { - cancel(); - } - } - } - - /** - * @brief Copy assignment is disabled - */ - task_base &operator=(const task_base &) = delete; - - /** - * @brief Move assignment, grabs another task's coroutine handle - * - * @param other Task to move the handle from - */ - task_base &operator=(task_base &&other) noexcept { - handle = std::exchange(other.handle, nullptr); - return (*this); - } - - /** - * @brief Check whether or not a call to co_await will suspend the caller. - * - * This function is called by the standard library as a first step when using co_await. If it returns true then the caller is not suspended. - * @throws logic_exception if the task is empty. - * @return bool Whether not to suspend the caller or not - */ - [[nodiscard]] bool await_ready() const { - if (!handle) { - throw dpp::logic_exception{"cannot co_await an empty task"}; - } - return handle.promise().state.load() == state_t::done; - } - - /** - * @brief Second function called by the standard library when the task is co_await-ed, if await_ready returned false. - * - * Stores the calling coroutine in the promise to resume when this task suspends. - * - * @remark Do not call this manually, use the co_await keyword instead. - * @param caller The calling coroutine, now suspended - * @return bool Whether to suspend the caller or not - */ - [[nodiscard]] bool await_suspend(std_coroutine::coroutine_handle<> caller) noexcept { - promise_t &my_promise = handle.promise(); - auto previous_state = state_t::started; - - my_promise.parent = caller; - // Replace `sent` state with `awaited` ; if that fails, the only logical option is the state was `done`, in which case return false to resume - if (!handle.promise().state.compare_exchange_strong(previous_state, state_t::awaited) && previous_state == state_t::done) { - return false; - } - return true; - } - - /** - * @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 && handle.promise().state.load(std::memory_order_relaxed) == state_t::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 - */ - dpp::task& cancel() & noexcept { - handle.promise().cancelled.exchange(true, std::memory_order_relaxed); - return static_cast &>(*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 - */ - dpp::task&& cancel() && noexcept { - handle.promise().cancelled.exchange(true, std::memory_order_relaxed); - return static_cast &&>(*this); - } - - /** - * @brief Function called by the standard library when resuming. - * - * @return Return value of the coroutine, handed to the caller of co_await. - */ - decltype(auto) await_resume() & { - return static_cast &>(*this).await_resume_impl(); - } - - /** - * @brief Function called by the standard library when resuming. - * - * @return Return value of the coroutine, handed to the caller of co_await. - */ - decltype(auto) await_resume() const & { - return static_cast &>(*this).await_resume_impl(); - } - - /** - * @brief Function called by the standard library when resuming. - * - * @return Return value of the coroutine, handed to the caller of co_await. - */ - decltype(auto) await_resume() && { - return static_cast &&>(*this).await_resume_impl(); - } -}; - } // namespace task } // namespace detail @@ -283,59 +92,35 @@ template #ifndef _DOXYGEN_ requires (!std::is_reference_v) #endif -class task : private detail::task::task_base { - /** - * @brief Internal use only base class containing common logic between task and task. It also serves to prevent await_suspend and await_resume from being used directly. - * - * @warning For internal use only, do not use. - * @see operator co_await() - */ - friend class detail::task::task_base; +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; - /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @throw Throws any exception thrown or uncaught by the coroutine - * @return The result of the coroutine. This is returned to the awaiter as the result of co_await - */ - R& await_resume_impl() & { - detail::task::promise_t &promise = this->handle.promise(); - if (promise.exception) { - std::rethrow_exception(promise.exception); - } - return *reinterpret_cast(promise.result_storage.data()); - } + using handle_t = detail::task::handle_t; + using state_flags = detail::promise::state_flags; + + handle_t handle{}; +protected: /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @throw Throws any exception thrown or uncaught by the coroutine - * @return The result of the coroutine. This is returned to the awaiter as the result of co_await + * @brief Construct from a coroutine handle. Internal use only */ - const R& await_resume_impl() const & { - detail::task::promise_t &promise = this->handle.promise(); - if (promise.exception) { - std::rethrow_exception(promise.exception); - } - return *reinterpret_cast(promise.result_storage.data()); - } + explicit task(handle_t handle_) : awaitable(&handle_.promise()), handle(handle_) {} /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @throw Throws any exception thrown or uncaught by the coroutine - * @return The result of the coroutine. This is returned to the awaiter as the result of co_await + * @brief Clean up our handle, cancelling any running task */ - R&& await_resume_impl() && { - detail::task::promise_t &promise = this->handle.promise(); - if (promise.exception) { - std::rethrow_exception(promise.exception); + void cleanup() { + if (handle && this->valid()) { + if (this->abandon() & state_flags::sf_done) { + handle.destroy(); + } else { + cancel(); + } + handle = nullptr; } - return *reinterpret_cast(promise.result_storage.data()); } public: -#ifdef _DOXYGEN_ // :) /** * @brief Default constructor, creates a task not bound to a coroutine. */ @@ -351,15 +136,7 @@ class task : private detail::task::task_base { * * @param other Task to move the handle from */ - task(task &&other) noexcept; - - /** - * @brief Destructor. - * - * Destroys the handle. - * @warning The coroutine must be finished before this is called, otherwise it runs the risk of being resumed after it is destroyed, resuming in use-after-free undefined behavior. - */ - ~task(); + task(task &&other) noexcept : awaitable(std::move(other)), handle(std::exchange(other.handle, nullptr)) {} /** * @brief Copy assignment is disabled @@ -371,131 +148,51 @@ class task : private detail::task::task_base { * * @param other Task to move the handle from */ - task &operator=(task &&other) noexcept; - - /** - * @brief Function to check if the task has finished its execution entirely - * - * @return bool Whether the task is finished. - */ - [[nodiscard]] bool done() const noexcept; - - /** - * @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. - */ - dpp::task& cancel() & noexcept; - - /** - * @brief Check whether or not a call to co_await will suspend the caller. - * - * This function is called by the standard library as a first step when using co_await. If it returns true then the caller is not suspended. - * @throws logic_exception if the task is empty. - * @return bool Whether not to suspend the caller or not - */ - [[nodiscard]] bool await_ready() const; -#else - using detail::task::task_base::task_base; // use task_base's constructors - using detail::task::task_base::operator=; // use task_base's assignment operators - using detail::task::task_base::done; // expose done() as public - using detail::task::task_base::cancel; // expose cancel() as public - using detail::task::task_base::await_ready; // expose await_ready as public -#endif - - /** - * @brief Suspend the current coroutine until the task completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as a reference. - */ - [[nodiscard]] auto& operator co_await() & noexcept { - return static_cast&>(*this); - } - - /** - * @brief Suspend the current coroutine until the task completes. - * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as a const reference. - */ - [[nodiscard]] const auto& operator co_await() const & noexcept { - return static_cast&>(*this); + task &operator=(task &&other) noexcept { + cleanup(); + handle = std::exchange(other.handle, nullptr); + awaitable::operator=(std::move(other)); + return *this; } /** - * @brief Suspend the current coroutine until the task completes. + * @brief Destructor. * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. + * Destroys the handle. If the task is still running, it will be cancelled. */ - [[nodiscard]] auto&& operator co_await() && noexcept { - return static_cast&&>(*this); + ~task() { + cleanup(); } -}; - -#ifndef _DOXYGEN_ // don't generate this on doxygen because `using` doesn't work and 2 copies of coroutine_base's docs is enough -/** - * @brief A coroutine task. It starts immediately on construction and can be co_await-ed, making it perfect for parallel coroutines returning a value. - * - * Can be used in conjunction with coroutine events via dpp::event_router_t::co_attach, or on its own. - * - * @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 Return type of the coroutine. Cannot be a reference, can be void. - */ -template <> -class task : private detail::task::task_base { - /** - * @brief Private base class containing common logic between task and task. It also serves to prevent await_suspend and await_resume from being used directly. - * - * @see operator co_await() - */ - friend class detail::task::task_base; - - /** - * @brief Function called by the standard library when the coroutine is resumed. - * - * @remark Do not call this manually, use the co_await keyword instead. - * @throw Throws any exception thrown or uncaught by the coroutine - */ - void await_resume_impl() const; - -public: - using detail::task::task_base::task_base; // use task_base's constructors - using detail::task::task_base::operator=; // use task_base's assignment operators - using detail::task::task_base::done; // expose done() as public - using detail::task::task_base::cancel; // expose cancel() as public - using detail::task::task_base::await_ready; // expose await_ready as public /** - * @brief Suspend the current coroutine until the task completes. + * @brief Function to check if the task has finished its execution entirely * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, returns a reference to the contained result. + * @return bool Whether the task is finished. */ - auto& operator co_await() & { - return static_cast&>(*this); + [[nodiscard]] bool done() const noexcept { + return handle && (!this->valid() || handle.promise().state.load(std::memory_order_acq_rel) == state_flags::sf_done); } /** - * @brief Suspend the current coroutine until the task completes. + * @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. * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, returns a const reference to the contained result. + * @return *this */ - const auto& operator co_await() const & { - return static_cast&>(*this); + task& cancel() & noexcept { + handle.promise().cancelled.exchange(true, std::memory_order_relaxed); + return *this; } /** - * @brief Suspend the current coroutine until the task completes. + * @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. * - * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. - * @return On resumption, returns a reference to the contained result. + * @return *this */ - auto&& operator co_await() && { - return static_cast&&>(*this); + task&& cancel() && noexcept { + handle.promise().cancelled.exchange(true, std::memory_order_relaxed); + return *this; } }; -#endif /* _DOXYGEN_ */ namespace detail::task { /** @@ -527,29 +224,13 @@ struct final_awaiter { /** * @brief Base implementation of task::promise_t, without the logic that would depend on the return type. Meant to be inherited from */ -struct promise_base { - /** - * @brief State of the task, used to keep track of lifetime and status - */ - std::atomic state = state_t::started; - +template +struct promise_base : basic_promise { /** * @brief Whether the task is cancelled or not. */ std::atomic cancelled = false; - /** - * @brief Parent coroutine to return to for nested coroutines. - */ - detail::std_coroutine::coroutine_handle<> parent = nullptr; - - /** - * @brief Exception ptr if any was thrown during the coroutine - * - * @see std::suspend_never Don't suspend, the coroutine starts immediately. */ - [[nodiscard]] std_coroutine::suspend_never initial_suspend() const noexcept { + std_coroutine::suspend_never initial_suspend() const noexcept { return {}; } @@ -575,10 +256,10 @@ struct promise_base { * Stores the exception pointer to rethrow on co_await. If the task object is destroyed and was not cancelled, throw instead */ void unhandled_exception() { - exception = std::current_exception(); - if ((state.load() == task::state_t::dangling) && !cancelled) { + if ((this->state.load() & promise::state_flags::sf_broken) && !cancelled) { throw; } + this->template set_exception(std::current_exception()); } /** @@ -589,7 +270,7 @@ struct promise_base { template struct proxy_awaiter { /** @brief The promise_t object bound to this proxy */ - const task::promise_base &promise; + const promise_base &promise; /** @brief The inner awaitable being awaited */ A awaitable; @@ -601,8 +282,8 @@ struct promise_base { /** @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)); + [[nodiscard]] decltype(auto) await_suspend(T handle) noexcept(noexcept(awaitable.await_suspend(std::move(handle)))) { + return awaitable.await_suspend(std::move(handle)); } /** @@ -625,7 +306,7 @@ struct promise_base { * * @return @ref proxy_awaiter Returns a proxy awaiter that will check for cancellation on resumption */ - template + 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))}; @@ -636,21 +317,8 @@ struct promise_base { * @brief Implementation of task::promise_t for non-void return type */ template -struct promise_t : promise_base { - /** - * @brief Destructor. Destroys the value if it was constructed. - */ - ~promise_t() { - if (state.load() == state_t::done && !exception) { - std::destroy_at(reinterpret_cast(result_storage.data())); - } - } - - /** - * @brief Stored return value of the coroutine. - * - */ - alignas(R) std::array result_storage; +struct promise_t : promise_base { + friend struct final_awaiter; /** * @brief Function called by the standard library when the coroutine co_returns a value. @@ -660,7 +328,7 @@ struct promise_t : promise_base { * @param expr The value given to co_return */ void return_value(R&& expr) noexcept(std::is_nothrow_move_constructible_v) requires std::move_constructible { - std::construct_at(reinterpret_cast(result_storage.data()), static_cast(expr)); + this->template set_value(std::move(expr)); } /** @@ -671,7 +339,7 @@ struct promise_t : promise_base { * @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 { - std::construct_at(reinterpret_cast(result_storage.data()), expr); + this->template set_value(expr); } /** @@ -684,7 +352,7 @@ struct promise_t : promise_base { template requires (!std::is_same_v> && std::convertible_to) void return_value(T&& expr) noexcept (std::is_nothrow_convertible_v) { - std::construct_at(reinterpret_cast(result_storage.data()), std::forward(expr)); + this->template emplace_value(std::forward(expr)); } /** @@ -710,13 +378,17 @@ struct promise_t : promise_base { * @brief Implementation of task::promise_t for void return type */ template <> -struct promise_t : promise_base { +struct promise_t : promise_base { + friend struct final_awaiter; + /** * @brief Function called by the standard library when the coroutine co_returns * - * Does nothing but is required by the standard library. + * Sets the promise state to finished. */ - void return_void() const noexcept {} + void return_void() noexcept { + set_value(); + } /** * @brief Function called by the standard library when the coroutine is created. @@ -739,39 +411,29 @@ struct promise_t : promise_base { 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(); - state_t previous_state = promise.state.exchange(state_t::done); - - switch (previous_state) { - case state_t::started: // started but never awaited, suspend - return std_coroutine::noop_coroutine(); - case state_t::awaited: // co_await-ed, resume parent - return promise.parent; - case state_t::dangling: // task object is gone, free the handle - handle.destroy(); - return std_coroutine::noop_coroutine(); - case state_t::done: // what - // this should never happen. log it. we don't have a cluster so just write it on cerr - std::cerr << "dpp::task: final_suspend called twice. something went very wrong here, please report to GitHub issues or the D++ Discord server" << std::endl; + 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(); } - // TODO: replace with __builtin_unreachable when we confirm this never happens with normal usage return std_coroutine::noop_coroutine(); } } // namespace detail::task -#ifndef _DOXYGEN_ -inline void task::await_resume_impl() const { - if (handle.promise().exception) { - std::rethrow_exception(handle.promise().exception); - } -} -#endif /* _DOXYGEN_ */ - DPP_CHECK_ABI_COMPAT(task, task_dummy) DPP_CHECK_ABI_COMPAT(task, task_dummy) -} // namespace dpp +} /** * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise_t type from a coroutine function. @@ -781,4 +443,4 @@ struct dpp::detail::std_coroutine::coroutine_traits, Args...> { using promise_type = dpp::detail::task::promise_t; }; -#endif /* DPP_CORO */ +#endif /* DPP_NO_CORO */ diff --git a/3rdParty/dpp/coro/when_any.h b/3rdParty/dpp/coro/when_any.h index 4d31ba246b..fe29cc50f8 100644 --- a/3rdParty/dpp/coro/when_any.h +++ b/3rdParty/dpp/coro/when_any.h @@ -19,7 +19,7 @@ * ************************************************************************************/ -#ifdef DPP_CORO +#ifndef DPP_NO_CORO #pragma once #include "coro.h" @@ -53,23 +53,23 @@ namespace when_any { /** * @brief Current state of a when_any object */ -enum class await_state { - /** - * @brief Object was started but not awaited - */ - started, +enum await_state : uint8_t { /** * @brief Object is being awaited */ - waiting, + waiting = 1 << 0, /** * @brief Object was resumed */ - done, + done = 1 << 1, + /** + * @brief Result is ready to retrieve + */ + ready = 1 << 2, /** * @brief Object was destroyed */ - dangling + dangling = 1 << 3 }; /** @@ -197,7 +197,7 @@ class when_any { * * @see detail::when_any::await_state */ - std::atomic owner_state{detail::when_any::await_state::started}; + std::atomic owner_state{}; }; /** @@ -212,15 +212,17 @@ class when_any { * @return dpp::job Job handling the Nth argument */ template - static dpp::job make_job(std::shared_ptr shared_state) { + static job make_job(std::shared_ptr shared_state) { + using namespace detail::when_any; + /** * 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>) { + if constexpr (!std::same_as, 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) { + if (auto s = shared_state->owner_state.fetch_or(await_state::done, std::memory_order_relaxed); (s & (await_state::done | await_state::dangling)) != 0) { co_return; } @@ -238,20 +240,17 @@ class when_any { } } 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) { + + if (auto s = shared_state->owner_state.fetch_or(await_state::done, std::memory_order_relaxed); (s & (await_state::done | await_state::dangling)) != 0) { 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->index_finished = N; + if (auto s = shared_state->owner_state.fetch_or(await_state::ready, std::memory_order_acq_rel); (s & (await_state::waiting)) != 0) { + assert(shared_state->handle); shared_state->handle.resume(); } } @@ -261,9 +260,12 @@ class when_any { * 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{}); + constexpr auto impl = [](when_any *self, std::index_sequence) { + // We create an array to guarantee evaluation order here + // https://eel.is/c++draft/dcl.init.aggr#7 + [[maybe_unused]] dpp::job jobs[] = { make_job(self->my_state)... }; + }; + impl(this, std::index_sequence_for{}); } public: @@ -417,9 +419,11 @@ class when_any { * @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; + using namespace detail::when_any; + 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) + auto prev = self->my_state->owner_state.fetch_or(await_state::waiting, std::memory_order_acq_rel); + return (prev & await_state::ready) == 0; // true (suspend) if the state was not `ready` -- false (resume) if it was } /** @@ -428,7 +432,7 @@ class when_any { * @see result */ result await_resume() const noexcept { - return {self->my_state}; + return { self->my_state }; } }; @@ -448,7 +452,7 @@ class when_any { #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))...)} { + when_any(Args_&&... args) : my_state{ std::make_shared(detail::when_any::arg_helper::get(std::forward(args))...) } { make_jobs(); } @@ -506,7 +510,7 @@ class when_any { * @return bool Whether co_await would suspend */ [[nodiscard]] bool await_ready() const noexcept { - return my_state->owner_state == detail::when_any::await_state::done; + return (my_state->owner_state.load(std::memory_order_acquire) & detail::when_any::await_state::ready) != 0; } /** @@ -517,7 +521,7 @@ class when_any { * @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}; + return { this }; } }; diff --git a/3rdParty/dpp/discord_webhook_server.h b/3rdParty/dpp/discord_webhook_server.h new file mode 100644 index 0000000000..7e420613dd --- /dev/null +++ b/3rdParty/dpp/discord_webhook_server.h @@ -0,0 +1,74 @@ +/************************************************************************************ + * + * 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 Creates a HTTP server which listens for incoming + * Discord interactions, and if verified as valid, raises them + * as cluster events, returning the response back. + * Note that Discord requires all interaction endpoints to + * have a valid SSL certificate (not self signed) so in most + * cases you should put this port behind a reverse proxy, e.g. + * nginx, apache, etc. + */ +struct discord_webhook_server : public http_server { + + /** + * @brief Verifier for signed requests + */ + signature_verifier verifier; + + /** + * @brief Public key from application dashboard + */ + std::string public_key_hex; + + /** + * @brief Constructor for creation of a HTTP(S) server + * @param creator Cluster creator + * @param discord_public_key Public key for the application from the application dashboard page + * @param address address to bind to, use "0.0.0.0" to bind to all local addresses + * @param port port to bind to. You should generally use a port > 1024. + * @param ssl_private_key Private key PEM file for HTTPS/SSL. If empty, a plaintext server is created + * @param ssl_public_key Public key PEM file for HTTPS/SSL. If empty, a plaintext server is created + */ + discord_webhook_server(cluster* creator, const std::string& discord_public_key, const std::string_view address, uint16_t port, const std::string& ssl_private_key = "", const std::string& ssl_public_key = ""); + + /** + * @brief Handle Discord outbound webhook + * @param request Request from discord + */ + void handle_request(http_server_request* request); + + /** + * @brief Virtual dtor + */ + virtual ~discord_webhook_server() = default; +}; + +} diff --git a/3rdParty/dpp/discordclient.h b/3rdParty/dpp/discordclient.h index 503ad26181..c0ed025cba 100644 --- a/3rdParty/dpp/discordclient.h +++ b/3rdParty/dpp/discordclient.h @@ -31,37 +31,141 @@ #include #include #include +#include #include +#include #include #include +#include +namespace dpp { +/** + * @brief Discord API version for shard websockets and HTTPS API requests + */ +#define DISCORD_API_VERSION "10" -#define DISCORD_API_VERSION "10" -#define API_PATH "/api/v" DISCORD_API_VERSION -namespace dpp { +/** + * @brief HTTPS Request base path for API calls + */ +#define API_PATH "/api/v" DISCORD_API_VERSION -// Forward declarations +/* Forward declarations */ class cluster; /** - * @brief This is an opaque class containing zlib library specific structures. - * We define it this way so that the public facing D++ library doesn't require - * the zlib headers be available to build against it. + * @brief How many seconds to wait between (re)connections. DO NOT change this. + * It is mandated by the Discord API spec! + */ +constexpr time_t RECONNECT_INTERVAL = 5; + +/** + * @brief Represents different event opcodes sent and received on a shard websocket + * + * These are used internally to route frames. + */ +enum shard_frame_type : int { + + /** + * @brief An event was dispatched. + * @note Receive only + */ + ft_dispatch = 0, + + /** + * @brief Fired periodically by the client to keep the connection alive. + * @note Send/Receive + */ + ft_heartbeat = 1, + + /** + * @brief Starts a new session during the initial handshake. + * @note Send only + */ + ft_identify = 2, + + /** + * @brief Update the client's presence. + * @note Send only + */ + ft_presence = 3, + + /** + * @brief Used to join/leave or move between voice channels. + * @note Send only + */ + ft_voice_state_update = 4, + + /** + * @brief Resume a previous session that was disconnected. + * @note Send only + */ + ft_resume = 6, + + /** + * @brief You should attempt to reconnect and resume immediately. + * @note Receive only + */ + ft_reconnect = 7, + + /** + * @brief Request information about offline guild members in a large guild. + * @note Send only + */ + ft_request_guild_members = 8, + + /** + * @brief The session has been invalidated. You should reconnect and identify/resume accordingly. + * @note Receive only + */ + ft_invalid_session = 9, + + /** + * @brief Sent immediately after connecting, contains the heartbeat interval to use. + * @note Receive only + */ + ft_hello = 10, + + /** + * @brief Sent in response to receiving a heartbeat to acknowledge that it has been received. + * @note Receive only + */ + ft_heartbeat_ack = 11, + + /** + * @brief Request information about soundboard sounds in a set of guilds. + * @note Send only + */ + ft_request_soundboard_sounds = 31, +}; + +/** + * @brief Callback to send a Gateway request to start a new voice connection. */ -class zlibcontext; +using voice_connection_gateway_request_callback_t = std::function; /** * @brief Represents a connection to a voice channel. * A client can only connect to one voice channel per guild at a time, so these are stored in a map * in the dpp::discord_client keyed by guild_id. */ -class DPP_EXPORT voiceconn { +class DPP_EXPORT voiceconn : public std::enable_shared_from_this { /** * @brief Owning dpp::discord_client instance */ class discord_client* creator; + + /** + * @brief Function to ask a discord_client instance to make a request to Gateway to connect to a voice channel + */ + voice_connection_gateway_request_callback_t request_callback; + public: + /** + * @brief Guild to connect to the voice channel on + */ + snowflake guild_id; + /** * @brief Voice Channel ID */ @@ -85,20 +189,24 @@ class DPP_EXPORT voiceconn { /** * @brief voice websocket client */ - class discord_voice_client* voiceclient; + std::unique_ptr voiceclient; /** - * @brief Construct a new voiceconn object + * @brief True to enable DAVE E2EE + * @warning This is an EXPERIMENTAL feature! */ - voiceconn() = default; + bool dave; /** * @brief Construct a new voiceconn object * * @param o owner - * @param _channel_id voice channel id + * @param request_callback Function to ask a discord_client instance to make a request to Gateway to connect to a voice channel + * @param guild_id Guild to connect to the voice channel on + * @param channel_id voice channel id + * @param enable_dave True to enable DAVE E2EE */ - voiceconn(class discord_client* o, snowflake _channel_id); + voiceconn(class discord_client* o, voice_connection_gateway_request_callback_t request_callback, snowflake guild_id, snowflake channel_id, bool enable_dave); /** * @brief Destroy the voiceconn object @@ -111,31 +219,37 @@ class DPP_EXPORT voiceconn { * * @return true if ready to connect */ - bool is_ready(); + bool is_ready() const; /** * @brief return true if the connection is active (websocket exists) * * @return true if has an active websocket */ - bool is_active(); + bool is_active() const; + + voiceconn& request(); /** * @brief Create websocket object and connect it. * Needs hostname, token and session_id to be set or does nothing. - * - * @param guild_id Guild to connect to the voice channel on + * * @return reference to self * @note It can spawn a thread to establish the connection, so this is NOT a synchronous blocking call! * You shouldn't call this directly. Use a wrapper function instead. e.g. dpp::guild::connect_member_voice */ - voiceconn& connect(snowflake guild_id); + voiceconn& connect(); /** * @brief Disconnect from the currently connected voice channel * @return reference to self */ voiceconn& disconnect(); + + /** + * @brief Reassigns the owner to the given discord_client. + */ + void reassign_owner(class discord_client* o); }; /** @brief Implements a discord client. Each discord_client connects to one shard and derives from a websocket client. */ @@ -157,11 +271,6 @@ class DPP_EXPORT discord_client : public websocket_client */ friend class dpp::cluster; - /** - * @brief True if the shard is terminating - */ - bool terminating; - /** * @brief Disconnect from the connected voice channel on a guild * @@ -171,38 +280,40 @@ class DPP_EXPORT discord_client : public websocket_client */ void disconnect_voice_internal(snowflake guild_id, bool send_json = true); -private: - /** - * @brief Mutex for message queue + * @brief Start connecting the websocket + * + * Called from the constructor, or during reconnection */ - std::shared_mutex queue_mutex; + void start_connecting(); /** - * @brief Queue of outbound messages + * @brief Stores the most recent ping message on this shard, which we check + * for to monitor latency */ - std::deque message_queue; + std::string last_ping_message; + +private: /** - * @brief Thread this shard is executing on + * @brief Mutex for message queue */ - std::thread* runner; + std::shared_mutex queue_mutex; /** - * @brief Run shard loop under a thread. - * Calls discord_client::run() from within a std::thread. + * @brief Mutex for zlib pointer */ - void thread_run(); + std::mutex zlib_mutex; /** - * @brief If true, stream compression is enabled + * @brief Queue of outbound messages */ - bool compressed; + std::deque message_queue; /** - * @brief ZLib decompression buffer + * @brief If true, stream compression is enabled */ - unsigned char* decomp_buffer; + bool compressed; /** * @brief Decompressed string @@ -215,12 +326,7 @@ class DPP_EXPORT discord_client : public websocket_client * are wrapped within this opaque object so that this header * file does not bring in a dependency on zlib.h. */ - zlibcontext* zlib; - - /** - * @brief Total decompressed received bytes - */ - uint64_t decompressed_total; + std::unique_ptr zlib{}; /** * @brief Last connect time of cluster @@ -235,7 +341,7 @@ class DPP_EXPORT discord_client : public websocket_client /** * @brief ETF parser for when in ws_etf mode */ - class etf_parser* etf; + std::unique_ptr etf; /** * @brief Convert a JSON object to string. @@ -247,17 +353,6 @@ class DPP_EXPORT discord_client : public websocket_client */ std::string jsonobj_to_string(const nlohmann::json& json); - /** - * @brief Initialise ZLib (websocket compression) - * @throw dpp::exception if ZLib cannot be initialised - */ - void setup_zlib(); - - /** - * @brief Shut down ZLib (websocket compression) - */ - void end_zlib(); - /** * @brief Update the websocket hostname with the resume url * from the last READY event @@ -295,11 +390,6 @@ class DPP_EXPORT discord_client : public websocket_client */ uint32_t max_shards; - /** - * @brief Thread ID - */ - std::thread::native_handle_type thread_id; - /** * @brief Last sequence number received, for resumes and pings */ @@ -359,7 +449,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 @@ -373,7 +463,7 @@ class DPP_EXPORT discord_client : public websocket_client * @param severity The log level from dpp::loglevel * @param msg The log message to output */ - virtual void log(dpp::loglevel severity, const std::string &msg) const; + virtual void log(dpp::loglevel severity, const std::string &msg) const override; /** * @brief Handle an event (opcode 0) @@ -404,8 +494,11 @@ class DPP_EXPORT discord_client : public websocket_client */ uint64_t get_channel_count(); - /** Fires every second from the underlying socket I/O loop, used for sending heartbeats */ - virtual void one_second_timer(); + /** + * @brief Fires every second from the underlying socket I/O loop, used for sending heartbeats + * and any queued outbound websocket frames. + */ + virtual void one_second_timer() override; /** * @brief Queue a message to be sent via the websocket @@ -459,37 +552,54 @@ class DPP_EXPORT discord_client : public websocket_client */ 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); + /** + * @brief Construct a discord_client object from another discord_client object + * Used when resuming, the url to connect to will be taken from the resume url of the + * other object, along with the seq number. + * + * @param old Previous connection to resume from + * @param sequence Sequence number of previous session + * @param session_id Session ID of previous session + */ + explicit discord_client(discord_client& old, uint64_t sequence, const std::string& session_id); + /** * @brief Destroy the discord client object */ - virtual ~discord_client(); + virtual ~discord_client() = default; /** - * @brief Get the decompressed bytes in objectGet decompressed total bytes received - * @return uint64_t bytes received + * @brief Get decompressed total bytes received + * + * This will always return 0 if the connection is not compressed + * @return uint64_t compressed bytes received */ uint64_t get_decompressed_bytes_in(); /** * @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) override; /** * @brief Handle a websocket error. * @param errorcode The error returned from the websocket */ - virtual void error(uint32_t errorcode); + virtual void error(uint32_t errorcode) override; /** * @brief Start and monitor I/O loop. - * @note this is a blocking call and is usually executed within a - * thread by whatever creates the object. */ void run(); + /** + * @brief Called when the HTTP socket is closed + */ + virtual void on_disconnect() override; + /** * @brief Connect to a voice channel * @@ -497,12 +607,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 * @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 = true); /** * @brief Disconnect from the connected voice channel on a guild @@ -523,4 +634,4 @@ class DPP_EXPORT discord_client : public websocket_client voiceconn* get_voice(snowflake guild_id); }; -} // namespace dpp +} diff --git a/3rdParty/dpp/discordevents.h b/3rdParty/dpp/discordevents.h index 0577c0d2c5..8b05d1001b 100644 --- a/3rdParty/dpp/discordevents.h +++ b/3rdParty/dpp/discordevents.h @@ -55,7 +55,7 @@ void DPP_EXPORT set_snowflake_array_not_null(const nlohmann::json* j, const char /** * @brief Applies a function to each element of a json array. - * @param j nlohmann::json instance to retrieve value from + * @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 */ @@ -65,7 +65,7 @@ void DPP_EXPORT for_each_json(nlohmann::json* parent, std::string_view key, cons * @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 keyname key name to check for the values + * @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) { @@ -229,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); -} // namespace dpp +} diff --git a/3rdParty/dpp/discordvoiceclient.h b/3rdParty/dpp/discordvoiceclient.h index 915a6f726a..15f7856bee 100644 --- a/3rdParty/dpp/discordvoiceclient.h +++ b/3rdParty/dpp/discordvoiceclient.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include struct OpusDecoder; struct OpusEncoder; @@ -55,8 +57,28 @@ 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 @@ -64,6 +86,10 @@ class audio_mixer; 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. */ @@ -100,6 +126,115 @@ struct DPP_EXPORT voice_out_packet { uint64_t duration; }; +/** + * @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, +}; + +/** + * @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 A callback for a full reconnection after the voice client disconnects due to error. + */ +using full_reconnection_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. */ @@ -110,6 +245,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ void cleanup(); + /** + * @brief A frame of silence packet + */ + static constexpr uint8_t silence_packet[3] = { 0xf8, 0xff, 0xfe }; + /** * @brief Mutex for outbound packet stream */ @@ -121,24 +261,21 @@ class DPP_EXPORT discord_voice_client : public websocket_client std::shared_mutex queue_mutex; /** - * @brief Queue of outbound messages - */ - std::deque message_queue; - - /** - * @brief Thread this connection is executing on + * @brief Our public IP address + * + * Use discord_voice_client::discover_ip() to access this value */ - std::thread* runner; + std::string external_ip; /** - * @brief Run shard loop under a thread + * @brief Queue of outbound messages */ - void thread_run(); + std::deque message_queue; /** * @brief Last connect time of voice session */ - time_t connect_time; + time_t connect_time{}; /* * @brief For mixing outgoing voice data. @@ -153,12 +290,12 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Port number of UDP/RTP endpoint */ - uint16_t port; + uint16_t port{}; /** * @brief SSRC value */ - uint64_t ssrc; + uint64_t ssrc{}; /** * @brief List of supported audio encoding modes @@ -168,7 +305,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Timescale in nanoseconds */ - uint64_t timescale; + uint64_t timescale{}; /** * @brief Output buffer @@ -295,19 +432,43 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief If true, audio packet sending is paused */ - bool paused; + 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{}; + + /** + * @brief Number of times we have tried to reconnect in the last few seconds + */ + size_t times_looped{0}; + + /** + * @brief Last time we reconnected + */ + time_t last_loop_time{0}; #ifdef HAVE_VOICE /** * @brief libopus encoder */ - OpusEncoder* encoder; + OpusEncoder* encoder{}; /** * @brief libopus repacketizer * (merges frames into one packet) */ - OpusRepacketizer* repacketizer; + 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 @@ -319,32 +480,73 @@ 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 */ - dpp::socket fd; + dpp::socket fd{}; /** * @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 * once per frame sent. */ - uint16_t sequence; + 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 * how many frames are sent. */ - uint32_t timestamp; + 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 @@ -354,7 +556,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Fraction of the sleep that was not executed after the last audio packet was sent */ - std::chrono::nanoseconds last_sleep_remainder; + std::chrono::nanoseconds last_sleep_remainder{}; /** * @brief Maps receiving ssrc to user id @@ -366,7 +568,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * When this moves from false to true, this causes the * client to send the 'talking' notification to the websocket. */ - bool sending; + bool sending{}; /** * @brief Number of track markers in the buffer. For example if there @@ -377,7 +579,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * If the buffer is empty, there are zero tracks in the * buffer. */ - uint32_t tracks; + uint32_t tracks{}; /** * @brief Meta data associated with each track. @@ -389,7 +591,21 @@ class DPP_EXPORT discord_voice_client : public websocket_client /** * @brief Encoding buffer for opus repacketizer and encode */ - uint8_t encode_buffer[65536]; + uint8_t encode_buffer[65536]{}; + + /** + * @brief DAVE - Discord Audio Visual Encryption + * Used for E2EE encryption. dave_protocol_none is + * the default right now. + */ + 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. @@ -411,25 +627,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client int udp_recv(char* data, size_t max_length); /** - * @brief This hooks the ssl_client, returning the file - * descriptor if we want to send buffered data, or - * -1 if there is nothing to send - * - * @return int file descriptor or -1 - */ - dpp::socket want_write(); - - /** - * @brief This hooks the ssl_client, returning the file - * descriptor if we want to receive buffered data, or - * -1 if we are not wanting to receive - * - * @return int file descriptor or -1 - */ - dpp::socket want_read(); - - /** - * @brief Called by ssl_client when the socket is ready + * @brief Called by socketengine when the socket is ready * for writing, at this point we pick the head item off * the buffer and send it. So long as it doesn't error * completely, we pop it off the head of the queue. @@ -437,7 +635,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client void write_ready(); /** - * @brief Called by ssl_client when there is data to be + * @brief Called by socketengine 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++ @@ -450,8 +648,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 @@ -490,53 +690,60 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ size_t encode(uint8_t *input, size_t inDataSize, uint8_t *output, size_t &outDataSize); -public: + /** + * 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); /** - * @brief Owning cluster + * @brief Called in constructor and on reconnection of websocket + */ + void setup(); + + /** + * @brief Events for UDP Socket IO */ - class dpp::cluster* creator; + dpp::socket_events udp_events; + +public: /** - * @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 Owning cluster */ - static bool sodium_initialised; + class dpp::cluster* creator{}; + + full_reconnection_callback_t reconnection_callback; /** * @brief True when the thread is shutting down */ - bool terminating; + bool terminating{}; /** * @brief The gain value for the end of the current voice iteration. */ - float end_gain; + float end_gain{}; /** * @brief The gain value for the current voice iteration. */ - float current_gain; + float current_gain{}; /** * @brief The amount to increment each successive sample for, for the current voice iteration. */ - float increment; + float increment{}; /** * @brief Heartbeat interval for sending heartbeat keepalive */ - uint32_t heartbeat_interval; + uint32_t heartbeat_interval{}; /** * @brief Last voice channel websocket heartbeat */ - time_t last_heartbeat; - - /** - * @brief Thread ID - */ - std::thread::native_handle_type thread_id; + time_t last_heartbeat{}; /** * @brief Discord voice session token @@ -564,7 +771,10 @@ class DPP_EXPORT discord_voice_client : public websocket_client snowflake channel_id; /** - * @brief The audio type to be sent. The default type is recorded audio. + * @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. @@ -594,10 +804,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. @@ -624,13 +839,13 @@ class DPP_EXPORT discord_voice_client : public websocket_client * @param severity The log level from dpp::loglevel * @param msg The log message to output */ - virtual void log(dpp::loglevel severity, const std::string &msg) const; + virtual void log(dpp::loglevel severity, const std::string &msg) const override; /** * @brief Fires every second from the underlying socket I/O loop, used for sending heartbeats * @throw dpp::exception if the socket needs to disconnect */ - virtual void one_second_timer(); + virtual void one_second_timer() override; /** * @brief voice client is ready to stream audio. @@ -654,38 +869,48 @@ 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 _reconnection_callback The callback this voice client calls if the voice connection was closed due to error * @param _channel_id The channel id to identify the voice connection as * @param _server_id The server id (guild id) to identify the voice connection as * @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 */ - 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, full_reconnection_callback_t _reconnection_callback, snowflake _channel_id, snowflake _server_id, const std::string &_token, const std::string &_session_id, const std::string &_host, bool enable_dave = true); /** * @brief Destroy the discord voice client object */ - virtual ~discord_voice_client(); + virtual ~discord_voice_client() override; /** * @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) override; /** * @brief Handle a websocket error. * @param errorcode The error returned from the websocket */ - virtual void error(uint32_t errorcode); + virtual void error(uint32_t errorcode) override; /** - * @brief Start and monitor I/O loop + * @brief Start and monitor websocket I/O */ void run(); @@ -694,7 +919,7 @@ class DPP_EXPORT discord_voice_client : public websocket_client * * 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 @@ -734,7 +959,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 @@ -744,6 +969,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 @@ -754,7 +983,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 @@ -762,7 +991,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 * @@ -781,7 +1010,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 @@ -794,6 +1023,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. * @@ -844,6 +1086,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 * @@ -933,7 +1191,83 @@ 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); + + /** + * @brief Called on websocket disconnection + */ + void on_disconnect() override; }; -} // namespace dpp +} diff --git a/3rdParty/dpp/dispatcher.h b/3rdParty/dpp/dispatcher.h index e0d93d57d4..3fc337d8a1 100644 --- a/3rdParty/dpp/dispatcher.h +++ b/3rdParty/dpp/dispatcher.h @@ -20,14 +20,8 @@ * ************************************************************************************/ #pragma once -#include -#include -#include -#include -#include -#include -#include #include +#include #include #include #include @@ -45,13 +39,14 @@ #include #include #include +#include #include #include #include -#ifdef DPP_CORO +#ifndef DPP_NO_CORO #include -#endif /* DPP_CORO */ +#endif /* DPP_NO_CORO */ namespace dpp { @@ -65,6 +60,20 @@ class discord_voice_client; */ using command_completion_event_t = std::function; +/** + * @brief Route interaction event + * + * @param creator Creating cluster + * @param shard_id Shard ID or 0 + * @param d JSON data for the event + * @param raw Raw JSON string + * @param from_webhook True if the interaction comes from a webhook + * @return JSON interaction response, only valid when from_webhook is true + */ +namespace events { + std::string DPP_EXPORT internal_handle_interaction(cluster* creator, uint16_t shard_id, json &d, const std::string &raw, bool from_webhook); +} + /** @brief Base event parameter struct. * Each event you receive from the library will have its parameter derived from this class. * The class contains the raw event data, and a pointer to the current shard's dpp::discord_client object. @@ -84,9 +93,13 @@ struct DPP_EXPORT event_dispatch_t { /** * @brief Shard the event came from. - * Note that for some events, notably voice events, this may be nullptr. */ - discord_client* from = nullptr; + uint32_t shard = 0; + + /** + * @brief Cluster owning the event dispatch + */ + dpp::cluster* owner = nullptr; /** * @brief Whether the event was cancelled using cancel_event(). @@ -115,18 +128,24 @@ struct DPP_EXPORT event_dispatch_t { /** * @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 shard_id The shard the event originated on. * @param raw Raw event data as JSON or ETF */ - event_dispatch_t(discord_client* client, const std::string& raw); + event_dispatch_t(dpp::cluster* creator, uint32_t shard_id, const std::string& raw); + + /** + * @brief Returns the shard object for the events shard id + * @return discord client object + */ + discord_client* from() const; /** * @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 shard_id The shard the event originated on. * @param raw Raw event data as JSON or ETF */ - event_dispatch_t(discord_client* client, std::string&& raw); + event_dispatch_t(dpp::cluster* creator, uint32_t shard_id, std::string&& raw); /** * @brief Copy another event_dispatch_t object @@ -194,6 +213,19 @@ struct DPP_EXPORT log_t : public event_dispatch_t { std::string message = {}; }; +/** + * @brief Closure of socket (removal from socket engine) + */ +struct DPP_EXPORT socket_close_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Socket file descriptor + */ + socket fd{INVALID_SOCKET}; +}; + namespace utility { /** * @brief Get a default logger that outputs to std::cout. @@ -441,6 +473,19 @@ struct DPP_EXPORT stage_instance_delete_t : public event_dispatch_t { stage_instance deleted = {}; }; +/** + * @brief Voice channel effect send + */ +struct DPP_EXPORT voice_channel_effect_send_t : public event_dispatch_t { + using event_dispatch_t::event_dispatch_t; + using event_dispatch_t::operator=; + + /** + * @brief Voice channel effect + */ + voice_channel_effect effect = {}; +}; + /** * @brief Voice state update */ @@ -461,6 +506,27 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { using event_dispatch_t::event_dispatch_t; using event_dispatch_t::operator=; + /** + * @brief Returns a generic http success confirmation + * @return success + */ + confirmation_callback_t success() const; + + /** + * @brief True if from a HTTP interaction webhook, false if from websocket + */ + bool from_webhook{false}; + + /** + * @brief If this interaction is created from a webhook server, + * it fills this value with a JSON string which is sent as the HTTP response. + * This is thread local so that it is preserved when the event is copied, we + * guarantee that the request/response is in the same thread so this will always + * be valid. + * @param response response to set + */ + void set_queued_response(const std::string& response) const; + /** * @brief Acknowledge interaction without displaying a message to the user, * for use with button and select menu components. @@ -510,6 +576,22 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { */ void reply(const std::string& mt, command_completion_event_t callback = utility::log_error()) const; + /** + * @brief Create a follow-up message for this interaction. + * @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 follow_up(const message& m, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Create a follow-up message for this interaction. + * @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 follow_up(const std::string& mt, command_completion_event_t callback = utility::log_error()) const; + /** * @brief Reply to interaction with a dialog box * @@ -571,7 +653,13 @@ 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 queued response when responding to a HTTP request + * @return response JSON + */ + std::string get_queued_response() const; + +#ifndef DPP_NO_CORO /** * @brief Acknowledge interaction without displaying a message to the user, * for use with button and select menu components. @@ -587,7 +675,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_reply(interaction_response_type t, message m) const; /** * @brief Send a reply for this interaction @@ -596,7 +684,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_reply(interaction_response_type t, std::string mt) const; /** * @brief Send a reply for this interaction. @@ -605,7 +693,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_reply(message m) const; /** * @brief Send a reply for this interaction. @@ -614,7 +702,23 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_reply(std::string mt) const; + + /** + * @brief Create a follow-up 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::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_follow_up(message m) const; + + /** + * @brief Create a follow-up message 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_follow_up(std::string mt) const; /** * @brief Reply to interaction with a dialog box @@ -622,7 +726,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_dialog(interaction_modal_response mr) const; /** * @brief Edit the response for this interaction @@ -630,7 +734,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_edit_response(message m) const; /** * @brief Edit the response for this interaction @@ -638,7 +742,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_edit_response(std::string mt) const; /** * @brief Set the bot to 'thinking' state where you have up to 15 minutes to respond @@ -661,7 +765,15 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * @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; + dpp::async co_edit_original_response(message m) const; + + /** + * @brief Edit original response message for this interaction + * + * @param mt The string value to send, for simple text only messages + * 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(std::string mt) const; /** * @brief Delete original response message for this interaction. This cannot be used on an ephemeral interaction response. @@ -669,7 +781,7 @@ struct DPP_EXPORT interaction_create_t : public event_dispatch_t { * 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 */ +#endif /* DPP_NO_CORO */ /** * @brief command interaction @@ -892,7 +1004,7 @@ struct DPP_EXPORT guild_stickers_update_t : public event_dispatch_t { /** * @brief Updating guild */ - guild* updating_guild = nullptr; + guild updating_guild; /** * @brief stickers being updated @@ -928,7 +1040,7 @@ struct DPP_EXPORT channel_delete_t : public event_dispatch_t { /** * @brief guild channel is being deleted from */ - guild* deleting_guild = nullptr; + guild deleting_guild; /** * @brief channel being deleted @@ -946,12 +1058,12 @@ struct DPP_EXPORT channel_update_t : public event_dispatch_t { /** * @brief guild channel is being updated on */ - guild* updating_guild = nullptr; + guild updating_guild; /** * @brief channel being updated */ - channel* updated = nullptr; + channel updated; }; /** @@ -970,6 +1082,16 @@ struct DPP_EXPORT ready_t : public event_dispatch_t { * @brief 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}; }; /** @@ -1007,7 +1129,7 @@ struct DPP_EXPORT guild_member_remove_t : public event_dispatch_t { /** * @brief guild user is being removed from */ - guild* removing_guild = nullptr; + guild removing_guild; /** * @brief Guild ID removed from @@ -1048,12 +1170,12 @@ struct DPP_EXPORT guild_role_create_t : public event_dispatch_t { /** * @brief guild role is being created on */ - guild* creating_guild = nullptr; + guild creating_guild; /** * @brief role being created */ - role* created = nullptr; + role created; }; /** @@ -1066,18 +1188,18 @@ struct DPP_EXPORT typing_start_t : public event_dispatch_t { /** * @brief guild user is typing on */ - guild* typing_guild = nullptr; + guild typing_guild; /** * @brief channel user is typing on */ - channel* typing_channel = nullptr; + channel typing_channel; /** * @brief user who is typing. * Can be nullptr if user is not cached */ - user* typing_user = nullptr; + user typing_user; /** * @brief User id of user typing. @@ -1120,7 +1242,7 @@ struct DPP_EXPORT message_reaction_add_t : public event_dispatch_t { /** * @brief Guild reaction occurred on */ - guild* reacting_guild = nullptr; + guild reacting_guild; /** * @brief User who reacted @@ -1141,7 +1263,7 @@ struct DPP_EXPORT message_reaction_add_t : public event_dispatch_t { * @brief channel the reaction happened on (Optional) * @note only filled when the channel is cached */ - channel* reacting_channel = nullptr; + channel reacting_channel; /** * @brief emoji of reaction @@ -1169,12 +1291,12 @@ struct DPP_EXPORT guild_members_chunk_t : public event_dispatch_t { /** * @brief guild the members chunk is for */ - guild* adding = nullptr; + guild adding; /** * @brief list of members in the chunk */ - guild_member_map* members = nullptr; + guild_member_map members; }; /** @@ -1187,7 +1309,7 @@ struct DPP_EXPORT message_reaction_remove_t : public event_dispatch_t { /** * @brief Guild reaction occurred on */ - guild* reacting_guild = nullptr; + guild reacting_guild; /** * @brief User who reacted @@ -1203,7 +1325,7 @@ struct DPP_EXPORT message_reaction_remove_t : public event_dispatch_t { * @brief channel the reaction happened on (optional) * @note only filled when the channel is cached */ - channel* reacting_channel = nullptr; + channel reacting_channel; /** * @brief emoji of reaction @@ -1226,7 +1348,7 @@ struct DPP_EXPORT guild_create_t : public event_dispatch_t { /** * @brief guild that was created */ - guild* created = nullptr; + guild created; /** * @brief List of presences of all users on the guild. @@ -1267,12 +1389,12 @@ struct DPP_EXPORT channel_create_t : public event_dispatch_t { /** * @brief guild channel was created on */ - guild* creating_guild = nullptr; + guild creating_guild; /** * @brief channel that was created */ - channel* created = nullptr; + channel created; }; /** @@ -1285,7 +1407,7 @@ struct DPP_EXPORT message_reaction_remove_emoji_t : public event_dispatch_t { /** * @brief Guild reaction occurred on */ - guild* reacting_guild = nullptr; + guild reacting_guild; /** * @brief Channel ID the reactions was removed in @@ -1296,7 +1418,7 @@ struct DPP_EXPORT message_reaction_remove_emoji_t : public event_dispatch_t { * @brief channel the reaction happened on (optional) * @note only filled when the channel is cached */ - channel* reacting_channel = nullptr; + channel reacting_channel; /** * @brief emoji of reaction @@ -1319,17 +1441,17 @@ struct DPP_EXPORT message_delete_bulk_t : public event_dispatch_t { /** * @brief guild messages are being deleted upon */ - guild* deleting_guild = nullptr; + guild deleting_guild; /** * @brief user who is deleting the messages */ - user* deleting_user = nullptr; + user deleting_user; /** * @brief channel messages are being deleted from */ - channel* deleting_channel = nullptr; + channel deleting_channel; /** * @brief list of message ids of deleted messages @@ -1347,12 +1469,12 @@ struct DPP_EXPORT guild_role_update_t : public event_dispatch_t { /** * @brief guild where roles are being updated */ - guild* updating_guild = nullptr; + guild updating_guild; /** * @brief the role being updated */ - role* updated = nullptr; + role updated; }; /** @@ -1365,12 +1487,12 @@ struct DPP_EXPORT guild_role_delete_t : public event_dispatch_t { /** * @brief guild where role is being deleted */ - guild* deleting_guild = nullptr; + guild deleting_guild; /** * @brief role being deleted */ - role* deleted = nullptr; + role deleted; /** * @brief ID of the deleted role @@ -1388,12 +1510,12 @@ struct DPP_EXPORT channel_pins_update_t : public event_dispatch_t { /** * @brief guild where message is being pinned */ - guild* pin_guild = nullptr; + guild pin_guild; /** * @brief channel where message is being pinned */ - channel* pin_channel = nullptr; + channel pin_channel; /** * @brief timestamp of pin @@ -1411,7 +1533,7 @@ struct DPP_EXPORT message_reaction_remove_all_t : public event_dispatch_t { /** * @brief Guild reaction occurred on */ - guild* reacting_guild = nullptr; + guild reacting_guild; /** * @brief Channel ID the reactions was removed in @@ -1422,7 +1544,7 @@ struct DPP_EXPORT message_reaction_remove_all_t : public event_dispatch_t { * @brief channel the reaction happened on (optional) * @note only filled when the channel is cached */ - channel* reacting_channel = nullptr; + channel reacting_channel; /** * @brief message id of the message reacted upon @@ -1469,7 +1591,7 @@ struct DPP_EXPORT guild_emojis_update_t : public event_dispatch_t { /** * @brief guild where emojis are being updated */ - guild* updating_guild = nullptr; + guild updating_guild; }; /** @@ -1496,12 +1618,12 @@ struct DPP_EXPORT webhooks_update_t : public event_dispatch_t { /** * @brief guild where webhooks are being updated */ - guild* webhook_guild = nullptr; + guild webhook_guild; /** * @brief channel where webhooks are being updated */ - channel* webhook_channel = nullptr; + channel webhook_channel; }; /** @@ -1514,7 +1636,7 @@ struct DPP_EXPORT guild_member_add_t : public event_dispatch_t { /** * @brief guild which gained new member */ - guild* adding_guild = nullptr; + guild adding_guild; /** * @brief member which was added @@ -1545,7 +1667,7 @@ struct DPP_EXPORT guild_update_t : public event_dispatch_t { /** * @brief guild being updated */ - guild* updated = nullptr; + guild updated; }; /** @@ -1558,7 +1680,7 @@ struct DPP_EXPORT guild_integrations_update_t : public event_dispatch_t { /** * @brief guild where integrations are being updated */ - guild* updating_guild = nullptr; + guild updating_guild; }; /** @@ -1571,7 +1693,7 @@ struct DPP_EXPORT guild_member_update_t : public event_dispatch_t { /** * @brief guild where member is being updated */ - guild* updating_guild = nullptr; + guild updating_guild; /** * @brief member being updated @@ -1674,6 +1796,108 @@ struct DPP_EXPORT message_create_t : public event_dispatch_t { * @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; + +#ifndef DPP_NO_CORO + /** + * @brief Send a text to the same channel as the channel_id in received event. + * + * @param m Text 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_send(std::string m) const; + + /** + * @brief Send a message to the same channel as the channel_id in received event. + * + * @param msg Message 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_send(message msg) const; + + /** + * @brief Reply to the message received in the event. + * + * @param m Text to send as a reply. + * @param mention_replied_user mentions (pings) the author of message replied to, if true + * 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(std::string m, bool mention_replied_user = false) const; + + /** + * @brief Reply to the message received in the event. + * + * @param msg Message to send as a reply. + * @param mention_replied_user mentions (pings) the author of message replied to, if true + * 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(message msg, bool mention_replied_user = false) const; +#endif /* DPP_NO_CORO */ +}; + +/** + * @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 + */ + uint32_t answer_id; }; /** @@ -1699,7 +1923,7 @@ struct DPP_EXPORT guild_ban_add_t : public event_dispatch_t { /** * @brief guild where ban was added */ - guild* banning_guild = nullptr; + guild banning_guild; /** * @brief user being banned @@ -1717,7 +1941,7 @@ struct DPP_EXPORT guild_ban_remove_t : public event_dispatch_t { /** * @brief guild where ban is being removed */ - guild* unbanning_guild = nullptr; + guild unbanning_guild; /** * @brief user being unbanned @@ -1774,7 +1998,7 @@ struct DPP_EXPORT thread_create_t : public event_dispatch_t { /** * @brief guild where thread was created */ - guild* creating_guild = nullptr; + guild creating_guild; /** * @brief thread created @@ -1792,7 +2016,7 @@ struct DPP_EXPORT thread_update_t : public event_dispatch_t { /** * @brief guild where thread was updated */ - guild* updating_guild = nullptr; + guild updating_guild; /** * @brief thread updated @@ -1810,7 +2034,7 @@ struct DPP_EXPORT thread_delete_t : public event_dispatch_t { /** * @brief guild where thread was deleted */ - guild* deleting_guild = nullptr; + guild deleting_guild; /** * @brief thread deleted @@ -1828,7 +2052,7 @@ struct DPP_EXPORT thread_list_sync_t : public event_dispatch_t { /** * @brief guild where thread list was synchronised */ - guild* updating_guild = nullptr; + guild updating_guild; /** * @brief list of threads (channels) synchronised @@ -1903,34 +2127,16 @@ struct DPP_EXPORT voice_buffer_send_t : public event_dispatch_t { /** * @brief encoded size of sent buffer */ - int buffer_size = 0; -}; - -/** - * @brief voice user talking - */ -struct DPP_EXPORT voice_user_talking_t : public event_dispatch_t { - using event_dispatch_t::event_dispatch_t; - using event_dispatch_t::operator=; - - /** - * @brief voice client where user is talking - */ - class discord_voice_client* voice_client = nullptr; - - /** - * @brief talking user id - */ - snowflake user_id = {}; + uint64_t buffer_size = 0; /** - * @brief flags for talking user + * @brief number of packet waiting to be sent in the queue */ - uint8_t talking_flags = 0; + size_t packets_left = 0; }; /** - * @brief voice user talking + * @brief voice ready */ struct DPP_EXPORT voice_ready_t : public event_dispatch_t { using event_dispatch_t::event_dispatch_t; @@ -1959,28 +2165,28 @@ struct DPP_EXPORT voice_receive_t : public event_dispatch_t { /** * @brief Construct a new voice receive t object * - * @param client The shard the event originated on. - * WILL ALWAYS be NULL. + * @param creator The creating cluster + * @param shard_id Shard the voice channel exists on * @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(discord_client* client, const std::string& raw, class discord_voice_client* vc, snowflake _user_id, const uint8_t* pcm, size_t length); + voice_receive_t(dpp::cluster* creator, uint32_t shard_id, 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 creator The creating cluster + * @param shard_id Shard the voice channel exists on * @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(discord_client* client, std::string&& raw, class discord_voice_client* vc, snowflake _user_id, const uint8_t* pcm, size_t length); + voice_receive_t(dpp::cluster* creator, uint32_t shard_id, std::string&& raw, class discord_voice_client* vc, snowflake _user_id, const uint8_t* pcm, size_t length); /** * @brief Voice client @@ -2002,7 +2208,7 @@ struct DPP_EXPORT voice_receive_t : public event_dispatch_t { /** * @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) @@ -2062,6 +2268,44 @@ struct DPP_EXPORT voice_client_disconnect_t : public event_dispatch_t { 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 */ @@ -2101,5 +2345,5 @@ struct DPP_EXPORT entitlement_delete_t : public event_dispatch_t { entitlement deleted = {}; }; -} // namespace dpp +} diff --git a/3rdParty/dpp/dns.h b/3rdParty/dpp/dns.h index 502e4da642..95983bd759 100644 --- a/3rdParty/dpp/dns.h +++ b/3rdParty/dpp/dns.h @@ -31,38 +31,60 @@ #include #include #include +#include +#include +#include namespace dpp { /** * @brief Represents a cached DNS result. - * Used by the ssl_client class to store cached copies of dns lookups. + * Used by the ssl_connection class to store cached copies of dns lookups. */ - struct dns_cache_entry { + struct DPP_EXPORT 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; }; /** * @brief Cache container type */ - using dns_cache_t = std::unordered_map; + using dns_cache_t = std::unordered_map>; /** * @brief Resolve a hostname to an addrinfo @@ -72,5 +94,5 @@ namespace dpp { * @return dns_cache_entry* First IP address associated with the hostname DNS record * @throw dpp::connection_exception On failure to resolve hostname */ - const dns_cache_entry* resolve_hostname(const std::string& hostname, const std::string& port); -} // namespace dpp + DPP_EXPORT 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 e1c563c1aa..f5834c7195 100644 --- a/3rdParty/dpp/dpp.h +++ b/3rdParty/dpp/dpp.h @@ -37,12 +37,15 @@ #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -68,8 +71,13 @@ #include #include #include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include diff --git a/3rdParty/dpp/dtemplate.h b/3rdParty/dpp/dtemplate.h index f7110d40cc..3de77a8f1c 100644 --- a/3rdParty/dpp/dtemplate.h +++ b/3rdParty/dpp/dtemplate.h @@ -112,4 +112,4 @@ class DPP_EXPORT dtemplate : public json_interface { */ typedef std::unordered_map dtemplate_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/emoji.h b/3rdParty/dpp/emoji.h index 6142c0227f..b45f8e6d2a 100644 --- a/3rdParty/dpp/emoji.h +++ b/3rdParty/dpp/emoji.h @@ -45,17 +45,17 @@ enum emoji_flags : uint8_t { /** * @brief Managed (introduced by application) */ - e_managed = 0b00000010, + e_managed = 0b00000010, /** * @brief Animated emoji. */ - e_animated = 0b00000100, + e_animated = 0b00000100, /** * @brief Available (false if the guild doesn't meet boosting criteria, etc) */ - e_available = 0b00001000, + e_available = 0b00001000, }; /** @@ -208,7 +208,8 @@ class DPP_EXPORT emoji : public managed, public json_interface { /** * @brief Load an image into the object * - * @param image_blob Image binary data + * @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 @@ -248,4 +249,4 @@ class DPP_EXPORT emoji : public managed, public json_interface { */ typedef std::unordered_map emoji_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/entitlement.h b/3rdParty/dpp/entitlement.h index 30434af33b..099b314b41 100644 --- a/3rdParty/dpp/entitlement.h +++ b/3rdParty/dpp/entitlement.h @@ -35,35 +35,72 @@ namespace dpp { * */ enum entitlement_type : uint8_t { /** - * @brief A subscription for a guild. - * @warning This can only be used when creating a test entitlement. + * @brief Entitlement was purchased by user */ - GUILD_SUBSCRIPTION = 1, + PURCHASE = 1, /** - * @brief A subscription for a user. - * @warning This can only be used when creating a test entitlement. + * @brief Entitlement for Discord Nitro subscription */ - USER_SUBSCRIPTION = 2, + PREMIUM_SUBSCRIPTION = 2, /** - * @brief Entitlement was purchased as an app subscription. + * @brief Entitlement was gifted by developer */ - APPLICATION_SUBSCRIPTION = 8 + 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 : uint16_t { +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_deleted = 0b000000000000001, + 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: @@ -85,7 +122,11 @@ class DPP_EXPORT entitlement : public managed, public json_interface entitlement_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/etf.h b/3rdParty/dpp/etf.h index 6eb19d2596..b0bfb24ef4 100644 --- a/3rdParty/dpp/etf.h +++ b/3rdParty/dpp/etf.h @@ -708,4 +708,4 @@ class DPP_EXPORT etf_parser { std::string build(const nlohmann::json& j); }; -} // namespace dpp +} diff --git a/3rdParty/dpp/event.h b/3rdParty/dpp/event.h index dadb3faa86..79bcf534e1 100644 --- a/3rdParty/dpp/event.h +++ b/3rdParty/dpp/event.h @@ -100,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); @@ -120,6 +122,7 @@ event_decl(invite_delete,INVITE_DELETE); /* Voice */ event_decl(voice_state_update,VOICE_STATE_UPDATE); +event_decl(voice_channel_effect_send,VOICE_CHANNEL_EFFECT_SEND); event_decl(voice_server_update,VOICE_SERVER_UPDATE); /* Webhooks */ @@ -154,4 +157,4 @@ event_decl(entitlement_create, ENTITLEMENT_CREATE); event_decl(entitlement_update, ENTITLEMENT_UPDATE); event_decl(entitlement_delete, ENTITLEMENT_DELETE); -} // namespace dpp::events +} diff --git a/3rdParty/dpp/event_router.h b/3rdParty/dpp/event_router.h index 8a43134349..fc604d0e1b 100644 --- a/3rdParty/dpp/event_router.h +++ b/3rdParty/dpp/event_router.h @@ -39,7 +39,7 @@ namespace dpp { -#ifdef DPP_CORO +#ifndef DPP_NO_CORO template class event_router_t; @@ -212,7 +212,7 @@ template class event_router_t { */ using event_handler_abi_t = std::variant>; -#ifdef DPP_CORO +#ifndef DPP_NO_CORO friend class detail::event_router::awaitable; /** @brief dpp::task coro event handler */ @@ -247,7 +247,7 @@ template class event_router_t { */ std::map dispatch_container; -#ifdef DPP_CORO +#ifndef DPP_NO_CORO /** * @brief Mutex for messing with coro_awaiters. */ @@ -305,13 +305,13 @@ template class event_router_t { 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"); + throw dpp::logic_exception("cannot handle a coroutine event handler with a library built without DPP_CORO"); } } }; } -#ifdef DPP_CORO +#ifndef DPP_NO_CORO /** * @brief Handle an event as a coroutine, ensuring the lifetime of the event object. */ @@ -425,7 +425,7 @@ template class event_router_t { * 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 +#ifndef DPP_NO_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 { @@ -449,7 +449,7 @@ template class event_router_t { * @param event Class to pass as parameter to all listeners. */ void call(const T& event) const { -#ifdef DPP_CORO +#ifndef DPP_NO_CORO handle_coro(event); #else handle(event); @@ -463,14 +463,14 @@ template class event_router_t { * @param event Class to pass as parameter to all listeners. */ void call(T&& event) const { -#ifdef DPP_CORO +#ifndef DPP_NO_CORO handle_coro(std::move(event)); #else handle(std::move(event)); #endif }; -#ifdef DPP_CORO +#ifndef DPP_NO_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. @@ -478,7 +478,7 @@ template class event_router_t { * saving it in a variable is recommended to avoid variable lifetime issues. * * @details Example: @code{cpp} - * dpp::job my_handler(dpp::slashcommand_t event) { + * 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"; }); @@ -512,9 +512,8 @@ template class event_router_t { * @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::job my_handler(dpp::slashcommand_t event) { + * 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; @@ -543,7 +542,7 @@ template class event_router_t { * @retval false if there are some listeners */ [[nodiscard]] bool empty() const { -#ifdef DPP_CORO +#ifndef DPP_NO_CORO std::shared_lock lock{mutex}; std::shared_lock coro_lock{coro_mutex}; @@ -570,7 +569,7 @@ template class event_router_t { /** * @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), + * `dpp::task(const T&)`, * where T is the event type for this event router. * * This has the exact same behavior as using \ref attach(F&&) "attach". @@ -586,7 +585,7 @@ template class event_router_t { /** * @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), + * `dpp::task(const T&)`, * where T is the event type for this event router. * * @param fun Callable to attach to event @@ -596,7 +595,7 @@ template class event_router_t { template [[maybe_unused]] event_handle attach(F&& fun); #else /* not _DOXYGEN_ */ -# ifdef DPP_CORO +# ifndef DPP_NO_CORO /** * @brief Attach a callable to the event, adding a listener. * The callable should either be of the form `void(const T&)` or @@ -607,7 +606,7 @@ template class event_router_t { * detach the listener from the event later if necessary. */ template - requires (utility::callable_returns || utility::callable_returns, const T&> || utility::callable_returns) + requires (utility::callable_returns, const T&> || utility::callable_returns) [[maybe_unused]] event_handle operator()(F&& fun) { return this->attach(std::forward(fun)); } @@ -649,28 +648,6 @@ template class event_router_t { 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; } - - /** - * @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. - * - * @deprecated dpp::job event handlers are deprecated and will be removed in a future version, use dpp::task instead. - * @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) - DPP_DEPRECATED("dpp::job event handlers are deprecated and will be removed in a future version, use dpp::task instead") - [[maybe_unused]] event_handle attach(F&& fun) { - assert(dpp::utility::is_coro_enabled()); - - std::unique_lock l(mutex); - event_handle h = next_handle++; - 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; - } # else /** * @brief Attach a callable to the event, adding a listener. @@ -690,7 +667,7 @@ template class event_router_t { * @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 @@ -704,7 +681,7 @@ template class event_router_t { dispatch_container.emplace(h, std::forward(fun)); return h; } -# endif /* DPP_CORO */ +# endif /* DPP_NO_CORO */ #endif /* _DOXYGEN_ */ /** * @brief Detach a listener from the event using a previously obtained ID. @@ -721,7 +698,7 @@ template class event_router_t { } }; -#ifdef DPP_CORO +#ifndef DPP_NO_CORO namespace detail::event_router { @@ -764,4 +741,4 @@ const T &awaitable::await_resume() { } #endif -} // namespace dpp +} diff --git a/3rdParty/dpp/exception.h b/3rdParty/dpp/exception.h index e8dfcbed64..005d9fd86f 100644 --- a/3rdParty/dpp/exception.h +++ b/3rdParty/dpp/exception.h @@ -27,6 +27,359 @@ 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_cant_start_shard = 32, + 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. @@ -39,6 +392,11 @@ class exception : public std::exception */ std::string msg; + /** + * @brief Exception error code + */ + exception_error_code error_code; + public: using std::exception::exception; @@ -53,7 +411,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 @@ -61,14 +427,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 @@ -77,6 +451,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) */ @@ -113,6 +495,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_ @@ -121,15 +510,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 @@ -186,27 +579,29 @@ class exception : public std::exception * @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 +#ifndef DPP_NO_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 */ +#endif /* DPP_NO_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 +# ifndef DPP_NO_CORO derived_exception(task_cancelled_exception, exception); -# endif /* DPP_CORO */ +# endif /* DPP_NO_CORO */ #endif -} // namespace dpp +} diff --git a/3rdParty/dpp/export.h b/3rdParty/dpp/export.h index 9ce3f46756..e0e560a4ca 100644 --- a/3rdParty/dpp/export.h +++ b/3rdParty/dpp/export.h @@ -33,6 +33,17 @@ #error "D++ Requires a C++17 compatible C++ compiler. Please ensure that you have enabled C++17 in your compiler flags." #endif +/* If not using c++20, define DPP_CPP17_COMPAT and DPP_NO_CORO if DPP_NO_CORO is not already defined. + */ +#if !(defined(__cplusplus) && __cplusplus >= 202002L) && !(defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define DPP_CPP17_COMPAT + #if !defined(DPP_CORO) || !DPP_CORO // Allow overriding this because why not + #ifndef DPP_NO_CORO + #define DPP_NO_CORO + #endif + #endif +#endif + #ifndef DPP_STATIC /* Dynamic linked build as shared object or dll */ #ifdef DPP_BUILD @@ -115,10 +126,10 @@ extern bool DPP_EXPORT validate_configuration(); } -#ifndef _WIN32 - #define SOCKET int -#else - #define NOMINMAX +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif #include #endif diff --git a/3rdParty/dpp/guild.h b/3rdParty/dpp/guild.h index 5ae1478483..b5f17e10f7 100644 --- a/3rdParty/dpp/guild.h +++ b/3rdParty/dpp/guild.h @@ -34,6 +34,7 @@ namespace dpp { class channel; +class cluster; /* 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. @@ -520,6 +521,14 @@ class DPP_EXPORT guild_member : public json_interface { */ 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 * @@ -1277,15 +1286,17 @@ class DPP_EXPORT guild : public managed, public json_interface { /** * @brief Connect to a voice channel another guild member is in * + * @param owner Cluster the user's shard is on * @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 * @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(const cluster& owner, snowflake user_id, bool self_mute = false, bool self_deaf = false, bool dave = true); /** * @brief Get the banner url of the guild if it have one, otherwise returns an empty string @@ -1990,7 +2001,7 @@ struct DPP_EXPORT onboarding : public json_interface { /** * @brief Set guild_id of this onboarding object * - * @param guild_id Guild ID to set + * @param id Guild ID to set * @return Reference to self, so these method calls may be chained */ onboarding& set_guild_id(const snowflake id); @@ -2038,4 +2049,4 @@ typedef std::unordered_map guild_member_map; */ guild_member DPP_EXPORT find_guild_member(const snowflake guild_id, const snowflake user_id); -} // namespace dpp +} diff --git a/3rdParty/dpp/http_server.h b/3rdParty/dpp/http_server.h new file mode 100644 index 0000000000..87fb1a4f9c --- /dev/null +++ b/3rdParty/dpp/http_server.h @@ -0,0 +1,74 @@ +/************************************************************************************ + * + * 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 Creates a simple HTTP server which listens on a TCP port for a + * plaintext or SSL incoming request, and passes that request to a callback + * to generate the response. + */ +struct DPP_EXPORT http_server : public socket_listener { + + /** + * @brief Request handler callback to use for all incoming HTTP(S) requests + */ + http_server_request_event request_handler; + + /** + * @brief Port we are listening on + */ + uint16_t bound_port; + + /** + * @brief Constructor for creation of a HTTP(S) server + * @param creator Cluster creator + * @param address address to bind to, use "0.0.0.0" to bind to all local addresses + * @param port port to bind to. You should generally use a port > 1024. + * @param handle_request Callback to call for each pending request + * @param private_key Private key PEM file for HTTPS/SSL. If empty, a plaintext server is created + * @param public_key Public key PEM file for HTTPS/SSL. If empty, a plaintext server is created + */ + http_server(cluster* creator, const std::string_view address, uint16_t port, http_server_request_event handle_request, const std::string& private_key = "", const std::string& public_key = ""); + + /** + * @brief Emplace a new request into the connection pool + * @param newfd file descriptor of new request + */ + void emplace(socket newfd) override; + + /** + * @brief Destructor + */ + virtual ~http_server() { + detail::release_ssl_context(bound_port); + } +}; + +} diff --git a/3rdParty/dpp/http_server_request.h b/3rdParty/dpp/http_server_request.h new file mode 100644 index 0000000000..2a91ab68bd --- /dev/null +++ b/3rdParty/dpp/http_server_request.h @@ -0,0 +1,260 @@ +/************************************************************************************ + * + * 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 +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Callback type for HTTP server request callbacks + */ +using http_server_request_event = std::function; + +/* + * @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 http_server_request : public ssl_connection { + /** + * @brief The request body, e.g. form data + */ + std::string request_body; + + /** + * @brief Headers from the client + */ + http_headers request_headers; + + /** + * @brief Time at which the request should be abandoned + */ + time_t timeout; + + /** + * @brief Headers for our response + */ + http_headers response_headers; + + /** + * @brief Handler to handle the inbound request + */ + http_server_request_event handler{}; + + /** + * @brief Response body + */ + std::string response_body; + +protected: + + /** + * @brief The type of the request, e.g. GET, POST + */ + std::string request_type; + + /** + * @brief Path part of URL for HTTPS connection + */ + std::string path; + + /** + * @brief Current connection state + */ + http_state state; + + /** + * @brief HTTP status code for response + */ + uint16_t status{0}; + + /** + * @brief Start the connection + */ + virtual void connect() override; + + /** + * @brief Called when the output buffer is drained to empty + */ + void on_buffer_drained() override; + + /** + * @brief Maximum size of POST body + */ + [[nodiscard]] uint64_t get_max_post_size() const; + + /** + * @brief Maximum size of headers + */ + [[nodiscard]] uint64_t get_max_header_size() const; + + /** + * @brief Reply with an error message + * @param error_code error code + * @param message message + */ + void generate_error(uint16_t error_code, const std::string& message); + +public: + /** + * @brief If true the response timed out while waiting + */ + bool timed_out; + + /** + * @brief Get request state + * @return request state + */ + http_state get_state() const; + + /** + * @brief Get current response body + * @return response body + */ + std::string get_response_body() const; + + /** + * @brief Get current request body + * @return request body + */ + std::string get_request_body() const; + + /** + * @brief Get current status code + * @return status code + */ + uint16_t get_status() const; + + /** + * @brief Content length sent by client + */ + uint64_t content_length{ULLONG_MAX}; + + /** + * @brief Construct a new server request object. + * Server request objects are instantiated for an incoming server connection, as such they already + * have a file descriptor. + * @param creator creating owner + * @param fd file descriptor + * @param port Port the connection came in on + * @param plaintext_downgrade true if plaintext, false if SSL + * @param private_key if SSL, the path to the private key PEM + * @param public_key if SSL, the path to the public key PEM + * @param handle_request request handler callback + */ + http_server_request(cluster* creator, socket fd, uint16_t port, bool plaintext_downgrade, const std::string& private_key, const std::string& public_key, http_server_request_event handle_request); + + /** + * @brief Destroy the https client object + */ + virtual ~http_server_request() override; + + /** + * @brief Processes incoming data 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) override; + + /** + * @brief Close HTTPS socket + */ + virtual void close() override; + + /** + * @brief Fires every second from the underlying socket I/O loop, used for timeouts + */ + virtual void one_second_timer() override; + + /** + * @brief Get a HTTP request header + * + * @param header_name Header name to find, case insensitive + * @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 + */ + [[nodiscard]] const std::string get_header(const 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 + */ + [[nodiscard]] size_t get_header_count(const std::string& header_name) const; + + /** + * @brief Get a set of HTTP request headers with a common name + * + * @param header_name + * @return A list of headers with the same name, or an empty list if not found + */ + [[nodiscard]] std::list get_header_list(const std::string& header_name) const; + + /** + * @brief Get all HTTP request headers + * + * @return headers as a map + */ + [[nodiscard]] std::multimap get_headers() const; + + /** + * @brief Set a response header + * @param header header name + * @param value header value + * @return ref to self + */ + http_server_request& set_response_header(const std::string& header, const std::string& value); + + /** + * @brief Set the response content + * + * @param new_content response content + */ + http_server_request& set_response_body(const std::string& new_content); + + /** + * @brief Set the response HTTP status, e.g. + * 200 for OK, 404 for not found, 429 for rate limited etc. + * + * @param new_status HTTP status + */ + http_server_request& set_status(uint16_t new_status); + + /** + * @brief Get whole response as a string + */ + [[nodiscard]] std::string get_response(); +}; + +} diff --git a/3rdParty/dpp/httpsclient.h b/3rdParty/dpp/httpsclient.h index 876c534eda..49b9f8ddf0 100644 --- a/3rdParty/dpp/httpsclient.h +++ b/3rdParty/dpp/httpsclient.h @@ -26,16 +26,14 @@ #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) + ")"; + + 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"; @@ -111,7 +109,7 @@ struct http_connect_info { /** * @brief True if the connection should be SSL */ - bool is_ssl; + bool is_ssl{}; /** * @brief The request scheme, e.g. 'https' or 'http' @@ -127,18 +125,16 @@ struct http_connect_info { * @brief The port number, either determined from the scheme, * or from the part of the hostname after a colon ":" character */ - uint16_t port; + uint16_t port{}; }; +using https_client_completion_event = std::function; + /** * @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 { - /** - * @brief Current connection state - */ - http_state state; +class DPP_EXPORT https_client : public ssl_connection { /** * @brief The type of the request, e.g. GET, POST @@ -209,19 +205,11 @@ class DPP_EXPORT https_client : public ssl_client { */ std::multimap response_headers; - /** - * @brief Handle input buffer - * - * @param buffer Buffer to read - * @return returns true if the connection should remain open - */ - bool do_buffer(std::string& buffer); - protected: /** * @brief Start the connection */ - virtual void connect(); + virtual void connect() override; /** * @brief Get request state @@ -230,6 +218,21 @@ 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 Function to call when HTTP request is completed + */ + https_client_completion_event completed; + + /** + * @brief Current connection state + */ + http_state state; + /** * @brief Connect to a specific HTTP(S) server and complete a request. * @@ -249,14 +252,15 @@ 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 http_protocol Request HTTP protocol + * @param protocol Request HTTP protocol (default: 1.1) + * @param done Function to call when https_client request is completed */ - 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"); + https_client(cluster* creator, 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", https_client_completion_event done = {}); /** * @brief Destroy the https client object */ - virtual ~https_client() = default; + virtual ~https_client() override; /** * @brief Build a multipart content from a set of files and some json @@ -274,17 +278,17 @@ class DPP_EXPORT https_client : public ssl_client { * * @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) override; /** * @brief Close HTTPS socket */ - virtual void close(); + virtual void close() override; /** * @brief Fires every second from the underlying socket I/O loop, used for timeouts */ - virtual void one_second_timer(); + virtual void one_second_timer() override; /** * @brief Get a HTTP response header @@ -348,7 +352,6 @@ class DPP_EXPORT https_client : public ssl_client { * @return Split URL */ static http_connect_info get_host_info(std::string url); - }; -} // namespace dpp +} diff --git a/3rdParty/dpp/integration.h b/3rdParty/dpp/integration.h index 94fc189c03..02adee6891 100644 --- a/3rdParty/dpp/integration.h +++ b/3rdParty/dpp/integration.h @@ -30,10 +30,24 @@ namespace dpp { +/** + * @brief Where an app can be installed, also called its supported installation contexts. + */ +enum application_integration_types : uint8_t { + /** + * @brief Installable to servers + */ + ait_guild_install = 0, + /** + * @brief Installable to users + */ + ait_user_install = 1, +}; + /** * @brief Integration types */ -enum integration_type { +enum integration_type : uint8_t { /** * @brief Twitch integration */ @@ -58,7 +72,7 @@ enum integration_type { /** * @brief Integration flags */ -enum integration_flags { +enum integration_flags : uint8_t { /** * @brief Is this integration enabled? */ @@ -335,5 +349,5 @@ typedef std::unordered_map integration_map; */ typedef std::unordered_map connection_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/intents.h b/3rdParty/dpp/intents.h index 4d8dc4d04b..2af071a932 100644 --- a/3rdParty/dpp/intents.h +++ b/3rdParty/dpp/intents.h @@ -126,6 +126,16 @@ enum intents { */ i_auto_moderation_execution = (1 << 21), + /** + * @brief Intent for receipt of guild message poll votes. + */ + i_guild_message_polls = (1 << 24), + + /** + * @brief Intent for receipt of direct message poll votes. + */ + i_direct_message_polls = (1 << 25), + /** * @brief Default D++ intents (all non-privileged intents). */ @@ -134,7 +144,7 @@ enum intents { 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, + dpp::i_auto_moderation_execution | dpp::i_guild_message_polls | dpp::i_direct_message_polls, /** * @brief Privileged intents requiring ID. @@ -152,4 +162,4 @@ enum intents { i_unverified_default_intents = dpp::i_default_intents | dpp::i_message_content }; -} // namespace dpp +} diff --git a/3rdParty/dpp/invite.h b/3rdParty/dpp/invite.h index ce1191f57e..ec275a6301 100644 --- a/3rdParty/dpp/invite.h +++ b/3rdParty/dpp/invite.h @@ -242,4 +242,4 @@ class DPP_EXPORT invite : public json_interface { */ typedef std::unordered_map invite_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/isa/avx.h b/3rdParty/dpp/isa/avx.h index 3b6faa5e14..d771ed7a36 100644 --- a/3rdParty/dpp/isa/avx.h +++ b/3rdParty/dpp/isa/avx.h @@ -24,11 +24,13 @@ #include #include +#include +#include namespace dpp { using avx_float = __m128; - + /** * @brief A class for audio mixing operations using AVX instructions. */ @@ -73,7 +75,10 @@ namespace dpp { } protected: - alignas(16) float values[byte_blocks_per_register]{};///< Array for storing the values to be loaded/stored. + /** + * @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. @@ -102,6 +107,6 @@ namespace dpp { } }; -} // namespace dpp +} #endif \ No newline at end of file diff --git a/3rdParty/dpp/isa/avx2.h b/3rdParty/dpp/isa/avx2.h index b53ef239cd..23fe51bb9c 100644 --- a/3rdParty/dpp/isa/avx2.h +++ b/3rdParty/dpp/isa/avx2.h @@ -24,6 +24,8 @@ #include #include +#include +#include namespace dpp { @@ -76,7 +78,10 @@ namespace dpp { } protected: - alignas(32) float values[byte_blocks_per_register]{};///< Array for storing the values to be loaded/stored. + /** + * @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. @@ -105,6 +110,6 @@ namespace dpp { } }; -} // namespace dpp +} #endif \ No newline at end of file diff --git a/3rdParty/dpp/isa/avx512.h b/3rdParty/dpp/isa/avx512.h index ca7c402e85..2d1ea1dcd6 100644 --- a/3rdParty/dpp/isa/avx512.h +++ b/3rdParty/dpp/isa/avx512.h @@ -24,11 +24,13 @@ #include #include +#include +#include namespace dpp { using avx_512_float = __m512; - + /** * @brief A class for audio mixing operations using AVX512 instructions. */ @@ -79,7 +81,10 @@ namespace dpp { } protected: - alignas(64) float values[byte_blocks_per_register]{};///< Array for storing the values to be loaded/stored. + /** + * @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. @@ -108,6 +113,6 @@ namespace dpp { } }; -} // namespace dpp +} #endif \ No newline at end of file diff --git a/3rdParty/dpp/isa/fallback.h b/3rdParty/dpp/isa/fallback.h index 1f4a000f5b..9ac63140f1 100644 --- a/3rdParty/dpp/isa/fallback.h +++ b/3rdParty/dpp/isa/fallback.h @@ -20,7 +20,10 @@ ************************************************************************************/ #pragma once +#include #include +#include +#include namespace dpp { @@ -73,4 +76,4 @@ namespace dpp { } }; -} // namespace dpp +} 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 index 1bc2a299c9..a3cecf365c 100644 --- a/3rdParty/dpp/isa_detection.h +++ b/3rdParty/dpp/isa_detection.h @@ -20,7 +20,9 @@ ************************************************************************************/ #pragma once -#if AVX_TYPE == 512 +#if AVX_TYPE == 1024 + #include "isa/neon.h" +#elif AVX_TYPE == 512 #include "isa/avx512.h" #elif AVX_TYPE == 2 #include "isa/avx2.h" diff --git a/3rdParty/dpp/json_interface.h b/3rdParty/dpp/json_interface.h index 3745ced05e..fa97b01806 100644 --- a/3rdParty/dpp/json_interface.h +++ b/3rdParty/dpp/json_interface.h @@ -21,7 +21,7 @@ #pragma once #include -#include +#include namespace dpp { @@ -33,7 +33,7 @@ namespace dpp { * @tparam T Type of class that implements the interface */ template -struct DPP_EXPORT json_interface { +struct json_interface { /** * @brief Convert object from nlohmann::json * @@ -66,8 +66,8 @@ struct DPP_EXPORT json_interface { */ template ().to_json_impl(bool{}))> std::string build_json(bool with_id = false) const { - return to_json(with_id).dump(); + return to_json(with_id).dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); } }; -} // namespace dpp +} diff --git a/3rdParty/dpp/managed.h b/3rdParty/dpp/managed.h index 9a369fee83..1e829ad39e 100644 --- a/3rdParty/dpp/managed.h +++ b/3rdParty/dpp/managed.h @@ -113,4 +113,4 @@ class DPP_EXPORT managed { } }; -} // namespace dpp +} diff --git a/3rdParty/dpp/message.h b/3rdParty/dpp/message.h index eea4a0c49a..87fbe18e9f 100644 --- a/3rdParty/dpp/message.h +++ b/3rdParty/dpp/message.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -36,33 +37,113 @@ 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 Section + * @note Available in components v2 only + */ + cot_section = 9, + + /** + * @brief Simple text + * @note Available in components v2 only + */ + cot_text_display = 10, + + /** + * @brief Image thumbnail, clickable to expand + * @note Available in components v2 only + */ + cot_thumbnail = 11, + + /** + * @brief Collection of media (images, videos) + * @note Available in components v2 only + */ + cot_media_gallery = 12, + + /** + * @brief File attachment from the uploads of the message + * @note Available in components v2 only + */ + cot_file = 13, + + /** + * @brief Separator between sections or other components + * @note Available in components v2 only + */ + cot_separator = 14, + + /** + * @brief Content inventory entry + */ + cot_content_inventory_entry = 16, + + /** + * @brief Container for other components + * @note Available in components v2 only + */ + cot_container = 17, + + /** + * @brief Container associating a label and description with a component + */ + cot_label = 18, + + /** + * @brief Component for uploading files + */ + cot_file_upload = 19 }; /** - * @brief An emoji for a component (select menus included). + * @brief An emoji reference for a component (select menus included) or a poll. * - * 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 dpp::component::set_emoji method. + * 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 component_emoji { +struct partial_emoji { /** * @brief The name of the emoji. * @@ -70,7 +151,7 @@ struct component_emoji { * actual unicode value of the emoji e.g. "😄" * and not for example ":smile:" */ - std::string name{""}; + std::string name{}; /** * @brief The emoji ID value for emojis that are custom @@ -91,13 +172,49 @@ struct component_emoji { 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, }; @@ -105,16 +222,37 @@ 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, }; /** @@ -136,6 +274,7 @@ 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 */ @@ -160,14 +299,17 @@ struct DPP_EXPORT select_option : public json_interface { * @brief User-facing name of the option */ std::string label; + /** * @brief Dev-defined value of the option */ std::string value; + /** * @brief Additional description of the option */ std::string description; + /** * @brief True if option is the default option */ @@ -176,7 +318,7 @@ struct DPP_EXPORT select_option : public json_interface { /** * @brief The emoji for the select option. */ - component_emoji emoji; + partial_emoji emoji; /** * @brief Construct a new select option object @@ -195,7 +337,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 @@ -203,7 +345,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 @@ -211,7 +353,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 @@ -219,7 +361,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 @@ -229,7 +371,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 @@ -248,6 +390,98 @@ struct DPP_EXPORT select_option : public json_interface { select_option& set_animated(bool anim); }; +/** + * @brief Loading state for "unfurled" media, e.g. thumbnails and images in a message or component + */ +enum media_loading_state : uint8_t { + + /** + * @brief Loading state is unknown + */ + ls_unknown = 0, + + /** + * @brief Discord is loading the media + */ + ls_loading = 1, + + /** + * @brief The image was processed and loaded successfully + */ + ls_loaded_success = 2, + + /** + * @brief The media was not found + */ + ls_not_found = 3, +}; + +/** + * @brief An video, image or thumbnail in a dpp::embed or dpp::component (v2) + */ +struct DPP_EXPORT embed_image { + /** + * @brief URL to image or video. + */ + std::string url; + + /** + * @brief Proxied image url. + */ + std::string proxy_url; + + /** + * @brief Height (calculated by discord). + */ + uint32_t height{0}; + + /** + * @brief Width (calculated by discord). + */ + uint32_t width{0}; + + /** + * @brief Media loading state + */ + media_loading_state loading_state{ls_unknown}; + + /** + * @brief placeholder + */ + std::string placeholder; + + /** + * @brief Placeholder version + */ + uint8_t placeholder_version{1}; + + /** + * @brief Content type + */ + std::string content_type; + + /** + * @brief Flags (documented as present, but contents not documented) + */ + uint32_t flags{0}; +}; + +/** + * @brief Spacing types for components v2 separator + */ +enum separator_spacing : uint8_t { + + /** + * @brief Small separator + */ + sep_small = 1, + + /** + * @brief Large separator + */ + sep_large = 2, +}; + /** * @brief Represents the component object. * A component is a clickable button or drop down list @@ -271,98 +505,180 @@ class DPP_EXPORT component : public json_interface { 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 (0-25). - * 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 (0-25). - * 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; /** - * List of default values for auto-populated select menu components. The amount of default values must be in the range defined by dpp::component::min_value and dpp::component::max_values. + * @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. * - * @note 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. + * @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; - /** Disabled flag (for buttons) + /** + * @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; /** * @brief The emoji for this component. */ - component_emoji emoji; + partial_emoji emoji; + + /** + * @brief Text content for section and text display types (v2) + */ + std::string content; + + /** + * @brief accessory component for components which support it (v2) + * Can be a button or a thumbnail. + */ + std::shared_ptr accessory; + + /** + * @brief Description for media items (v2) + */ + std::string description; + + /** + * @brief Spoiler state for media and file items + */ + bool spoiler; + + /** + * @brief can be set for separator types (v2) + */ + bool is_divider; + + /** + * @brief Valid for separator types (v2) + */ + separator_spacing spacing; + + /** + * @brief Unfurled media for thumbnail objects (v2) + */ + std::optional thumbnail; + + /** + * @brief Unfurled file URL for file objects (v2) + * Should be in the format "attachment://..." + */ + std::optional file; + + /** + * @brief Media gallery items for media galleries (v2) + */ + std::vector> media_gallery_items; + + /** + * @brief Accent colour for container types (v2) + */ + std::optional accent; - /** Constructor + /** + * @brief Constructor */ component(); - /** Destructor + /** + * @brief Destructor */ virtual ~component() = default; @@ -374,6 +690,83 @@ class DPP_EXPORT component : public json_interface { */ component& add_channel_type(uint8_t ct); + /** + * @brief For text content types, set content + * @note Ignored for other types + * @param text text to set + * @return + */ + component& set_content(const std::string& text); + + /** + * @brief Set divider visibility for separator type (v2) + * @param divider true to show a visible divider + * @return self + */ + component& set_divider(bool divider); + + /** + * @brief Set accent colour for container type (v2) + * @param accent_colour Accent colour for container + * @return self + */ + component& set_accent(uint32_t accent_colour); + + /** + * @brief Set separator spacing for separator type (v2) + * @param sep_spacing separator spacing, small or large + * @return self + */ + component& set_spacing(separator_spacing sep_spacing); + + /** + * @brief Set the attachment url for file type components. + * The format of the attachment url should be of the type + * "attachment://...". + * @param attachment_url url to attachment. Should have been + * attached via add_file(). + * @return self + */ + component& set_file(std::string_view attachment_url); + + /** + * @brief Add a media gallery item to a media gallery type (v2) + * @param media_gallery_item item to add + * @return + */ + component& add_media_gallery_item(const component& media_gallery_item); + + /** + * @brief For media types, set description + * @note Ignored for other types + * @param text text to set + * @return + */ + component& set_description(const std::string& text); + + /** + * @brief For media and file component types, set spoiler status + * @note Ignored for other types + * @param spoiler_enable True to enable spoiler masking + * @return + */ + component& set_spoiler(bool spoiler_enable); + + /** + * @brief Set component thumbnail for thumbnail type (v2. + * @param url The embed thumbnail url (only supports http(s) and attachments) + * @return A reference to self so this method may be "chained". + */ + component& set_thumbnail(std::string_view url); + + /** + * @brief Set accessory component for components which support it. + * Can be a button or a thumbnail image. + * @param accessory_component accessory component + * @return + */ + component& set_accessory(const component& accessory_component); + /** * @brief Set the type of the component. Button components * (type dpp::cot_button) should always be contained within @@ -387,6 +780,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` @@ -404,7 +808,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. @@ -414,7 +818,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. @@ -424,7 +828,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. @@ -447,13 +851,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); @@ -475,7 +879,7 @@ 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 minimum number of items that must be chosen for a select menu @@ -522,24 +926,42 @@ class DPP_EXPORT component : public json_interface { * Adding subcomponents to a component will automatically * set this component's type to dpp::cot_action_row. * + * @note Automatic creation of action rows is retained for backwards + * compatibility with components v1. If you want to add components v2, + * and do not want automatic creation of action rows, use + * dpp::component::add_component_v2() instead. + * * @param c The sub-component to add * @return component& reference to self */ 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 Add a sub-component, does not automatically create + * action rows. This should be used for components v2 where + * you do not want to add action rows automatically. + * + * @param c The sub-component to add + * @return component& reference to self + */ + component& add_component_v2(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 @@ -552,60 +974,64 @@ 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); + 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 (only supports http(s) and attachments) */ + + /** + * @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); -}; - -/** - * @brief An video, image or thumbnail in a dpp::embed - */ -struct DPP_EXPORT embed_image { - /** URL to image or video */ - std::string url; - /** Proxied image url */ - std::string proxy_url; - /** Height (calculated by discord) */ - std::string height; - /** Width (calculated by discord) */ - std::string width; + embed_footer& set_proxy(std::string_view p); }; /** * @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; }; @@ -613,186 +1039,298 @@ 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 (only supports http(s)) */ + + /** + * @brief Author url. + * + * @warning Only supports http(s). + */ std::string url; - /** Author icon url (only supports http(s) and attachments) */ + + /** + * @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 (max length 256) */ + /** + * @brief Name of field (max length 256). + */ std::string name; - /** Value of field (max length 1024) */ + + /** + * @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 */ - std::optional 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 */ + /** + * @brief Optional: Title of embed. + */ + std::string title; + + /** + * @brief Optional: Type of embed. + * + * @note Always "rich" for webhook embeds. + */ + std::string type; + + /** + * @brief Optional: Description of embed. + */ + std::string description; + + /** + * @brief Optional: URL of embed. + */ + std::string url; + + /** + * @brief Optional: Timestamp of embed content. + */ + time_t timestamp; + + /** + * @brief Optional: Color code of the embed. + */ + std::optional color; + + /** + * @brief Optional: Footer information. + */ + std::optional footer; + + /** + * @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(); - /** Constructor to build embed from json object + /** + * @brief Constructor to build embed from json object * @param j JSON to read content from */ embed(nlohmann::json* j); - /** Destructor */ + /** + * @brief Destructor + */ ~embed(); - /** Set embed title. Returns the embed itself so these method calls may be "chained" + /** + * @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 + * @return A reference to self so this method may be "chained". */ - embed& set_title(const std::string &text); + embed& set_title(std::string_view text); - /** Set embed description. Returns the embed itself so these method calls may be "chained" + /** + * @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 + * @return A reference to self so this method may be "chained". */ - embed& set_description(const std::string &text); + embed& set_description(std::string_view text); - /** Set the footer of the embed. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set the footer of the embed. * @param f the footer to set - * @return A reference to self + * @return A reference to self so this method may be "chained". */ embed& set_footer(const embed_footer& f); - /** 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 (only supports http(s) and attachments) - * @return A reference to self - */ - embed& set_footer(const std::string& text, const std::string& icon_url); + /** + * @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); - /** Set embed colour. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed colour. * @param col The colour of the embed - * @return A reference to self + * @return A reference to self so this method may be "chained". */ embed& set_color(uint32_t col); - /** Set embed colour. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed colour. * @param col The colour of the embed - * @return A reference to self + * @return A reference to self so this method may be "chained". */ embed& set_colour(uint32_t col); - /** Set embed timestamp. Returns the embed itself so these method calls may be "chained" + /** + * @brief Set embed timestamp. * @param tstamp The timestamp to show in the footer, should be in UTC - * @return A reference to self + * @return A reference to self so this method may be "chained". */ embed& set_timestamp(time_t tstamp); - /** Set embed url. Returns the embed itself so these method calls may be "chained" + /** + * @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 (only supports http(s)) * @param icon_url The icon URL of the author (only supports http(s) and attachments) - * @return A reference to self + * @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" + /** + * @brief Set embed image. * @param url The embed image URL (only supports http(s) and attachments) - * @return A reference to self + * @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" + /** + * @brief Set embed thumbnail. * @param url The embed thumbnail url (only supports http(s) and attachments) - * @return A reference to self + * @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 { - /** Total number of times this emoji has been used to react (including super reacts) */ + /** + * @brief Total number of times this emoji has been used to react (including super reacts) + */ uint32_t count; - /** Count of super reactions */ + + /** + * @brief Count of super reactions + */ uint32_t count_burst; - /** Count of normal reactions */ + + /** + * @brief Count of normal reactions + */ uint32_t count_normal; - /** ID of emoji for reaction */ + + /** + * @brief ID of emoji for reaction + */ snowflake emoji_id; - /** Name of emoji, if applicable */ + + /** + * @brief Name of emoji, if applicable + */ std::string emoji_name; - /** Whether your bot reacted using this emoji */ + + /** + * @brief Whether your bot reacted using this emoji + */ bool me; - /** Whether your bot super-reacted using this emoji */ + + /** + * @brief Whether your bot super-reacted using this emoji + */ bool me_burst; - /** HEX colors used for super reaction. Stored as integers */ + + /** + * @brief HEX colors used for super reaction. + * + * @note Stored as integers. + */ std::vector burst_colors; /** @@ -816,7 +1354,9 @@ struct DPP_EXPORT reaction { * @brief Bitmask flags for a dpp::attachment */ enum attachment_flags : uint8_t { - /// this attachment has been edited using the remix feature on mobile + /** + * @brief This attachment has been edited using the remix feature on mobile. + */ a_is_remix = 1 << 2, }; @@ -824,33 +1364,79 @@ enum attachment_flags : uint8_t { * @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; - /** The duration of the audio file (currently for voice messages) */ + + /** + * @brief The duration of the audio file. + * + * @note Currently for voice messages. + */ double duration_secs; - /** base64 encoded bytearray representing a sampled waveform (currently for voice messages) */ + + /** + * @brief Base64 encoded bytearray representing a sampled waveform. + * + * @note Currently for voice messages. + */ std::string waveform; - /** Flags. Made of bits in dpp::attachment_flags */ - uint8_t flags; - /** Owning message */ + + /** + * @brief Flags made from dpp::attachment_flags. + */ + uint8_t flags; + + /** + * @brief Owning message + */ struct message* owner; /** @@ -873,6 +1459,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. @@ -887,15 +1474,34 @@ struct DPP_EXPORT attachment { * @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 { - /// An official sticker in a pack + /** + * @brief An official sticker in a pack. + */ st_standard = 1, - /// Guild sticker + + /** + * @brief Guild sticker. + */ st_guild = 2 }; @@ -929,78 +1535,120 @@ struct DPP_EXPORT sticker : public managed, public json_interface { virtual json to_json_impl(bool with_id = true) const; public: - /** Optional: for standard stickers, id of the pack the sticker is from + /** + * @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; /** - * @brief Get the sticker url. + * @brief Optional: Whether this guild sticker can be used. * - * @return std::string The sticker url or an empty string, if the id is empty + * @note May be false due to loss of Server Boosts. */ - std::string get_url() const; + bool available; /** - * @brief Set the filename - * - * @param fn filename - * @return message& reference to self + * @brief Optional: ID of the guild that owns this sticker. */ - sticker& set_filename(const std::string &fn); + snowflake guild_id; /** - * @brief Set the file content - * - * @param fc raw file content contained in std::string - * @return message& reference to self + * @brief Optional: The user that uploaded the guild sticker. */ - sticker& set_file_content(const std::string &fc); + user sticker_user; -}; + /** + * @brief Optional: The standard sticker's sort order within its pack. + */ + uint8_t sort_value; -/** - * @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 { -protected: + /** + * @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. + * + * @return std::string The sticker url or an empty string, if the id is empty + */ + std::string get_url() const; + + /** + * @brief Set the filename + * + * @param fn filename + * @return message& reference to self + */ + sticker& set_filename(std::string_view fn); + + /** + * @brief Set the file content + * + * @param fc raw file content + * @return message& reference to self + */ + sticker& set_file_content(std::string_view fc); + +}; + +/** + * @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 { +protected: friend struct json_interface; /** Read class values from json object @@ -1016,85 +1664,379 @@ struct DPP_EXPORT sticker_pack : public managed, public json_interface 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 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 + */ + 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 Construct a new sticker pack object + * @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 */ - sticker_pack(); + poll& add_answer(std::string_view text, std::string_view emoji); - virtual ~sticker_pack() = default; + /** + * @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, - /// this message will not trigger push and desktop notifications + + /** + * @brief This message will not trigger push and desktop notifications. + */ m_suppress_notifications = 1 << 12, - /// this message is a voice message + + /** + * @brief This message is a voice message. + */ m_is_voice_message = 1 << 13, + + /** + * @brief Contains forwarded snapshot + */ + m_has_snapshot = 1 << 14, + + /** + * @brief Message components vector contains v2 components + */ + m_using_components_v2 = 1 << 15, }; /** * @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 */ @@ -1104,69 +2046,163 @@ 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 +enum message_type : uint8_t { + /** + * @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, - /// Role subscription purchase - mt_role_subscription_purchase = 25, - /// Interaction premium upsell - mt_interaction_premium_upsell = 26, - /// Stage start - mt_stage_start = 27, - /// Stage end - mt_stage_end = 28, - /// Stage speaker - mt_stage_speaker = 29, - /// Stage topic - mt_stage_topic = 31, - /// Guild application premium subscription - mt_application_premium_subscription = 32, + + /** + * @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, }; /** @@ -1180,11 +2216,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 @@ -1221,34 +2259,89 @@ struct DPP_EXPORT cache_policy_t { /** * @brief Caching policy for roles */ - cache_policy_setting_t channel_policy = cp_aggressive; + 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 Caching policy for roles + * @brief Metadata for the interaction that was used to open the modal, present only on modal submit interactions */ - cache_policy_setting_t guild_policy = cp_aggressive; + // interaction_metadata_type triggering_interaction_metadata; }; /** - * @brief Contains a set of predefined cache policies for use when constructing a dpp::cluster + * @brief Message Reference type */ -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 }; - +enum DPP_EXPORT message_ref_type : uint8_t { /** - * @brief A shortcut constant for a more balanced caching policy for use in dpp::cluster constructor + * A reply or crosspost */ - inline constexpr cache_policy_t cpol_balanced = { cp_lazy, cp_lazy, cp_lazy, cp_aggressive, cp_aggressive }; - + mrt_default = 0, /** - * @brief A shortcut constant for all caching disabled for use in dpp::cluster constructor + * A forwarded message */ - inline constexpr cache_policy_t cpol_none = { cp_none, cp_none, cp_none, cp_none, cp_none }; + mrt_forward = 1, +}; +template struct message_snapshot { + std::vector messages; }; /** @@ -1274,79 +2367,180 @@ struct DPP_EXPORT message : public managed, json_interface { return to_json(with_id, false); } public: - /** 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 */ + /** + * @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 Pre-generated components as JSON. This overrides the components + * array if it is set. + */ + json components_json; + + /** + * @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; - /** Up to 10 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; - /** File content to upload (raw binary) */ - std::vector filecontent; + /** + * @brief Partial stickers. Only id, name and format_type are filled + */ + std::vector stickers; - /** Mime type of files to upload */ - std::vector filemimetype; + /** + * @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 */ @@ -1355,22 +2549,27 @@ struct DPP_EXPORT message : public managed, json_interface { * @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 */ @@ -1378,28 +2577,57 @@ struct DPP_EXPORT message : public managed, json_interface { } 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. @@ -1408,11 +2636,6 @@ struct DPP_EXPORT message : public managed, json_interface { */ message(class cluster* o); - /** - * @brief Destroy the message object - */ - virtual ~message(); - /** * @brief Construct a new message object with a channel and content * @@ -1420,7 +2643,14 @@ struct DPP_EXPORT message : public managed, json_interface { * @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 @@ -1428,7 +2658,7 @@ struct DPP_EXPORT message : public managed, json_interface { * @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 @@ -1436,7 +2666,28 @@ struct DPP_EXPORT message : public managed, json_interface { * @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 @@ -1445,22 +2696,23 @@ struct DPP_EXPORT message : public managed, json_interface { * @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; @@ -1501,6 +2753,14 @@ struct DPP_EXPORT message : public managed, json_interface { */ 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 * @@ -1558,13 +2818,58 @@ struct DPP_EXPORT message : public managed, json_interface { bool is_voice_message() const; /** - * @brief Add a component (button) to message + * @brief True if the message has a snapshot + * + * @return True if voice message + */ + bool has_snapshot() const; + + /** + * @brief True if the message is using components v2 + * + * @return True if voice message + */ + bool is_using_components_v2() const; + + /** + * @brief Add a component to a message + * + * @note If the component type you add is only available in + * components v2, this method will automatically add the + * m_using_components_v2 flag to your message. * * @param c component to add * @return message& reference to self */ message& add_component(const component& c); + /** + * @brief Add pre-generated components to a message + * + * @note This is intended to accept pre-generated component + * json from external tools such as https://discord.builders + * + * @param json components json to add. The JSON will be validated + * @return message& reference to self + */ + message& add_json_components(const std::string& json); + + /** + * @brief Add a component to a message + * + * @note If the component type you add is only available in + * components v2, this method will automatically add the + * m_using_components_v2 flag to your message. + * + * This is an alias of add_component() for readability when + * using components v2, so you can use add_component_v2() + * everywhere. It does exactly the same as add_component(). + * + * @param c component to add + * @return message& reference to self + */ + message& add_component_v2(const component& c); + /** * @brief Add an embed to message * @@ -1573,6 +2878,24 @@ struct DPP_EXPORT message : public managed, json_interface { */ 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 * @@ -1596,26 +2919,26 @@ struct DPP_EXPORT message : public managed, json_interface { * @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, const std::string &filemimetype = ""); + message& add_file(std::string_view filename, std::string_view filecontent, std::string_view filemimetype = ""); /** * @brief Set the message content @@ -1623,7 +2946,7 @@ struct DPP_EXPORT message : public managed, json_interface { * @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 @@ -1661,15 +2984,120 @@ struct DPP_EXPORT message : public managed, json_interface { * @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; +}; + +/** + * @brief Represents a pinned message. + */ +class DPP_EXPORT message_pin : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill in object from json data + * + * @param j JSON data + * @return message_pin& Reference to self + */ + message_pin& fill_from_json_impl(nlohmann::json* j); + + /** Build JSON from this object. + * @return The JSON text of the message_pin + */ + virtual json to_json() const; + +public: + + message_pin(); + + message_pin(time_t _pinned_at, const message& _pinned_message); + + /* + * @brief Construct a new message_pin object + * @param msg_pin message_pin to copy + */ + message_pin(const message_pin& msg_pin) = default; + + /* + * @brief Construct a new message_pin object + * @param msg_pin message_pin to move + */ + message_pin(message_pin&& msg_pin) = default; + + /** + * @brief Destroy the message_pin object + */ + ~message_pin() = default; + + /** + * @brief Copy a message_pin object + * + * @param msg_pin message_pin to copy + * @return message_pin& Reference to self + */ + message_pin &operator=(const message_pin& msg_pin) = default; + + /** + * @brief Move a message_pin object + * + * @param msg_pin message_pin to move + * @return message_pin& Reference to self + */ + message_pin &operator=(message_pin&& msg_pin) = default; + +public: + + /** + * @brief The time the message was pinned. + */ + time_t pinned_at; + + /** + * @brief The pinned message. + */ + message pinned_message; }; -/** A group of messages */ +/** + * @brief A group of messages + */ typedef std::unordered_map message_map; -/** A group of stickers */ +/** + * @brief A group of pinned messages + */ +typedef std::unordered_map message_pin_map; + +/** + * @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; -} // namespace dpp +} diff --git a/3rdParty/dpp/misc-enum.h b/3rdParty/dpp/misc-enum.h index b45c7143d7..e9fbbf67b4 100644 --- a/3rdParty/dpp/misc-enum.h +++ b/3rdParty/dpp/misc-enum.h @@ -21,14 +21,15 @@ ************************************************************************************/ #pragma once #include -#include +#include +#include namespace dpp { /** * @brief Supported image types for profile pictures and CDN endpoints */ -enum image_type { +enum image_type : uint8_t { /** * @brief image/png */ @@ -47,7 +48,6 @@ enum image_type { /** * @brief Webp. */ - /// WebP i_webp, }; @@ -86,4 +86,4 @@ enum loglevel { ll_critical }; -} // namespace dpp +} diff --git a/3rdParty/dpp/nlohmann/json.hpp b/3rdParty/dpp/nlohmann/json.hpp index c7278ce88d..96f59e4e7d 100644 --- a/3rdParty/dpp/nlohmann/json.hpp +++ b/3rdParty/dpp/nlohmann/json.hpp @@ -24300,7 +24300,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) { - return j.dump(); + return j.dump(-1, ' ', false, json::error_handler_t::ignore); } inline namespace literals diff --git a/3rdParty/dpp/once.h b/3rdParty/dpp/once.h index 36982ac37f..3860591d49 100644 --- a/3rdParty/dpp/once.h +++ b/3rdParty/dpp/once.h @@ -43,4 +43,4 @@ template auto run_once() { return !std::exchange(called, true); }; -} // namespace dpp +} diff --git a/3rdParty/dpp/permissions.h b/3rdParty/dpp/permissions.h index f56cd53e00..9bb846a345 100644 --- a/3rdParty/dpp/permissions.h +++ b/3rdParty/dpp/permissions.h @@ -251,6 +251,22 @@ enum permissions : uint64_t { */ p_use_soundboard = 0x40000000000, + /** + * @brief Allows for creating emojis, stickers, and soundboard sounds, and editing and deleting those created by the current user. + * + * @note Not yet available to developers. + * @see https://discord.com/developers/docs/change-log#clarification-on-permission-splits-for-expressions-and-events + */ + p_create_guild_expressions = 0x80000000000, + + /** + * @brief Allows for creating scheduled events, and editing and deleting those created by the current user. + * + * @note Not yet available to developers. + * @see https://discord.com/developers/docs/change-log#clarification-on-permission-splits-for-expressions-and-events + */ + p_create_events = 0x0000100000000000, + /** * @brief Allows the usage of custom soundboard sounds from other servers. */ @@ -265,6 +281,24 @@ enum permissions : uint64_t { * @brief Allows use of Clyde AI. */ p_use_clyde_ai = 0x0000800000000000, + + /** + * @brief Allows sending polls + */ + p_send_polls = 0x0002000000000000, + + /** + * @brief Allows user-installed apps to send public responses. + * When disabled, users will still be allowed to use their apps but the responses will be ephemeral. + * + * @note This only applies to apps not also installed to the server. + */ + p_use_external_apps = 0x0004000000000000, + + /** + * @brief Allows pinning and unpinning messages + */ + p_pin_messages = 0x0008000000000000, }; /** @@ -456,4 +490,4 @@ class DPP_EXPORT permission { } }; -} // namespace dpp +} diff --git a/3rdParty/dpp/presence.h b/3rdParty/dpp/presence.h index e2089219e1..0a1e2ce5dd 100644 --- a/3rdParty/dpp/presence.h +++ b/3rdParty/dpp/presence.h @@ -595,4 +595,4 @@ class DPP_EXPORT presence : public json_interface { */ typedef std::unordered_map presence_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/prune.h b/3rdParty/dpp/prune.h index 92fb5a933a..7cdfb13e21 100644 --- a/3rdParty/dpp/prune.h +++ b/3rdParty/dpp/prune.h @@ -71,10 +71,10 @@ struct DPP_EXPORT prune : public json_interface { /** * @brief Build JSON from this object. * - * @param with_prune_count True if the prune count boolean is to be set in the built JSON + * @param with_id True if the prune count boolean is to be set in the built JSON * @return The JSON of the prune object */ json to_json(bool with_id = false) const; // Intentional shadow of json_interface, mostly present for documentation }; -} // namespace dpp +} diff --git a/3rdParty/dpp/queues.h b/3rdParty/dpp/queues.h index b638ad248b..08d5d17b27 100644 --- a/3rdParty/dpp/queues.h +++ b/3rdParty/dpp/queues.h @@ -25,18 +25,20 @@ #include #include #include -#include #include +#include #include #include -#include +#include +#include +#include namespace dpp { /** * @brief Error values. Most of these are currently unused in https_client. */ -enum http_error { +enum http_error : uint8_t { /** * @brief Request successful. */ @@ -170,8 +172,11 @@ struct DPP_EXPORT http_request_completion_t { * @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. + * code which means they execute in a separate thread, results for the requests going + * into a dpp::thread_pool. Completion events may not arrive in order depending on if + * one request takes longer than another. Using the callbacks or using coroutines + * correctly ensures that the order they arrive in the queue does not negatively affect + * your code. */ typedef std::function http_completion_event; @@ -230,6 +235,28 @@ class DPP_EXPORT http_request { * @brief True for requests that are not going to discord (rate limits code skipped). */ bool non_discord; + + /** + * @brief HTTPS client + */ + std::unique_ptr cli; + + /** + * @brief Mutex for this_captured_signal and this_captured + */ + std::mutex this_captured_mutex; + + /** + * @brief Condition variable to signal this is no longer captured by an ongoing lambda + */ + std::condition_variable this_captured_signal; + + + /** + * @brief True if this is currently captured in a lambda and cannot be deleted + */ + bool this_captured{false}; + public: /** * @brief Endpoint name @@ -348,12 +375,22 @@ class DPP_EXPORT http_request { /** * @brief Execute the HTTP request and mark the request complete. + * @param processor Request concurrency queue this request was created by * @param owner creating cluster */ - http_request_completion_t run(class cluster* owner); + http_request_completion_t run(class request_concurrency_queue* processor, class cluster* owner); - /** @brief Returns true if the request is complete */ + /** + * @brief Returns true if the request is complete + * @return True if completed + * */ bool is_completed(); + + /** + * @brief Get the HTTPS client used to perform this request, or nullptr if there is none + * @return https_client object + */ + https_client* get_client() const; }; /** @@ -389,27 +426,32 @@ struct DPP_EXPORT bucket_t { /** - * @brief Represents a thread in the thread pool handling requests to HTTP(S) servers. - * There are several of these, the total defined by a constant in queues.cpp, and each + * @brief Represents a timer instance in a pool handling requests to HTTP(S) servers. + * There are several of these, the total defined by a constant in cluster.cpp, and each * one will always receive requests for the same rate limit bucket based on its endpoint * portion of the url. This makes rate limit handling reliable and easy to manage. - * Each of these also has its own mutex, so that requests are less likely to block while - * waiting for internal containers to be usable. + * Each of these also has its own mutex, making it thread safe to call and use these + * from anywhere in the code. */ -class DPP_EXPORT in_thread { -private: +class DPP_EXPORT request_concurrency_queue { +public: + /** + * @brief Queue index + */ + int in_index{0}; + /** * @brief True if ending. */ - bool terminating; + std::atomic terminating; /** - * @brief Request queue that owns this in_thread. + * @brief Request queue that owns this request_concurrency_queue. */ class request_queue* requests; /** - * @brief The cluster that owns this in_thread. + * @brief The cluster that owns this request_concurrency_queue. */ class cluster* creator; @@ -419,53 +461,62 @@ class DPP_EXPORT in_thread { std::shared_mutex in_mutex; /** - * @brief Inbound queue thread. + * @brief Inbound queue timer. The timer is called every second, + * and when it wakes up it checks for requests pending to be sent in the queue. + * If there are any requests and we are not waiting on rate limit, it will send them, + * else it will wait for the rate limit to expire. */ - std::thread* in_thr; + dpp::timer in_timer; /** - * @brief Inbound queue condition, signalled when there are requests to fulfill. + * @brief Rate-limit bucket counters. */ - std::condition_variable in_ready; + std::map buckets; /** - * @brief Rate-limit bucket counters. + * @brief Queue of requests to be made. Sorted by http_request::endpoint. */ - std::map buckets; + std::vector> requests_in; /** - * @brief Queue of requests to be made. + * @brief Requests to remove after a set amount of time has passed */ - std::map> requests_in; + std::vector> removals; /** - * @brief Inbound queue thread loop. - * @param index Thread index + * @brief Timer callback + * @param index Index ID for this timer */ - void in_loop(uint32_t index); -public: + void tick_and_deliver_requests(uint32_t index); + /** - * @brief Construct a new in thread object + * @brief Construct a new concurrency queue object * * @param owner Owning cluster * @param req_q Owning request queue - * @param index Thread index number + * @param index Queue index number, uniquely identifies this queue for hashing */ - in_thread(class cluster* owner, class request_queue* req_q, uint32_t index); + request_concurrency_queue(class cluster* owner, class request_queue* req_q, uint32_t index); /** - * @brief Destroy the in thread object - * This will end the thread that is owned by this object by joining it. + * @brief Destroy the concurrency queue object + * This will stop the timer. */ - ~in_thread(); + ~request_concurrency_queue(); /** - * @brief Post a http_request to this thread. + * @brief Flags the queue as terminating + * This will set the internal atomic bool that indicates this queue is to accept no more requests + */ + void terminate(); + + /** + * @brief Post a http_request to this queue. * * @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); }; /** @@ -474,22 +525,24 @@ class DPP_EXPORT in_thread { * * It ensures asynchronous delivery of events and queueing of requests. * - * It will spawn two threads, one to make outbound HTTP requests and push the returned - * results into a queue, and the second to call the callback methods with these results. - * They are separated so that if the user decides to take a long time processing a reply - * in their callback it won't affect when other requests are sent, and if a HTTP request - * takes a long time due to latency, it won't hold up user processing. + * It will spawn multiple timers to make outbound HTTP requests and then call the callbacks + * of those requests on completion within the dpp::thread_pool for the cluster. + * If the user decides to take a long time processing a reply in their callback it won't affect + * when other requests are sent, and if a HTTP request takes a long time due to latency, it won't + * hold up user processing. * * There are usually two request_queue objects in each dpp::cluster, one of which is used * internally for the various REST methods to Discord such as sending messages, and the other - * used to support user REST calls via dpp::cluster::request(). + * used to support user REST calls via dpp::cluster::request(). They are separated so that the + * one for user requests can be specifically configured to never ever send the Discord token + * unless it is explicitly placed into the request, for security reasons. */ class DPP_EXPORT request_queue { -protected: +public: /** - * @brief Required so in_thread can access these member variables + * @brief Required so request_concurrency_queue can access these member variables */ - friend class in_thread; + friend class request_concurrency_queue; /** * @brief The cluster that owns this request_queue @@ -497,120 +550,107 @@ class DPP_EXPORT request_queue { class cluster* creator; /** - * @brief Outbound queue mutex thread safety - */ - std::shared_mutex out_mutex; - - /** - * @brief Outbound queue thread - * Note that although there are many 'in queues', which handle the HTTP requests, - * there is only ever one 'out queue' which dispatches the results to the caller. - * This is to simplify thread management in bots that use the library, as less mutexing - * and thread safety boilerplate is required. - */ - std::thread* out_thread; - - /** - * @brief Outbound queue condition. - * Signalled when there are requests completed to call callbacks for. + * @brief A completed request. Contains both the request and the response */ - std::condition_variable out_ready; + struct completed_request { + /** + * @brief Request sent + */ + std::unique_ptr request; - /** - * @brief Completed requests queue - */ - std::queue> responses_out; + /** + * @brief Response to the request + */ + std::unique_ptr response; + }; /** - * @brief A vector of inbound request threads forming a pool. - * There are a set number of these defined by a constant in queues.cpp. A request is always placed + * @brief A vector of timers forming a pool. + * + * There are a set number of these defined by a constant in cluster.cpp. A request is always placed * on the same element in this vector, based upon its url, so that two conditions are satisfied: - * 1) Any requests for the same ratelimit bucket are handled by the same thread in the pool so that + * + * 1) Any requests for the same ratelimit bucket are handled by the same concurrency queue in the pool so that * they do not create unnecessary 429 errors, * 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; - - /** - * @brief Completed requests to delete + * A global ratelimit event pauses all timers in the pool. These are few and far between. */ - std::multimap> responses_to_delete; + std::vector> requests_in; /** - * @brief Set to true if the threads should terminate + * @brief Set to true if the timers should terminate. + * When this is set to true no further requests are accepted to the queues. */ - bool terminating; + std::atomic terminating; /** - * @brief True if globally rate limited - makes the entire request thread wait + * @brief True if globally rate limited + * + * When globally rate limited the concurrency queues associated with this request queue + * will not process any requests in their timers until the global rate limit expires. */ bool globally_ratelimited; /** - * @brief How many seconds we are globally rate limited for + * @brief When we are globally rate limited until (unix epoch) * - * @note Only if globally_ratelimited is true. + * @note Only valid if globally_rate limited is true. If we are globally rate limited, + * queues in this class will not process requests until the current unix epoch time + * is greater than this time. */ - uint64_t globally_limited_for; + time_t globally_limited_until; /** - * @brief Number of request threads in the thread pool + * @brief Number of request queues in the pool. This is the direct size of the requests_in + * vector. */ - uint32_t in_thread_pool_size; - - /** - * @brief Outbound queue thread loop - */ - void out_loop(); -public: + uint32_t in_queue_pool_size; /** * @brief constructor * @param owner The creating cluster. - * @param request_threads The number of http request threads to allocate to the threadpool. - * By default eight threads are allocated. - * Side effects: Creates threads for the queue + * @param request_concurrency The number of http request queues to allocate. + * Each request queue is a dpp::timer which ticks every second looking for new + * requests to run. The timer will hold back requests if we are waiting as to comply + * with rate limits. Adding a request to this class will cause the queue it is placed in + * to run immediately but this cannot override rate limits. + * By default eight concurrency queues are allocated. + * Side effects: Creates timers for the queue */ - request_queue(class cluster* owner, uint32_t request_threads = 8); + request_queue(class cluster* owner, uint32_t request_concurrency = 8); /** - * @brief Add more request threads to the library at runtime. - * @note You should do this at a quiet time when there are few requests happening. - * This will reorganise the hashing used to place requests into the thread pool so if you do - * this while the bot is busy there is a small chance of receiving "429 rate limited" errors. - * @param request_threads Number of threads to add. It is not possible to scale down at runtime. - * @return reference to self - */ - request_queue& add_request_threads(uint32_t request_threads); - - /** - * @brief Get the request thread count - * @return uint32_t number of request threads that are active + * @brief Get the request queue concurrency count + * @return uint32_t number of request queues that are active */ - uint32_t get_request_thread_count() const; + uint32_t get_request_queue_count() const; /** * @brief Destroy the request queue object. - * Side effects: Joins and deletes queue threads + * Side effects: Ends and deletes concurrency timers */ ~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 * @return true if globally rate limited */ bool is_globally_ratelimited() const; + + /** + * @brief Returns the number of active requests on this queue + * @return Total number of active requests + */ + size_t get_active_request_count() const; }; -} // namespace dpp +} diff --git a/3rdParty/dpp/restrequest.h b/3rdParty/dpp/restrequest.h index 0fecee2b0f..4c62b8c028 100644 --- a/3rdParty/dpp/restrequest.h +++ b/3rdParty/dpp/restrequest.h @@ -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) * @@ -172,6 +173,7 @@ template<> inline void rest_request_list(dpp::cluster* c, const cha } }); } + /** * @brief Templated REST request helper to save on typing (for returned lists, specialised for bans) * @@ -201,6 +203,7 @@ template<> inline void rest_request_list(dpp::cluster* c, const char* basep } }); } + /** * @brief Templated REST request helper to save on typing (for returned lists, specialised for sticker packs) * @@ -232,6 +235,36 @@ template<> inline void rest_request_list(dpp::cluster* c, const ch }); } +/** + * @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) * @@ -260,5 +293,4 @@ template inline void rest_request_vector(dpp::cluster* c, const char* b }); } - -} // namespace dpp +} diff --git a/3rdParty/dpp/restresults.h b/3rdParty/dpp/restresults.h index 8abb408929..60e63b631c 100644 --- a/3rdParty/dpp/restresults.h +++ b/3rdParty/dpp/restresults.h @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -53,6 +52,11 @@ namespace dpp { */ typedef std::map shard_list; +/** + * @brief List of shards awaiting reconnection, by id with earliest possible reconnect time + */ +typedef std::map reconnect_list; + /** * @brief Represents the various information from the 'get gateway bot' api call */ @@ -131,6 +135,7 @@ typedef std::variant< confirmation, message, message_map, + message_pin_map, user, user_identified, user_map, @@ -156,6 +161,7 @@ typedef std::variant< ban_map, voiceregion, voiceregion_map, + voicestate, integration, integration_map, webhook, @@ -220,7 +226,7 @@ struct DPP_EXPORT error_detail { /** * @brief Object field index */ - int index = 0; + DPP_DEPRECATED("index is unused and will be removed in a future version") int index = 0; }; /** @@ -334,4 +340,4 @@ typedef std::function command_completion_e * @brief Automatically JSON encoded HTTP result */ typedef std::function json_encode_t; -} // namespace dpp +} diff --git a/3rdParty/dpp/role.h b/3rdParty/dpp/role.h index 3d25072ee6..2251e5e056 100644 --- a/3rdParty/dpp/role.h +++ b/3rdParty/dpp/role.h @@ -30,21 +30,51 @@ 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 (if the role is pinned in the user listing) - r_managed = 0b00000010, //!< Managed role (introduced by a bot or application) - r_mentionable = 0b00000100, //!< Whether this role is mentionable with a ping - r_premium_subscriber = 0b00001000, //!< Whether this is the guild's booster role - r_available_for_purchase = 0b00010000, //!< Whether the role is available for purchase - r_guild_connections = 0b00100000, //!< Whether the role is a guild's linked role - r_in_prompt = 0b01000000, //!< Whether the role can be selected by members in an onboarding prompt + /** + * @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. @@ -75,31 +105,65 @@ class DPP_EXPORT role : public managed, public json_interface { * Between 1 and 100 characters. */ std::string name{}; + /** * @brief 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{0}; - /** Role position */ + + /** + * @brief Role position. + * + * @warning multiple roles can have the same position number. + * As a result, comparing roles by position alone can lead to subtle bugs when checking for role hierarchy. + * The recommended and correct way to compare for roles in the hierarchy is using the comparison operators on the role objects themselves. + */ uint8_t position{0}; - /** Role permissions bitmask values from dpp::permissions */ + + /** + * @brief Role permissions bitmask values from dpp::permissions. + */ permission permissions{}; - /** Role flags from dpp::role_flags */ + + /** + * @brief Role flags from dpp::role_flags + */ uint8_t flags{0}; - /** Integration id if any (e.g. role is a bot's role created when it was invited) */ + + /** + * @brief 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) */ + + /** + * @brief Bot id if any. + * (e.g. role is a bot's role created when it was invited) + */ snowflake bot_id{}; - /** The id of the role's subscription sku and listing */ + + /** + * @brief The id of the role's subscription sku and listing. + */ snowflake subscription_listing_id{}; - /** The unicode emoji used for the role's icon, can be an empty string */ + + /** + * @brief The unicode emoji used for the role's icon. + * + * @note This can be an empty string. + */ std::string unicode_emoji{}; - /** The role icon */ + + /** + * @brief The role icon. + */ utility::icon icon{}; /** @@ -148,7 +212,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 @@ -158,7 +222,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(). @@ -167,7 +231,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. @@ -176,7 +240,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 @@ -184,7 +248,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 @@ -192,7 +256,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 @@ -200,7 +264,7 @@ 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 @@ -219,14 +283,14 @@ class DPP_EXPORT role : public managed, public json_interface { role& fill_from_json(snowflake guild_id, nlohmann::json* j); /** - * @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 url 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, * otherwise the default sized icon is returned. @@ -236,7 +300,7 @@ class DPP_EXPORT role : public managed, public json_interface { std::string get_icon_url(uint16_t size = 0, const image_type format = i_png) const; /** - * @brief Load a role icon + * @brief Load a role icon. * * @param image_blob Image binary data * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. @@ -245,9 +309,10 @@ class DPP_EXPORT role : public managed, public json_interface { role& load_image(std::string_view image_blob, const image_type type); /** - * @brief Load a role icon + * @brief Load a role icon. * - * @param image_blob Image binary data + * @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 */ @@ -260,8 +325,12 @@ 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) { + if (lhs.position == rhs.position) { + // the higher the IDs are, the lower the position + return lhs.id > rhs.id; + } + return lhs.position < rhs.position; } @@ -272,9 +341,8 @@ 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) - { - return lhs.position > rhs.position; + friend inline bool operator> (const role& lhs, const role& rhs) { + return !(lhs < rhs); } /** @@ -283,9 +351,8 @@ 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 - { - return this->position == other.position; + inline bool operator== (const role& other) const { + return this->id == other.id; } /** @@ -294,358 +361,489 @@ 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 - { - return this->position != other.position; + inline bool operator!= (const role& other) const { + return this->id != other.id; } /** - * @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 + * @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 + * @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 + * @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 + * @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. */ bool has_moderate_members() const; + /** * @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 create guild expressions permission. + * + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * + * @note Not yet available to developers. + * @return bool True if user has the create guild expressions permission or is administrator. + */ + bool has_create_guild_expressions() const; + + /** + * @brief True if has the create events permission. + * + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * + * @note Not yet available to developers. + * @return bool True if user has the create events permission or is administrator. + */ + bool has_create_events() 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. @@ -653,7 +851,35 @@ class DPP_EXPORT role : public managed, public json_interface { bool has_use_clyde_ai() const; /** - * @brief Get guild members who have this role + * @brief True if has the send polls 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 polls permission or is administrator. + */ + bool has_send_polls() const; + + /** + * @brief True if has the use external apps 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 apps permission or is administrator. + */ + bool has_use_external_apps() const; + + /** + * @brief True if has permission to use pin messages. + * + * @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 Pin Messages permission or is administrator. + */ + bool has_pin_messages() 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 */ @@ -663,17 +889,51 @@ 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, }; /** @@ -698,15 +958,38 @@ class DPP_EXPORT application_role_connection_metadata : public json_interface name_localizations; //!< Translations of the name - std::string description; //!< Description of the metadata field (1-200 characters) - std::map description_localizations; //!< Translations of the description + /** + * @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; /** - * Constructor + * @brief Translations of the name. + */ + 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(); @@ -720,7 +1003,8 @@ class DPP_EXPORT application_role_connection : public json_interface; - /** Fill this record from json. + /** + * @brief Fill this record from json. * @param j The json to fill this record from * @return Reference to self */ @@ -735,23 +1019,38 @@ class DPP_EXPORT application_role_connection : public json_interface 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 + /** + * @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; /** - * Constructor + * @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 */ 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; -} // namespace dpp +} diff --git a/3rdParty/dpp/scheduled_event.h b/3rdParty/dpp/scheduled_event.h index 48fea98b47..c234fc73d6 100644 --- a/3rdParty/dpp/scheduled_event.h +++ b/3rdParty/dpp/scheduled_event.h @@ -31,69 +31,94 @@ 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 { protected: @@ -115,29 +140,100 @@ 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; diff --git a/3rdParty/dpp/signature_verifier.h b/3rdParty/dpp/signature_verifier.h new file mode 100644 index 0000000000..3bc1aec36a --- /dev/null +++ b/3rdParty/dpp/signature_verifier.h @@ -0,0 +1,53 @@ +/************************************************************************************ + * + * 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 + +namespace dpp { + +/** + * @brief Verifies signatures on incoming webhooks using OpenSSL + */ +class signature_verifier { +public: + /** + * @brief Constructor initializes the OpenSSL context and public key buffer + */ + signature_verifier(); + + /** + * @brief Verifies the signature with the provided public key, timestamp, body, and signature + * @param timestamp The timestamp of the request + * @param body The body of the request + * @param signature The hex-encoded signature to verify + * @param public_key_hex The hex-encoded public key + * @return true if the signature is valid, false otherwise + */ + bool verify_signature(const std::string& timestamp, const std::string& body, const std::string& signature, const std::string& public_key_hex); + +}; + +} + diff --git a/3rdParty/dpp/sku.h b/3rdParty/dpp/sku.h index 1c4b8bd4e7..59c149c89a 100644 --- a/3rdParty/dpp/sku.h +++ b/3rdParty/dpp/sku.h @@ -34,6 +34,14 @@ 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 */ @@ -91,7 +99,7 @@ class DPP_EXPORT sku : public managed, public json_interface { /** * @brief The type of SKU. */ - sku_type type = sku_type::SUBSCRIPTION; + sku_type type{sku_type::SUBSCRIPTION}; /** * @brief ID of the parent application @@ -121,7 +129,13 @@ class DPP_EXPORT sku : public managed, public json_interface { /** * @brief Construct a new SKU object with all data required. * - * @param id SKU id. + * @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); @@ -159,4 +173,4 @@ class DPP_EXPORT sku : public managed, public json_interface { */ typedef std::unordered_map sku_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/snowflake.h b/3rdParty/dpp/snowflake.h index 20e135d794..a675964ebe 100644 --- a/3rdParty/dpp/snowflake.h +++ b/3rdParty/dpp/snowflake.h @@ -26,6 +26,10 @@ #include #include +#ifdef DPP_FORMATTERS +#include +#endif + /** * @brief The main namespace for D++ functions. classes and types */ @@ -265,7 +269,7 @@ class DPP_EXPORT snowflake final { } }; -} // namespace dpp +} template<> struct std::hash @@ -281,3 +285,23 @@ struct std::hash 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 7e2ed919dc..03962adbf0 100644 --- a/3rdParty/dpp/socket.h +++ b/3rdParty/dpp/socket.h @@ -1,19 +1,44 @@ +/************************************************************************************ + * + * 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 -#endif +#include +#include + +#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; -} // namespace dpp +/** + * @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 /** @@ -28,3 +53,135 @@ namespace dpp */ #define INVALID_SOCKET ~0 #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); +}; + +enum raii_socket_type { + rst_udp, + rst_tcp, + +}; + +/** + * @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(raii_socket_type type = rst_udp); + + /** + * @brief Convert an established fd to an raii_socket + * @param plain_fd + */ + raii_socket(socket plain_fd); + + /** + * @brief Non-copyable + */ + raii_socket(raii_socket&) = delete; + + /** + * @brief Non-movable + */ + raii_socket(raii_socket&&) = delete; + + /** + * @brief Sets the value of a socket option. + * @tparam T type to set option for + * @param level The level at which to change the socket options + * @param name The option to change the value of + * @param value The value to set + * @return True if set successfully + */ + template bool set_option(int level, int name, T value); + + /** + * @brief Bind socket to IP/port + * @param address address to bind to + * @return true on success + */ + bool bind(address_t address); + + /** + * @brief Listen on previously bound port + * @return true on success + */ + bool listen(); + + /** + * @brief Accept a pending connection on listening socket + * @return new connection file descriptor + */ + socket accept(); + + /** + * @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(); +}; + +extern template DPP_EXPORT bool raii_socket::set_option(int level, int name, int value); + + +} diff --git a/3rdParty/dpp/socket_listener.h b/3rdParty/dpp/socket_listener.h new file mode 100644 index 0000000000..040a8e708b --- /dev/null +++ b/3rdParty/dpp/socket_listener.h @@ -0,0 +1,154 @@ +/************************************************************************************ + * + * 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 +#include +#include +#include + +namespace dpp { + +enum socket_listener_type : uint8_t { + li_plaintext, + li_ssl, +}; + +/** + * @brief Listens on a TCP socket for new connections, and whenever a new connection is + * received, accept it and spawn a new connection of type T. + * @tparam T type for socket connection, must be derived from ssl_connection + */ +template>> +struct socket_listener { + /** + * @brief The listening socket for incoming connections + */ + raii_socket fd; + + /** + * @brief Active connections for the server of type T + */ + std::unordered_map> connections; + + /** + * @brief Cluster creator + */ + cluster* creator{nullptr}; + + /** + * @brief True if plain text connections to the server are allowed + */ + bool plaintext{true}; + + /** + * @brief Private key PEM file path, if running an SSL server + */ + std::string private_key_file; + + /** + * @brief Public key PEM file path, if running an SSL server + */ + std::string public_key_file; + + /** + * @brief Event to handle socket removal from the connection map + */ + event_handle close_event; + + /** + * @brief Socket events for listen socket in the socket engine + */ + socket_events events; + + /** + * @brief Create a new socket listener (TCP server) + * @param owner Owning cluster + * @param address IP address to bind the listening socket to, use 0.0.0.0 to bind all interfaces + * @param port Port number to bind the listening socket to + * @param type Type of server, plaintext or SSL + * @param private_key For SSL servers, a path to the PEM private key file + * @param public_key For SSL servers, a path to the PEM public key file + * @throws connection_exception on failure to bind or listen to the port/interface + */ + socket_listener(cluster* owner, const std::string_view address, uint16_t port, socket_listener_type type = li_plaintext, const std::string& private_key = "", const std::string& public_key = "") + : fd(rst_tcp), creator(owner), plaintext(type == li_plaintext), private_key_file(private_key), public_key_file(public_key) + { + fd.set_option(SOL_SOCKET, SO_REUSEADDR, 1); + if (!fd.bind(address_t(address, port))) { + // error + throw dpp::connection_exception("Could not bind to " + std::string(address) + ":" + std::to_string(port)); + } + if (!fd.listen()) { + // error + throw dpp::connection_exception("Could not listen for connections on " + std::string(address) + ":" + std::to_string(port)); + } + events = dpp::socket_events( + fd.fd, + WANT_READ | WANT_ERROR, + [this](socket sfd, const struct socket_events &e) { + handle_accept(sfd, e); + }, + [](socket, const struct socket_events&) { }, + [](socket, const struct socket_events&, int) { } + ); + owner->socketengine->register_socket(events); + + close_event = creator->on_socket_close([this](const socket_close_t& event) { + connections.erase(event.fd); + }); + } + + /** + * @brief Destructor, detaches on_socket_close event + */ + ~socket_listener() { + creator->on_socket_close.detach(close_event); + } + + /** + * @brief Handle a new incoming socket with accept() + * Accepts a new connection, and calls emplace() if valid + * @param sfd File descriptor for listening socket + * @param e socket events for the listening socket + */ + virtual void handle_accept(socket sfd, const struct socket_events &e) { + socket new_fd{fd.accept()}; + if (new_fd >= 0) { + emplace(new_fd); + } + } + + /** + * @brief Emplace a new connection into the connection map for the server. + * This is a factory function which must be implemented by the deriving class + * @param newfd File descriptor for new connection + */ + virtual void emplace(socket newfd) = 0; +}; + +} diff --git a/3rdParty/dpp/socketengine.h b/3rdParty/dpp/socketengine.h new file mode 100644 index 0000000000..931249d5c5 --- /dev/null +++ b/3rdParty/dpp/socketengine.h @@ -0,0 +1,332 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Types of IO events a socket may subscribe to. + */ +enum socket_event_flags : uint8_t { + /** + * @brief Socket wants to receive events when it can be read from. + * This is provided by the underlying implementation. + */ + WANT_READ = 1, + /** + * @brief Socket wants to receive events when it can be written to. + * This is provided by the underlying implementation, and will be + * a one-off event. If you want to receive ongoing write events you + * must re-request this event type each time. + */ + WANT_WRITE = 2, + /** + * @brief Socket wants to receive events that indicate an error condition. + * Note that EOF (graceful close) is not an error condition and is indicated + * by errno being 0 and ::read() returning 0. + */ + WANT_ERROR = 4, + /** + * @brief Socket should be removed as soon as is safe to do so. Generally, this is + * after the current iteration through the active event list. + */ + WANT_DELETION = 8, +}; + +/** + * @brief Read ready event + */ +using socket_read_event = std::function; + +/** + * @brief Write ready event + */ +using socket_write_event = std::function; + +/** + * @brief Error event + */ +using socket_error_event = std::function; + +/** + * @brief Contains statistics about the IO loop + */ +struct DPP_EXPORT socket_stats { + /** + * @brief Number of reads since startup + */ + uint64_t reads{0}; + + /** + * @brief Number of writes since startup + */ + uint64_t writes{0}; + + /** + * @brief Number of errors since startup + */ + uint64_t errors{0}; + + /** + * @brief Number of updates to file descriptors + */ + uint64_t updates{0}; + + /** + * @brief Number of deletions of file descriptors + */ + uint64_t deletions{0}; + + /** + * @brief Number of loop iterations since startup + */ + uint64_t iterations{0}; + + /** + * @brief Number of currently active file descriptors + */ + uint64_t active_fds{0}; + + /** + * @brief Socket engine type + */ + std::string_view engine_type; +}; + +/** + * @brief Represents an active socket event set in the socket engine. + * + * An event set contains a file descriptor, a set of event handler callbacks, and + * a set of bitmask flags which indicate which events it wants to receive. + * It is possible to quickly toggle event types on or off, as it is not always necessary + * or desired to receive all events all the time, in fact doing so can cause an event + * storm which will consume 100% CPU (e.g. if you request to receive write events all + * the time). + */ +struct DPP_EXPORT socket_events { + /** + * @brief File descriptor + * + * This should be a valid file descriptor created via ::socket(). + */ + dpp::socket fd{INVALID_SOCKET}; + + /** + * @brief Flag bit mask of values from dpp::socket_event_flags + */ + uint8_t flags{0}; + + /** + * @brief Read ready event + * @note This function will be called from a different thread to that + * which adds the event set to the socket engine. + */ + socket_read_event on_read{}; + + /** + * @brief Write ready event + * @note This function will be called from a different thread to that + * which adds the event set to the socket engine. + */ + socket_write_event on_write{}; + + /** + * @brief Error event + * @note This function will be called from a different thread to that + * which adds the event set to the socket engine. + */ + socket_error_event on_error{}; + + /** + * @brief Construct a new socket_events + * @param socket_fd file descriptor + * @param _flags initial flags bitmask + * @param read_event read ready event + * @param write_event write ready event + * @param error_event error event + */ + socket_events(dpp::socket socket_fd, uint8_t _flags, const socket_read_event& read_event, const socket_write_event& write_event = {}, const socket_error_event& error_event = {}) + : fd(socket_fd), flags(_flags), on_read(read_event), on_write(write_event), on_error(error_event) { } + + /** + * @brief Default constructor + */ + socket_events() = default; +}; + +/** + * @brief Container of event sets keyed by socket file descriptor + */ +using socket_container = std::unordered_map>; + +/** + * @brief This is the base class for socket engines. + * The actual implementation is OS specific and the correct implementation is detected by + * CMake. It is then compiled specifically into DPP so only one implementation can exist + * in the implementation. All implementations should behave identically to the user, abstracting + * out implementation-specific behaviours (e.g. difference between edge and level triggered + * event mechanisms etc). + */ +struct DPP_EXPORT socket_engine_base { + + /** + * @brief Owning cluster + */ + class cluster* owner{nullptr}; + + /** + * @brief Default constructor + * @param creator Owning cluster + */ + socket_engine_base(class cluster* creator); + + /** + * @brief Non-copyable + */ + socket_engine_base(const socket_engine_base&) = delete; + + /** + * @brief Non-copyable + */ + socket_engine_base(socket_engine_base&&) = delete; + + /** + * @brief Non-movable + */ + socket_engine_base& operator=(const socket_engine_base&) = delete; + + /** + * @brief Non-movable + */ + socket_engine_base& operator=(socket_engine_base&&) = delete; + + /** + * @brief Default destructor + */ + virtual ~socket_engine_base(); + + /** + * @brief Should be called repeatedly in a loop. + * Will run for a maximum of 1 second. + */ + virtual void process_events() = 0; + + /** + * @brief Register a new socket with the socket engine + * @param e Socket events + * @return true if socket was added + */ + virtual bool register_socket(const socket_events& e); + + /** + * @brief Update an existing socket in the socket engine + * @param e Socket events + * @return true if socket was updated + */ + virtual bool update_socket(const socket_events& e); + + /** + * @brief Delete a socket from the socket engine + * @note This will not remove the socket immediately. It will set the + * WANT_DELETION flag causing it to be removed as soon as is safe to do so + * (once all events associated with it are completed). + * @param e File descriptor + * @return true if socket was queued for deletion + */ + bool delete_socket(dpp::socket fd); + + /** + * @brief Iterate through the list of sockets and remove any + * with WANT_DELETION set. This will also call implementation-specific + * remove_socket() on each entry to be removed. + */ + void prune(); + + /** + * @brief Merge new flags in with the given file descriptor + * @param fd file descriptor + * @param extra_flags extra flags to add + */ + void inplace_modify_fd(dpp::socket fd, uint8_t extra_flags); + + /** + * @brief Get statistics for socket engine + * @return socket stats + */ + const socket_stats& get_stats() const; + +protected: + + /** + * @brief Mutex for fds + */ + std::shared_mutex fds_mutex; + + /** + * @brief File descriptors, and their states + */ + socket_container fds; + + /** + * @brief Socket engine statistics + */ + socket_stats stats{}; + + /** + * @brief Find a file descriptors socket events + * @param fd file descriptor + * @return file descriptor or nullptr if doesn't exist + */ + socket_events* get_fd(dpp::socket fd); + + /** + * @brief Called by the prune() function to remove sockets when safe to do so. + * This is normally at the end or before an iteration of the event loop. + * @param fd File descriptor to remove + */ + virtual bool remove_socket(dpp::socket fd); +}; + +/** + * @brief This is implemented by whatever derived form socket_engine takes + * @param creator Creating cluster + */ +DPP_EXPORT std::unique_ptr create_socket_engine(class cluster *creator); + +#ifndef _WIN32 + /** + * @brief Set up a signal handler to be ignored + * @param signal Signal to set. If the signal is already set up with a handler, + * this will do nothing. + */ + void set_signal_handler(int signal); +#endif + +}; diff --git a/3rdParty/dpp/ssl_context.h b/3rdParty/dpp/ssl_context.h new file mode 100644 index 0000000000..26e8e24dcb --- /dev/null +++ b/3rdParty/dpp/ssl_context.h @@ -0,0 +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 +#include +#include +#include + +namespace dpp::detail { + +struct wrapped_ssl_ctx; + +/** + * @brief Generate a new wrapped SSL context. + * If an SSL context already exists for the given port number, it will be returned, else a new one will be + * generated and cached. Contexts with port = 0 will be considered client contexts. There can only be one + * client context at a time and it covers all SSL client connections. There can be many SSL server contexts, + * individual ones can be cached per-port, each with their own loaded SSL private and public key PEM certificate. + * + * @param port Port number. Pass zero to create or get the client context. + * @param private_key Private key PEM pathname for server contexts + * @param public_key Public key PEM pathname for server contexts + * @return wrapped SSL context + */ +DPP_EXPORT wrapped_ssl_ctx* generate_ssl_context(uint16_t port = 0, const std::string &private_key = "", const std::string &public_key = ""); + +/** + * @brief Release an SSL context + * @warning Only do this if you are certain no SSL connections remain that use this context. + * As OpenSSL is a C library it is impossible for us to track this on its behalf. Be careful! + * @param port port number to release + */ +DPP_EXPORT void release_ssl_context(uint16_t port = 0); + +}; diff --git a/3rdParty/dpp/sslclient.h b/3rdParty/dpp/sslclient.h deleted file mode 100644 index 2098c5cf7d..0000000000 --- a/3rdParty/dpp/sslclient.h +++ /dev/null @@ -1,259 +0,0 @@ -/************************************************************************************ - * - * 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 This is an opaque class containing openssl library specific structures. - * We define it this way so that the public facing D++ library doesn't require - * the openssl headers be available to build against it. - */ -class openssl_connection; - -/** - * @brief A callback for socket status - */ -typedef std::function socket_callback_t; - -/** - * @brief A socket notification callback - */ -typedef std::function socket_notification_t; - -/** - * @brief Close a socket - * - * @param sfd Socket to close - * @return false on error, true on success - */ -bool close_socket(dpp::socket sfd); - -/** - * @brief Set a socket to blocking or non-blocking IO - * - * @param sockfd socket to act upon - * @return false on error, true on success - */ -bool set_nonblocking(dpp::socket sockfd, bool non_blocking); - -/** - * @brief Implements a simple non-blocking SSL stream client. - * - * @note although the design is non-blocking the run() method will - * execute in an infinite loop until the socket disconnects. This is intended - * to be run within a std::thread. - */ -class DPP_EXPORT ssl_client -{ -private: - /** - * @brief Clean up resources - */ - void cleanup(); -protected: - /** - * @brief Input buffer received from socket - */ - std::string buffer; - - /** - * @brief Output buffer for sending to socket - */ - std::string obuffer; - - /** - * @brief True if in nonblocking mode. The socket switches to nonblocking mode - * once ReadLoop is called. - */ - bool nonblocking; - - /** - * @brief Raw file descriptor of connection - */ - dpp::socket sfd; - - /** - * @brief Openssl opaque contexts - */ - openssl_connection* ssl; - - /** - * @brief SSL cipher in use - */ - std::string cipher; - - /** - * @brief For timers - */ - time_t last_tick; - - /** - * @brief Hostname connected to - */ - std::string hostname; - - /** - * @brief Port connected to - */ - std::string port; - - /** - * @brief Bytes out - */ - uint64_t bytes_out; - - /** - * @brief Bytes in - */ - uint64_t bytes_in; - - /** - * @brief True for a plain text connection - */ - bool plaintext; - - /** - * @brief True if we are establishing a new connection, false if otherwise. - */ - bool make_new; - - - /** - * @brief Called every second - */ - virtual void one_second_timer(); - - /** - * @brief Start SSL connection and connect to TCP endpoint - * @throw dpp::exception Failed to initialise connection - */ - virtual void connect(); -public: - /** - * @brief Get the bytes out objectGet total bytes sent - * @return uint64_t bytes sent - */ - uint64_t get_bytes_out(); - - /** - * @brief Get total bytes received - * @return uint64_t bytes received - */ - uint64_t get_bytes_in(); - - /** - * @brief Get SSL cipher name - * @return std::string ssl cipher name - */ - std::string get_cipher(); - - /** - * @brief Attaching an additional file descriptor to this function will send notifications when there is data to read. - * - * NOTE: Only hook this if you NEED it as it can increase CPU usage of the thread! - * Returning -1 means that you don't want to be notified. - */ - socket_callback_t custom_readable_fd; - - /** - * @brief Attaching an additional file descriptor to this function will send notifications when you are able to write - * to the socket. - * - * NOTE: Only hook this if you NEED it as it can increase CPU usage of the thread! You should toggle this - * to -1 when you do not have anything to write otherwise it'll keep triggering repeatedly (it is level triggered). - */ - socket_callback_t custom_writeable_fd; - - /** - * @brief This event will be called when you can read from the custom fd - */ - socket_notification_t custom_readable_ready; - - /** - * @brief This event will be called when you can write to a custom fd - */ - socket_notification_t custom_writeable_ready; - - /** - * @brief True if we are keeping the connection alive after it has finished - */ - bool keepalive; - - /** - * @brief Connect to a specified host and port. Throws std::runtime_error on fatal error. - * @param _hostname The hostname to connect to - * @param _port the Port number to connect to - * @param plaintext_downgrade Set to true to connect using plaintext only, without initialising SSL. - * @param reuse Attempt to reuse previous connections for this hostname and port, if available - * Note that no Discord endpoints will function when downgraded. This option is provided only for - * connection to non-Discord addresses such as within dpp::cluster::request(). - * @throw dpp::exception Failed to initialise connection - */ - ssl_client(const std::string &_hostname, const std::string &_port = "443", bool plaintext_downgrade = false, bool reuse = false); - - /** - * @brief Nonblocking I/O loop - * @throw std::exception Any std::exception (or derivative) thrown from read_loop() causes reconnection of the shard - */ - void read_loop(); - - /** - * @brief Destroy the ssl_client object - */ - virtual ~ssl_client(); - - /** - * @brief Handle input from the input buffer. This function will be called until - * all data in the buffer has been processed and the buffer is empty. - * @param buffer the buffer content. Will be modified removing any processed front elements - * @return bool True if the socket should remain connected - */ - virtual bool handle_buffer(std::string &buffer); - - /** - * @brief Write to the output 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); - - /** - * @brief Close socket connection - */ - virtual void close(); - - /** - * @brief Log a message - * @param severity severity of log message - * @param msg Log message to send - */ - virtual void log(dpp::loglevel severity, const std::string &msg) const; -}; - -} // namespace dpp diff --git a/3rdParty/dpp/sslconnection.h b/3rdParty/dpp/sslconnection.h new file mode 100644 index 0000000000..a7273d199f --- /dev/null +++ b/3rdParty/dpp/sslconnection.h @@ -0,0 +1,403 @@ +/************************************************************************************ + * + * 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 +#include +#include +#include + +namespace dpp { + +/** + * @brief This is an opaque class containing openssl library specific structures. + * We define it this way so that the public facing D++ library doesn't require + * the openssl headers be available to build against it. + */ +class openssl_connection; + +/** + * @brief Close a socket + * + * @param sfd Socket to close + * @return false on error, true on success + */ +DPP_EXPORT 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 + */ +DPP_EXPORT bool set_nonblocking(dpp::socket sockfd, bool non_blocking); + +/** + * @brief SSL_read buffer size + * + * You'd think that we would get better performance with a bigger buffer, but SSL frames are 16k each. + * SSL_read in non-blocking mode will only read 16k at a time. There's no point in a bigger buffer as + * it'd go unused. + */ +constexpr uint16_t DPP_BUFSIZE{16 * 1024}; + +/** + * @brief Represents a failed socket system call, e.g. connect() failure + */ +constexpr int ERROR_STATUS{-1}; + +/** + * @brief Maximum number of internal connect() retries on TCP connections + */ +constexpr int MAX_RETRIES{4}; + +/** + * @brief Implements a simple non-blocking SSL stream connection. + */ +class DPP_EXPORT ssl_connection +{ +private: + /** + * @brief Clean up resources + */ + void cleanup(); + + /** + * @brief Mutex for creation of internal SSL pointers by openssl + */ + std::mutex ssl_mutex; + + /** + * @brief Mutex for output buffer + */ + std::mutex out_mutex; + + /** + * @brief Start offset into internal ring buffer for client to server IO + */ + size_t client_to_server_length = 0; + + /** + * @brief Start offset into internal ring buffer for server to client IO + */ + size_t client_to_server_offset = 0; + + /** + * @brief Internal ring buffer for client to server IO + */ + char client_to_server_buffer[DPP_BUFSIZE]; + + /** + * @brief Internal ring buffer for server to client IO + */ + char server_to_client_buffer[DPP_BUFSIZE]; + + /** + * @brief True if this connection is a server inbound connection from accept() + */ + bool is_server = false; + +protected: + /** + * @brief Input buffer received from socket + */ + std::string buffer; + + /** + * @brief Output buffer for sending to socket + */ + std::string obuffer; + + /** + * @brief Raw file descriptor of connection + */ + dpp::socket sfd; + + /** + * @brief Openssl opaque contexts + */ + openssl_connection* ssl; + + /** + * @brief SSL cipher in use + */ + std::string cipher; + + /** + * @brief For timers + */ + time_t last_tick; + + /** + * @brief Start time of connection + */ + time_t start; + + /** + * @brief How many times we retried connect() + */ + uint8_t connect_retries{0}; + + /** + * @brief Hostname connected to + */ + std::string hostname; + + /** + * @brief Port connected to + */ + std::string port; + + /** + * @brief Bytes out + */ + uint64_t bytes_out; + + /** + * @brief Bytes in + */ + uint64_t bytes_in; + + /** + * @brief True for a plain text connection + */ + bool plaintext; + + /** + * @brief True if connection is completed + */ + bool connected{false}; + + /** + * @brief True if tcp connect() succeeded + */ + bool tcp_connect_done{false}; + + /** + * @brief Timer handle for one second timer + */ + timer timer_handle; + + /** + * @brief Unique ID of socket used as a nonce + * You can use this to identify requests vs reply + * if you want. D++ itself only sets this, and does + * not use it in any logic. It starts at 1 and increments + * for each request made. + */ + uint64_t unique_id; + + /** + * @brief Called every second + */ + virtual void one_second_timer(); + + /** + * @brief Start SSL connection and connect to TCP endpoint + * @throw dpp::exception Failed to initialise connection + */ + virtual void connect(); + + /** + * @brief Set this to true to log all IO to debug for this connection. + * This is an internal developer facility. Do not enable it unless you + * need to, as it will be very noisy. + */ + bool raw_trace{false}; + + /** + * @brief If raw_trace is set to true, log a debug message for this connection + * @param message debug message + */ + void do_raw_trace(const std::string& message) const; + + virtual void on_buffer_drained(); + + /** + * @brief Start connecting to a TCP socket. + * This simply calls connect() and checks for error return, as the timeout is now handled in the main + * IO events for the ssl_connection class. + * + * @param sockfd socket descriptor + * @param addr address to connect to + * @param addrlen address length + * @param timeout_ms timeout in milliseconds + * @return int -1 on error, 0 on success just like POSIX connect() + * @throw dpp::connection_exception on failure + */ + int start_connecting(dpp::socket sockfd, const struct sockaddr *addr, socklen_t addrlen); + +public: + /** + * @brief For low-level debugging, calling this function will + * enable low level I/O logging for this connection to the logger. + * This can be very loud, and output a lot of data, so only enable it + * selectively where you need it. + * + * Generally, you won't need this, it is a library development utility. + */ + void enable_raw_tracing(); + + /** + * @brief Get the bytes out objectGet total bytes sent + * @return uint64_t bytes sent + */ + uint64_t get_bytes_out(); + + /** + * @brief Get total bytes received + * @return uint64_t bytes received + */ + uint64_t get_bytes_in(); + + /** + * @brief Every request made has a unique ID. This increments + * for every request, starting at 1. You can use this for statistics, + * or to associate requests and replies in external event loops. + * @return Unique ID + */ + uint64_t get_unique_id() const; + + /** + * @brief Get SSL cipher name + * @return std::string ssl cipher name + */ + std::string get_cipher(); + + /** + * @brief True if we are keeping the connection alive after it has finished + */ + bool keepalive; + + /** + * @brief Owning cluster + */ + class cluster* owner; + + /** + * @brief Private key PEM file path for inbound SSL connections + */ + std::string private_key_file; + + /** + * @brief Public key PEM file path for inbound SSL connections + */ + std::string public_key_file; + + /** + * @brief Connect to a specified host and port. Throws std::runtime_error on fatal error. + * @param creator Creating cluster + * @param _hostname The hostname to connect to + * @param _port the Port number to connect to + * @param plaintext_downgrade Set to true to connect using plaintext only, without initialising SSL. + * @param reuse Attempt to reuse previous connections for this hostname and port, if available + * Note that no Discord endpoints will function when downgraded. This option is provided only for + * connection to non-Discord addresses such as within dpp::cluster::request(). + * @throw dpp::exception Failed to initialise connection + */ + ssl_connection(cluster* creator, const std::string &_hostname, const std::string &_port = "443", bool plaintext_downgrade = false, bool reuse = false); + + /** + * @brief Accept a new connection from listen()/accept() socket + * @param creator Creating cluster + * @param fd Socket file descriptor assigned by accept() + * @param port Port the new fd came from + * @param plaintext_downgrade Set to true to connect using plaintext only, without initialising SSL. + * @param private_key if plaintext_downgrade is set to false, a private key PEM file for SSL connections + * @param public_key if plaintext_downgrade is set to false, a public key PEM file for SSL connections + */ + ssl_connection(cluster* creator, socket fd, uint16_t port, bool plaintext_downgrade = false, const std::string& private_key = "", const std::string& public_key = ""); + + /** + * @brief Set up non blocking I/O and configure on_read, on_write and on_error. + * @throw std::exception Any std::exception (or derivative) thrown from read_loop() indicates setup failed + */ + void read_loop(); + + /** + * @brief Destroy the ssl_connection object + */ + virtual ~ssl_connection(); + + /** + * @brief Handle input from the input buffer. This function will be called until + * all data in the buffer has been processed and the buffer is empty. + * @param buffer the buffer content. Will be modified removing any processed front elements + * @return bool True if the socket should remain connected + */ + virtual bool handle_buffer(std::string &buffer); + + /** + * @brief Write to the output 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. + */ + void socket_write(const std::string_view data); + + /** + * @brief Close socket connection + */ + virtual void close(); + + /** + * @brief Log a message + * @param severity severity of log message + * @param msg Log message to send + */ + virtual void log(dpp::loglevel severity, const std::string &msg) const; + + /** + * @brief Called while SSL handshake is in progress. + * If the handshake completes, the state of the socket is progressed to + * an established state. + * @param ev Socket events for the socket + */ + void complete_handshake(const struct socket_events* ev); + + /** + * @brief Called when the TCP socket has data to read + * @param fd File descriptor + * @param ev Socket events + */ + void on_read(dpp::socket fd, const struct dpp::socket_events& ev); + + /** + * @brief Called when the TCP socket can be written to without blocking + * @param fd File descriptor + * @param e Socket events + */ + void on_write(dpp::socket fd, const struct dpp::socket_events& e); + + /** + * @brief Called when there is an error on the TCP socket + * @param fd File descriptor + * @param error_code Error code + */ + void on_error(dpp::socket fd, const struct dpp::socket_events&, int error_code); +}; + +} diff --git a/3rdParty/dpp/stage_instance.h b/3rdParty/dpp/stage_instance.h index eb1693b79b..bffeac46fa 100644 --- a/3rdParty/dpp/stage_instance.h +++ b/3rdParty/dpp/stage_instance.h @@ -33,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 }; @@ -63,15 +68,29 @@ struct DPP_EXPORT stage_instance : public managed, public json_interface stage_instance_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/stringops.h b/3rdParty/dpp/stringops.h index e24da1cdf9..60a5045a46 100644 --- a/3rdParty/dpp/stringops.h +++ b/3rdParty/dpp/stringops.h @@ -139,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; @@ -217,4 +220,4 @@ template std::string leading_zeroes(T i, size_t width) return stream.str(); } -} // namespace dpp +} diff --git a/3rdParty/dpp/sync.h b/3rdParty/dpp/sync.h deleted file mode 100644 index 240d10cc39..0000000000 --- a/3rdParty/dpp/sync.h +++ /dev/null @@ -1,80 +0,0 @@ -/************************************************************************************ - * - * 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 -#include -#include -#include - -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()); - } - }); - - /* Blocking calling thread until rest request is completed. - * Exceptions encountered on the other thread are re-thrown. - */ - return _f.get(); - } - -} // namespace dpp 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/thread_pool.h b/3rdParty/dpp/thread_pool.h new file mode 100644 index 0000000000..85771b2053 --- /dev/null +++ b/3rdParty/dpp/thread_pool.h @@ -0,0 +1,117 @@ +/************************************************************************************ + * + * 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 A work unit is a lambda executed in the thread pool + */ +using work_unit = std::function; + +/** + * @brief A task within a thread pool. A simple lambda that accepts no parameters and returns void. + */ +struct DPP_EXPORT thread_pool_task { + /** + * @brief Task priority, lower value is higher priority + */ + int priority; + /** + * @brief Work unit to execute as the task + */ + work_unit function; +}; + +/** + * @brief Compares two thread pool tasks by priority + */ +struct DPP_EXPORT thread_pool_task_comparator { + /** + * @brief Compare two tasks + * @param a first task + * @param b second task + * @return true if a > b + */ + bool operator()(const thread_pool_task &a, const thread_pool_task &b) const { + return a.priority > b.priority; + }; +}; + +/** + * @brief A thread pool contains 1 or more worker threads which accept thread_pool_task lambadas + * into a queue, which is processed in-order by whichever thread is free. + */ +struct DPP_EXPORT thread_pool { + + /** + * @brief Threads that comprise the thread pool + */ + std::vector threads; + + /** + * @brief Priority queue of tasks to be executed + */ + std::priority_queue, thread_pool_task_comparator> tasks; + + /** + * @brief Mutex for accessing the priority queue + */ + std::mutex queue_mutex; + + /** + * @brief Condition variable to notify for new tasks to run + */ + std::condition_variable cv; + + /** + * @brief True if the thread pool is due to stop + */ + bool stop{false}; + + /** + * @brief Create a new priority thread pool + * @param creator creating cluster (for logging) + * @param num_threads number of threads in the pool + */ + explicit thread_pool(class cluster* creator, size_t num_threads = std::thread::hardware_concurrency()); + + /** + * @brief Destroy the thread pool + */ + ~thread_pool(); + + /** + * @brief Enqueue a new task to the thread pool + * @param task task to enqueue + */ + void enqueue(thread_pool_task task); +}; + +} \ No newline at end of file diff --git a/3rdParty/dpp/timed_listener.h b/3rdParty/dpp/timed_listener.h index ebe22e3943..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: @@ -92,4 +102,4 @@ template class timed_listene } }; -} // namespace dpp +} diff --git a/3rdParty/dpp/timer.h b/3rdParty/dpp/timer.h index 1fe00c0ca6..6bc5037a7f 100644 --- a/3rdParty/dpp/timer.h +++ b/3rdParty/dpp/timer.h @@ -22,11 +22,13 @@ #pragma once #include -#include +#include #include #include -#include +#include #include +#include +#include #include namespace dpp { @@ -50,35 +52,55 @@ struct DPP_EXPORT timer_t { /** * @brief Timer handle */ - timer handle; + timer handle{0}; + /** * @brief Next timer tick as unix epoch */ - time_t next_tick; + time_t next_tick{0}; + /** * @brief Frequency between ticks */ - uint64_t frequency; + uint64_t frequency{0}; + /** * @brief Lambda to call on tick */ - timer_callback_t on_tick; + timer_callback_t on_tick{}; + /** * @brief Lambda to call on stop (optional) */ - timer_callback_t on_stop; + timer_callback_t on_stop{}; +}; + +/** + * @brief Used to compare two timers next tick times in a priority queue + */ +struct DPP_EXPORT timer_comparator { + /** + * @brief Compare two timers + * @param a first timer + * @param b second timer + * @return returns true if a > b + */ + bool operator()(const timer_t &a, const timer_t &b) const { + return a.next_tick > b.next_tick; + }; }; + /** - * @brief A map of timers, ordered by earliest first so that map::begin() is always the + * @brief A priority timers, ordered by earliest first so that the head is always the * soonest to be due. */ -typedef std::multimap timer_next_t; +typedef std::priority_queue, timer_comparator> timer_next_t; /** - * @brief A map of timers stored by handle + * @brief A set of deleted timer handles */ -typedef std::unordered_map timer_reg_t; +typedef std::set timers_deleted_t; /** * @brief Trigger a timed event once. @@ -87,9 +109,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: /** @@ -120,6 +147,4 @@ class DPP_EXPORT oneshot_timer ~oneshot_timer(); }; - - -} // namespace dpp +} diff --git a/3rdParty/dpp/unicode_emoji.h b/3rdParty/dpp/unicode_emoji.h index 9c9946822e..accb13fe3d 100644 --- a/3rdParty/dpp/unicode_emoji.h +++ b/3rdParty/dpp/unicode_emoji.h @@ -3,9 +3,11 @@ namespace dpp { /** - * The unicode emojis in this namespace are auto-generated from https://raw.githubusercontent.com/ArkinSolomon/discord-emoji-converter/master/emojis.json + * @brief Emoji unicodes. * - * If you want to use this, you have to pull the header in separately. e.g. + * @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 @@ -15,6 +17,7 @@ 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[] = "⚾"; @@ -29,6 +32,7 @@ namespace unicode_emoji { 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[] = "🏏"; @@ -37,23 +41,27 @@ namespace unicode_emoji { 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[] = "🏂🏻"; @@ -593,10 +601,13 @@ namespace unicode_emoji { 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[] = "🪘"; @@ -606,21 +617,25 @@ namespace unicode_emoji { 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 pirate_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[] = "🇦🇱"; @@ -882,17 +897,17 @@ namespace unicode_emoji { inline constexpr const char flag_sj[] = "🇸🇯"; inline constexpr const char flag_ta[] = "🇹🇦"; inline constexpr const char flag_um[] = "🇺🇲"; - inline constexpr const char united_nations[] = "🇺🇳"; 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 blueberries[] = "🫐"; 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[] = "🍑"; @@ -901,27 +916,30 @@ namespace unicode_emoji { 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 olive[] = "🫒"; + inline constexpr const char pea_pod[] = "🫛"; inline constexpr const char broccoli[] = "🥦"; inline constexpr const char leafy_green[] = "🥬"; - inline constexpr const char bell_pepper[] = "🫑"; 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 flatbread[] = "🫓"; inline constexpr const char pretzel[] = "🥨"; inline constexpr const char cheese[] = "🧀"; inline constexpr const char cheese_wedge[] = "🧀"; @@ -939,7 +957,9 @@ namespace unicode_emoji { 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[] = "🥙"; @@ -956,15 +976,20 @@ namespace unicode_emoji { 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[] = "🥠"; @@ -977,7 +1002,9 @@ namespace unicode_emoji { 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[] = "🍮"; @@ -998,6 +1025,7 @@ namespace unicode_emoji { 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[] = "🧃"; @@ -1005,6 +1033,7 @@ namespace unicode_emoji { 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[] = "🥂"; @@ -1025,21 +1054,29 @@ namespace unicode_emoji { 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[] = "🐵"; @@ -1053,26 +1090,31 @@ namespace unicode_emoji { 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 worm[] = "🪱"; inline constexpr const char lady_beetle[] = "🐞"; inline constexpr const char ant[] = "🐜"; inline constexpr const char fly[] = "🪰"; - inline constexpr const char mosquito[] = "🦟"; - inline constexpr const char cockroach[] = "🪳"; 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[] = "🕸️"; @@ -1084,26 +1126,26 @@ namespace unicode_emoji { 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 seal[] = "🦭"; 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 elephant[] = "🐘"; inline constexpr const char mammoth[] = "🦣"; - inline constexpr const char bison[] = "🦬"; + inline constexpr const char elephant[] = "🐘"; inline constexpr const char hippopotamus[] = "🦛"; inline constexpr const char rhino[] = "🦏"; inline constexpr const char rhinoceros[] = "🦏"; @@ -1111,13 +1153,16 @@ namespace unicode_emoji { 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[] = "🦌"; @@ -1128,6 +1173,7 @@ namespace unicode_emoji { 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[] = "🦤"; @@ -1157,29 +1203,32 @@ namespace unicode_emoji { 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 empty_nest[] = "🪹"; 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 wood[] = "🪵"; inline constexpr const char ear_of_rice[] = "🌾"; - inline constexpr const char potted_plant[] = "🪴"; + 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[] = "🌸"; @@ -1190,6 +1239,7 @@ namespace unicode_emoji { 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[] = "🌗"; @@ -1206,16 +1256,21 @@ namespace unicode_emoji { 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[] = "⛅"; @@ -1236,13 +1291,16 @@ namespace unicode_emoji { 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[] = "📱"; @@ -1258,11 +1316,15 @@ namespace unicode_emoji { 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[] = "📹"; @@ -1274,7 +1336,9 @@ namespace unicode_emoji { 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[] = "🎙️"; @@ -1294,6 +1358,7 @@ namespace unicode_emoji { 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[] = "🪔"; @@ -1303,14 +1368,19 @@ namespace unicode_emoji { 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[] = "🪛"; @@ -1326,18 +1396,22 @@ namespace unicode_emoji { 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[] = "⚱️"; @@ -1348,6 +1422,7 @@ namespace unicode_emoji { 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[] = "🔬"; @@ -1380,9 +1455,11 @@ namespace unicode_emoji { 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[] = "🔑"; @@ -1394,6 +1471,7 @@ namespace unicode_emoji { 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[] = "🛌🏼"; @@ -1414,14 +1492,18 @@ namespace unicode_emoji { 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[] = "🪩"; @@ -1481,6 +1563,7 @@ namespace unicode_emoji { 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[] = "🔗"; @@ -1495,8 +1578,10 @@ namespace unicode_emoji { 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[] = "🖌️"; @@ -1510,8 +1595,11 @@ namespace unicode_emoji { 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[] = "😁"; @@ -1524,6 +1612,7 @@ namespace unicode_emoji { 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[] = "🙂"; @@ -1531,11 +1620,14 @@ namespace unicode_emoji { 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[] = "😋"; @@ -1552,33 +1644,45 @@ namespace unicode_emoji { 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[] = "😓"; @@ -1599,19 +1703,23 @@ namespace unicode_emoji { 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[] = "😵‍💫"; @@ -1635,13 +1743,16 @@ namespace unicode_emoji { 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[] = "💀"; @@ -1649,17 +1760,21 @@ namespace unicode_emoji { 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[] = "🫶🏻"; @@ -1690,6 +1805,7 @@ namespace unicode_emoji { 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[] = "🙌🏽"; @@ -1703,6 +1819,7 @@ namespace unicode_emoji { 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[] = "🫱🏻‍🫲🏼"; @@ -1714,6 +1831,7 @@ namespace unicode_emoji { 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[] = "🫱🏼‍🫲🏽"; @@ -1725,6 +1843,7 @@ namespace unicode_emoji { 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[] = "🫱🏽‍🫲🏾"; @@ -1736,6 +1855,7 @@ namespace unicode_emoji { 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[] = "🫱🏾‍🫲🏿"; @@ -1747,10 +1867,12 @@ namespace unicode_emoji { 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[] = "👍🏻"; @@ -1769,6 +1891,7 @@ namespace unicode_emoji { 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[] = "👎🏻"; @@ -1785,12 +1908,14 @@ namespace unicode_emoji { 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[] = "✊🏽"; @@ -1820,6 +1945,28 @@ namespace unicode_emoji { 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[] = "🤞🏻"; @@ -1833,6 +1980,7 @@ namespace unicode_emoji { 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[] = "✌🏽"; @@ -1984,6 +2132,7 @@ namespace unicode_emoji { 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[] = "🖖🏼"; @@ -1995,6 +2144,7 @@ namespace unicode_emoji { 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[] = "👋🏽"; @@ -2035,6 +2185,7 @@ namespace unicode_emoji { 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[] = "💪🏽"; @@ -2060,6 +2211,7 @@ namespace unicode_emoji { 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[] = "🙏🏽"; @@ -2101,7 +2253,9 @@ namespace unicode_emoji { 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[] = "👅"; @@ -2169,6 +2323,7 @@ namespace unicode_emoji { 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[] = "🧑🏼"; @@ -2247,6 +2402,7 @@ namespace unicode_emoji { 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[] = "👨🏼‍🦰"; @@ -2358,6 +2514,7 @@ namespace unicode_emoji { 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[] = "🧔🏼"; @@ -2391,6 +2548,7 @@ namespace unicode_emoji { 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[] = "🧓🏼"; @@ -2403,6 +2561,7 @@ namespace unicode_emoji { 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[] = "👵🏼"; @@ -2414,6 +2573,7 @@ namespace unicode_emoji { 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[] = "👴🏽"; @@ -3326,6 +3486,7 @@ namespace unicode_emoji { 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[] = "🎅🏽"; @@ -3504,6 +3665,7 @@ namespace unicode_emoji { 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[] = "👼🏽"; @@ -4022,6 +4184,7 @@ namespace unicode_emoji { 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[] = "💅🏽"; @@ -4034,6 +4197,7 @@ namespace unicode_emoji { 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[] = "💃🏽"; @@ -4871,6 +5035,7 @@ namespace unicode_emoji { 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[] = "👨🏻‍❤️‍💋‍👨🏼"; @@ -4956,6 +5121,7 @@ namespace unicode_emoji { 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[] = "🩳"; @@ -4967,16 +5133,21 @@ namespace unicode_emoji { 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[] = "🎓"; @@ -4986,31 +5157,40 @@ namespace unicode_emoji { 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 brown_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[] = "💝"; @@ -5024,6 +5204,7 @@ namespace unicode_emoji { 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[] = "🕎"; @@ -5040,6 +5221,7 @@ namespace unicode_emoji { 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[] = "♒"; @@ -5076,16 +5258,20 @@ namespace unicode_emoji { 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[] = "🔞"; @@ -5094,6 +5280,7 @@ namespace unicode_emoji { 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[] = "⁉️"; @@ -5115,29 +5302,37 @@ namespace unicode_emoji { 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 elevator[] = "🛗"; + 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[] = "🔠"; @@ -5158,6 +5353,7 @@ namespace unicode_emoji { 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[] = "*️⃣"; @@ -5181,14 +5377,20 @@ namespace unicode_emoji { 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[] = "↩️"; @@ -5201,6 +5403,7 @@ namespace unicode_emoji { 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[] = "➗"; @@ -5210,17 +5413,24 @@ namespace unicode_emoji { 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[] = "⚪"; @@ -5257,11 +5467,13 @@ namespace unicode_emoji { 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[] = "🗨️"; @@ -5271,36 +5483,65 @@ namespace unicode_emoji { 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[] = "⚧"; @@ -5332,9 +5573,11 @@ namespace unicode_emoji { 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[] = "🏎️"; @@ -5342,16 +5585,17 @@ namespace unicode_emoji { inline constexpr const char police_car[] = "🚓"; inline constexpr const char ambulance[] = "🚑"; inline constexpr const char fire_engine[] = "🚒"; - inline constexpr const char minibus[] = "🚐"; 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 crutch[] = "🩼"; 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[] = "🏍️"; @@ -5368,12 +5612,15 @@ namespace unicode_emoji { 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[] = "🚊"; @@ -5393,6 +5640,7 @@ namespace unicode_emoji { 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[] = "⛴️"; @@ -5401,16 +5649,20 @@ namespace unicode_emoji { 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[] = "🎡"; @@ -5435,6 +5687,7 @@ namespace unicode_emoji { 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[] = "🛖"; @@ -5463,16 +5716,19 @@ namespace unicode_emoji { 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[] = "🌃"; diff --git a/3rdParty/dpp/user.h b/3rdParty/dpp/user.h index e8bbd846eb..e67294038f 100644 --- a/3rdParty/dpp/user.h +++ b/3rdParty/dpp/user.h @@ -28,57 +28,132 @@ 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 = 0b00000000000000000000000000000001, - /// User is a system user (Clyde!) - u_system = 0b00000000000000000000000000000010, - /// User has multi-factor authentication enabled - u_mfa_enabled = 0b00000000000000000000000000000100, - /// User is verified (verified email address) - u_verified = 0b00000000000000000000000000001000, - /// User has full nitro - u_nitro_full = 0b00000000000000000000000000010000, - /// User has nitro classic - u_nitro_classic = 0b00000000000000000000000000100000, - /// User is discord staff - u_discord_employee = 0b00000000000000000000000001000000, - /// User owns a partnered server - u_partnered_owner = 0b00000000000000000000000010000000, - /// User is a member of hypesquad events - u_hypesquad_events = 0b00000000000000000000000100000000, - /// User has BugHunter level 1 - u_bughunter_1 = 0b00000000000000000000001000000000, - /// User is a member of House Bravery - u_house_bravery = 0b00000000000000000000010000000000, - /// User is a member of House Brilliance - u_house_brilliance = 0b00000000000000000000100000000000, - /// User is a member of House Balance - u_house_balance = 0b00000000000000000001000000000000, - /// User is an early supporter - u_early_supporter = 0b00000000000000000010000000000000, - /// User is a team user - u_team_user = 0b00000000000000000100000000000000, - /// User is has Bug Hunter level 2 - u_bughunter_2 = 0b00000000000000001000000000000000, - /// User is a verified bot - u_verified_bot = 0b00000000000000010000000000000000, - /// User has the Early Verified Bot Developer badge - u_verified_bot_dev = 0b00000000000000100000000000000000, - /// User's icon is animated - u_animated_icon = 0b00000000000001000000000000000000, - /// User is a certified moderator - u_certified_moderator = 0b00000000000010000000000000000000, - /// User is a bot using HTTP interactions (shows online even when not connected to a websocket) - u_bot_http_interactions = 0b00000000000100000000000000000000, - /// User has nitro basic - u_nitro_basic = 0b00000000001000000000000000000000, - /// User has the active developer badge - u_active_developer = 0b00000000010000000000000000000000, - /// User's banner is animated + /** + * @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, }; @@ -104,23 +179,47 @@ class DPP_EXPORT user : public managed, public json_interface { virtual json to_json_impl(bool with_id = true) const; public: - /** Discord username */ + /** + * @brief Discord username. + */ std::string username; - /** Global display name */ + + /** + * @brief Global display name. + */ std::string global_name; - /** Avatar hash */ + + /** + * @brief Avatar hash. + */ utility::iconhash avatar; - /** Avatar decoration hash */ + + /** + * @brief Avatar decoration hash. + */ utility::iconhash avatar_decoration; - /** Flags built from a bitmask of values in dpp::user_flags */ + + /** + * @brief Primary guild object (server tag) + */ + utility::primaryguild primary_guild; + + /** + * @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(). * 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; /** @@ -134,10 +233,10 @@ class DPP_EXPORT user : public managed, public json_interface { 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); /** @@ -191,30 +290,35 @@ class DPP_EXPORT user : public managed, public json_interface { * @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. @@ -222,6 +326,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. @@ -229,6 +334,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. @@ -236,84 +342,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. @@ -321,6 +441,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 * @@ -332,6 +453,9 @@ class DPP_EXPORT user : public managed, public json_interface { * @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 */ @@ -362,11 +486,37 @@ class DPP_EXPORT user_identified : public user, public json_interface user_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/utility.h b/3rdParty/dpp/utility.h index 041d91bc67..d8d1704ada 100644 --- a/3rdParty/dpp/utility.h +++ b/3rdParty/dpp/utility.h @@ -40,7 +40,9 @@ namespace dpp { enum sticker_format : uint8_t; -/** @brief Macro that expands to static_asserts checking sizeof and alignof are equal between two types */ +/** + * @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"); \ @@ -51,7 +53,9 @@ static_assert(alignof(a) == alignof(b), #a " and " #b " must be the same alignme namespace utility { /** - * For internal use only. Helper function to easily create discord's cdn endpoint urls + * @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`) @@ -66,7 +70,9 @@ namespace utility { 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); /** - * For internal use only. Helper function to easily create discord's cdn endpoint urls + * @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`) @@ -82,7 +88,9 @@ std::string DPP_EXPORT cdn_endpoint_url(const std::vector &allowed_f 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); /** - * For internal use only. Helper function to easily create discord's cdn endpoint urls (specialised for stickers) + * @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 @@ -98,14 +106,17 @@ 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 */ @@ -120,31 +131,59 @@ enum avx_type_t : uint8_t { * 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 "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 { - /// _Customize_ tab with the server's dpp::onboarding_prompt + /** + * @brief _Customize_ tab with the server's dpp::onboarding_prompt + */ gnt_customize, - /// _Browse Channels_ tab + + /** + * @brief "16:20" _Browse Channels_ tab + */ gnt_browse, - /// Server Guide + + /** + * @brief Server Guide + */ gnt_guide, }; @@ -166,8 +205,8 @@ 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 - * ``` + * 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"; * }); @@ -218,9 +257,14 @@ std::string DPP_EXPORT loglevel(dpp::loglevel in); * the value back in string form. */ struct DPP_EXPORT iconhash { - /** @brief High 64 bits */ + /** + * @brief High 64 bits. + */ uint64_t first; - /** @brief Low 64 bits */ + + /** + * @brief Low 64 bits. + */ uint64_t second; /** @@ -279,6 +323,33 @@ struct DPP_EXPORT iconhash { std::string to_string() const; }; +/** + * @brief User's primary guild (server tag) + */ +struct DPP_EXPORT primaryguild { + /** + * @brief The id of the user's primary guild + * + * @see guild + */ + snowflake id; + + /** + * @brief Whether the user is displaying the primary guild's server tag + */ + bool enabled; + + /** + * @brief The text of the user's server tag. Limited to 4 characters + */ + std::string tag; + + /** + * @brief The server tag badge + */ + iconhash badge; +}; + /** * @brief Image to be received or sent to API calls. * @@ -321,13 +392,13 @@ struct DPP_EXPORT image_data { * * @param rhs Image to copy */ - image_data(image_data&&) noexcept = default; + image_data(image_data&& rhs) noexcept = default; /** * @brief Construct from string buffer * * @param format Image format - * @param str Data in a string + * @param bytes Data in a string * @see image_type */ image_data(image_type format, std::string_view bytes); @@ -336,8 +407,8 @@ struct DPP_EXPORT image_data { * @brief Construct from byte buffer * * @param format Image format - * @param buf Byte buffer - * @param size_t Image size in bytes + * @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); @@ -361,8 +432,8 @@ struct DPP_EXPORT image_data { /** * @brief Set image data. * - * @param format Format of the image - * @param data Data of the image + * @param format Image format + * @param bytes Data of the image */ void set(image_type format, std::string_view bytes); @@ -370,7 +441,8 @@ struct DPP_EXPORT image_data { * @brief Set image data. * * @param format Format of the image - * @param data Data 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); @@ -463,7 +535,7 @@ struct icon { /** * @brief Get as icon hash. * - * @warn The behavior is undefined if `is_iconhash() == false` + * @warning The behavior is undefined if `is_iconhash() == false` * @return iconhash& This iconhash */ iconhash& as_iconhash() &; @@ -471,7 +543,7 @@ struct icon { /** * @brief Get as icon hash. * - * @warn The behavior is undefined if `is_iconhash() == false` + * @warning The behavior is undefined if `is_iconhash() == false` * @return iconhash& This iconhash */ const iconhash& as_iconhash() const&; @@ -479,7 +551,7 @@ struct icon { /** * @brief Get as icon hash. * - * @warn The behavior is undefined if `is_iconhash() == false` + * @warning The behavior is undefined if `is_iconhash() == false` * @return iconhash& This iconhash */ iconhash&& as_iconhash() &&; @@ -495,7 +567,7 @@ struct icon { /** * @brief Get as image data. * - * @warn The behavior is undefined if `is_image_data() == false` + * @warning The behavior is undefined if `is_image_data() == false` * @return image_data& This image */ image_data& as_image_data() &; @@ -503,7 +575,7 @@ struct icon { /** * @brief Get as image. * - * @warn The behavior is undefined if `is_image_data() == false` + * @warning The behavior is undefined if `is_image_data() == false` * @return image_data& This image */ const image_data& as_image_data() const&; @@ -511,7 +583,7 @@ struct icon { /** * @brief Get as image. * - * @warn The behavior is undefined if `is_image_data() == false` + * @warning The behavior is undefined if `is_image_data() == false` * @return image_data& This image */ image_data&& as_image_data() &&; @@ -529,7 +601,7 @@ 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) + * @return bool True if voice support is compiled in (libopus) */ bool DPP_EXPORT has_voice(); @@ -562,10 +634,25 @@ std::string DPP_EXPORT bytes(uint64_t c); * 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 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 @@ -676,25 +763,39 @@ uint32_t DPP_EXPORT hsl(int h, int s, int l); * @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); +std::string DPP_EXPORT debug_dump(const uint8_t* data, size_t length); /** - * @brief Returns the length of a UTF-8 string in codepoints - * + * @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 (0 for invalid utf8) + * @return size_t Length of string */ -size_t DPP_EXPORT utf8len(const std::string &str); +size_t DPP_EXPORT utf8len(std::string_view str); /** - * @brief Return substring of a UTF-8 encoded string in codepoints - * + * @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 Substring in UTF-8 or empty string if invalid UTF-8 passed in + * @return std::string The requested substring */ -std::string DPP_EXPORT utf8substr(const std::string& str, std::string::size_type start, std::string::size_type length); +std::string DPP_EXPORT utf8substr(std::string_view str, size_t start, size_t length); /** * @brief Read a whole file into a std::string. @@ -992,4 +1093,4 @@ struct alignas(T) dummy { }; } // namespace utility -} // namespace dpp +} diff --git a/3rdParty/dpp/version.h b/3rdParty/dpp/version.h index 3845a96558..7a8d2086bf 100644 --- a/3rdParty/dpp/version.h +++ b/3rdParty/dpp/version.h @@ -21,10 +21,10 @@ ************************************************************************************/ #pragma once -#if !defined(DPP_VERSION_LONG) -#define DPP_VERSION_LONG 0x00100028 -#define DPP_VERSION_SHORT 100028 -#define DPP_VERSION_TEXT "D++ 10.0.28 (27-Oct-2023)" +#ifndef DPP_VERSION_LONG +#define DPP_VERSION_LONG 0x00100105 +#define DPP_VERSION_SHORT 100105 +#define DPP_VERSION_TEXT "D++ 10.1.5 (21-Dec-2025)" #define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) #define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) diff --git a/3rdParty/dpp/voice_channel_effect.h b/3rdParty/dpp/voice_channel_effect.h new file mode 100644 index 0000000000..1a12cd4318 --- /dev/null +++ b/3rdParty/dpp/voice_channel_effect.h @@ -0,0 +1,119 @@ +/************************************************************************************ + * + * 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 Animation types that a voice channel effect can have + */ +enum animation_types : uint8_t { + at_premium = 0, + at_basic = 1 +}; + +/** + * @brief Represents a voice channel effect e.g. a soundboard sound or an emoji reaction. + */ +class DPP_EXPORT voice_channel_effect : public json_interface { +protected: + friend struct json_interface; + + /** + * @brief Fill voice channel effect object from json data + * + * @param j JSON data to fill from + * @return voice_channel_effect& Reference to self + */ + voice_channel_effect& fill_from_json_impl(nlohmann::json* j); + +public: + /** + * @brief Owning shard. + */ + int32_t shard_id{0}; + + /** + * @brief The channel id the user who sent the effect is connected to. + */ + snowflake channel_id{0}; + + /** + * @brief The guild id with the channel this effect was sent in. + */ + snowflake guild_id{0}; + + /** + * @brief The user id who sent the effect. + */ + snowflake user_id{0}; + + /** + * @brief The emoji of the voice channel effect. + */ + dpp::emoji emoji; + + /** + * @brief The type of emoji animation, for emoji reaction and soundboard effects. + * + * @note This may be empty. + */ + uint8_t animation_type{0}; + + /** + * @brief The ID of the emoji animation, for emoji reaction and soundboard effects. + * + * @note This may be empty. + */ + uint32_t animation_id{0}; + + /** + * @brief The ID of the soundboard sound, for soundboard effects. + * + * @note This may be empty. + */ + snowflake sound_id{0}; + + /** + * @brief The volume of the soundboard sound, from 0 to 1, for soundboard effects. + * + * @note This may be empty. + */ + double sound_volume{0}; + + /** + * @brief Construct a new voice_channel_effect object + */ + voice_channel_effect(); + + /** + * @brief Destroy the voice_channel_effect object + */ + virtual ~voice_channel_effect() = default; +}; + +} diff --git a/3rdParty/dpp/voiceregion.h b/3rdParty/dpp/voiceregion.h index e2ef309e0e..ce14995854 100644 --- a/3rdParty/dpp/voiceregion.h +++ b/3rdParty/dpp/voiceregion.h @@ -31,10 +31,20 @@ 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 }; /** @@ -106,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; }; /** @@ -120,4 +123,4 @@ class DPP_EXPORT voiceregion : public json_interface { */ typedef std::unordered_map voiceregion_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/voicestate.h b/3rdParty/dpp/voicestate.h index 49011e47fb..ad6a517542 100644 --- a/3rdParty/dpp/voicestate.h +++ b/3rdParty/dpp/voicestate.h @@ -32,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 }; /** @@ -59,13 +86,44 @@ class DPP_EXPORT voicestate : public json_interface { 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. + */ + int32_t shard_id{0}; + + /** + * @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 @@ -77,30 +135,45 @@ class DPP_EXPORT voicestate : public json_interface { */ virtual ~voicestate() = default; - /// Return true if the user is deafened by the server + /** + * @brief Return true if the user is deafened by the server. + */ bool is_deaf() const; - /// Return true if the user is muted by the server + /** + * @brief 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; -} // namespace dpp +} diff --git a/3rdParty/dpp/webhook.h b/3rdParty/dpp/webhook.h index 991ef87561..92ca9833f5 100644 --- a/3rdParty/dpp/webhook.h +++ b/3rdParty/dpp/webhook.h @@ -37,9 +37,20 @@ namespace dpp { * @brief Defines types of webhook */ enum webhook_type { - w_incoming = 1, //!< Incoming webhook - w_channel_follower = 2, //!< Channel following webhook - w_application = 3 //!< Application webhooks for interactions. + /** + * @brief Incoming webhook. + */ + w_incoming = 1, + + /** + * @brief Channel following webhook. + */ + w_channel_follower = 2, + + /** + * @brief Application webhooks for interactions. + */ + w_application = 3 }; /** @@ -73,18 +84,21 @@ class DPP_EXPORT webhook : public managed, public json_interface { /** * @brief The guild id this webhook is for. + * * @note This field is optional, and may also be empty. */ snowflake guild_id; /** * @brief The channel id this webhook is for. + * * @note This may be empty. */ snowflake channel_id; /** * @brief The user this webhook was created by. + * * @note This field is optional. * @warning This is not returned when getting a webhook with its token! */ @@ -92,36 +106,51 @@ class DPP_EXPORT webhook : public managed, public json_interface { /** * @brief The default name of the webhook. + * * @note This may be empty. */ std::string name; /** - * @brief The default avatar of the webhook + * @brief The default avatar of the webhook. + * + * @note This value will not have any effect when `avatar_url` is set, they are mutually exclusive. * @note This may be empty. */ utility::iconhash avatar; + /** + * @brief Avatar URL to use instead of the default if it is set. + * + * @note This will override `avatar` if it is set, they are mutually exclusive. + * @note This may be empty. + */ + std::string avatar_url; + /** * @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; @@ -133,6 +162,7 @@ class DPP_EXPORT webhook : public managed, public json_interface { /** * @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. */ std::string image_data; @@ -175,4 +205,4 @@ class DPP_EXPORT webhook : public managed, public json_interface { */ typedef std::unordered_map webhook_map; -} // namespace dpp +} diff --git a/3rdParty/dpp/wrapped_ssl_ctx.h b/3rdParty/dpp/wrapped_ssl_ctx.h new file mode 100644 index 0000000000..e7a38ac494 --- /dev/null +++ b/3rdParty/dpp/wrapped_ssl_ctx.h @@ -0,0 +1,106 @@ +/************************************************************************************ + * + * 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. + * + ************************************************************************************/ +#include +#include +#include +#include +#pragma once + +namespace dpp::detail { + +/** + * @brief This class wraps a raw SSL_CTX pointer, managing moving, + * creation, and RAII destruction. + */ +struct wrapped_ssl_ctx { + + /** + * @brief SSL_CTX pointer, raw C pointer nastiness + */ + SSL_CTX *context{nullptr}; + + /** + * @brief Get last SSL error message + * @return SSL error message + */ + std::string get_ssl_error() { + unsigned long error_code = ERR_get_error(); + if (error_code == 0) { + return "No error"; + } + char error_buffer[1024]{0}; + ERR_error_string_n(error_code, error_buffer, sizeof(error_buffer)); + return std::string(error_buffer); + } + + /** + * @brief Create a wrapped SSL context + * @param is_server true to create a server context, false to create a client context + * @throws dpp::connection_exception if context could not be created + */ + explicit wrapped_ssl_ctx(bool is_server = false) : context(SSL_CTX_new(is_server ? TLS_server_method() : TLS_client_method())) { + if (context == nullptr) { + throw dpp::connection_exception(err_ssl_context, "Failed to create SSL client context: " + get_ssl_error()); + } + } + + /** + * @brief Copy constructor + * @note Intentionally deleted + */ + wrapped_ssl_ctx(const wrapped_ssl_ctx&) = delete; + + /** + * @brief Copy assignment operator + * @note Intentionally deleted + */ + wrapped_ssl_ctx& operator=(const wrapped_ssl_ctx&) = delete; + + /** + * @brief Move constructor + * @param other source context + */ + wrapped_ssl_ctx(wrapped_ssl_ctx&& other) noexcept : context(other.context) { + other.context = nullptr; + } + + /** + * @brief Move assignment operator + * @param other source context + * @return self + */ + wrapped_ssl_ctx& operator=(wrapped_ssl_ctx&& other) noexcept { + if (this != &other) { + /* Free current context if any and transfer ownership */ + SSL_CTX_free(context); + context = other.context; + other.context = nullptr; + } + return *this; + } + + ~wrapped_ssl_ctx() { + SSL_CTX_free(context); + } +}; + +}; diff --git a/3rdParty/dpp/wsclient.h b/3rdParty/dpp/wsclient.h index 5fcc335ce0..f1d19a1683 100644 --- a/3rdParty/dpp/wsclient.h +++ b/3rdParty/dpp/wsclient.h @@ -23,9 +23,7 @@ #include #include #include -#include -#include -#include +#include namespace dpp { @@ -37,7 +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 */ @@ -64,44 +62,47 @@ enum ws_state : uint8_t { /** * @brief Low-level websocket opcodes for frames */ -enum ws_opcode : uint8_t -{ +enum ws_opcode : uint8_t { /** * @brief Continuation. */ - OP_CONTINUATION = 0x00, + OP_CONTINUATION = 0x00, /** * @brief Text frame. */ - OP_TEXT = 0x01, + OP_TEXT = 0x01, /** * @brief Binary frame. */ - OP_BINARY = 0x02, + OP_BINARY = 0x02, /** * @brief Close notification with close code. */ - OP_CLOSE = 0x08, + OP_CLOSE = 0x08, /** * @brief Low level ping. */ - OP_PING = 0x09, + OP_PING = 0x09, /** * @brief Low level pong. */ - OP_PONG = 0x0a + 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_connection { /** * @brief Connection key used in the HTTP headers */ @@ -132,16 +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); - - /** - * @brief Unpack a frame and pass completed frames up the stack. - * @param buffer The buffer to operate on. Gets modified to remove completed frames on the head of the buffer - * @param offset The offset to start at (reserved for future use) - * @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 parseheader(std::string& buffer); /** * @brief Fill a header for outbound messages @@ -153,71 +145,88 @@ 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: /** - * @brief (Re)connect + * @brief Connect to websocket server */ - virtual void connect(); + virtual void connect() override; /** * @brief Get websocket state * @return websocket state */ - ws_state get_state(); + [[nodiscard]] ws_state get_state() const; + + /** + * @brief If true the connection timed out while waiting, + * when waiting for SSL negotiation, TCP connect(), or HTTP. + */ + bool timed_out; + + /** + * @brief Time at which the connection should be abandoned, + * if we are still connecting or negotiating with a HTTP server + */ + time_t timeout; public: /** * @brief Connect to a specific websocket server. + * @param creator Creating cluster * @param hostname Hostname to connect to * @param port Port to connect to * @param urlpath The URL path components of the HTTP request to send * @param opcode The encoding type to use, either OP_BINARY or OP_TEXT - * @note Voice websockets only support OP_TEXT, and other websockets must be - * OP_BINARY if you are going to send ETF. + * @note This just indicates the default for frames sent. Certain sockets, + * such as voice websockets, may send a combination of OP_TEXT and OP_BINARY + * frames, whereas shard websockets will only ever send OP_BINARY for ETF and + * OP_TEXT for JSON. */ - websocket_client(const std::string &hostname, const std::string &port = "443", const std::string &urlpath = "", ws_opcode opcode = OP_BINARY); + websocket_client(cluster* creator, 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() = default; + 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) override; /** * @brief Close websocket */ - virtual void close(); + virtual void close() override; /** * @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); @@ -225,13 +234,19 @@ class DPP_EXPORT websocket_client : public ssl_client /** * @brief Fires every second from the underlying socket I/O loop, used for sending websocket pings */ - virtual void one_second_timer(); + virtual void one_second_timer() override; /** * @brief Send OP_CLOSE error code 1000 to the other side of the connection. * This indicates graceful close. + * @note This informs Discord to invalidate the session, you cannot resume if you send this */ void send_close_packet(); + + /** + * @brief Called on HTTP socket closure + */ + virtual void on_disconnect(); }; -} // namespace dpp +} diff --git a/3rdParty/dpp/zlibcontext.h b/3rdParty/dpp/zlibcontext.h new file mode 100644 index 0000000000..58ec39c9c4 --- /dev/null +++ b/3rdParty/dpp/zlibcontext.h @@ -0,0 +1,85 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include + +/** + * @brief Forward declaration for zlib stream type + */ +typedef struct z_stream_s z_stream; + +namespace dpp { + +/** + * @brief Size of decompression buffer for zlib compressed traffic + */ +constexpr size_t DECOMP_BUFFER_SIZE = 512 * 1024; + +/** + * @brief This is an opaque class containing zlib library specific structures. + * This wraps the C pointers needed for zlib with unique_ptr and gives us a nice + * buffer abstraction so we don't need to wrestle with raw pointers. + */ +class DPP_EXPORT zlibcontext { +public: + /** + * @brief Zlib stream struct. The actual type is defined in zlib.h + * so is only defined in the implementation file. + */ + z_stream* d_stream{}; + + /** + * @brief ZLib decompression buffer. + * This is automatically set to DECOMP_BUFFER_SIZE bytes when + * the class is constructed. + */ + std::vector decomp_buffer{}; + + /** + * @brief Total decompressed received bytes counter + */ + uint64_t decompressed_total{}; + + /** + * @brief Initialise zlib struct via inflateInit() + * and size the buffer + */ + zlibcontext(); + + /** + * @brief Destroy zlib struct via inflateEnd() + */ + ~zlibcontext(); + + /** + * @brief Decompress zlib deflated buffer + * @param buffer input compressed stream + * @param decompressed output decompressed content + * @return an error code on error, or err_no_code_specified (0) on success + */ + exception_error_code decompress(const std::string& buffer, std::string& decompressed); +}; + +} \ No newline at end of file diff --git a/3rdPartyBinaries/dpp.dll b/3rdPartyBinaries/dpp.dll index 02772eeab5..f68d516fc2 100644 Binary files a/3rdPartyBinaries/dpp.dll and b/3rdPartyBinaries/dpp.dll differ diff --git a/3rdPartyBinaries/dpp.lib b/3rdPartyBinaries/dpp.lib index 1aaa94c39f..dd6a79a331 100644 Binary files a/3rdPartyBinaries/dpp.lib and b/3rdPartyBinaries/dpp.lib differ diff --git a/3rdPartyBinaries/dppd.lib b/3rdPartyBinaries/dppd.lib deleted file mode 100644 index 1aaa94c39f..0000000000 Binary files a/3rdPartyBinaries/dppd.lib and /dev/null differ