diff --git a/include/dpp/utility.h b/include/dpp/utility.h index bf7f416cd3..ed954058fa 100644 --- a/include/dpp/utility.h +++ b/include/dpp/utility.h @@ -30,736 +30,729 @@ #include #include -#ifndef MAX_CND_IMAGE_SIZE - #define MAX_CDN_IMAGE_SIZE 4096 -#endif -#ifndef MIN_CDN_IMAGE_SIZE - #define MIN_CDN_IMAGE_SIZE 16 -#endif - /** * @brief The main namespace for D++ functions, classes and types */ namespace dpp { - enum sticker_format : uint8_t; +enum sticker_format : uint8_t; + +/** + * @brief Utility helper functions, generally for logging, running programs, time/date manipulation, etc + */ +namespace utility { + +/** + * For internal use only. Helper function to easily create discord's cdn endpoint urls + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param allowed_formats A vector of supported formats for the endpoint from the discord docs + * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) + * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string + * @param size the image size which will be appended as a querystring to the url. + * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) + * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. + * In this case, the `format`-parameter is only used for non-animated images. + * @param is_animated Whether the image is actually animated or not + * @return std::string endpoint url or empty string + */ +std::string DPP_EXPORT cdn_endpoint_url(const std::vector &allowed_formats, const std::string &path_without_extension, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); + +/** + * For internal use only. Helper function to easily create discord's cdn endpoint urls + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param allowed_formats A vector of supported formats for the endpoint from the discord docs + * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) + * @param hash The hash (optional). If not empty, it will be prefixed with `a_` for animated images (`is_animated`=true) + * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string + * @param size the image size which will be appended as a querystring to the url. + * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) + * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. + * In this case, the `format`-parameter is only used for non-animated images. + * @param is_animated Whether the image is actually animated or not + * @return std::string endpoint url or empty string + */ +std::string DPP_EXPORT cdn_endpoint_url_hash(const std::vector &allowed_formats, const std::string &path_without_extension, const std::string &hash, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); + +/** + * For internal use only. Helper function to easily create discord's cdn endpoint urls (specialised for stickers) + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param sticker_id The sticker ID. Returns empty string if 0 + * @param format The format type + * @return std::string endpoint url or empty string + */ +std::string DPP_EXPORT cdn_endpoint_url_sticker(snowflake sticker_id, sticker_format format); + +/** + * @brief Supported AVX instruction set type for audio mixing + */ +enum avx_type_t : uint8_t { + /** + * @brief No AVX Support + */ + avx_none, + /** + * @brief AVX support + */ + avx_1, + /** + * @brief AVX2 support + */ + avx_2, + /** + * @brief AVX512 support + */ + avx_512, +}; + +/** + * @brief Timestamp formats for dpp::utility::timestamp() + * + * @note These values are the actual character values specified by the Discord API + * and should not be changed unless the Discord API changes the specification! + * They have been sorted into numerical order of their ASCII value to keep C++ happy. + */ +enum time_format : uint8_t { + /// "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 Guild navigation types for dpp::utility::guild_navigation() + */ +enum guild_navigation_type { + /// _Customize_ tab with the server's dpp::onboarding_prompt + gnt_customize, + /// _Browse Channels_ tab + gnt_browse, + /// Server Guide + gnt_guide, +}; + +/** + * @brief The base URL for CDN content such as profile pictures and guild icons. + */ +inline const std::string cdn_host = "https://cdn.discordapp.com"; + +/** + * @brief The base URL for message/user/channel links. + */ +inline const std::string url_host = "https://discord.com"; + +/** + * @brief Callback for the results of a command executed via dpp::utility::exec + */ +typedef std::function cmd_result_t; + +/** + * @brief Run a commandline program asynchronously. The command line program + * is spawned in a separate std::thread, and when complete, its output from + * stdout is passed to the callback function in its string parameter. For example + * ``` + * dpp::utility::exec("/bin/ls", {"-al"}, [](const std::string& output) { + * std::cout << "Output of 'ls -al': " << output << "\n"; + * }); + * ``` + * + * @param cmd The command to run. + * @param parameters Command line parameters. Each will be escaped using `std::quoted`. + * @param callback The callback to call on completion. + */ +void DPP_EXPORT exec(const std::string& cmd, std::vector parameters = {}, cmd_result_t callback = {}); + +/** + * @brief Return a mentionable timestamp (used in a message). These timestamps will display the given timestamp in the user's timezone and locale. + * + * @param ts Time stamp to convert + * @param tf Format of timestamp using dpp::utility::time_format + * @return std::string The formatted timestamp + */ +std::string DPP_EXPORT timestamp(time_t ts, time_format tf = tf_short_datetime); + +/** + * @brief Create a mentionable guild navigation (used in a message). + * + * @param guild_id The guild ID + * @param gnt Guild navigation type using dpp::utility::guild_navigation_type + * @return std::string The formatted timestamp + */ +std::string DPP_EXPORT guild_navigation(const snowflake guild_id, guild_navigation_type gnt); + +/** + * @brief Returns current date and time + * + * @return std::string Current date and time in "Y-m-d H:M:S" format + */ +std::string DPP_EXPORT current_date_time(); +/** + * @brief Convert a dpp::loglevel enum value to a string + * + * @param in log level to convert + * @return std::string string form of log level + */ +std::string DPP_EXPORT loglevel(dpp::loglevel in); + +/** + * @brief Store a 128 bit icon hash (profile picture, server icon etc) + * as a 128 bit binary value made of two uint64_t. + * Has a constructor to build one from a string, and a method to fetch + * the value back in string form. + */ +struct DPP_EXPORT iconhash { + + uint64_t first; //!< High 64 bits + uint64_t second; //!< Low 64 bits /** - * @brief Utility helper functions, generally for logging, running programs, time/date manipulation, etc + * @brief Construct a new iconhash object + * @param _first Leftmost portion of the hash value + * @param _second Rightmost portion of the hash value */ - namespace utility { - - /** - * For internal use only. Helper function to easily create discord's cdn endpoint urls - * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints - * @param allowed_formats A vector of supported formats for the endpoint from the discord docs - * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) - * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string - * @param size the image size which will be appended as a querystring to the url. - * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) - * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. - * In this case, the `format`-parameter is only used for non-animated images. - * @param is_animated Whether the image is actually animated or not - * @return std::string endpoint url or empty string - */ - std::string DPP_EXPORT cdn_endpoint_url(const std::vector &allowed_formats, const std::string &path_without_extension, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); - - /** - * For internal use only. Helper function to easily create discord's cdn endpoint urls - * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints - * @param allowed_formats A vector of supported formats for the endpoint from the discord docs - * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) - * @param hash The hash (optional). If not empty, it will be prefixed with `a_` for animated images (`is_animated`=true) - * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string - * @param size the image size which will be appended as a querystring to the url. - * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) - * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. - * In this case, the `format`-parameter is only used for non-animated images. - * @param is_animated Whether the image is actually animated or not - * @return std::string endpoint url or empty string - */ - std::string DPP_EXPORT cdn_endpoint_url_hash(const std::vector &allowed_formats, const std::string &path_without_extension, const std::string &hash, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); - - /** - * For internal use only. Helper function to easily create discord's cdn endpoint urls (specialised for stickers) - * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints - * @param sticker_id The sticker ID. Returns empty string if 0 - * @param format The format type - * @return std::string endpoint url or empty string - */ - std::string DPP_EXPORT cdn_endpoint_url_sticker(snowflake sticker_id, sticker_format format); - - /** - * @brief Supported AVX instruction set type for audio mixing - */ - enum avx_type_t : uint8_t { - /** - * @brief No AVX Support - */ - avx_none, - /** - * @brief AVX support - */ - avx_1, - /** - * @brief AVX2 support - */ - avx_2, - /** - * @brief AVX512 support - */ - avx_512, - }; - - /** - * @brief Timestamp formats for dpp::utility::timestamp() - * - * @note These values are the actual character values specified by the Discord API - * and should not be changed unless the Discord API changes the specification! - * They have been sorted into numerical order of their ASCII value to keep C++ happy. - */ - enum time_format : uint8_t { - /// "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 Guild navigation types for dpp::utility::guild_navigation() - */ - enum guild_navigation_type { - /// _Customize_ tab with the server's dpp::onboarding_prompt - gnt_customize, - /// _Browse Channels_ tab - gnt_browse, - /// Server Guide - gnt_guide, - }; - - /** - * @brief The base URL for CDN content such as profile pictures and guild icons. - */ - inline const std::string cdn_host = "https://cdn.discordapp.com"; - - /** - * @brief The base URL for message/user/channel links. - */ - inline const std::string url_host = "https://discord.com"; - - /** - * @brief Callback for the results of a command executed via dpp::utility::exec - */ - typedef std::function cmd_result_t; - - /** - * @brief Run a commandline program asynchronously. The command line program - * is spawned in a separate std::thread, and when complete, its output from - * stdout is passed to the callback function in its string parameter. For example - * ``` - * dpp::utility::exec("/bin/ls", {"-al"}, [](const std::string& output) { - * std::cout << "Output of 'ls -al': " << output << "\n"; - * }); - * ``` - * - * @param cmd The command to run. - * @param parameters Command line parameters. Each will be escaped using `std::quoted`. - * @param callback The callback to call on completion. - */ - void DPP_EXPORT exec(const std::string& cmd, std::vector parameters = {}, cmd_result_t callback = {}); - - /** - * @brief Return a mentionable timestamp (used in a message). These timestamps will display the given timestamp in the user's timezone and locale. - * - * @param ts Time stamp to convert - * @param tf Format of timestamp using dpp::utility::time_format - * @return std::string The formatted timestamp - */ - std::string DPP_EXPORT timestamp(time_t ts, time_format tf = tf_short_datetime); - - /** - * @brief Create a mentionable guild navigation (used in a message). - * - * @param guild_id The guild ID - * @param gnt Guild navigation type using dpp::utility::guild_navigation_type - * @return std::string The formatted timestamp - */ - std::string DPP_EXPORT guild_navigation(const snowflake guild_id, guild_navigation_type gnt); - - /** - * @brief Returns current date and time - * - * @return std::string Current date and time in "Y-m-d H:M:S" format - */ - std::string DPP_EXPORT current_date_time(); - /** - * @brief Convert a dpp::loglevel enum value to a string - * - * @param in log level to convert - * @return std::string string form of log level - */ - std::string DPP_EXPORT loglevel(dpp::loglevel in); - - /** - * @brief Store a 128 bit icon hash (profile picture, server icon etc) - * as a 128 bit binary value made of two uint64_t. - * Has a constructor to build one from a string, and a method to fetch - * the value back in string form. - */ - struct DPP_EXPORT iconhash { - - uint64_t first; //!< High 64 bits - uint64_t second; //!< Low 64 bits - - /** - * @brief Construct a new iconhash object - * @param _first Leftmost portion of the hash value - * @param _second Rightmost portion of the hash value - */ - iconhash(uint64_t _first = 0, uint64_t _second = 0); - - /** - * @brief Construct a new iconhash object - */ - iconhash(const iconhash&); - - /** - * @brief Destroy the iconhash object - */ - ~iconhash(); - - /** - * @brief Construct a new iconhash object - * - * @param hash String hash to construct from. - * Must contain a 32 character hex string. - * - * @throws std::length_error if the provided - * string is not exactly 32 characters long. - */ - iconhash(const std::string &hash); - - /** - * @brief Assign from std::string - * - * @param assignment string to assign from. - * - * @throws std::length_error if the provided - * string is not exactly 32 characters long. - */ - iconhash& operator=(const std::string &assignment); - - /** - * @brief Check if one iconhash is equal to another - * - * @param other other iconhash to compare - * @return True if the iconhash objects match - */ - bool operator==(const iconhash& other) const; - - /** - * @brief Change value of iconhash object - * - * @param hash String hash to change to. - * Must contain a 32 character hex string. - * - * @throws std::length_error if the provided - * string is not exactly 32 characters long. - */ - void set(const std::string &hash); - - /** - * @brief Convert iconhash back to 32 character - * string value. - * - * @return std::string Hash value - */ - std::string to_string() const; - }; - - /** - * @brief Return the current time with fractions of seconds. - * This is a unix epoch time with the fractional seconds part - * after the decimal place. - * - * @return double time with fractional seconds - */ - double DPP_EXPORT time_f(); - - /** - * @brief Returns true if D++ was built with voice support - * - * @return bool True if voice support is compiled in (libsodium/libopus) - */ - bool DPP_EXPORT has_voice(); - - /** - * @brief Returns an enum value indicating which AVX instruction - * set is used for mixing received voice data, if any - * - * @return avx_type_t AVX type - */ - avx_type_t DPP_EXPORT voice_avx(); - - /** - * @brief Returns true if D++ was built with coroutine support - * - * @return bool True if coroutines are supported - */ - bool DPP_EXPORT is_coro_enabled(); - - /** - * @brief Convert a byte count to display value - * - * @param c number of bytes - * @return std::string display value suffixed with M, G, T where necessary - */ - std::string DPP_EXPORT bytes(uint64_t c); - - /** - * @brief A class used to represent an uptime in hours, minutes, - * seconds and days, with helper functions to convert from time_t - * and display as a string. - */ - struct DPP_EXPORT uptime { - uint16_t days; //!< Number of days - uint8_t hours; //!< Number of hours - uint8_t mins; //!< Number of minutes - uint8_t secs; //!< Number of seconds - - /** - * @brief Construct a new uptime object - */ - uptime(); - - /** - * @brief Construct a new uptime object - * - * @param diff A time_t to initialise the object from - */ - uptime(time_t diff); - - /** - * @brief Construct a new uptime object - * - * @param diff A time_t to initialise the object from - */ - uptime(double diff); - - /** - * @brief Get uptime as string - * - * @return std::string Uptime as string - */ - std::string to_string() const; - - /** - * @brief Get uptime as seconds - * - * @return uint64_t Uptime as seconds - */ - uint64_t to_secs() const; - - /** - * @brief Get uptime as milliseconds - * - * @return uint64_t Uptime as milliseconds - */ - uint64_t to_msecs() const; - }; - - /** - * @brief Convert doubles to RGB for sending in embeds - * - * @param red red value, between 0 and 1 inclusive - * @param green green value, between 0 and 1 inclusive - * @param blue blue value, between 0 and 1 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT rgb(double red, double green, double blue); - - /** - * @brief Convert ints to RGB for sending in embeds - * - * @param red red value, between 0 and 255 inclusive - * @param green green value, between 0 and 255 inclusive - * @param blue blue value, between 0 and 255 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT rgb(int red, int green, int blue); - - /** - * @brief Convert doubles to CMYK for sending in embeds - * - * @param c cyan value, between 0 and 1 inclusive - * @param m magenta value, between 0 and 1 inclusive - * @param y yellow value, between 0 and 1 inclusive - * @param k key (black) value, between 0 and 1 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT cmyk(double c, double m, double y, double k); - - /** - * @brief Convert ints to CMYK for sending in embeds - * - * @param c cyan value, between 0 and 255 inclusive - * @param m magenta value, between 0 and 255 inclusive - * @param y yellow value, between 0 and 255 inclusive - * @param k key (black) value, between 0 and 255 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT cmyk(int c, int m, int y, int k); - - /** - * @brief Convert doubles to HSL for sending in embeds - * - * @param h hue value, between 0 and 1 inclusive - * @param s saturation value in percentage, between 0 and 1 inclusive - * @param l lightness value in percentage, between 0 and 1 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT hsl(double h, double s, double l); - - /** - * @brief Convert ints to HSL for sending in embeds - * - * @param h hue value, between 0 and 360 inclusive - * @param s saturation value in percentage, between 0 and 100 inclusive - * @param l lightness value in percentage, between 0 and 100 inclusive - * @return uint32_t returned integer colour value - */ - uint32_t DPP_EXPORT hsl(int h, int s, int l); - - /** - * @brief Output hex values of a section of memory for debugging - * - * @param data The start of the data to display - * @param length The length of data to display - */ - std::string DPP_EXPORT debug_dump(uint8_t* data, size_t length); - - /** - * @brief Returns the length of a UTF-8 string in codepoints - * - * @param str string to count length of - * @return size_t length of string (0 for invalid utf8) - */ - size_t DPP_EXPORT utf8len(const std::string &str); - - /** - * @brief Return substring of a UTF-8 encoded string in codepoints - * - * @param str string to return substring from - * @param start start codepoint offset - * @param length length in codepoints - * @return std::string Substring in UTF-8 or empty string if invalid UTF-8 passed in - */ - std::string DPP_EXPORT utf8substr(const std::string& str, std::string::size_type start, std::string::size_type length); - - /** - * @brief Read a whole file into a std::string. - * @note This function can take a while if this is a large file, causing events to not reply and also taking up a lot of memory. Make sure you have enough memory, and use dpp::interaction_create_t::thinking in events where you call this function on big files. - * @warning Be aware this function can block! If you are regularly reading large files, consider caching them. - * @param filename The path to the file to read - * @return std::string The file contents - * @throw dpp::file_exception on failure to read the entire file - */ - std::string DPP_EXPORT read_file(const std::string& filename); - - /** - * @brief Validate a string value - * In the event the length of the string is less than _min, then an exception of type dpp:length_exception - * will be thrown. If the string is longer than _max UTF8 codepoints it will be truncated to fit. - * - * @param value The value to validate - * @param _min Minimum length - * @param _max Maximum length - * @param exception_message Exception message to throw if value length < _min - * @return std::string Validated string, truncated if necessary. - * @throw dpp::length_exception if value UTF8 length < _min - */ - std::string DPP_EXPORT validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message); - - /** - * @brief Get the url query parameter for the cdn endpoint. Internally used to build url getters. - * - * @param size size to generate url parameter for. Must be any power of two between 16 and 4096 (inclusive) or it'll return an empty string. - * @return std::string url query parameter e.g. `?size=128`, or an empty string - */ - std::string DPP_EXPORT avatar_size(uint32_t size); - - /** - * @brief Split (tokenize) a string into a vector, using the given separators - * - * @param in Input string - * @param sep Separator characters - * @return std::vector Tokenized strings - */ - std::vector DPP_EXPORT tokenize(std::string const &in, const char* sep = "\r\n"); - - /** - * @brief Create a bot invite - * - * @param bot_id Bot ID - * @param permissions Permission bitmask of the bot to invite - * @param scopes Scopes to use - * @return Invite URL - */ - std::string DPP_EXPORT bot_invite_url(const snowflake bot_id, const uint64_t permissions = 0, const std::vector& scopes = {"bot", "applications.commands"}); - - /** - * @brief Escapes Discord's markdown sequences in a string - * - * @param text Text to escape - * @param escape_code_blocks If set to false, then code blocks are not escaped. - * This means that you can still use a code block, and the text within will be left as-is. - * If set to true, code blocks will also be escaped so that ` symbol may be used as a normal - * character. - * @return std::string The text with the markdown special characters escaped with a backslash - */ - std::string DPP_EXPORT markdown_escape(const std::string& text, bool escape_code_blocks = false); - - /** - * @brief Encodes a url parameter similar to [php urlencode()](https://www.php.net/manual/en/function.urlencode.php) - * - * @param value String to encode - * @return std::string URL encoded string - */ - std::string DPP_EXPORT url_encode(const std::string &value); - - /** - * @brief Create a mentionable slashcommand (used in a message). - * @param command_id The ID of the slashcommand - * @param command_name The command name - * @param subcommand Optional: The subcommand name (for mentioning a subcommand) - * @return std::string The formatted mention - */ - std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand = ""); - - /** - * @brief Create a mentionable slashcommand (used in a message). - * @param command_id The ID of the slashcommand - * @param command_name The command name - * @param subcommand_group The subcommand group name - * @param subcommand The subcommand name - * @return std::string The formatted mention of the slashcommand with its subcommand - */ - std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand); - - /** - * @brief Create a mentionable user. - * @param id The ID of the user. - * @return std::string The formatted mention of the user. - */ - std::string DPP_EXPORT user_mention(const snowflake& id); - - /** - * @brief Create a mentionable channel. - * @param id The ID of the channel. - * @return std::string The formatted mention of the channel. - */ - std::string DPP_EXPORT channel_mention(const snowflake& id); - - /** - * @brief Create a mentionable emoji - * @param name The name of the emoji. - * @param id The ID of the emoji. - * @param is_animated is emoji animated. - * @return std::string The formatted mention of the emoji. - */ - std::string DPP_EXPORT emoji_mention(std::string_view name, snowflake id, bool is_animated = false); - - /** - * @brief Create a mentionable role. - * @param id The ID of the role. - * @return std::string The formatted mention of the role. - */ - std::string DPP_EXPORT role_mention(const snowflake& id); - - /** - * @brief Create a URL for message. - * @param guild_id The ID of the guild where message is written. - * @param channel_id The ID of the channel where message is written. - * @param message_id The ID of the message. - * @return std::string The URL to message or empty string if any of ids is 0. - */ - std::string DPP_EXPORT message_url(const snowflake& guild_id, const snowflake& channel_id, const snowflake& message_id); - - /** - * @brief Create a URL for message. - * @param guild_id The ID of the guild where channel is located. - * @param channel_id The ID of the channel. - * @return std::string The URL to message or empty string if any of ids is 0. - */ - std::string DPP_EXPORT channel_url(const snowflake& guild_id, const snowflake& channel_id); - - /** - * @brief Create a URL for message. - * @param guild_id The ID of the guild where thread is located. - * @param thread_id The ID of the thread. - * @return std::string The URL to message or empty string if any of ids is 0. - */ - std::string DPP_EXPORT thread_url(const snowflake& guild_id, const snowflake& thread_id); - - /** - * @brief Create a URL for message. - * @param user_id The ID of the guild where thread is located. - * @return std::string The URL to message or empty string if id is 0. - */ - std::string DPP_EXPORT user_url(const snowflake& user_id); + iconhash(uint64_t _first = 0, uint64_t _second = 0); + + /** + * @brief Construct a new iconhash object + */ + iconhash(const iconhash&); + + /** + * @brief Destroy the iconhash object + */ + ~iconhash(); + + /** + * @brief Construct a new iconhash object + * + * @param hash String hash to construct from. + * Must contain a 32 character hex string. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + iconhash(const std::string &hash); + + /** + * @brief Assign from std::string + * + * @param assignment string to assign from. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + iconhash& operator=(const std::string &assignment); + + /** + * @brief Check if one iconhash is equal to another + * + * @param other other iconhash to compare + * @return True if the iconhash objects match + */ + bool operator==(const iconhash& other) const; + + /** + * @brief Change value of iconhash object + * + * @param hash String hash to change to. + * Must contain a 32 character hex string. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + void set(const std::string &hash); + + /** + * @brief Convert iconhash back to 32 character + * string value. + * + * @return std::string Hash value + */ + std::string to_string() const; +}; + +/** + * @brief Return the current time with fractions of seconds. + * This is a unix epoch time with the fractional seconds part + * after the decimal place. + * + * @return double time with fractional seconds + */ +double DPP_EXPORT time_f(); + +/** + * @brief Returns true if D++ was built with voice support + * + * @return bool True if voice support is compiled in (libsodium/libopus) + */ +bool DPP_EXPORT has_voice(); + +/** + * @brief Returns an enum value indicating which AVX instruction + * set is used for mixing received voice data, if any + * + * @return avx_type_t AVX type + */ +avx_type_t DPP_EXPORT voice_avx(); + +/** + * @brief Returns true if D++ was built with coroutine support + * + * @return bool True if coroutines are supported + */ +bool DPP_EXPORT is_coro_enabled(); + +/** + * @brief Convert a byte count to display value + * + * @param c number of bytes + * @return std::string display value suffixed with M, G, T where necessary + */ +std::string DPP_EXPORT bytes(uint64_t c); + +/** + * @brief A class used to represent an uptime in hours, minutes, + * seconds and days, with helper functions to convert from time_t + * and display as a string. + */ +struct DPP_EXPORT uptime { + uint16_t days; //!< Number of days + uint8_t hours; //!< Number of hours + uint8_t mins; //!< Number of minutes + uint8_t secs; //!< Number of seconds + + /** + * @brief Construct a new uptime object + */ + uptime(); + + /** + * @brief Construct a new uptime object + * + * @param diff A time_t to initialise the object from + */ + uptime(time_t diff); + + /** + * @brief Construct a new uptime object + * + * @param diff A time_t to initialise the object from + */ + uptime(double diff); + + /** + * @brief Get uptime as string + * + * @return std::string Uptime as string + */ + std::string to_string() const; + + /** + * @brief Get uptime as seconds + * + * @return uint64_t Uptime as seconds + */ + uint64_t to_secs() const; + + /** + * @brief Get uptime as milliseconds + * + * @return uint64_t Uptime as milliseconds + */ + uint64_t to_msecs() const; +}; + +/** + * @brief Convert doubles to RGB for sending in embeds + * + * @param red red value, between 0 and 1 inclusive + * @param green green value, between 0 and 1 inclusive + * @param blue blue value, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT rgb(double red, double green, double blue); + +/** + * @brief Convert ints to RGB for sending in embeds + * + * @param red red value, between 0 and 255 inclusive + * @param green green value, between 0 and 255 inclusive + * @param blue blue value, between 0 and 255 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT rgb(int red, int green, int blue); + +/** + * @brief Convert doubles to CMYK for sending in embeds + * + * @param c cyan value, between 0 and 1 inclusive + * @param m magenta value, between 0 and 1 inclusive + * @param y yellow value, between 0 and 1 inclusive + * @param k key (black) value, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT cmyk(double c, double m, double y, double k); + +/** + * @brief Convert ints to CMYK for sending in embeds + * + * @param c cyan value, between 0 and 255 inclusive + * @param m magenta value, between 0 and 255 inclusive + * @param y yellow value, between 0 and 255 inclusive + * @param k key (black) value, between 0 and 255 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT cmyk(int c, int m, int y, int k); + +/** + * @brief Convert doubles to HSL for sending in embeds + * + * @param h hue value, between 0 and 1 inclusive + * @param s saturation value in percentage, between 0 and 1 inclusive + * @param l lightness value in percentage, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT hsl(double h, double s, double l); + +/** + * @brief Convert ints to HSL for sending in embeds + * + * @param h hue value, between 0 and 360 inclusive + * @param s saturation value in percentage, between 0 and 100 inclusive + * @param l lightness value in percentage, between 0 and 100 inclusive + * @return uint32_t returned integer colour value + */ +uint32_t DPP_EXPORT hsl(int h, int s, int l); + +/** + * @brief Output hex values of a section of memory for debugging + * + * @param data The start of the data to display + * @param length The length of data to display + */ +std::string DPP_EXPORT debug_dump(uint8_t* data, size_t length); + +/** + * @brief Returns the length of a UTF-8 string in codepoints + * + * @param str string to count length of + * @return size_t length of string (0 for invalid utf8) + */ +size_t DPP_EXPORT utf8len(const std::string &str); + +/** + * @brief Return substring of a UTF-8 encoded string in codepoints + * + * @param str string to return substring from + * @param start start codepoint offset + * @param length length in codepoints + * @return std::string Substring in UTF-8 or empty string if invalid UTF-8 passed in + */ +std::string DPP_EXPORT utf8substr(const std::string& str, std::string::size_type start, std::string::size_type length); + +/** + * @brief Read a whole file into a std::string. + * @note This function can take a while if this is a large file, causing events to not reply and also taking up a lot of memory. Make sure you have enough memory, and use dpp::interaction_create_t::thinking in events where you call this function on big files. + * @warning Be aware this function can block! If you are regularly reading large files, consider caching them. + * @param filename The path to the file to read + * @return std::string The file contents + * @throw dpp::file_exception on failure to read the entire file + */ +std::string DPP_EXPORT read_file(const std::string& filename); + +/** + * @brief Validate a string value + * In the event the length of the string is less than _min, then an exception of type dpp:length_exception + * will be thrown. If the string is longer than _max UTF8 codepoints it will be truncated to fit. + * + * @param value The value to validate + * @param _min Minimum length + * @param _max Maximum length + * @param exception_message Exception message to throw if value length < _min + * @return std::string Validated string, truncated if necessary. + * @throw dpp::length_exception if value UTF8 length < _min + */ +std::string DPP_EXPORT validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message); + +/** + * @brief Get the url query parameter for the cdn endpoint. Internally used to build url getters. + * + * @param size size to generate url parameter for. Must be any power of two between 16 and 4096 (inclusive) or it'll return an empty string. + * @return std::string url query parameter e.g. `?size=128`, or an empty string + */ +std::string DPP_EXPORT avatar_size(uint32_t size); + +/** + * @brief Split (tokenize) a string into a vector, using the given separators + * + * @param in Input string + * @param sep Separator characters + * @return std::vector Tokenized strings + */ +std::vector DPP_EXPORT tokenize(std::string const &in, const char* sep = "\r\n"); + +/** + * @brief Create a bot invite + * + * @param bot_id Bot ID + * @param permissions Permission bitmask of the bot to invite + * @param scopes Scopes to use + * @return Invite URL + */ +std::string DPP_EXPORT bot_invite_url(const snowflake bot_id, const uint64_t permissions = 0, const std::vector& scopes = {"bot", "applications.commands"}); + +/** + * @brief Escapes Discord's markdown sequences in a string + * + * @param text Text to escape + * @param escape_code_blocks If set to false, then code blocks are not escaped. + * This means that you can still use a code block, and the text within will be left as-is. + * If set to true, code blocks will also be escaped so that ` symbol may be used as a normal + * character. + * @return std::string The text with the markdown special characters escaped with a backslash + */ +std::string DPP_EXPORT markdown_escape(const std::string& text, bool escape_code_blocks = false); + +/** + * @brief Encodes a url parameter similar to [php urlencode()](https://www.php.net/manual/en/function.urlencode.php) + * + * @param value String to encode + * @return std::string URL encoded string + */ +std::string DPP_EXPORT url_encode(const std::string &value); + +/** + * @brief Create a mentionable slashcommand (used in a message). + * @param command_id The ID of the slashcommand + * @param command_name The command name + * @param subcommand Optional: The subcommand name (for mentioning a subcommand) + * @return std::string The formatted mention + */ +std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand = ""); + +/** + * @brief Create a mentionable slashcommand (used in a message). + * @param command_id The ID of the slashcommand + * @param command_name The command name + * @param subcommand_group The subcommand group name + * @param subcommand The subcommand name + * @return std::string The formatted mention of the slashcommand with its subcommand + */ +std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand); + +/** + * @brief Create a mentionable user. + * @param id The ID of the user. + * @return std::string The formatted mention of the user. + */ +std::string DPP_EXPORT user_mention(const snowflake& id); + +/** +* @brief Create a mentionable channel. +* @param id The ID of the channel. +* @return std::string The formatted mention of the channel. +*/ +std::string DPP_EXPORT channel_mention(const snowflake& id); + +/** +* @brief Create a mentionable emoji +* @param name The name of the emoji. +* @param id The ID of the emoji. +* @param is_animated is emoji animated. +* @return std::string The formatted mention of the emoji. +*/ +std::string DPP_EXPORT emoji_mention(std::string_view name, snowflake id, bool is_animated = false); + +/** +* @brief Create a mentionable role. +* @param id The ID of the role. +* @return std::string The formatted mention of the role. +*/ +std::string DPP_EXPORT role_mention(const snowflake& id); + +/** +* @brief Create a URL for message. +* @param guild_id The ID of the guild where message is written. +* @param channel_id The ID of the channel where message is written. +* @param message_id The ID of the message. +* @return std::string The URL to message or empty string if any of ids is 0. +*/ +std::string DPP_EXPORT message_url(const snowflake& guild_id, const snowflake& channel_id, const snowflake& message_id); + +/** +* @brief Create a URL for message. +* @param guild_id The ID of the guild where channel is located. +* @param channel_id The ID of the channel. +* @return std::string The URL to message or empty string if any of ids is 0. +*/ +std::string DPP_EXPORT channel_url(const snowflake& guild_id, const snowflake& channel_id); + +/** +* @brief Create a URL for message. +* @param guild_id The ID of the guild where thread is located. +* @param thread_id The ID of the thread. +* @return std::string The URL to message or empty string if any of ids is 0. +*/ +std::string DPP_EXPORT thread_url(const snowflake& guild_id, const snowflake& thread_id); + +/** +* @brief Create a URL for message. +* @param user_id The ID of the guild where thread is located. +* @return std::string The URL to message or empty string if id is 0. +*/ +std::string DPP_EXPORT user_url(const snowflake& user_id); #ifdef _DOXYGEN_ - /** - * @brief Get the mime type for an image type. - * @param type Image type - * @return std::string The mime type for this image type - */ - std::string DPP_EXPORT mime_type(image_type type); - - /** - * @brief Get the mime type for a sticker format. - * @param format Sticker format - * @return std::string The mime type for this sticker format - */ - std::string DPP_EXPORT mime_type(sticker_format format); - - /** - * @brief Get the file extension for an image type. - * @param type Image type - * @return std::string The file extension (e.g. ".png") for this image type - */ - std::string DPP_EXPORT file_extension(image_type type); - - /** - * @brief Get the file extension for a sticker format. - * @param format Sticker format - * @return std::string The file extension (e.g. ".png") for this sticker format - */ - std::string DPP_EXPORT file_extension(sticker_format format); +/** + * @brief Get the mime type for an image type. + * @param type Image type + * @return std::string The mime type for this image type + */ +std::string DPP_EXPORT mime_type(image_type type); + +/** + * @brief Get the mime type for a sticker format. + * @param format Sticker format + * @return std::string The mime type for this sticker format + */ +std::string DPP_EXPORT mime_type(sticker_format format); + +/** + * @brief Get the file extension for an image type. + * @param type Image type + * @return std::string The file extension (e.g. ".png") for this image type + */ +std::string DPP_EXPORT file_extension(image_type type); + +/** + * @brief Get the file extension for a sticker format. + * @param format Sticker format + * @return std::string The file extension (e.g. ".png") for this sticker format + */ +std::string DPP_EXPORT file_extension(sticker_format format); #else - /** - * @brief Get the mime type for an image type. - * @param type Image type - * @return std::string The mime type for this image type - */ - template - extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T type); - - /** - * @brief Get the mime type for a sticker format. - * @param format Sticker format - * @return std::string The mime type for this sticker format - */ - template - extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T format); - - /** - * @brief Get the file extension for an image type. - * @param type Image type - * @return std::string The file extension (e.g. ".png") for this image type - */ - template - extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T type); - - /** - * @brief Get the file extension for a sticker format. - * @param format Sticker format - * @return std::string The file extension (e.g. ".png") for this sticker format - */ - template - extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T format); +/** + * @brief Get the mime type for an image type. + * @param type Image type + * @return std::string The mime type for this image type + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T type); + +/** + * @brief Get the mime type for a sticker format. + * @param format Sticker format + * @return std::string The mime type for this sticker format + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T format); + +/** + * @brief Get the file extension for an image type. + * @param type Image type + * @return std::string The file extension (e.g. ".png") for this image type + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T type); + +/** + * @brief Get the file extension for a sticker format. + * @param format Sticker format + * @return std::string The file extension (e.g. ".png") for this sticker format + */ +template +extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T format); #endif - /** - * @brief Returns the library's version string - * - * @return std::string version - */ - std::string DPP_EXPORT version(); - - /** - * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. - * Entries with empty key names or values are omitted. - * - * @param parameters parameters to create a url query string for - * @return std::string A correctly encoded url query string - */ - std::string DPP_EXPORT make_url_parameters(const std::map& parameters); - - /** - * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. - * Entries with empty key names or zero values are omitted. - * - * @param parameters parameters to create a url query string for - * @return std::string A correctly encoded url query string - */ - std::string DPP_EXPORT make_url_parameters(const std::map& parameters); - - /** - * @brief Set the name of the current thread for debugging and statistical reporting - * - * @param name New name to set - */ - void DPP_EXPORT set_thread_name(const std::string& name); +/** + * @brief Returns the library's version string + * + * @return std::string version + */ +std::string DPP_EXPORT version(); + +/** + * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. + * Entries with empty key names or values are omitted. + * + * @param parameters parameters to create a url query string for + * @return std::string A correctly encoded url query string + */ +std::string DPP_EXPORT make_url_parameters(const std::map& parameters); + +/** + * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. + * Entries with empty key names or zero values are omitted. + * + * @param parameters parameters to create a url query string for + * @return std::string A correctly encoded url query string + */ +std::string DPP_EXPORT make_url_parameters(const std::map& parameters); + +/** + * @brief Set the name of the current thread for debugging and statistical reporting + * + * @param name New name to set + */ +void DPP_EXPORT set_thread_name(const std::string& name); #ifdef __cpp_concepts // if c++20 - /** - * @brief Concept satisfied if a callable F can be called using the arguments Args, and that its return value is convertible to R. - * - * @tparam F Callable object - * @tparam R Return type to check for convertibility to - * @tparam Args... Arguments to use to resolve the overload - * @return Whether the expression `F(Args...)` is convertible to R - */ - template - concept callable_returns = std::convertible_to, R>; - - /** - * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. - * - * @deprecated In C++20 mode, prefer using the concept `callable_returns`. - * @tparam F Callable object - * @tparam R Return type to check for convertibility to - * @tparam Args... Arguments to use to resolve the overload - * @return Whether the expression `F(Args...)` is convertible to R - */ - template - inline constexpr bool callable_returns_v = callable_returns; +/** + * @brief Concept satisfied if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ +template +concept callable_returns = std::convertible_to, R>; + +/** + * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @deprecated In C++20 mode, prefer using the concept `callable_returns`. + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ +template +inline constexpr bool callable_returns_v = callable_returns; #else - /** - * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. - * - * @tparam F Callable object - * @tparam R Return type to check for convertibility to - * @tparam Args... Arguments to use to resolve the overload - * @return Whether the expression `F(Args...)` is convertible to R - */ - template - inline constexpr bool callable_returns_v = std::is_convertible_v, R>; +/** + * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ +template +inline constexpr bool callable_returns_v = std::is_convertible_v, R>; #endif - /** - * @brief Utility struct that has the same size and alignment as another but does nothing. Useful for ABI compatibility. - * - * @tparam T Type to mimic - */ - template - struct alignas(T) dummy { - /** @brief Array of bytes with a size mimicking T */ - std::array data; - }; - - } // namespace utility +/** + * @brief Utility struct that has the same size and alignment as another but does nothing. Useful for ABI compatibility. + * + * @tparam T Type to mimic + */ +template +struct alignas(T) dummy { + /** @brief Array of bytes with a size mimicking T */ + std::array data; +}; + +} // namespace utility } // namespace dpp diff --git a/src/dpp/utility.cpp b/src/dpp/utility.cpp index d1015226f6..e9ae00c077 100644 --- a/src/dpp/utility.cpp +++ b/src/dpp/utility.cpp @@ -52,765 +52,769 @@ using namespace std::literals; namespace dpp::utility { - std::string 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, bool is_animated) { - return cdn_endpoint_url_hash(allowed_formats, path_without_extension, "", format, size, prefer_animated, is_animated); - } - - std::string 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, bool is_animated) { - - if (std::find(allowed_formats.begin(), allowed_formats.end(), format) == allowed_formats.end()) { - return std::string(); // if the given format is not allowed for this endpoint - } - - std::string extension; - if (is_animated && (prefer_animated || format == i_gif)) { - extension = ".gif"; - } else if (format == i_png) { - extension = ".png"; - } else if (format == i_jpg) { - extension = ".jpg"; - } else if (format == i_webp) { - extension = ".webp"; - } else { - return std::string(); - } - - std::string suffix = (hash.empty() ? "" : (is_animated ? "/a_" : "/") + hash); // > In the case of endpoints that support GIFs, the hash will begin with a_ if it is available in GIF format. - - return cdn_host + '/' + path_without_extension + suffix + extension + utility::avatar_size(size); - } - - std::string cdn_endpoint_url_sticker(snowflake sticker_id, sticker_format format) { - if (!sticker_id) { - return std::string(); - } - - std::string extension = file_extension(format); - - return extension.empty() ? std::string{} : (utility::cdn_host + "/stickers/" + std::to_string(sticker_id) + extension); - } - - double time_f() - { - using namespace std::chrono; - auto tp = system_clock::now() + 0ns; - return static_cast(tp.time_since_epoch().count()) / 1000000000.0; - } - - bool has_voice() { +constexpr int max_cdn_image_size{4096}; +constexpr int min_cdn_image_size{16}; + +std::string 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, bool is_animated) { + return cdn_endpoint_url_hash(allowed_formats, path_without_extension, "", format, size, prefer_animated, is_animated); +} + +std::string 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, bool is_animated) { + + if (std::find(allowed_formats.begin(), allowed_formats.end(), format) == allowed_formats.end()) { + return std::string(); // if the given format is not allowed for this endpoint + } + + std::string extension; + if (is_animated && (prefer_animated || format == i_gif)) { + extension = ".gif"; + } else if (format == i_png) { + extension = ".png"; + } else if (format == i_jpg) { + extension = ".jpg"; + } else if (format == i_webp) { + extension = ".webp"; + } else { + return std::string(); + } + + std::string suffix = (hash.empty() ? "" : (is_animated ? "/a_" : "/") + hash); // > In the case of endpoints that support GIFs, the hash will begin with a_ if it is available in GIF format. + + return cdn_host + '/' + path_without_extension + suffix + extension + utility::avatar_size(size); +} + +std::string cdn_endpoint_url_sticker(snowflake sticker_id, sticker_format format) { + if (!sticker_id) { + return std::string(); + } + + std::string extension = file_extension(format); + + return extension.empty() ? std::string{} : (utility::cdn_host + "/stickers/" + std::to_string(sticker_id) + extension); +} + +double time_f() +{ + using namespace std::chrono; + auto tp = system_clock::now() + 0ns; + return static_cast(tp.time_since_epoch().count()) / 1000000000.0; +} + +bool has_voice() { #if HAVE_VOICE - return true; + return true; #else - return false; + return false; #endif - } +} - avx_type_t voice_avx() { +avx_type_t voice_avx() { #if AVX_TYPE == 512 - return avx_512; + return avx_512; #elif AVX_TYPE == 2 - return avx_2; + return avx_2; #elif AVX_TYPE == 1 - return avx_1; + return avx_1; #else - return avx_none; + return avx_none; #endif - } +} - bool is_coro_enabled() { +bool is_coro_enabled() { #ifdef DPP_CORO - return true; + return true; #else - return false; + return false; #endif - } +} - std::string current_date_time() { +std::string current_date_time() { #ifdef _WIN32 - std::time_t curr_time = time(nullptr); - return trim(std::ctime(&curr_time)); + std::time_t curr_time = time(nullptr); + return trim(std::ctime(&curr_time)); #else - auto t = std::time(nullptr); - struct tm timedata; - localtime_r(&t, &timedata); - std::stringstream s; - s << std::put_time(&timedata, "%Y-%m-%d %H:%M:%S"); - return trim(s.str()); + auto t = std::time(nullptr); + struct tm timedata; + localtime_r(&t, &timedata); + std::stringstream s; + s << std::put_time(&timedata, "%Y-%m-%d %H:%M:%S"); + return trim(s.str()); #endif - } - - std::string loglevel(dpp::loglevel in) { - switch (in) { - case dpp::ll_trace: return "TRACE"; - case dpp::ll_debug: return "DEBUG"; - case dpp::ll_info: return "INFO"; - case dpp::ll_warning: return "WARN"; - case dpp::ll_error: return "ERROR"; - case dpp::ll_critical: return "CRIT"; - default: return "???"; - } - } - - uptime::uptime() : days(0), hours(0), mins(0), secs(0) { - } - - uptime::uptime(double diff) : uptime((time_t)diff) { - } - - uptime::uptime(time_t diff) : uptime() { - days = (uint16_t)(diff / (3600 * 24)); - hours = (uint8_t)(diff % (3600 * 24) / 3600); - mins = (uint8_t)(diff % 3600 / 60); - secs = (uint8_t)(diff % 60); - } - - std::string uptime::to_string() const { - char print_buffer[64]; - if (hours == 0 && days == 0) { - snprintf(print_buffer, 64, "%02d:%02d", mins, secs); - return print_buffer; - } else { - char print_buffer[64]; - std::string daystr; - if (days) { - daystr = std::to_string(days) + " day" + (days > 1 ? "s, " : ", "); - } - snprintf(print_buffer, 64, "%s%02d:%02d:%02d", daystr.c_str(), hours, mins, secs); - return print_buffer; - } - } - - uint64_t uptime::to_secs() const { - return secs + (mins * 60) + (hours * 60 * 60) + (days * 60 * 60 * 24); - } - - uint64_t uptime::to_msecs() const { - return to_secs() * 1000; - } - - iconhash::iconhash(uint64_t _first, uint64_t _second) : first(_first), second(_second) { - } - - iconhash::iconhash(const iconhash&) = default; - - iconhash::~iconhash() = default; - - void iconhash::set(const std::string &hash) { - std::string clean_hash(hash); - if (hash.empty()) { // Clear values if empty hash - first = second = 0; - return; - } - if (hash.length() == 34 && hash.substr(0, 2) == "a_") { - /* Someone passed in an animated icon. numpty. - * Clean that mess up! - */ - clean_hash = hash.substr(2); - } - if (clean_hash.length() != 32) { - throw std::length_error("iconhash must be exactly 32 characters in length, passed value is: '" + clean_hash + "'"); - } - this->first = from_string(clean_hash.substr(0, 16), std::hex); - this->second = from_string(clean_hash.substr(16, 16), std::hex); - } - - iconhash::iconhash(const std::string &hash) { - set(hash); - } - - iconhash& iconhash::operator=(const std::string &assignment) { - set(assignment); - return *this; - } - - bool iconhash::operator==(const iconhash& other) const { - return other.first == first && other.second == second; - } - - std::string iconhash::to_string() const { - if (first == 0 && second == 0) { - return ""; - } else { - return to_hex(this->first) + to_hex(this->second); - } - } - - std::string debug_dump(uint8_t* data, size_t length) { - std::ostringstream out; - size_t addr = (size_t)data; - size_t extra = addr % 16; - if (extra != 0) { - addr -= extra; - out << to_hex(addr); - } - for (size_t n = 0; n < extra; ++n) { - out << "-- "; - } - std::string ascii; - for (uint8_t* ptr = data; ptr < data + length; ++ptr) { - if (((size_t)ptr % 16) == 0) { - out << ascii << "\n[" << to_hex((size_t)ptr) << "] : "; - ascii.clear(); - } - ascii.push_back(*ptr >= ' ' && *ptr <= '~' ? *ptr : '.'); - out << to_hex(*ptr); - } - out << " " << ascii; - out << "\n"; - return out.str(); - } - - std::string bytes(uint64_t c) { - char print_buffer[64] = { 0 }; - if (c > 1099511627776) { // 1TB - snprintf(print_buffer, 64, "%.2fT", (c / 1099511627776.0)); - } else if (c > 1073741824) { // 1GB - snprintf(print_buffer, 64, "%.2fG", (c / 1073741824.0)); - } else if (c > 1048576) { // 1MB - snprintf(print_buffer, 64, "%.2fM", (c / 1048576.0)); - } else if (c > 1024) { // 1KB - snprintf(print_buffer, 64, "%.2fK", (c / 1024.0)); - } else { // Bytes - return std::to_string(c); - } - return print_buffer; - } - - uint32_t rgb(double red, double green, double blue) { - return (((uint32_t)(red * 255)) << 16) | (((uint32_t)(green * 255)) << 8) | ((uint32_t)(blue * 255)); - } - - /* NOTE: Parameters here are `int` instead of `uint32_t` or `uint8_t` to prevent ambiguity error with rgb(float, float, float) */ - uint32_t rgb(int red, int green, int blue) { - return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; - } - - uint32_t cmyk(double c, double m, double y, double k) { - int r = (int)(255 * (1 - c) * (1 - k)); - int g = (int)(255 * (1 - m) * (1 - k)); - int b = (int)(255 * (1 - y) * (1 - m)); - return rgb(r, g, b); - } - - /* NOTE: Parameters here are `int` instead of `uint32_t` or `uint8_t` to prevent ambiguity error with cmyk(float, float, float, float) */ - uint32_t cmyk(int c, int m, int y, int k) { - return cmyk(c / 255.0, m / 255.0, y / 255.0, k / 255.0); - } - - uint32_t hsl(int h, int s, int l) { - double hue = static_cast(h) / 360.0; - double saturation = static_cast(s) / 100.0; - double lightness = static_cast(l) / 100.0; - return hsl(hue, saturation, lightness); - } - - uint32_t hsl(double h, double s, double l) { - const auto hue_to_rgb = [](double p, double q, double t){ - if (t < 0) { - t += 1; - } else if (t > 1) { - t -= 1; - } - if (t < 1.0 / 6.0) { - return p + (q - p) * 6.0 * t; - } else if (t < 0.5) { - return q; - } else if (t < 2.0 / 3.0) { - return p + (q - p) * (2.0 / 3.0 - t) * 6.0; - } - return p; - }; - - double r, g, b; - - if (s == 0) { - r = g = b = l; // Gray scale - } else { - double q = l < 0.5 ? l * (1 + s) : l + s - l * s; - double p = 2 * l - q; - r = hue_to_rgb(p, q, h + 1.0 / 3.0); - g = hue_to_rgb(p, q, h); - b = hue_to_rgb(p, q, h - 1.0 / 3.0); - } - return rgb(r, g, b); - } - - void exec(const std::string& cmd, std::vector parameters, cmd_result_t callback) { - auto t = std::thread([cmd, parameters, callback]() { - utility::set_thread_name("async_exec"); - std::array buffer; - std::vector my_parameters = parameters; - std::string result; - std::stringstream cmd_and_parameters; - cmd_and_parameters << cmd; - for (auto & parameter : my_parameters) { - cmd_and_parameters << " " << std::quoted(parameter); - } - /* Capture stderr */ - cmd_and_parameters << " 2>&1"; - std::unique_ptr pipe(popen(cmd_and_parameters.str().c_str(), "r"), pclose); - if (!pipe) { - return; - } - while (fgets(buffer.data(), (int)buffer.size(), pipe.get()) != nullptr) { - result += buffer.data(); - } - if (callback) { - callback(result); - } - }); - t.detach(); - } - - size_t utf8len(const std::string &str) - { - size_t i = 0, iBefore = 0, count = 0; - const char* s = str.c_str(); - if (*s == 0) { - return 0; - } - - while (s[i] > 0) { - ascii: - i++; - } - - count += i - iBefore; - - while (s[i]) { - if (s[i] > 0) { - iBefore = i; - goto ascii; - } else { - switch (0xF0 & s[i]) { - case 0xE0: - i += 3; - break; - case 0xF0: - i += 4; - break; - default: - i += 2; - break; - } - } - - count++; - } - - return count; - } - - std::string utf8substr(const std::string& str, std::string::size_type start, std::string::size_type leng) - { - if (leng == 0) { - return ""; - } - if (start == 0 && leng >= utf8len(str)) { - return str; - } - std::string::size_type i, ix, q, min = std::string::npos, max = std::string::npos; - for (q = 0, i = 0, ix = str.length(); i < ix; i++, q++) { - if (q == start) { - min = i; - } - if (q <= start + leng || leng == std::string::npos) { - max = i; - } - - unsigned char c = (unsigned char)str[i]; - if (c < 0x80) { - i += 0; - } else if ((c & 0xE0) == 0xC0) { - i += 1; - } else if ((c & 0xF0) == 0xE0) { - i += 2; - } else if ((c & 0xF8) == 0xF0) { - i += 3; - } else { - return ""; //invalid utf8 - } - } - if (q <= start + leng || leng == std::string::npos) { - max = i; - } - if (min == std::string::npos || max == std::string::npos) { - return ""; - } - - return str.substr(min, max); - } - - std::string read_file(const std::string& filename) - { - try { - std::ifstream ifs(filename, std::ios::binary); - return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); - } - catch (const std::exception& e) { - /* Rethrow as dpp::file_exception */ - throw dpp::file_exception(e.what()); - } - } - - std::string validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message) { - if (utf8len(value) < _min) { - throw dpp::length_exception(exception_message); - } else if (utf8len(value) > _max) { - return utf8substr(value, 0, _max); - } - return value; - } - - - std::string timestamp(time_t ts, time_format tf) { - char format[2] = { (char)tf, 0 }; - return ""; - } - - std::string guild_navigation(const snowflake guild_id, guild_navigation_type gnt) { - std::string type; - switch (gnt) { - case gnt_customize: - type = "customize"; - break; - case gnt_browse: - type = "browse"; - break; - case gnt_guide: - type = "guide"; - break; - default: - return ""; - } - return "<" + std::to_string(guild_id) + ":" + type + ">"; - } - - std::string avatar_size(uint32_t size) { - if (size) { - if ( (size & (size - 1)) != 0 ) { // check if the size is a power of 2 - return std::string(); - } - if (size > MAX_CDN_IMAGE_SIZE || size < MIN_CDN_IMAGE_SIZE) { - return std::string(); - } - return "?size=" + std::to_string(size); - } +} + +std::string loglevel(dpp::loglevel in) { + switch (in) { + case dpp::ll_trace: return "TRACE"; + case dpp::ll_debug: return "DEBUG"; + case dpp::ll_info: return "INFO"; + case dpp::ll_warning: return "WARN"; + case dpp::ll_error: return "ERROR"; + case dpp::ll_critical: return "CRIT"; + default: return "???"; + } +} + +uptime::uptime() : days(0), hours(0), mins(0), secs(0) { +} + +uptime::uptime(double diff) : uptime((time_t)diff) { +} + +uptime::uptime(time_t diff) : uptime() { + days = (uint16_t)(diff / (3600 * 24)); + hours = (uint8_t)(diff % (3600 * 24) / 3600); + mins = (uint8_t)(diff % 3600 / 60); + secs = (uint8_t)(diff % 60); +} + +std::string uptime::to_string() const { + char print_buffer[64]; + if (hours == 0 && days == 0) { + snprintf(print_buffer, 64, "%02d:%02d", mins, secs); + return print_buffer; + } else { + char print_buffer[64]; + std::string daystr; + if (days) { + daystr = std::to_string(days) + " day" + (days > 1 ? "s, " : ", "); + } + snprintf(print_buffer, 64, "%s%02d:%02d:%02d", daystr.c_str(), hours, mins, secs); + return print_buffer; + } +} + +uint64_t uptime::to_secs() const { + return secs + (mins * 60) + (hours * 60 * 60) + (days * 60 * 60 * 24); +} + +uint64_t uptime::to_msecs() const { + return to_secs() * 1000; +} + +iconhash::iconhash(uint64_t _first, uint64_t _second) : first(_first), second(_second) { +} + +iconhash::iconhash(const iconhash&) = default; + +iconhash::~iconhash() = default; + +void iconhash::set(const std::string &hash) { + std::string clean_hash(hash); + if (hash.empty()) { // Clear values if empty hash + first = second = 0; + return; + } + if (hash.length() == 34 && hash.substr(0, 2) == "a_") { + /* Someone passed in an animated icon. numpty. + * Clean that mess up! + */ + clean_hash = hash.substr(2); + } + if (clean_hash.length() != 32) { + throw std::length_error("iconhash must be exactly 32 characters in length, passed value is: '" + clean_hash + "'"); + } + this->first = from_string(clean_hash.substr(0, 16), std::hex); + this->second = from_string(clean_hash.substr(16, 16), std::hex); +} + +iconhash::iconhash(const std::string &hash) { + set(hash); +} + +iconhash& iconhash::operator=(const std::string &assignment) { + set(assignment); + return *this; +} + +bool iconhash::operator==(const iconhash& other) const { + return other.first == first && other.second == second; +} + +std::string iconhash::to_string() const { + if (first == 0 && second == 0) { + return ""; + } else { + return to_hex(this->first) + to_hex(this->second); + } +} + +std::string debug_dump(uint8_t* data, size_t length) { + std::ostringstream out; + size_t addr = (size_t)data; + size_t extra = addr % 16; + if (extra != 0) { + addr -= extra; + out << to_hex(addr); + } + for (size_t n = 0; n < extra; ++n) { + out << "-- "; + } + std::string ascii; + for (uint8_t* ptr = data; ptr < data + length; ++ptr) { + if (((size_t)ptr % 16) == 0) { + out << ascii << "\n[" << to_hex((size_t)ptr) << "] : "; + ascii.clear(); + } + ascii.push_back(*ptr >= ' ' && *ptr <= '~' ? *ptr : '.'); + out << to_hex(*ptr); + } + out << " " << ascii; + out << "\n"; + return out.str(); +} + +std::string bytes(uint64_t c) { + char print_buffer[64] = { 0 }; + if (c > 1099511627776) { // 1TB + snprintf(print_buffer, 64, "%.2fT", (c / 1099511627776.0)); + } else if (c > 1073741824) { // 1GB + snprintf(print_buffer, 64, "%.2fG", (c / 1073741824.0)); + } else if (c > 1048576) { // 1MB + snprintf(print_buffer, 64, "%.2fM", (c / 1048576.0)); + } else if (c > 1024) { // 1KB + snprintf(print_buffer, 64, "%.2fK", (c / 1024.0)); + } else { // Bytes + return std::to_string(c); + } + return print_buffer; +} + +uint32_t rgb(double red, double green, double blue) { + return (((uint32_t)(red * 255)) << 16) | (((uint32_t)(green * 255)) << 8) | ((uint32_t)(blue * 255)); +} + +/* NOTE: Parameters here are `int` instead of `uint32_t` or `uint8_t` to prevent ambiguity error with rgb(float, float, float) */ +uint32_t rgb(int red, int green, int blue) { + return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; +} + +uint32_t cmyk(double c, double m, double y, double k) { + int r = (int)(255 * (1 - c) * (1 - k)); + int g = (int)(255 * (1 - m) * (1 - k)); + int b = (int)(255 * (1 - y) * (1 - m)); + return rgb(r, g, b); +} + +/* NOTE: Parameters here are `int` instead of `uint32_t` or `uint8_t` to prevent ambiguity error with cmyk(float, float, float, float) */ +uint32_t cmyk(int c, int m, int y, int k) { + return cmyk(c / 255.0, m / 255.0, y / 255.0, k / 255.0); +} + +uint32_t hsl(int h, int s, int l) { + double hue = static_cast(h) / 360.0; + double saturation = static_cast(s) / 100.0; + double lightness = static_cast(l) / 100.0; + return hsl(hue, saturation, lightness); +} + +uint32_t hsl(double h, double s, double l) { + const auto hue_to_rgb = [](double p, double q, double t){ + if (t < 0) { + t += 1; + } else if (t > 1) { + t -= 1; + } + if (t < 1.0 / 6.0) { + return p + (q - p) * 6.0 * t; + } else if (t < 0.5) { + return q; + } else if (t < 2.0 / 3.0) { + return p + (q - p) * (2.0 / 3.0 - t) * 6.0; + } + return p; + }; + + double r, g, b; + + if (s == 0) { + r = g = b = l; // Gray scale + } else { + double q = l < 0.5 ? l * (1 + s) : l + s - l * s; + double p = 2 * l - q; + r = hue_to_rgb(p, q, h + 1.0 / 3.0); + g = hue_to_rgb(p, q, h); + b = hue_to_rgb(p, q, h - 1.0 / 3.0); + } + return rgb(r, g, b); +} + +void exec(const std::string& cmd, std::vector parameters, cmd_result_t callback) { + auto t = std::thread([cmd, parameters, callback]() { + utility::set_thread_name("async_exec"); + std::array buffer; + std::vector my_parameters = parameters; + std::string result; + std::stringstream cmd_and_parameters; + cmd_and_parameters << cmd; + for (auto & parameter : my_parameters) { + cmd_and_parameters << " " << std::quoted(parameter); + } + /* Capture stderr */ + cmd_and_parameters << " 2>&1"; + std::unique_ptr pipe(popen(cmd_and_parameters.str().c_str(), "r"), pclose); + if (!pipe) { + return; + } + while (fgets(buffer.data(), (int)buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + if (callback) { + callback(result); + } + }); + t.detach(); +} + +size_t utf8len(const std::string &str) +{ + size_t i = 0, iBefore = 0, count = 0; + const char* s = str.c_str(); + if (*s == 0) { + return 0; + } + + while (s[i] > 0) { +ascii: + i++; + } + + count += i - iBefore; + + while (s[i]) { + if (s[i] > 0) { + iBefore = i; + goto ascii; + } else { + switch (0xF0 & s[i]) { + case 0xE0: + i += 3; + break; + case 0xF0: + i += 4; + break; + default: + i += 2; + break; + } + } + + count++; + } + + return count; +} + +std::string utf8substr(const std::string& str, std::string::size_type start, std::string::size_type leng) +{ + if (leng == 0) { + return ""; + } + if (start == 0 && leng >= utf8len(str)) { + return str; + } + std::string::size_type i, ix, q, min = std::string::npos, max = std::string::npos; + for (q = 0, i = 0, ix = str.length(); i < ix; i++, q++) { + if (q == start) { + min = i; + } + if (q <= start + leng || leng == std::string::npos) { + max = i; + } + + unsigned char c = (unsigned char)str[i]; + if (c < 0x80) { + i += 0; + } else if ((c & 0xE0) == 0xC0) { + i += 1; + } else if ((c & 0xF0) == 0xE0) { + i += 2; + } else if ((c & 0xF8) == 0xF0) { + i += 3; + } else { + return ""; //invalid utf8 + } + } + if (q <= start + leng || leng == std::string::npos) { + max = i; + } + if (min == std::string::npos || max == std::string::npos) { + return ""; + } + + return str.substr(min, max); +} + +std::string read_file(const std::string& filename) +{ + try { + std::ifstream ifs(filename, std::ios::binary); + return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); + } + catch (const std::exception& e) { + /* Rethrow as dpp::file_exception */ + throw dpp::file_exception(e.what()); + } +} + +std::string validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message) { + if (utf8len(value) < _min) { + throw dpp::length_exception(exception_message); + } else if (utf8len(value) > _max) { + return utf8substr(value, 0, _max); + } + return value; +} + + +std::string timestamp(time_t ts, time_format tf) { + char format[2] = { (char)tf, 0 }; + return ""; +} + +std::string guild_navigation(const snowflake guild_id, guild_navigation_type gnt) { + std::string type; + switch (gnt) { + case gnt_customize: + type = "customize"; + break; + case gnt_browse: + type = "browse"; + break; + case gnt_guide: + type = "guide"; + break; + default: + return ""; + } + return "<" + std::to_string(guild_id) + ":" + type + ">"; +} + +std::string avatar_size(uint32_t size) { + if (size) { + if ( (size & (size - 1)) != 0 ) { // check if the size is a power of 2 return std::string(); } - - std::vector tokenize(std::string const &in, const char* sep) { - std::string::size_type b = 0; - std::vector result; - - while ((b = in.find_first_not_of(sep, b)) != std::string::npos) { - auto e = in.find(sep, b); - result.push_back(in.substr(b, e-b)); - b = e; - } - return result; - } - - std::string bot_invite_url(const snowflake bot_id, const uint64_t permissions, const std::vector& scopes) { - std::string scope; - if (scopes.size()) { - for (auto& s : scopes) { - scope += s + "+"; - } - scope = scope.substr(0, scope.length() - 1); - } - return "https://discord.com/oauth2/authorize?client_id=" + std::to_string(bot_id) + "&permissions=" + std::to_string(permissions) + "&scope=" + scope; - } - - std::function cout_logger() { - return [](const dpp::log_t& event) { - if (event.severity > dpp::ll_trace) { - std::cout << "[" << dpp::utility::current_date_time() << "] " << dpp::utility::loglevel(event.severity) << ": " << event.message << "\n"; - } - }; - } - - std::function log_error() { - return [](const dpp::confirmation_callback_t& detail) { - if (detail.is_error()) { - if (detail.bot) { - error_info e = detail.get_error(); - detail.bot->log( - dpp::ll_error, - "Error: " + e.human_readable - ); - } - } - }; - } - - /* Hexadecimal sequence for URL encoding */ - static const char* hex = "0123456789ABCDEF"; - - std::string url_encode(const std::string &value) { - // Reserve worst-case encoded length of string, input length * 3 - std::string escaped(value.length() * 3, '\0'); - char* data = escaped.data(); - for (auto i = value.begin(); i != value.end(); ++i) { - unsigned char c = (unsigned char)(*i); - if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { - // Keep alphanumeric and other accepted characters intact - *data++ = c; - } else { - // Any other characters are percent-encoded - *data++ = '%'; - *data++ = hex[c >> 4]; - *data++ = hex[c & 0x0f]; - } - } - *data = 0; - return escaped.data(); - } - - std::string slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand) { - return ""; - } - - std::string slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand) { - return ""; - } - - std::string user_mention(const snowflake& id) { - return "<@" + std::to_string(id) + ">"; - } - - std::string channel_mention(const snowflake &id) { - return "<#" + std::to_string(id) + ">"; - } - - std::string emoji_mention(std::string_view name, snowflake id, bool is_animated) { - if (id) { - std::string s{}; - - s += '<'; - s += (is_animated ? "a:" : ":"); - s += name; - s += ':'; - s += id.str(); - s += '>'; - return s; - } else { - return ":" + std::string{name} + ":"; - } - } - - std::string role_mention(const snowflake &id) { - return "<@&" + std::to_string(id) + ">"; - } - - std::string message_url(const snowflake& guild_id, const snowflake& channel_id, const snowflake& message_id){ - if (guild_id.empty() || channel_id.empty() || message_id.empty()) { - return ""; - } - return url_host + "/channels/" + std::to_string(guild_id) + "/" + std::to_string(channel_id) + "/" + std::to_string(message_id); - } - - std::string channel_url(const snowflake& guild_id, const snowflake& channel_id){ - if (guild_id.empty() || channel_id.empty()) { - return ""; - } - return url_host + "/channels/" + std::to_string(guild_id) + "/" + std::to_string(channel_id); - } - - std::string thread_url(const snowflake& guild_id, const snowflake& thread_id){ - return channel_url(guild_id, thread_id); - }; - - std::string user_url(const snowflake& user_id){ - if (user_id.empty()) { - return ""; - } - return url_host + "/users/" + std::to_string(user_id); - }; - - template - std::enable_if_t, std::string> mime_type(T type) { - static constexpr auto get_image_mime = [](image_type t) constexpr noexcept { - using namespace std::string_view_literals; - - switch (t) { - case i_png: - return "image/png"sv; - - case i_jpg: - return "image/jpeg"sv; - - case i_gif: - return "image/gif"sv; - - case i_webp: - return "image/webp"sv; - } - return std::string_view{}; // unknown - }; - - return std::string{get_image_mime(type)}; - } - - // Explicit instantiation, shoves it into the DLL - template std::string mime_type(image_type t); - - template - std::enable_if_t, std::string> mime_type(T format) { - static constexpr auto get_sticker_mime = [](sticker_format f) constexpr noexcept { - using namespace std::string_view_literals; - - switch (f) { - case sf_png: - return "image/png"sv; - - case sf_apng: - return "image/apng"sv; - - case sf_lottie: - return "application/json"sv; - - case sf_gif: - return "image/gif"sv; - } - return std::string_view{}; // unknown - }; - return std::string{get_sticker_mime(format)}; - } - - template std::string mime_type(sticker_format t); - - template - std::enable_if_t, std::string> file_extension(T type) { - static constexpr auto get_image_ext = [](image_type t) constexpr noexcept { - using namespace std::string_view_literals; - - switch (t) { - case i_png: - return ".png"sv; - - case i_jpg: - return ".jpg"sv; - - case i_gif: - return ".gif"sv; - - case i_webp: - return ".webp"sv; - } - return std::string_view{}; // unknown - }; - - return std::string{get_image_ext(type)}; - } - - template std::string file_extension(image_type t); - - template - std::enable_if_t, std::string> file_extension(T format) { - static constexpr auto get_sticker_ext = [](sticker_format f) constexpr noexcept { - using namespace std::string_view_literals; - - switch (f) { - case sf_png: - case sf_apng: - return ".png"sv; - - case sf_lottie: - return ".json"sv; - - case sf_gif: - return ".gif"sv; - } - return std::string_view{}; // unknown - }; - return std::string{get_sticker_ext(format)}; - } - - template std::string file_extension(sticker_format t); - - std::string make_url_parameters(const std::map& parameters) { - std::string output; - for(auto& [k, v] : parameters) { - if (!k.empty() && !v.empty()) { - output.append("&").append(k).append("=").append(url_encode(v)); - } - } - if (!output.empty()) { - output[0] = '?'; - } - return output; - } - - std::string make_url_parameters(const std::map& parameters) { - std::map params; - for(auto& [k, v] : parameters) { - if (v != 0) { - params[k] = std::to_string(v); - } - } - return make_url_parameters(params); + if (size > max_cdn_image_size || size < min_cdn_image_size) { + return std::string(); } - - std::string markdown_escape(const std::string& text, bool escape_code_blocks) { - /** - * @brief Represents the current state of the finite state machine - * for the markdown_escape function. - */ - enum md_state { - /// normal text - md_normal = 0, - /// a paragraph code block, represented by three backticks - md_big_code_block = 1, - /// an inline code block, represented by one backtick - md_small_code_block = 2, - }; - - md_state state = md_normal; - std::string output; - const std::string markdown_chars("\\*_|~[]()>"); - - for (size_t n = 0; n < text.length(); ++n) { - if (text.substr(n, 3) == "```") { - /* Start/end a paragraph code block */ - output += (escape_code_blocks ? "\\`\\`\\`" : "```"); - n += 2; - state = (state == md_normal) ? md_big_code_block : md_normal; - } else if (text[n] == '`' && (escape_code_blocks || state != md_big_code_block)) { - /* Start/end of an inline code block */ - output += (escape_code_blocks ? "\\`" : "`"); - state = (state == md_normal) ? md_small_code_block : md_normal; - } else { - /* Normal text */ - if (escape_code_blocks || state == md_normal) { - /* Markdown sequence characters */ - if (markdown_chars.find(text[n]) != std::string::npos) { - output += "\\"; - } - } - output += text[n]; + return "?size=" + std::to_string(size); + } + return std::string(); +} + +std::vector tokenize(std::string const &in, const char* sep) { + std::string::size_type b = 0; + std::vector result; + + while ((b = in.find_first_not_of(sep, b)) != std::string::npos) { + auto e = in.find(sep, b); + result.push_back(in.substr(b, e-b)); + b = e; + } + return result; +} + +std::string bot_invite_url(const snowflake bot_id, const uint64_t permissions, const std::vector& scopes) { + std::string scope; + if (scopes.size()) { + for (auto& s : scopes) { + scope += s + "+"; + } + scope = scope.substr(0, scope.length() - 1); + } + return "https://discord.com/oauth2/authorize?client_id=" + std::to_string(bot_id) + "&permissions=" + std::to_string(permissions) + "&scope=" + scope; +} + +std::function cout_logger() { + return [](const dpp::log_t& event) { + if (event.severity > dpp::ll_trace) { + std::cout << "[" << dpp::utility::current_date_time() << "] " << dpp::utility::loglevel(event.severity) << ": " << event.message << "\n"; + } + }; +} + +std::function log_error() { + return [](const dpp::confirmation_callback_t& detail) { + if (detail.is_error()) { + if (detail.bot) { + error_info e = detail.get_error(); + detail.bot->log( + dpp::ll_error, + "Error: " + e.human_readable + ); + } + } + }; +} + +/* Hexadecimal sequence for URL encoding */ +static const char* hex = "0123456789ABCDEF"; + +std::string url_encode(const std::string &value) { + // Reserve worst-case encoded length of string, input length * 3 + std::string escaped(value.length() * 3, '\0'); + char* data = escaped.data(); + for (auto i = value.begin(); i != value.end(); ++i) { + unsigned char c = (unsigned char)(*i); + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + // Keep alphanumeric and other accepted characters intact + *data++ = c; + } else { + // Any other characters are percent-encoded + *data++ = '%'; + *data++ = hex[c >> 4]; + *data++ = hex[c & 0x0f]; + } + } + *data = 0; + return escaped.data(); +} + +std::string slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand) { + return ""; +} + +std::string slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand) { + return ""; +} + +std::string user_mention(const snowflake& id) { + return "<@" + std::to_string(id) + ">"; +} + +std::string channel_mention(const snowflake &id) { + return "<#" + std::to_string(id) + ">"; +} + +std::string emoji_mention(std::string_view name, snowflake id, bool is_animated) { + if (id) { + std::string s{}; + + s += '<'; + s += (is_animated ? "a:" : ":"); + s += name; + s += ':'; + s += id.str(); + s += '>'; + return s; + } else { + return ":" + std::string{name} + ":"; + } +} + +std::string role_mention(const snowflake &id) { + return "<@&" + std::to_string(id) + ">"; +} + +std::string message_url(const snowflake& guild_id, const snowflake& channel_id, const snowflake& message_id){ + if (guild_id.empty() || channel_id.empty() || message_id.empty()) { + return ""; + } + return url_host + "/channels/" + std::to_string(guild_id) + "/" + std::to_string(channel_id) + "/" + std::to_string(message_id); +} + +std::string channel_url(const snowflake& guild_id, const snowflake& channel_id){ + if (guild_id.empty() || channel_id.empty()) { + return ""; + } + return url_host + "/channels/" + std::to_string(guild_id) + "/" + std::to_string(channel_id); +} + +std::string thread_url(const snowflake& guild_id, const snowflake& thread_id){ + return channel_url(guild_id, thread_id); +}; + +std::string user_url(const snowflake& user_id){ + if (user_id.empty()) { + return ""; + } + return url_host + "/users/" + std::to_string(user_id); +}; + +template +std::enable_if_t, std::string> mime_type(T type) { + static constexpr auto get_image_mime = [](image_type t) constexpr noexcept { + using namespace std::string_view_literals; + + switch (t) { + case i_png: + return "image/png"sv; + + case i_jpg: + return "image/jpeg"sv; + + case i_gif: + return "image/gif"sv; + + case i_webp: + return "image/webp"sv; + } + return std::string_view{}; // unknown + }; + + return std::string{get_image_mime(type)}; +} + +// Explicit instantiation, shoves it into the DLL +template std::string mime_type(image_type t); + +template +std::enable_if_t, std::string> mime_type(T format) { + static constexpr auto get_sticker_mime = [](sticker_format f) constexpr noexcept { + using namespace std::string_view_literals; + + switch (f) { + case sf_png: + return "image/png"sv; + + case sf_apng: + return "image/apng"sv; + + case sf_lottie: + return "application/json"sv; + + case sf_gif: + return "image/gif"sv; + } + return std::string_view{}; // unknown + }; + return std::string{get_sticker_mime(format)}; +} + +template std::string mime_type(sticker_format t); + +template +std::enable_if_t, std::string> file_extension(T type) { + static constexpr auto get_image_ext = [](image_type t) constexpr noexcept { + using namespace std::string_view_literals; + + switch (t) { + case i_png: + return ".png"sv; + + case i_jpg: + return ".jpg"sv; + + case i_gif: + return ".gif"sv; + + case i_webp: + return ".webp"sv; + } + return std::string_view{}; // unknown + }; + + return std::string{get_image_ext(type)}; +} + +template std::string file_extension(image_type t); + +template +std::enable_if_t, std::string> file_extension(T format) { + static constexpr auto get_sticker_ext = [](sticker_format f) constexpr noexcept { + using namespace std::string_view_literals; + + switch (f) { + case sf_png: + case sf_apng: + return ".png"sv; + + case sf_lottie: + return ".json"sv; + + case sf_gif: + return ".gif"sv; + } + return std::string_view{}; // unknown + }; + return std::string{get_sticker_ext(format)}; +} + +template std::string file_extension(sticker_format t); + +std::string make_url_parameters(const std::map& parameters) { + std::string output; + for(auto& [k, v] : parameters) { + if (!k.empty() && !v.empty()) { + output.append("&").append(k).append("=").append(url_encode(v)); + } + } + if (!output.empty()) { + output[0] = '?'; + } + return output; +} + +std::string make_url_parameters(const std::map& parameters) { + std::map params; + for(auto& [k, v] : parameters) { + if (v != 0) { + params[k] = std::to_string(v); + } + } + return make_url_parameters(params); +} + +std::string markdown_escape(const std::string& text, bool escape_code_blocks) { + /** + * @brief Represents the current state of the finite state machine + * for the markdown_escape function. + */ + enum md_state { + /// normal text + md_normal = 0, + /// a paragraph code block, represented by three backticks + md_big_code_block = 1, + /// an inline code block, represented by one backtick + md_small_code_block = 2, + }; + + md_state state = md_normal; + std::string output; + const std::string markdown_chars("\\*_|~[]()>"); + + for (size_t n = 0; n < text.length(); ++n) { + if (text.substr(n, 3) == "```") { + /* Start/end a paragraph code block */ + output += (escape_code_blocks ? "\\`\\`\\`" : "```"); + n += 2; + state = (state == md_normal) ? md_big_code_block : md_normal; + } else if (text[n] == '`' && (escape_code_blocks || state != md_big_code_block)) { + /* Start/end of an inline code block */ + output += (escape_code_blocks ? "\\`" : "`"); + state = (state == md_normal) ? md_small_code_block : md_normal; + } else { + /* Normal text */ + if (escape_code_blocks || state == md_normal) { + /* Markdown sequence characters */ + if (markdown_chars.find(text[n]) != std::string::npos) { + output += "\\"; } } - return output; + output += text[n]; } + } + return output; +} - std::string version() { - return DPP_VERSION_TEXT; - } +std::string version() { + return DPP_VERSION_TEXT; +} - void set_thread_name(const std::string& name) { - #ifdef HAVE_PRCTL - prctl(PR_SET_NAME, reinterpret_cast(name.substr(0, 15).c_str()), NULL, NULL, NULL); - #else - #if HAVE_PTHREAD_SETNAME_NP - #if HAVE_SINGLE_PARAMETER_SETNAME_NP - pthread_setname_np(name.substr(0, 15).c_str()); - #endif - #if HAVE_TWO_PARAMETER_SETNAME_NP - pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); - #endif - #endif +void set_thread_name(const std::string& name) { + #ifdef HAVE_PRCTL + prctl(PR_SET_NAME, reinterpret_cast(name.substr(0, 15).c_str()), NULL, NULL, NULL); + #else + #if HAVE_PTHREAD_SETNAME_NP + #if HAVE_SINGLE_PARAMETER_SETNAME_NP + pthread_setname_np(name.substr(0, 15).c_str()); #endif - } + #if HAVE_TWO_PARAMETER_SETNAME_NP + pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); + #endif + #endif + #endif +} + } // namespace dpp::utility