diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000..b1312fbca0 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,2 @@ +# TODO: Discuss about -readability-identifier-length, -readability-avoid-const-params-in-decls +Checks: "-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,llvm-namespace-comment,modernize-*,performance-*,portability-*,readability-*,-bugprone-implicit-widening-of-multiplication-result, -bugprone-easily-swappable-parameters,-readability-identifier-length,-portability-restrict-system-includes,-modernize-use-trailing-return-type,-cppcoreguidelines-non-private-member-variables-in-classes,-readability-avoid-const-params-in-decls" diff --git a/.cspell.json b/.cspell.json index 4646ea76f3..2d6d73925b 100644 --- a/.cspell.json +++ b/.cspell.json @@ -127,7 +127,8 @@ "koko", "moyai", "kaaba", - "stringified" + "stringified", + "STDCORO" ], "flagWords": [ "hte" diff --git a/CMakeSettings.json b/CMakeSettings.json index ee1d09858c..ac2e6cbea0 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -3,6 +3,17 @@ { "name": "x64-Debug", "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "x64-Release", + "generator": "Ninja", "configurationType": "Release", "inheritEnvironments": [ "msvc_x64_x64" ], "buildRoot": "${projectDir}\\out\\build\\${name}", @@ -10,6 +21,28 @@ "cmakeCommandArgs": "", "buildCommandArgs": "", "ctestCommandArgs": "" + }, + { + "name": "x64-Debug-Coro", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "-DDPP_CORO=on", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "x64-Release-Coro", + "generator": "Ninja", + "configurationType": "Release", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "-DDPP_CORO=on", + "buildCommandArgs": "", + "ctestCommandArgs": "" } ] } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24ab40d357..e0dc64c4d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,4 @@ When contributing to this repository, please do not feel intimidated! We welcome including indent style etc). 7. Your PR must pass the CI actions before being allowed to be merged. Our PR actions check that the build will compile on various platforms before release and makes precompiled versions of the library. -8. If you are on the discord server for the project and your PR is accepted, let a moderator know and we - will grant you the 'Contributors' role. - +8. Automated changes e.g. via grammarly or a static analysis tool will not usually be accepted into the code without proper thought out justification (by a human being, not an AI or an App) as to why the changes are required. Generally a PR should do more than fix a single spelling error for example as this just takes precious time for something which could be resolved a direct commit to the dev branch. diff --git a/buildtools/classes/Generator/CoroGenerator.php b/buildtools/classes/Generator/CoroGenerator.php index e3ce67bb8a..faf0d41c72 100644 --- a/buildtools/classes/Generator/CoroGenerator.php +++ b/buildtools/classes/Generator/CoroGenerator.php @@ -54,6 +54,7 @@ public function generateHeaderStart(): string public function generateCppStart(): string { return $this->generateHeaderStart() . << #include @@ -80,6 +81,7 @@ public function checkForChanges(): bool } echo "-- Autogenerating include/dpp/cluster_coro_calls.h\n"; + echo "-- Autogenerating src/dpp/cluster_coro_calls.cpp\n"; return true; } @@ -88,11 +90,7 @@ public function checkForChanges(): bool */ public function generateHeaderDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterTypes, string $parameterNames): string { - $parameterNames = preg_replace('/^, /', '', $parameterNames); - if (!empty($parameterNames)) { - $parameterNames .= ', '; - } - return "auto inline co_{$currentFunction}($noDefaults) {\n\treturn dpp::awaitable(this, [&] (auto cc) { this->$currentFunction({$parameterNames}cc); }); \n}\n\n"; + return "awaitable co_{$currentFunction}($parameters);\n\n"; } /** @@ -100,7 +98,7 @@ public function generateHeaderDef(string $returnType, string $currentFunction, s */ public function generateCppDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterTypes, string $parameterNames): string { - return ''; + return "awaitable cluster::co_${currentFunction}($noDefaults) {\n\treturn {this, static_cast(&cluster::$currentFunction)$parameterNames};\n}\n\n"; } /** @@ -116,7 +114,7 @@ public function getCommentArray(): array */ public function saveHeader(string $content): void { - $content .= "auto inline co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap &headers = {}) {\n\treturn dpp::awaitable(this, [&] (auto cc) { this->request(url, method, cc, mimetype, headers); }); \n}\n\n"; + $content .= "awaitable co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap &headers = {});\n\n"; file_put_contents('include/dpp/cluster_coro_calls.h', $content); } @@ -125,7 +123,11 @@ public function saveHeader(string $content): void */ public function saveCpp(string $cppcontent): void { - /* No cpp file to save, code is all inline */ + $cppcontent .= "dpp::awaitable dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap &headers) {\n\treturn awaitable{[&](auto &&cc) { this->request(url, method, cc, postdata, mimetype, headers); }};\n} + +#endif +"; + file_put_contents('src/dpp/cluster_coro_calls.cpp', $cppcontent); } } diff --git a/buildtools/emojis.php b/buildtools/emojis.php index 65bcb2175b..0bc412847c 100644 --- a/buildtools/emojis.php +++ b/buildtools/emojis.php @@ -2,10 +2,28 @@ echo "-- Autogenrating include/dpp/unicode_emoji.h\n"; -$header = "#pragma once\n\nnamespace dpp { namespace unicode_emoji {\n"; +$url = "https://raw.githubusercontent.com/ArkinSolomon/discord-emoji-converter/master/emojis.json"; + +$header = << + * #include + * ``` + */ +namespace unicode_emoji { + +END; /* This JSON is generated originally via the NPM package maintained by Discord themselves at https://www.npmjs.com/package/discord-emoji */ -$emojis = json_decode(file_get_contents("https://raw.githubusercontent.com/ArkinSolomon/discord-emoji-converter/master/emojis.json")); +$emojis = json_decode(file_get_contents($url)); if ($emojis) { foreach ($emojis as $name=>$code) { if (preg_match("/^\d+/", $name)) { diff --git a/docpages/04_advanced_reference.md b/docpages/04_advanced_reference.md index 1ad0dea434..dfb8f08383 100644 --- a/docpages/04_advanced_reference.md +++ b/docpages/04_advanced_reference.md @@ -5,3 +5,4 @@ * \subpage coding-standards "Coding Style Standards" * \subpage unit-tests "Unit Tests" * \subpage lambdas-and-locals "Ownership of local variables and safely transferring into a lambda" +* \subpage coroutines "Advanced commands with coroutines" diff --git a/docpages/05_deprecated_list.md b/docpages/05_deprecated_list.md new file mode 100644 index 0000000000..0beefb210d --- /dev/null +++ b/docpages/05_deprecated_list.md @@ -0,0 +1,7 @@ +\page deprecated Deprecated List + +### Deprecation policy + +We keep things marked as depreciated until next major API version. +If discord removes the function, we may remove the method from the library or replace it with a thrown exception depending on the type of function and at our discretion. +Such functions which are made to throw will then be removed at the next major API version. \ No newline at end of file diff --git a/docpages/advanced_reference/coroutines.md b/docpages/advanced_reference/coroutines.md new file mode 100644 index 0000000000..6f4551a8ba --- /dev/null +++ b/docpages/advanced_reference/coroutines.md @@ -0,0 +1,228 @@ +\page coroutines Advanced commands with coroutines + +\warning D++ Coroutines are a very new feature and are currently only supported by D++ on g++ 13.1 and MSVC 19.37, and the CMake option DPP_CORO must be enabled. They are experimental and may have bugs or even crashes, please report any to [GitHub Issues](https://github.com/brainboxdotcc/DPP/issues) or to our [Discord Server](https://discord.gg/dpp). + +### What is a coroutine? + +Introduced in C++20, coroutines are the solution to the impracticality of callbacks. In short, a coroutine is a function that can be paused and resumed later : they are an extremely powerful alternative to callbacks for asynchronous APIs in particular, as the function can be paused when waiting for an API response, and resumed when it is received. + +Let's revisit [attaching a downloaded file](/attach-file.html), but this time with a coroutine : + + +~~~~~~~~~~~~~~~{.cpp} +#include + +int main() { + dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); + + bot.on_log(dpp::utility::cout_logger()); + + /* Message handler to look for a command called !file */ + /* Make note of passing the event by value, this is important (explained below) */ + bot.on_message_create.co_attach([](dpp::message_create_t event) -> dpp::task { + dpp::cluster *cluster = event.from->creator; + + if (event.msg.content == "!file") { + // request an image and co_await the response + dpp::http_request_completion_t result = co_await cluster->co_request("https://dpp.dev/DPP-Logo.png", dpp::m_get); + + // create a message + dpp::message msg(event.msg.channel_id, "This is my new attachment:"); + + // attach the image on success + if (result.status == 200) { + msg.add_file("logo.png", result.body); + } + + // send the message + cluster->message_create(msg); + } + }); + + bot.start(dpp::st_wait); + return 0; +} +~~~~~~~~~~~~~~~ + + +Coroutines can make commands simpler by eliminating callbacks, which can be very handy in the case of complex commands that rely on a lot of different data or steps. + +In order to be a coroutine, a function has to return a special type with special functions; D++ offers `dpp::task` which is designed to work seamlessly with asynchronous calls through `dpp::awaitable`, which all the functions starting with `co_` such as `dpp::cluster::co_message_create` return. To turn a function into a coroutine, simply make it return `dpp::task` as seen in the example at line 10. + +When an awaitable is `co_await`-ed, the coroutine suspends (pauses) and returns back to its caller : in other words, the program is free to go and do other things while the data is being retrieved, D++ will resume your coroutine when it has the data you need which will be returned from the `co_await` expression. + +Inside of a `dpp::task`, someone can use `co_return` in place of `return`. + +\attention As a rule of thumb when making dpp::task objects and in general coroutines, always prefer taking parameters by value and avoid capture : this may be confusing but a coroutine is *not* the lambda creating it, the captures are not bound to it and the code isn't ran inside the lambda. The lambda that returns a dpp::task simply returns a task object containing the code, which goes on to live on its own, separate from the lambda. +Similarly, with reference parameters, the object they reference to might be destroyed while the coroutine is suspended and resumed in another thread, which is why you want to pass by value. See also [lambdas and locals](/lambdas-and-locals.html) except this also applies to parameters in the case of coroutines. + +### Several steps in one + +\note The next example assumes you are already familiar with how to use [slash commands](/firstbot.html), [parameters](/slashcommands.html), and [sending files through a command](/discord-application-command-file-upload.html). + +Coroutines allow to write asynchronous functions almost as if they were executed synchronously, without the need for callbacks, which can save a lot of pain with keeping track of different data. Here is another example of what is made easier with coroutines : an "addemoji" command taking a file and a name as a parameter. This means downloading the emoji, submitting it to Discord, and finally replying, with some error handling along the way. + +~~~~~~~~~~{.cpp} +#include + +int main() { + dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); + + bot.on_log(dpp::utility::cout_logger()); + + bot.on_slashcommand.co_attach([](dpp::slashcommand_t event) -> dpp::task { + if (event.command.get_command_name() == "addemoji") { + dpp::cluster *cluster = event.from->creator; + // Retrieve parameter values + dpp::snowflake file_id = std::get(event.get_parameter("file")); + std::string emoji_name = std::get(event.get_parameter("name")); + + // Get the attachment from the resolved list + const dpp::attachment &attachment = event.command.get_resolved_attachment(file_id); + + // For simplicity for this example we only support PNG + if (attachment.content_type != "image/png") { + // While event.co_reply is available, we can just use event.reply, as we will exit the command anyway and don't need to wait on the result + event.reply("Error: type " + attachment.content_type + " not supported"); + co_return; + } + // Send a " is thinking..." message, to wait on later so we can edit + dpp::awaitable thinking = event.co_thinking(false); + + // Download and co_await the result + dpp::http_request_completion_t response = co_await cluster->co_request(attachment.url, dpp::m_get); + + if (response.status != 200) { // Page didn't send the image + co_await thinking; // Wait for the thinking response to arrive so we can edit + event.edit_response("Error: could not download the attachment"); + } + else { + // Load the image data in a dpp::emoji + dpp::emoji emoji(emoji_name); + emoji.load_image(response.body, dpp::image_type::i_png); + + // Create the emoji and co_await the response + dpp::confirmation_callback_t confirmation = co_await cluster->co_guild_emoji_create(event.command.guild_id, emoji); + + co_await thinking; // Wait for the thinking response to arrive so we can edit + if (confirmation.is_error()) + event.edit_response("Error: could not add emoji: " + confirmation.get_error().message); + else // Success + event.edit_response("Successfully added " + confirmation.get().get_mention()); // Show the new emoji + } + } + }); + + bot.on_ready([&bot](const dpp::ready_t & event) { + if (dpp::run_once()) { + dpp::slashcommand command("addemoji", "Add an emoji", bot.me.id); + + // Add file and name as required parameters + command.add_option(dpp::command_option(dpp::co_attachment, "file", "Select an image", true)); + command.add_option(dpp::command_option(dpp::co_string, "name", "Name of the emoji to add", true)); + + bot.global_command_create(command); + } + }); + + bot.start(dpp::st_wait); +} +~~~~~~~~~~ + +### I heard you liked tasks + +\note This next example is fairly advanced and makes use of many of both C++ and D++'s advanced features. + +Lastly, `dpp::task` takes its return type as a template parameter, which allows you to use tasks inside tasks and return a result from them. + +Here is an example of a command making use of that to retrieve the avatar of a specified user, or if missing, the sender : + +~~~~~~~~~~{.cpp} +#include + +int main() { + dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); + + bot.on_log(dpp::utility::cout_logger()); + + bot.on_slashcommand.co_attach([](dpp::slashcommand_t event) -> dpp::task{ + if (event.command.get_command_name() == "avatar") { + // Make a nested coroutine to fetch the guild member requested, that returns it as an optional + constexpr auto resolve_member = [](const dpp::slashcommand_t &event) -> dpp::task> { + const dpp::command_value &user_param = event.get_parameter("user"); + dpp::snowflake user_id; + + if (std::holds_alternative(user_param)) + user_id = event.command.usr.id; // Parameter is empty so user is sender + else if (std::holds_alternative(user_param)) + user_id = std::get(user_param); // Parameter has a user + + // If we have the guild member in the command's resolved data, return it + const auto &member_map = event.command.resolved.members; + if (auto member = member_map.find(user_id); member != member_map.end()) + co_return member->second; + + // Try looking in guild cache + dpp::guild *guild = dpp::find_guild(event.command.guild_id); + if (guild) { + // Look in guild's member cache + if (auto member = guild->members.find(user_id); member != guild->members.end()) { + co_return member->second; + } + } + + // Finally if everything else failed, request API + dpp::confirmation_callback_t confirmation = co_await event.from->creator->co_guild_get_member(event.command.guild_id, user_id); + if (confirmation.is_error()) + co_return std::nullopt; // Member not found, return empty + else + co_return confirmation.get(); + }; + + // Send a " is thinking..." message, to wait on later so we can edit + dpp::awaitable thinking = event.co_thinking(false); + + // Call our coroutine defined above to retrieve the member requested + std::optional member = co_await resolve_member(event); + + if (!member.has_value()) { + // Wait for the thinking response to arrive to make sure we can edit + co_await thinking; + event.edit_original_response(dpp::message{"User not found in this server!"}); + co_return; + } + + std::string avatar_url = member->get_avatar_url(512); + if (avatar_url.empty()) { // Member does not have a custom avatar for this server, get their user avatar + dpp::confirmation_callback_t confirmation = co_await event.from->creator->co_user_get_cached(member->user_id); + + if (confirmation.is_error()) + { + // Wait for the thinking response to arrive to make sure we can edit + co_await thinking; + event.edit_original_response(dpp::message{"User not found!"}); + co_return; + } + avatar_url = confirmation.get().get_avatar_url(512); + } + + // Wait for the thinking response to arrive to make sure we can edit + co_await thinking; + event.edit_original_response(dpp::message{avatar_url}); + } + }); + + + bot.on_ready([&bot](const dpp::ready_t & event) { + if (dpp::run_once()) { + dpp::slashcommand command("avatar", "Get your or another user's avatar image", bot.me.id); + + command.add_option(dpp::command_option(dpp::co_user, "user", "User to fetch the avatar from")); + + bot.global_command_create(command); + } + }); + + bot.start(dpp::st_wait); +} +~~~~~~~~~~ diff --git a/docpages/example_programs/interactions_and_components/context_menus.md b/docpages/example_programs/interactions_and_components/context_menus.md index 5da0ed8807..08f206fa37 100644 --- a/docpages/example_programs/interactions_and_components/context_menus.md +++ b/docpages/example_programs/interactions_and_components/context_menus.md @@ -20,7 +20,7 @@ int main() if (dpp::run_once()) { /* Register the command */ bot.guild_command_create( - dpp::slashcommand() + dpp::slashcommand() .set_type(dpp::ctxm_user) .set_name("High Five") .set_application_id(bot.me.id), diff --git a/docpages/example_programs/interactions_and_components/upload_parameter.md b/docpages/example_programs/interactions_and_components/upload_parameter.md index 27b107cfe1..8b8f2656b5 100644 --- a/docpages/example_programs/interactions_and_components/upload_parameter.md +++ b/docpages/example_programs/interactions_and_components/upload_parameter.md @@ -12,37 +12,32 @@ something like `dpp::cluster::request()`. int main() { - dpp::cluster bot("token"); + dpp::cluster bot("token"); - bot.on_log(dpp::utility::cout_logger()); + bot.on_log(dpp::utility::cout_logger()); - bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { - if (event.command.type == dpp::it_application_command) { - dpp::command_interaction cmd_data = std::get(event.command.data); - if (cmd_data.name == "show") { - dpp::snowflake file_id = std::get(event.get_parameter("file")); - auto iter = event.command.resolved.attachments.find(file_id); - if (iter != event.command.resolved.attachments.end()) { - const dpp::attachment& att = iter->second; - event.reply(att.url); - } - } - } - }); + bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { + dpp::command_interaction cmd_data = std::get(event.command.data); + if (cmd_data.name == "show") { + dpp::snowflake file_id = std::get(event.get_parameter("file")); + dpp::attachment att = event.command.get_resolved_attachment(file_id); + event.reply(att.url); + } + }); - bot.on_ready([&bot](const dpp::ready_t & event) { + bot.on_ready([&bot](const dpp::ready_t & event) { - if (dpp::run_once()) { - dpp::slashcommand newcommand("show", "Show an uploaded file", bot.me.id); + if (dpp::run_once()) { + dpp::slashcommand newcommand("show", "Show an uploaded file", bot.me.id); - newcommand.add_option(dpp::command_option(dpp::co_attachment, "file", "Select an image")); + newcommand.add_option(dpp::command_option(dpp::co_attachment, "file", "Select an image")); - bot.global_command_create(newcommand); - } - }); + bot.global_command_create(newcommand); + } + }); - bot.start(dpp::st_wait); + bot.start(dpp::st_wait); - return 0; + return 0; } ~~~~~~~~~~~~~~~~ diff --git a/doxygen-awesome-css b/doxygen-awesome-css index df83fbf22c..00a52f6c74 160000 --- a/doxygen-awesome-css +++ b/doxygen-awesome-css @@ -1 +1 @@ -Subproject commit df83fbf22cfff76b875c13d324baf584c74e96d0 +Subproject commit 00a52f6c74065ffbd836cbd791ddfe8edf2836b8 diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h index 1177edf1be..f50499ad3a 100644 --- a/include/dpp/appcommand.h +++ b/include/dpp/appcommand.h @@ -474,7 +474,7 @@ struct DPP_EXPORT interaction_modal_response : public interaction_response, publ }; /** - * @brief Resolved snowflake ids to users, guild members, roles and channels. + * @brief Resolved snowflake ids to users, guild members, roles and channels. You can use the `interaction::get_resolved_*` methods to easily get a resolved set */ struct DPP_EXPORT command_resolved { /** @@ -695,7 +695,7 @@ class DPP_EXPORT interaction : public managed, public json_interface co_await-able object holding the timer_handle + */ + awaitable co_timer(uint64_t seconds); +#endif + /** * @brief Get the dm channel for a user id * @@ -2641,6 +2651,57 @@ class DPP_EXPORT cluster { */ void guild_get_vanity(snowflake guild_id, command_completion_event_t callback); + /** + * @brief Get the guild's onboarding configuration + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param o The onboarding object + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::onboarding object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_onboarding(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::onboarding object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit_onboarding(const struct onboarding& o, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::welcome_screen object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_welcome_screen(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::welcome_screen object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled, command_completion_event_t callback = utility::log_error()); + /** * @brief Create a webhook * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. @@ -3143,7 +3204,7 @@ class DPP_EXPORT cluster { * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @param callback Function to call when the API call completes * On success the callback will contain a dpp::thread_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). @@ -3154,7 +3215,7 @@ class DPP_EXPORT cluster { * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @param callback Function to call when the API call completes * On success the callback will contain a dpp::thread_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). diff --git a/include/dpp/cluster_coro_calls.h b/include/dpp/cluster_coro_calls.h index a49cb3cc66..b09d1a979c 100644 --- a/include/dpp/cluster_coro_calls.h +++ b/include/dpp/cluster_coro_calls.h @@ -38,9 +38,7 @@ * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_global_bulk_command_create(const std::vector &commands) { - return dpp::awaitable(this, [&] (auto cc) { this->global_bulk_command_create(commands, cc); }); -} +awaitable co_global_bulk_command_create(const std::vector &commands); /** * @brief Create a global slash command (a bot can have a maximum of 100 of these). @@ -51,9 +49,7 @@ auto inline co_global_bulk_command_create(const std::vector &comma * @return slashcommand returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_create(const slashcommand &s) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_create(s, cc); }); -} +awaitable co_global_command_create(const slashcommand &s); /** * @brief Get a global slash command @@ -64,9 +60,7 @@ auto inline co_global_command_create(const slashcommand &s) { * @return slashcommand returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_get(snowflake id) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_get(id, cc); }); -} +awaitable co_global_command_get(snowflake id); /** * @brief Delete a global slash command (a bot can have a maximum of 100 of these) @@ -77,9 +71,7 @@ auto inline co_global_command_get(snowflake id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_delete(snowflake id) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_delete(id, cc); }); -} +awaitable co_global_command_delete(snowflake id); /** * @brief Edit a global slash command (a bot can have a maximum of 100 of these) @@ -90,9 +82,7 @@ auto inline co_global_command_delete(snowflake id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_global_command_edit(const slashcommand &s) { - return dpp::awaitable(this, [&] (auto cc) { this->global_command_edit(s, cc); }); -} +awaitable co_global_command_edit(const slashcommand &s); /** * @brief Get the application's global slash commands @@ -102,9 +92,7 @@ auto inline co_global_command_edit(const slashcommand &s) { * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_global_commands_get() { - return dpp::awaitable(this, [&] (auto cc) { this->global_commands_get(cc); }); -} +awaitable co_global_commands_get(); /** * @brief Create/overwrite guild slash commands. @@ -118,9 +106,7 @@ auto inline co_global_commands_get() { * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_bulk_command_create(const std::vector &commands, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_bulk_command_create(commands, guild_id, cc); }); -} +awaitable co_guild_bulk_command_create(const std::vector &commands, snowflake guild_id); /** * @brief Get all slash command permissions of a guild @@ -131,9 +117,7 @@ auto inline co_guild_bulk_command_create(const std::vector &comman * @return guild_command_permissions_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_commands_get_permissions(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_commands_get_permissions(guild_id, cc); }); -} +awaitable co_guild_commands_get_permissions(snowflake guild_id); /** * @brief Edit/Overwrite the permissions of all existing slash commands in a guild @@ -149,9 +133,7 @@ auto inline co_guild_commands_get_permissions(snowflake guild_id) { * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead * \memberof dpp::cluster */ -auto inline co_guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_bulk_command_edit_permissions(commands, guild_id, cc); }); -} +awaitable co_guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id); /** * @brief Create a slash command local to a guild @@ -164,9 +146,7 @@ auto inline co_guild_bulk_command_edit_permissions(const std::vectorguild_command_create(s, guild_id, cc); }); -} +awaitable co_guild_command_create(const slashcommand &s, snowflake guild_id); /** * @brief Delete a slash command local to a guild @@ -178,9 +158,7 @@ auto inline co_guild_command_create(const slashcommand &s, snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_delete(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_delete(id, guild_id, cc); }); -} +awaitable co_guild_command_delete(snowflake id, snowflake guild_id); /** * @brief Edit slash command permissions of a guild @@ -193,9 +171,7 @@ auto inline co_guild_command_delete(snowflake id, snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_edit_permissions(const slashcommand &s, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_edit_permissions(s, guild_id, cc); }); -} +awaitable co_guild_command_edit_permissions(const slashcommand &s, snowflake guild_id); /** * @brief Get a slash command of a guild @@ -208,9 +184,7 @@ auto inline co_guild_command_edit_permissions(const slashcommand &s, snowflake g * @return slashcommand returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_get(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_get(id, guild_id, cc); }); -} +awaitable co_guild_command_get(snowflake id, snowflake guild_id); /** * @brief Get the permissions for a slash command of a guild @@ -222,9 +196,7 @@ auto inline co_guild_command_get(snowflake id, snowflake guild_id) { * @return guild_command_permissions returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_get_permissions(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_get_permissions(id, guild_id, cc); }); -} +awaitable co_guild_command_get_permissions(snowflake id, snowflake guild_id); /** * @brief Edit a slash command local to a guild @@ -236,9 +208,7 @@ auto inline co_guild_command_get_permissions(snowflake id, snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_command_edit(const slashcommand &s, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_command_edit(s, guild_id, cc); }); -} +awaitable co_guild_command_edit(const slashcommand &s, snowflake guild_id); /** * @brief Get the application's slash commands for a guild @@ -250,9 +220,7 @@ auto inline co_guild_command_edit(const slashcommand &s, snowflake guild_id) { * @return slashcommand_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_commands_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_commands_get(guild_id, cc); }); -} +awaitable co_guild_commands_get(snowflake guild_id); /** * @brief Respond to a slash command @@ -265,9 +233,7 @@ auto inline co_guild_commands_get(snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_response_create(interaction_id, token, r, cc); }); -} +awaitable co_interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r); /** * @brief Edit response to a slash command @@ -279,9 +245,7 @@ auto inline co_interaction_response_create(snowflake interaction_id, const std:: * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_response_edit(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_response_edit(token, m, cc); }); -} +awaitable co_interaction_response_edit(const std::string &token, const message &m); /** * @brief Get the original response to a slash command @@ -292,9 +256,7 @@ auto inline co_interaction_response_edit(const std::string &token, const message * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_response_get_original(const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_response_get_original(token, cc); }); -} +awaitable co_interaction_response_get_original(const std::string &token); /** * @brief Create a followup message to a slash command @@ -306,9 +268,7 @@ auto inline co_interaction_response_get_original(const std::string &token) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_create(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_create(token, m, cc); }); -} +awaitable co_interaction_followup_create(const std::string &token, const message &m); /** * @brief Edit original followup message to a slash command @@ -321,9 +281,7 @@ auto inline co_interaction_followup_create(const std::string &token, const messa * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_edit_original(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_edit_original(token, m, cc); }); -} +awaitable co_interaction_followup_edit_original(const std::string &token, const message &m); /** * @brief Delete the initial interaction response @@ -334,9 +292,7 @@ auto inline co_interaction_followup_edit_original(const std::string &token, cons * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_delete(const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_delete(token, cc); }); -} +awaitable co_interaction_followup_delete(const std::string &token); /** * @brief Edit followup message to a slash command @@ -349,9 +305,7 @@ auto inline co_interaction_followup_delete(const std::string &token) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_edit(const std::string &token, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_edit(token, m, cc); }); -} +awaitable co_interaction_followup_edit(const std::string &token, const message &m); /** * @brief Get the followup message to a slash command @@ -363,9 +317,7 @@ auto inline co_interaction_followup_edit(const std::string &token, const message * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_get(const std::string &token, snowflake message_id) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_get(token, message_id, cc); }); -} +awaitable co_interaction_followup_get(const std::string &token, snowflake message_id); /** * @brief Get the original followup message to a slash command @@ -377,9 +329,7 @@ auto inline co_interaction_followup_get(const std::string &token, snowflake mess * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_interaction_followup_get_original(const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->interaction_followup_get_original(token, cc); }); -} +awaitable co_interaction_followup_get_original(const std::string &token); /** * @brief Get all auto moderation rules for a guild @@ -388,9 +338,7 @@ auto inline co_interaction_followup_get_original(const std::string &token) { * @return automod_rule_map returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rules_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rules_get(guild_id, cc); }); -} +awaitable co_automod_rules_get(snowflake guild_id); /** * @brief Get a single auto moderation rule @@ -400,9 +348,7 @@ auto inline co_automod_rules_get(snowflake guild_id) { * @return automod_rule returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_get(snowflake guild_id, snowflake rule_id) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_get(guild_id, rule_id, cc); }); -} +awaitable co_automod_rule_get(snowflake guild_id, snowflake rule_id); /** * @brief Create an auto moderation rule @@ -412,9 +358,7 @@ auto inline co_automod_rule_get(snowflake guild_id, snowflake rule_id) { * @return automod_rule returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_create(snowflake guild_id, const automod_rule& r) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_create(guild_id, r, cc); }); -} +awaitable co_automod_rule_create(snowflake guild_id, const automod_rule& r); /** * @brief Edit an auto moderation rule @@ -424,9 +368,7 @@ auto inline co_automod_rule_create(snowflake guild_id, const automod_rule& r) { * @return automod_rule returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_edit(snowflake guild_id, const automod_rule& r) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_edit(guild_id, r, cc); }); -} +awaitable co_automod_rule_edit(snowflake guild_id, const automod_rule& r); /** * @brief Delete an auto moderation rule @@ -436,9 +378,7 @@ auto inline co_automod_rule_edit(snowflake guild_id, const automod_rule& r) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_automod_rule_delete(snowflake guild_id, snowflake rule_id) { - return dpp::awaitable(this, [&] (auto cc) { this->automod_rule_delete(guild_id, rule_id, cc); }); -} +awaitable co_automod_rule_delete(snowflake guild_id, snowflake rule_id); /** * @brief Create a channel @@ -456,9 +396,7 @@ auto inline co_automod_rule_delete(snowflake guild_id, snowflake rule_id) { * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_create(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_create(c, cc); }); -} +awaitable co_channel_create(const class channel &c); /** * @brief Remove a permission from a channel @@ -470,9 +408,7 @@ auto inline co_channel_create(const class channel &c) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_delete_permission(const class channel &c, snowflake overwrite_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_delete_permission(c, overwrite_id, cc); }); -} +awaitable co_channel_delete_permission(const class channel &c, snowflake overwrite_id); /** * @brief Delete a channel @@ -483,9 +419,7 @@ auto inline co_channel_delete_permission(const class channel &c, snowflake overw * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_delete(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_delete(channel_id, cc); }); -} +awaitable co_channel_delete(snowflake channel_id); /** * @brief Edit a channel's permissions @@ -501,9 +435,7 @@ auto inline co_channel_delete(snowflake channel_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_edit_permissions(c, overwrite_id, allow, deny, member, cc); }); -} +awaitable co_channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); /** * @brief Edit a channel's permissions @@ -519,9 +451,7 @@ auto inline co_channel_edit_permissions(const class channel &c, const snowflake * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_edit_permissions(channel_id, overwrite_id, allow, deny, member, cc); }); -} +awaitable co_channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); /** * @brief Edit multiple channels positions @@ -536,9 +466,7 @@ auto inline co_channel_edit_permissions(const snowflake channel_id, const snowfl * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_edit_positions(const std::vector &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_edit_positions(c, cc); }); -} +awaitable co_channel_edit_positions(const std::vector &c); /** * @brief Edit a channel @@ -549,9 +477,7 @@ auto inline co_channel_edit_positions(const std::vector &c) { * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_edit(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_edit(c, cc); }); -} +awaitable co_channel_edit(const class channel &c); /** * @brief Follow an announcement (news) channel @@ -562,9 +488,7 @@ auto inline co_channel_edit(const class channel &c) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_follow_news(const class channel &c, snowflake target_channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_follow_news(c, target_channel_id, cc); }); -} +awaitable co_channel_follow_news(const class channel &c, snowflake target_channel_id); /** * @brief Get a channel @@ -575,9 +499,7 @@ auto inline co_channel_follow_news(const class channel &c, snowflake target_chan * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_get(snowflake c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_get(c, cc); }); -} +awaitable co_channel_get(snowflake c); /** * @brief Create invite for a channel @@ -589,9 +511,7 @@ auto inline co_channel_get(snowflake c) { * @return invite returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_invite_create(const class channel &c, const class invite &i) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_invite_create(c, i, cc); }); -} +awaitable co_channel_invite_create(const class channel &c, const class invite &i); /** * @brief Get invites for a channel @@ -602,9 +522,7 @@ auto inline co_channel_invite_create(const class channel &c, const class invite * @return invite_map returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_invites_get(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_invites_get(c, cc); }); -} +awaitable co_channel_invites_get(const class channel &c); /** * @brief Trigger channel typing indicator @@ -614,9 +532,7 @@ auto inline co_channel_invites_get(const class channel &c) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_typing(const class channel &c) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_typing(c, cc); }); -} +awaitable co_channel_typing(const class channel &c); /** * @brief Trigger channel typing indicator @@ -626,9 +542,7 @@ auto inline co_channel_typing(const class channel &c) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_typing(snowflake cid) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_typing(cid, cc); }); -} +awaitable co_channel_typing(snowflake cid); /** * @brief Get all channels for a guild @@ -639,9 +553,7 @@ auto inline co_channel_typing(snowflake cid) { * @return channel_map returned object on completion * \memberof dpp::cluster */ -auto inline co_channels_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channels_get(guild_id, cc); }); -} +awaitable co_channels_get(snowflake guild_id); /** * @brief Create a dm channel @@ -651,9 +563,7 @@ auto inline co_channels_get(snowflake guild_id) { * @return channel returned object on completion * \memberof dpp::cluster */ -auto inline co_create_dm_channel(snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->create_dm_channel(user_id, cc); }); -} +awaitable co_create_dm_channel(snowflake user_id); /** * @brief Get current user DM channels @@ -661,9 +571,7 @@ auto inline co_create_dm_channel(snowflake user_id) { * @return channel_map returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_get_dms() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_get_dms(cc); }); -} +awaitable co_current_user_get_dms(); /** * @brief Create a direct message, also create the channel for the direct message if needed @@ -677,9 +585,7 @@ auto inline co_current_user_get_dms() { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_direct_message_create(snowflake user_id, const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->direct_message_create(user_id, m, cc); }); -} +awaitable co_direct_message_create(snowflake user_id, const message &m); /** * @brief Adds a recipient to a Group DM using their access token @@ -692,9 +598,7 @@ auto inline co_direct_message_create(snowflake user_id, const message &m) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick) { - return dpp::awaitable(this, [&] (auto cc) { this->gdm_add(channel_id, user_id, access_token, nick, cc); }); -} +awaitable co_gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick); /** * @brief Removes a recipient from a Group DM @@ -705,9 +609,7 @@ auto inline co_gdm_add(snowflake channel_id, snowflake user_id, const std::strin * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_gdm_remove(snowflake channel_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->gdm_remove(channel_id, user_id, cc); }); -} +awaitable co_gdm_remove(snowflake channel_id, snowflake user_id); /** * @brief Create single emoji. @@ -721,9 +623,7 @@ auto inline co_gdm_remove(snowflake channel_id, snowflake user_id) { * @return emoji returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_create(snowflake guild_id, const class emoji& newemoji) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_create(guild_id, newemoji, cc); }); -} +awaitable co_guild_emoji_create(snowflake guild_id, const class emoji& newemoji); /** * @brief Delete a guild emoji @@ -736,9 +636,7 @@ auto inline co_guild_emoji_create(snowflake guild_id, const class emoji& newemoj * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_delete(guild_id, emoji_id, cc); }); -} +awaitable co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id); /** * @brief Edit a single emoji. @@ -752,9 +650,7 @@ auto inline co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id) { * @return emoji returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_edit(guild_id, newemoji, cc); }); -} +awaitable co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji); /** * @brief Get a single emoji @@ -766,9 +662,7 @@ auto inline co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji) * @return emoji returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emoji_get(snowflake guild_id, snowflake emoji_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emoji_get(guild_id, emoji_id, cc); }); -} +awaitable co_guild_emoji_get(snowflake guild_id, snowflake emoji_id); /** * @brief Get all emojis for a guild @@ -779,9 +673,7 @@ auto inline co_guild_emoji_get(snowflake guild_id, snowflake emoji_id) { * @return emoji_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_emojis_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_emojis_get(guild_id, cc); }); -} +awaitable co_guild_emojis_get(snowflake guild_id); /** * @brief Get the gateway information for the bot using the token @@ -790,9 +682,7 @@ auto inline co_guild_emojis_get(snowflake guild_id) { * @return gateway returned object on completion * \memberof dpp::cluster */ -auto inline co_get_gateway_bot() { - return dpp::awaitable(this, [&] (auto cc) { this->get_gateway_bot(cc); }); -} +awaitable co_get_gateway_bot(); /** * @brief Modify current member @@ -808,9 +698,7 @@ auto inline co_get_gateway_bot() { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_current_member_edit(snowflake guild_id, const std::string &nickname) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_current_member_edit(guild_id, nickname, cc); }); -} +awaitable co_guild_current_member_edit(snowflake guild_id, const std::string &nickname); /** * @brief Get the audit log for a guild @@ -826,9 +714,7 @@ auto inline co_guild_current_member_edit(snowflake guild_id, const std::string & * @return auditlog returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_auditlog_get(guild_id, user_id, action_type, before, after, limit, cc); }); -} +awaitable co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit); /** * @brief Add guild ban @@ -844,9 +730,7 @@ auto inline co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_ * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_ban_add(guild_id, user_id, delete_message_seconds, cc); }); -} +awaitable co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0); /** * @brief Delete guild ban @@ -861,9 +745,7 @@ auto inline co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t del * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_ban_delete(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_ban_delete(guild_id, user_id, cc); }); -} +awaitable co_guild_ban_delete(snowflake guild_id, snowflake user_id); /** * @brief Create a guild @@ -886,9 +768,7 @@ auto inline co_guild_ban_delete(snowflake guild_id, snowflake user_id) { * @return guild returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_create(const class guild &g) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_create(g, cc); }); -} +awaitable co_guild_create(const class guild &g); /** * @brief Delete a guild @@ -901,9 +781,7 @@ auto inline co_guild_create(const class guild &g) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_delete(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_delete(guild_id, cc); }); -} +awaitable co_guild_delete(snowflake guild_id); /** * @brief Delete guild integration @@ -919,9 +797,7 @@ auto inline co_guild_delete(snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_delete_integration(snowflake guild_id, snowflake integration_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_delete_integration(guild_id, integration_id, cc); }); -} +awaitable co_guild_delete_integration(snowflake guild_id, snowflake integration_id); /** * @brief Edit a guild @@ -936,9 +812,7 @@ auto inline co_guild_delete_integration(snowflake guild_id, snowflake integratio * @return guild returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_edit(const class guild &g) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_edit(g, cc); }); -} +awaitable co_guild_edit(const class guild &g); /** * @brief Edit guild widget @@ -953,9 +827,7 @@ auto inline co_guild_edit(const class guild &g) { * @return guild_widget returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_edit_widget(snowflake guild_id, const class guild_widget &gw) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_edit_widget(guild_id, gw, cc); }); -} +awaitable co_guild_edit_widget(snowflake guild_id, const class guild_widget &gw); /** * @brief Get single guild ban @@ -968,9 +840,7 @@ auto inline co_guild_edit_widget(snowflake guild_id, const class guild_widget &g * @return ban returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_ban(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_ban(guild_id, user_id, cc); }); -} +awaitable co_guild_get_ban(snowflake guild_id, snowflake user_id); /** * @brief Get guild ban list @@ -986,14 +856,10 @@ auto inline co_guild_get_ban(snowflake guild_id, snowflake user_id) { * @return ban_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_bans(guild_id, before, after, limit, cc); }); -} +awaitable co_guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit); -auto inline co_guild_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get(guild_id, cc); }); -} +awaitable co_guild_get(snowflake guild_id); /** * @brief Get guild integrations @@ -1008,14 +874,10 @@ auto inline co_guild_get(snowflake guild_id) { * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. * \memberof dpp::cluster */ -auto inline co_guild_get_integrations(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_integrations(guild_id, cc); }); -} +awaitable co_guild_get_integrations(snowflake guild_id); -auto inline co_guild_get_preview(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_preview(guild_id, cc); }); -} +awaitable co_guild_get_preview(snowflake guild_id); /** * @brief Get guild vanity url, if enabled @@ -1027,9 +889,7 @@ auto inline co_guild_get_preview(snowflake guild_id) { * @return invite returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_vanity(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_vanity(guild_id, cc); }); -} +awaitable co_guild_get_vanity(snowflake guild_id); /** * @brief Get guild widget @@ -1042,9 +902,7 @@ auto inline co_guild_get_vanity(snowflake guild_id) { * @return guild_widget returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_widget(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_widget(guild_id, cc); }); -} +awaitable co_guild_get_widget(snowflake guild_id); /** * @brief Modify guild integration @@ -1057,9 +915,7 @@ auto inline co_guild_get_widget(snowflake guild_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_modify_integration(snowflake guild_id, const class integration &i) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_modify_integration(guild_id, i, cc); }); -} +awaitable co_guild_modify_integration(snowflake guild_id, const class integration &i); /** * @brief Get prune counts @@ -1076,9 +932,7 @@ auto inline co_guild_modify_integration(snowflake guild_id, const class integrat * @return prune returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_prune_counts(guild_id, pruneinfo, cc); }); -} +awaitable co_guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo); /** * @brief Begin guild prune @@ -1097,9 +951,7 @@ auto inline co_guild_get_prune_counts(snowflake guild_id, const struct prune& pr * @return prune returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_begin_prune(guild_id, pruneinfo, cc); }); -} +awaitable co_guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo); /** * @brief Change current user nickname @@ -1116,9 +968,7 @@ auto inline co_guild_begin_prune(snowflake guild_id, const struct prune& prunein * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_set_nickname(snowflake guild_id, const std::string &nickname) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_set_nickname(guild_id, nickname, cc); }); -} +awaitable co_guild_set_nickname(snowflake guild_id, const std::string &nickname); /** * @brief Sync guild integration @@ -1130,9 +980,62 @@ auto inline co_guild_set_nickname(snowflake guild_id, const std::string &nicknam * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sync_integration(snowflake guild_id, snowflake integration_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sync_integration(guild_id, integration_id, cc); }); -} +awaitable co_guild_sync_integration(snowflake guild_id, snowflake integration_id); + +/** + * @brief Get the guild's onboarding configuration + * + * @see dpp::cluster::guild_get_onboarding + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + */ +awaitable co_guild_get_onboarding(snowflake guild_id); + +/** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see dpp::cluster::guild_edit_onboarding + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + */ +awaitable co_guild_edit_onboarding(const struct onboarding& o); + +/** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see dpp::cluster::guild_get_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + */ +awaitable co_guild_get_welcome_screen(snowflake guild_id); + +/** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see dpp::cluster::guild_edit_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + */ +awaitable co_guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled); /** * @brief Add guild member. Needs a specific oauth2 scope, from which you get the access_token. @@ -1152,9 +1055,7 @@ auto inline co_guild_sync_integration(snowflake guild_id, snowflake integration_ * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_add_member(const guild_member& gm, const std::string &access_token) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_add_member(gm, access_token, cc); }); -} +awaitable co_guild_add_member(const guild_member& gm, const std::string &access_token); /** * @brief Edit the properties of an existing guild member @@ -1170,9 +1071,7 @@ auto inline co_guild_add_member(const guild_member& gm, const std::string &acces * @return guild_member returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_edit_member(const guild_member& gm) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_edit_member(gm, cc); }); -} +awaitable co_guild_edit_member(const guild_member& gm); /** * @brief Get a guild member @@ -1183,9 +1082,7 @@ auto inline co_guild_edit_member(const guild_member& gm) { * @return guild_member returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_member(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_member(guild_id, user_id, cc); }); -} +awaitable co_guild_get_member(snowflake guild_id, snowflake user_id); /** * @brief Get all guild members @@ -1199,9 +1096,7 @@ auto inline co_guild_get_member(snowflake guild_id, snowflake user_id) { * @return guild_member_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake after) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_members(guild_id, limit, after, cc); }); -} +awaitable co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake after); /** * @brief Add role to guild member @@ -1217,9 +1112,7 @@ auto inline co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake a * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_add_role(guild_id, user_id, role_id, cc); }); -} +awaitable co_guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Remove (kick) a guild member @@ -1235,9 +1128,7 @@ auto inline co_guild_member_add_role(snowflake guild_id, snowflake user_id, snow * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_delete(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_delete(guild_id, user_id, cc); }); -} +awaitable co_guild_member_delete(snowflake guild_id, snowflake user_id); /** * @brief Remove (kick) a guild member @@ -1252,9 +1143,7 @@ auto inline co_guild_member_delete(snowflake guild_id, snowflake user_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_kick(snowflake guild_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_kick(guild_id, user_id, cc); }); -} +awaitable co_guild_member_kick(snowflake guild_id, snowflake user_id); /** * @brief Set the timeout of a guild member @@ -1269,9 +1158,7 @@ auto inline co_guild_member_kick(snowflake guild_id, snowflake user_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_timeout(guild_id, user_id, communication_disabled_until, cc); }); -} +awaitable co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); /** * @brief Remove role from guild member @@ -1288,9 +1175,7 @@ auto inline co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_ * @deprecated Use dpp::cluster::guild_member_remove_role instead * \memberof dpp::cluster */ -auto inline co_guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_delete_role(guild_id, user_id, role_id, cc); }); -} +awaitable co_guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Remove role from guild member @@ -1306,9 +1191,7 @@ auto inline co_guild_member_delete_role(snowflake guild_id, snowflake user_id, s * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_remove_role(guild_id, user_id, role_id, cc); }); -} +awaitable co_guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id); /** * @brief Moves the guild member to a other voice channel, if member is connected to one. @@ -1325,9 +1208,7 @@ auto inline co_guild_member_remove_role(snowflake guild_id, snowflake user_id, s * @return guild_member returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_member_move(channel_id, guild_id, user_id, cc); }); -} +awaitable co_guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id); /** * @brief Search for guild members based on whether their username or nickname starts with the given string. @@ -1341,9 +1222,7 @@ auto inline co_guild_member_move(const snowflake channel_id, const snowflake gui * @return guild_member_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_search_members(guild_id, query, limit, cc); }); -} +awaitable co_guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit); /** * @brief Get guild invites @@ -1356,14 +1235,10 @@ auto inline co_guild_search_members(snowflake guild_id, const std::string& query * @return invite_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_invites(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_invites(guild_id, cc); }); -} +awaitable co_guild_get_invites(snowflake guild_id); -auto inline co_invite_delete(const std::string &invitecode) { - return dpp::awaitable(this, [&] (auto cc) { this->invite_delete(invitecode, cc); }); -} +awaitable co_invite_delete(const std::string &invitecode); /** * @brief Get details about an invite @@ -1374,9 +1249,7 @@ auto inline co_invite_delete(const std::string &invitecode) { * @return invite returned object on completion * \memberof dpp::cluster */ -auto inline co_invite_get(const std::string &invite_code) { - return dpp::awaitable(this, [&] (auto cc) { this->invite_get(invite_code, cc); }); -} +awaitable co_invite_get(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. @@ -1388,9 +1261,7 @@ auto inline co_invite_get(const std::string &invite_code) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_add_reaction(const struct message &m, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_add_reaction(m, reaction, cc); }); -} +awaitable co_message_add_reaction(const struct message &m, const std::string &reaction); /** * @brief Add a reaction to a message by id. The reaction string must be either an `emojiname:id` or a unicode character. @@ -1403,9 +1274,7 @@ auto inline co_message_add_reaction(const struct message &m, const std::string & * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_add_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_add_reaction(message_id, channel_id, reaction, cc); }); -} +awaitable co_message_add_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Send a message to a channel. The callback function is called when the message has been sent @@ -1416,9 +1285,7 @@ auto inline co_message_add_reaction(snowflake message_id, snowflake channel_id, * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_create(const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->message_create(m, cc); }); -} +awaitable co_message_create(const struct message &m); /** * @brief Crosspost a message. The callback function is called when the message has been sent @@ -1430,9 +1297,7 @@ auto inline co_message_create(const message &m) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_crosspost(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_crosspost(message_id, channel_id, cc); }); -} +awaitable co_message_crosspost(snowflake message_id, snowflake channel_id); /** * @brief Delete all reactions on a message @@ -1443,9 +1308,7 @@ auto inline co_message_crosspost(snowflake message_id, snowflake channel_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_all_reactions(const struct message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_all_reactions(m, cc); }); -} +awaitable co_message_delete_all_reactions(const struct message &m); /** * @brief Delete all reactions on a message by id @@ -1457,9 +1320,7 @@ auto inline co_message_delete_all_reactions(const struct message &m) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_all_reactions(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_all_reactions(message_id, channel_id, cc); }); -} +awaitable co_message_delete_all_reactions(snowflake message_id, snowflake channel_id); /** * @brief Bulk delete messages from a channel. The callback function is called when the message has been edited @@ -1474,9 +1335,7 @@ auto inline co_message_delete_all_reactions(snowflake message_id, snowflake chan * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_bulk(const std::vector& message_ids, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_bulk(message_ids, channel_id, cc); }); -} +awaitable co_message_delete_bulk(const std::vector &message_ids, snowflake channel_id); /** * @brief Delete a message from a channel. The callback function is called when the message has been edited @@ -1489,9 +1348,7 @@ auto inline co_message_delete_bulk(const std::vector& message_ids, sn * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete(message_id, channel_id, cc); }); -} +awaitable co_message_delete(snowflake message_id, snowflake channel_id); /** * @brief Delete own reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character. @@ -1503,9 +1360,7 @@ auto inline co_message_delete(snowflake message_id, snowflake channel_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_own_reaction(const struct message &m, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_own_reaction(m, reaction, cc); }); -} +awaitable co_message_delete_own_reaction(const struct message &m, const std::string &reaction); /** * @brief Delete own reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character. @@ -1518,9 +1373,7 @@ auto inline co_message_delete_own_reaction(const struct message &m, const std::s * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_own_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_own_reaction(message_id, channel_id, reaction, cc); }); -} +awaitable co_message_delete_own_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Delete a user's reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character @@ -1533,9 +1386,7 @@ auto inline co_message_delete_own_reaction(snowflake message_id, snowflake chann * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_reaction(const struct message &m, snowflake user_id, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_reaction(m, user_id, reaction, cc); }); -} +awaitable co_message_delete_reaction(const struct message &m, snowflake user_id, const std::string &reaction); /** * @brief Delete a user's reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character @@ -1549,9 +1400,7 @@ auto inline co_message_delete_reaction(const struct message &m, snowflake user_i * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_reaction(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_reaction(message_id, channel_id, user_id, reaction, cc); }); -} +awaitable co_message_delete_reaction(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction); /** * @brief Delete all reactions on a message using a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character @@ -1563,9 +1412,7 @@ auto inline co_message_delete_reaction(snowflake message_id, snowflake channel_i * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_reaction_emoji(const struct message &m, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_reaction_emoji(m, reaction, cc); }); -} +awaitable co_message_delete_reaction_emoji(const struct message &m, const std::string &reaction); /** * @brief Delete all reactions on a message using a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character @@ -1578,9 +1425,7 @@ auto inline co_message_delete_reaction_emoji(const struct message &m, const std: * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_delete_reaction_emoji(snowflake message_id, snowflake channel_id, const std::string &reaction) { - return dpp::awaitable(this, [&] (auto cc) { this->message_delete_reaction_emoji(message_id, channel_id, reaction, cc); }); -} +awaitable co_message_delete_reaction_emoji(snowflake message_id, snowflake channel_id, const std::string &reaction); /** * @brief Edit a message on a channel. The callback function is called when the message has been edited @@ -1591,9 +1436,7 @@ auto inline co_message_delete_reaction_emoji(snowflake message_id, snowflake cha * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_edit(const message &m) { - return dpp::awaitable(this, [&] (auto cc) { this->message_edit(m, cc); }); -} +awaitable co_message_edit(const struct message &m); /** * @brief Get a message @@ -1605,9 +1448,7 @@ auto inline co_message_edit(const message &m) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_message_get(snowflake message_id, snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_get(message_id, channel_id, cc); }); -} +awaitable co_message_get(snowflake message_id, snowflake channel_id); /** * @brief Get reactions on a message for a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character @@ -1622,9 +1463,7 @@ auto inline co_message_get(snowflake message_id, snowflake channel_id) { * @return user_map returned object on completion * \memberof dpp::cluster */ -auto inline co_message_get_reactions(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit) { - return dpp::awaitable(this, [&] (auto cc) { this->message_get_reactions(m, reaction, before, after, limit, cc); }); -} +awaitable co_message_get_reactions(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit); /** * @brief Get reactions on a message for a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character @@ -1640,9 +1479,7 @@ auto inline co_message_get_reactions(const struct message &m, const std::string * @return emoji_map returned object on completion * \memberof dpp::cluster */ -auto inline co_message_get_reactions(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit) { - return dpp::awaitable(this, [&] (auto cc) { this->message_get_reactions(message_id, channel_id, reaction, before, after, limit, cc); }); -} +awaitable co_message_get_reactions(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit); /** * @brief Pin a message @@ -1654,9 +1491,7 @@ auto inline co_message_get_reactions(snowflake message_id, snowflake channel_id, * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_pin(snowflake channel_id, snowflake message_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_pin(channel_id, message_id, cc); }); -} +awaitable co_message_pin(snowflake channel_id, snowflake message_id); /** * @brief Get multiple messages. @@ -1673,9 +1508,7 @@ auto inline co_message_pin(snowflake channel_id, snowflake message_id) { * @return message_map returned object on completion * \memberof dpp::cluster */ -auto inline co_messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->messages_get(channel_id, around, before, after, limit, cc); }); -} +awaitable co_messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); /** * @brief Unpin a message @@ -1687,9 +1520,7 @@ auto inline co_messages_get(snowflake channel_id, snowflake around, snowflake be * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_message_unpin(snowflake channel_id, snowflake message_id) { - return dpp::awaitable(this, [&] (auto cc) { this->message_unpin(channel_id, message_id, cc); }); -} +awaitable co_message_unpin(snowflake channel_id, snowflake message_id); /** * @brief Get a channel's pins @@ -1699,9 +1530,7 @@ auto inline co_message_unpin(snowflake channel_id, snowflake message_id) { * @return message_map returned object on completion * \memberof dpp::cluster */ -auto inline co_channel_pins_get(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->channel_pins_get(channel_id, cc); }); -} +awaitable co_channel_pins_get(snowflake channel_id); /** * @brief Create a role on a guild @@ -1716,9 +1545,7 @@ auto inline co_channel_pins_get(snowflake channel_id) { * @return role returned object on completion * \memberof dpp::cluster */ -auto inline co_role_create(const class role &r) { - return dpp::awaitable(this, [&] (auto cc) { this->role_create(r, cc); }); -} +awaitable co_role_create(const class role &r); /** * @brief Delete a role @@ -1733,9 +1560,7 @@ auto inline co_role_create(const class role &r) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_role_delete(snowflake guild_id, snowflake role_id) { - return dpp::awaitable(this, [&] (auto cc) { this->role_delete(guild_id, role_id, cc); }); -} +awaitable co_role_delete(snowflake guild_id, snowflake role_id); /** * @brief Edit a role on a guild @@ -1749,9 +1574,7 @@ auto inline co_role_delete(snowflake guild_id, snowflake role_id) { * @return role returned object on completion * \memberof dpp::cluster */ -auto inline co_role_edit(const class role &r) { - return dpp::awaitable(this, [&] (auto cc) { this->role_edit(r, cc); }); -} +awaitable co_role_edit(const class role &r); /** * @brief Edit multiple role's position in a guild. Returns a list of all roles of the guild on success. @@ -1767,9 +1590,7 @@ auto inline co_role_edit(const class role &r) { * @return role_map returned object on completion * \memberof dpp::cluster */ -auto inline co_roles_edit_position(snowflake guild_id, const std::vector &roles) { - return dpp::awaitable(this, [&] (auto cc) { this->roles_edit_position(guild_id, roles, cc); }); -} +awaitable co_roles_edit_position(snowflake guild_id, const std::vector &roles); /** * @brief Get a role for a guild @@ -1780,9 +1601,7 @@ auto inline co_roles_edit_position(snowflake guild_id, const std::vector & * @return role_map returned object on completion * \memberof dpp::cluster */ -auto inline co_roles_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->roles_get(guild_id, cc); }); -} +awaitable co_roles_get(snowflake guild_id); /** * @brief Get the application's role connection metadata records @@ -1793,9 +1612,7 @@ auto inline co_roles_get(snowflake guild_id) { * @return application_role_connection returned object on completion * \memberof dpp::cluster */ -auto inline co_application_role_connection_get(snowflake application_id) { - return dpp::awaitable(this, [&] (auto cc) { this->application_role_connection_get(application_id, cc); }); -} +awaitable co_application_role_connection_get(snowflake application_id); /** * @brief Update the application's role connection metadata records @@ -1808,9 +1625,7 @@ auto inline co_application_role_connection_get(snowflake application_id) { * @note An application can have a maximum of 5 metadata records. * \memberof dpp::cluster */ -auto inline co_application_role_connection_update(snowflake application_id, const std::vector &connection_metadata) { - return dpp::awaitable(this, [&] (auto cc) { this->application_role_connection_update(application_id, connection_metadata, cc); }); -} +awaitable co_application_role_connection_update(snowflake application_id, const std::vector &connection_metadata); /** * @brief Get user application role connection @@ -1821,9 +1636,7 @@ auto inline co_application_role_connection_update(snowflake application_id, cons * @return application_role_connection returned object on completion * \memberof dpp::cluster */ -auto inline co_user_application_role_connection_get(snowflake application_id) { - return dpp::awaitable(this, [&] (auto cc) { this->user_application_role_connection_get(application_id, cc); }); -} +awaitable co_user_application_role_connection_get(snowflake application_id); /** * @brief Update user application role connection @@ -1835,9 +1648,7 @@ auto inline co_user_application_role_connection_get(snowflake application_id) { * @return application_role_connection returned object on completion * \memberof dpp::cluster */ -auto inline co_user_application_role_connection_update(snowflake application_id, const application_role_connection &connection) { - return dpp::awaitable(this, [&] (auto cc) { this->user_application_role_connection_update(application_id, connection, cc); }); -} +awaitable co_user_application_role_connection_update(snowflake application_id, const application_role_connection &connection); /** * @brief Get all scheduled events for a guild @@ -1847,9 +1658,7 @@ auto inline co_user_application_role_connection_update(snowflake application_id, * @return scheduled_event_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_events_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_events_get(guild_id, cc); }); -} +awaitable co_guild_events_get(snowflake guild_id); /** * @brief Create a scheduled event on a guild @@ -1860,9 +1669,7 @@ auto inline co_guild_events_get(snowflake guild_id) { * @return scheduled_event returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_create(const scheduled_event& event) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_create(event, cc); }); -} +awaitable co_guild_event_create(const scheduled_event& event); /** * @brief Delete a scheduled event from a guild @@ -1874,9 +1681,7 @@ auto inline co_guild_event_create(const scheduled_event& event) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_delete(snowflake event_id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_delete(event_id, guild_id, cc); }); -} +awaitable co_guild_event_delete(snowflake event_id, snowflake guild_id); /** * @brief Edit/modify a scheduled event on a guild @@ -1887,9 +1692,7 @@ auto inline co_guild_event_delete(snowflake event_id, snowflake guild_id) { * @return scheduled_event returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_edit(const scheduled_event& event) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_edit(event, cc); }); -} +awaitable co_guild_event_edit(const scheduled_event& event); /** * @brief Get a scheduled event for a guild @@ -1901,14 +1704,10 @@ auto inline co_guild_event_edit(const scheduled_event& event) { * @return scheduled_event returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_event_get(snowflake guild_id, snowflake event_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_event_get(guild_id, event_id, cc); }); -} +awaitable co_guild_event_get(snowflake guild_id, snowflake event_id); -auto inline co_stage_instance_create(const stage_instance& si) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_create(si, cc); }); -} +awaitable co_stage_instance_create(const stage_instance& si); /** * @brief Get the stage instance associated with the channel id, if it exists. @@ -1918,14 +1717,10 @@ auto inline co_stage_instance_create(const stage_instance& si) { * @return stage_instance returned object on completion * \memberof dpp::cluster */ -auto inline co_stage_instance_get(const snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_get(channel_id, cc); }); -} +awaitable co_stage_instance_get(const snowflake channel_id); -auto inline co_stage_instance_edit(const stage_instance& si) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_edit(si, cc); }); -} +awaitable co_stage_instance_edit(const stage_instance& si); /** * @brief Delete a stage instance. @@ -1936,9 +1731,7 @@ auto inline co_stage_instance_edit(const stage_instance& si) { * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. * \memberof dpp::cluster */ -auto inline co_stage_instance_delete(const snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->stage_instance_delete(channel_id, cc); }); -} +awaitable co_stage_instance_delete(const snowflake channel_id); /** * @brief Create a sticker in a guild @@ -1949,9 +1742,7 @@ auto inline co_stage_instance_delete(const snowflake channel_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_create(sticker &s) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_create(s, cc); }); -} +awaitable co_guild_sticker_create(sticker &s); /** * @brief Delete a sticker from a guild @@ -1963,9 +1754,7 @@ auto inline co_guild_sticker_create(sticker &s) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_delete(sticker_id, guild_id, cc); }); -} +awaitable co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id); /** * @brief Get a guild sticker @@ -1976,9 +1765,7 @@ auto inline co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_get(snowflake id, snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_get(id, guild_id, cc); }); -} +awaitable co_guild_sticker_get(snowflake id, snowflake guild_id); /** * @brief Modify a sticker in a guild @@ -1989,9 +1776,7 @@ auto inline co_guild_sticker_get(snowflake id, snowflake guild_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_sticker_modify(sticker &s) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_sticker_modify(s, cc); }); -} +awaitable co_guild_sticker_modify(sticker &s); /** * @brief Get all guild stickers @@ -2001,9 +1786,7 @@ auto inline co_guild_sticker_modify(sticker &s) { * @return sticker_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_stickers_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_stickers_get(guild_id, cc); }); -} +awaitable co_guild_stickers_get(snowflake guild_id); /** * @brief Get a nitro sticker @@ -2013,9 +1796,7 @@ auto inline co_guild_stickers_get(snowflake guild_id) { * @return sticker returned object on completion * \memberof dpp::cluster */ -auto inline co_nitro_sticker_get(snowflake id) { - return dpp::awaitable(this, [&] (auto cc) { this->nitro_sticker_get(id, cc); }); -} +awaitable co_nitro_sticker_get(snowflake id); /** * @brief Get sticker packs @@ -2024,9 +1805,7 @@ auto inline co_nitro_sticker_get(snowflake id) { * @return sticker_pack_map returned object on completion * \memberof dpp::cluster */ -auto inline co_sticker_packs_get() { - return dpp::awaitable(this, [&] (auto cc) { this->sticker_packs_get(cc); }); -} +awaitable co_sticker_packs_get(); /** * @brief Create a new guild based on a template. @@ -2038,9 +1817,7 @@ auto inline co_sticker_packs_get() { * @return guild returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_create_from_template(const std::string &code, const std::string &name) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_create_from_template(code, name, cc); }); -} +awaitable co_guild_create_from_template(const std::string &code, const std::string &name); /** * @brief Creates a template for the guild @@ -2053,9 +1830,7 @@ auto inline co_guild_create_from_template(const std::string &code, const std::st * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_create(snowflake guild_id, const std::string &name, const std::string &description) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_create(guild_id, name, description, cc); }); -} +awaitable co_guild_template_create(snowflake guild_id, const std::string &name, const std::string &description); /** * @brief Deletes the template @@ -2067,9 +1842,7 @@ auto inline co_guild_template_create(snowflake guild_id, const std::string &name * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_delete(snowflake guild_id, const std::string &code) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_delete(guild_id, code, cc); }); -} +awaitable co_guild_template_delete(snowflake guild_id, const std::string &code); /** * @brief Modifies the template's metadata. @@ -2083,9 +1856,7 @@ auto inline co_guild_template_delete(snowflake guild_id, const std::string &code * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_modify(guild_id, code, name, description, cc); }); -} +awaitable co_guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); /** * @brief Get guild templates @@ -2096,9 +1867,7 @@ auto inline co_guild_template_modify(snowflake guild_id, const std::string &code * @return dtemplate_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_templates_get(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_templates_get(guild_id, cc); }); -} +awaitable co_guild_templates_get(snowflake guild_id); /** * @brief Syncs the template to the guild's current state. @@ -2110,9 +1879,7 @@ auto inline co_guild_templates_get(snowflake guild_id) { * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_template_sync(snowflake guild_id, const std::string &code) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_template_sync(guild_id, code, cc); }); -} +awaitable co_guild_template_sync(snowflake guild_id, const std::string &code); /** * @brief Get a template @@ -2122,9 +1889,7 @@ auto inline co_guild_template_sync(snowflake guild_id, const std::string &code) * @return dtemplate returned object on completion * \memberof dpp::cluster */ -auto inline co_template_get(const std::string &code) { - return dpp::awaitable(this, [&] (auto cc) { this->template_get(code, cc); }); -} +awaitable co_template_get(const std::string &code); /** * @brief Join a thread @@ -2134,9 +1899,7 @@ auto inline co_template_get(const std::string &code) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_join_thread(snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_join_thread(thread_id, cc); }); -} +awaitable co_current_user_join_thread(snowflake thread_id); /** * @brief Leave a thread @@ -2146,9 +1909,7 @@ auto inline co_current_user_join_thread(snowflake thread_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_leave_thread(snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_leave_thread(thread_id, cc); }); -} +awaitable co_current_user_leave_thread(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. @@ -2158,9 +1919,7 @@ auto inline co_current_user_leave_thread(snowflake thread_id) { * @return active_threads returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_active(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_active(guild_id, cc); }); -} +awaitable co_threads_get_active(snowflake guild_id); /** * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) @@ -2172,37 +1931,31 @@ auto inline co_threads_get_active(snowflake guild_id) { * @return thread_map returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_joined_private_archived(channel_id, before_id, limit, cc); }); -} +awaitable co_threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit); /** * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) * @see dpp::cluster::threads_get_private_archived * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_private_archived(channel_id, before_timestamp, limit, cc); }); -} +awaitable co_threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit); /** * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) * @see dpp::cluster::threads_get_public_archived * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads * @param channel_id Channel to get public archived threads for - * @param before_timestamp Get threads before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster */ -auto inline co_threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit) { - return dpp::awaitable(this, [&] (auto cc) { this->threads_get_public_archived(channel_id, before_timestamp, limit, cc); }); -} +awaitable co_threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit); /** * @brief Get a thread member @@ -2213,9 +1966,7 @@ auto inline co_threads_get_public_archived(snowflake channel_id, time_t before_t * @return thread_member returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_member_get(const snowflake thread_id, const snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_member_get(thread_id, user_id, cc); }); -} +awaitable co_thread_member_get(const snowflake thread_id, const snowflake user_id); /** * @brief Get members of a thread @@ -2225,9 +1976,7 @@ auto inline co_thread_member_get(const snowflake thread_id, const snowflake user * @return thread_member_map returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_members_get(snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_members_get(thread_id, cc); }); -} +awaitable co_thread_members_get(snowflake thread_id); /** * @brief Create a thread in forum channel @@ -2244,9 +1993,7 @@ auto inline co_thread_members_get(snowflake thread_id) { * @return thread returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_create_in_forum(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_create_in_forum(thread_name, channel_id, msg, auto_archive_duration, rate_limit_per_user, applied_tags, cc); }); -} +awaitable co_thread_create_in_forum(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}); /** * @brief Create a thread @@ -2263,9 +2010,7 @@ auto inline co_thread_create_in_forum(const std::string& thread_name, snowflake * @return thread returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_create(thread_name, channel_id, auto_archive_duration, thread_type, invitable, rate_limit_per_user, cc); }); -} +awaitable co_thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user); /** * @brief Edit a thread @@ -2277,9 +2022,7 @@ auto inline co_thread_create(const std::string& thread_name, snowflake channel_i * @return thread returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_edit(const thread &t) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_edit(t, cc); }); -} +awaitable co_thread_edit(const thread &t); /** * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) @@ -2294,9 +2037,7 @@ auto inline co_thread_edit(const thread &t) { * @return thread returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_create_with_message(thread_name, channel_id, message_id, auto_archive_duration, rate_limit_per_user, cc); }); -} +awaitable co_thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user); /** * @brief Add a member to a thread @@ -2307,9 +2048,7 @@ auto inline co_thread_create_with_message(const std::string& thread_name, snowfl * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_member_add(snowflake thread_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_member_add(thread_id, user_id, cc); }); -} +awaitable co_thread_member_add(snowflake thread_id, snowflake user_id); /** * @brief Remove a member from a thread @@ -2320,9 +2059,7 @@ auto inline co_thread_member_add(snowflake thread_id, snowflake user_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_thread_member_remove(snowflake thread_id, snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->thread_member_remove(thread_id, user_id, cc); }); -} +awaitable co_thread_member_remove(snowflake thread_id, snowflake user_id); /** * @brief Edit current (bot) user @@ -2338,9 +2075,7 @@ auto inline co_thread_member_remove(snowflake thread_id, snowflake user_id) { * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes * \memberof dpp::cluster */ -auto inline co_current_user_edit(const std::string &nickname, const std::string& image_blob, const image_type type) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_edit(nickname, image_blob, type, cc); }); -} +awaitable co_current_user_edit(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png); /** * @brief Get current (bot) application @@ -2350,9 +2085,7 @@ auto inline co_current_user_edit(const std::string &nickname, const std::string& * @return application returned object on completion * \memberof dpp::cluster */ -auto inline co_current_application_get() { - return dpp::awaitable(this, [&] (auto cc) { this->current_application_get(cc); }); -} +awaitable co_current_application_get(); /** * @brief Get current (bot) user @@ -2364,9 +2097,7 @@ auto inline co_current_application_get() { * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. * \memberof dpp::cluster */ -auto inline co_current_user_get() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_get(cc); }); -} +awaitable co_current_user_get(); /** * @brief Set the bot's voice state on a stage channel @@ -2391,9 +2122,7 @@ auto inline co_current_user_get() { * @throw std::logic_exception You attempted to set a request_to_speak_timestamp in the past which is not the value of 0. * \memberof dpp::cluster */ -auto inline co_current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress, time_t request_to_speak_timestamp) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_set_voice_state(guild_id, channel_id, suppress, request_to_speak_timestamp, cc); }); -} +awaitable co_current_user_set_voice_state(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 @@ -2417,9 +2146,7 @@ auto inline co_current_user_set_voice_state(snowflake guild_id, snowflake channe * @param suppress True if the user's audio should be suppressed, false if it should not * \memberof dpp::cluster */ -auto inline co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress) { - return dpp::awaitable(this, [&] (auto cc) { this->user_set_voice_state(user_id, guild_id, channel_id, suppress, cc); }); -} +awaitable co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); /** * @brief Get current user's connections (linked accounts, e.g. steam, xbox). @@ -2430,9 +2157,7 @@ auto inline co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowf * @return connection_map returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_connections_get() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_connections_get(cc); }); -} +awaitable co_current_user_connections_get(); /** * @brief Get current (bot) user guilds @@ -2441,9 +2166,7 @@ auto inline co_current_user_connections_get() { * @return guild_map returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_get_guilds() { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_get_guilds(cc); }); -} +awaitable co_current_user_get_guilds(); /** * @brief Leave a guild @@ -2453,9 +2176,7 @@ auto inline co_current_user_get_guilds() { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_current_user_leave_guild(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->current_user_leave_guild(guild_id, cc); }); -} +awaitable co_current_user_leave_guild(snowflake guild_id); /** * @brief Get a user by id, without using the cache @@ -2470,9 +2191,7 @@ auto inline co_current_user_leave_guild(snowflake guild_id) { * Call `dpp::find_user` instead that looks up the user in the cache rather than a REST call. * \memberof dpp::cluster */ -auto inline co_user_get(snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->user_get(user_id, cc); }); -} +awaitable co_user_get(snowflake user_id); /** * @brief Get a user by id, checking in the cache first @@ -2487,9 +2206,7 @@ auto inline co_user_get(snowflake user_id) { * 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 */ -auto inline co_user_get_cached(snowflake user_id) { - return dpp::awaitable(this, [&] (auto cc) { this->user_get_cached(user_id, cc); }); -} +awaitable co_user_get_cached(snowflake user_id); /** * @brief Get all voice regions @@ -2498,9 +2215,7 @@ auto inline co_user_get_cached(snowflake user_id) { * @return voiceregion_map returned object on completion * \memberof dpp::cluster */ -auto inline co_get_voice_regions() { - return dpp::awaitable(this, [&] (auto cc) { this->get_voice_regions(cc); }); -} +awaitable co_get_voice_regions(); /** * @brief Get guild voice regions. @@ -2515,9 +2230,7 @@ auto inline co_get_voice_regions() { * @return voiceregion_map returned object on completion * \memberof dpp::cluster */ -auto inline co_guild_get_voice_regions(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->guild_get_voice_regions(guild_id, cc); }); -} +awaitable co_guild_get_voice_regions(snowflake guild_id); /** * @brief Create a webhook @@ -2528,9 +2241,7 @@ auto inline co_guild_get_voice_regions(snowflake guild_id) { * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_create_webhook(const class webhook &w) { - return dpp::awaitable(this, [&] (auto cc) { this->create_webhook(w, cc); }); -} +awaitable co_create_webhook(const class webhook &w); /** * @brief Delete a webhook @@ -2541,9 +2252,7 @@ auto inline co_create_webhook(const class webhook &w) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_delete_webhook(snowflake webhook_id) { - return dpp::awaitable(this, [&] (auto cc) { this->delete_webhook(webhook_id, cc); }); -} +awaitable co_delete_webhook(snowflake webhook_id); /** * @brief Delete webhook message @@ -2556,9 +2265,7 @@ auto inline co_delete_webhook(snowflake webhook_id) { * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->delete_webhook_message(wh, message_id, thread_id, cc); }); -} +awaitable co_delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Delete webhook with token @@ -2569,9 +2276,7 @@ auto inline co_delete_webhook_message(const class webhook &wh, snowflake message * @return confirmation returned object on completion * \memberof dpp::cluster */ -auto inline co_delete_webhook_with_token(snowflake webhook_id, const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->delete_webhook_with_token(webhook_id, token, cc); }); -} +awaitable co_delete_webhook_with_token(snowflake webhook_id, const std::string &token); /** * @brief Edit webhook @@ -2582,9 +2287,7 @@ auto inline co_delete_webhook_with_token(snowflake webhook_id, const std::string * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_edit_webhook(const class webhook& wh) { - return dpp::awaitable(this, [&] (auto cc) { this->edit_webhook(wh, cc); }); -} +awaitable co_edit_webhook(const class webhook& wh); /** * @brief Edit webhook message @@ -2603,9 +2306,7 @@ auto inline co_edit_webhook(const class webhook& wh) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_edit_webhook_message(const class webhook &wh, const struct message& m, snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->edit_webhook_message(wh, m, thread_id, cc); }); -} +awaitable co_edit_webhook_message(const class webhook &wh, const struct message &m, snowflake thread_id = 0); /** * @brief Edit webhook with token (token is encapsulated in the webhook object) @@ -2615,9 +2316,7 @@ auto inline co_edit_webhook_message(const class webhook &wh, const struct messag * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_edit_webhook_with_token(const class webhook& wh) { - return dpp::awaitable(this, [&] (auto cc) { this->edit_webhook_with_token(wh, cc); }); -} +awaitable co_edit_webhook_with_token(const class webhook& wh); /** * @brief Execute webhook @@ -2633,9 +2332,7 @@ auto inline co_edit_webhook_with_token(const class webhook& wh) { * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. * \memberof dpp::cluster */ -auto inline co_execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, const std::string& thread_name) { - return dpp::awaitable(this, [&] (auto cc) { this->execute_webhook(wh, m, wait, thread_id, thread_name, cc); }); -} +awaitable co_execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = ""); /** * @brief Get channel webhooks @@ -2645,9 +2342,7 @@ auto inline co_execute_webhook(const class webhook &wh, const struct message& m, * @return webhook_map returned object on completion * \memberof dpp::cluster */ -auto inline co_get_channel_webhooks(snowflake channel_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_channel_webhooks(channel_id, cc); }); -} +awaitable co_get_channel_webhooks(snowflake channel_id); /** * @brief Get guild webhooks @@ -2657,9 +2352,7 @@ auto inline co_get_channel_webhooks(snowflake channel_id) { * @return webhook_map returned object on completion * \memberof dpp::cluster */ -auto inline co_get_guild_webhooks(snowflake guild_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_guild_webhooks(guild_id, cc); }); -} +awaitable co_get_guild_webhooks(snowflake guild_id); /** * @brief Get webhook @@ -2669,9 +2362,7 @@ auto inline co_get_guild_webhooks(snowflake guild_id) { * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_get_webhook(snowflake webhook_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_webhook(webhook_id, cc); }); -} +awaitable co_get_webhook(snowflake webhook_id); /** * @brief Get webhook message @@ -2684,9 +2375,7 @@ auto inline co_get_webhook(snowflake webhook_id) { * @return message returned object on completion * \memberof dpp::cluster */ -auto inline co_get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id) { - return dpp::awaitable(this, [&] (auto cc) { this->get_webhook_message(wh, message_id, thread_id, cc); }); -} +awaitable co_get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); /** * @brief Get webhook using token @@ -2697,13 +2386,9 @@ auto inline co_get_webhook_message(const class webhook &wh, snowflake message_id * @return webhook returned object on completion * \memberof dpp::cluster */ -auto inline co_get_webhook_with_token(snowflake webhook_id, const std::string &token) { - return dpp::awaitable(this, [&] (auto cc) { this->get_webhook_with_token(webhook_id, token, cc); }); -} +awaitable co_get_webhook_with_token(snowflake webhook_id, const std::string &token); /* End of auto-generated definitions */ -auto inline co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}) { - return dpp::awaitable(this, [&] (auto cc) { this->request(url, method, cc, mimetype, headers); }); -} +awaitable co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}); diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h index 25c89f9fd9..7a61f38d5f 100644 --- a/include/dpp/cluster_sync_calls.h +++ b/include/dpp/cluster_sync_calls.h @@ -1204,6 +1204,73 @@ confirmation guild_set_nickname_sync(snowflake guild_id, const std::string &nick */ 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. * @@ -2315,7 +2382,7 @@ thread_map threads_get_joined_private_archived_sync(snowflake channel_id, snowfl * @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 before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster @@ -2330,7 +2397,7 @@ thread_map threads_get_private_archived_sync(snowflake channel_id, time_t befor * @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 before this timestamp + * @param before_timestamp Get threads archived before this timestamp * @param limit Number of threads to get * @return thread_map returned object on completion * \memberof dpp::cluster diff --git a/include/dpp/collector.h b/include/dpp/collector.h index 0bdb930758..a512287a5f 100644 --- a/include/dpp/collector.h +++ b/include/dpp/collector.h @@ -163,6 +163,8 @@ class collected_reaction : public managed { channel* react_channel{}; /// Reacted emoji emoji react_emoji; + /// ID of the user who authored the message which was reacted to (Optional) + snowflake message_author_id{}; }; /** @@ -273,6 +275,7 @@ class reaction_collector : public reaction_collector_t { react.react_member = element.reacting_member; react.react_channel = element.reacting_channel; react.react_emoji = element.reacting_emoji; + react.message_author_id = element.message_author_id; return &react; } else { return nullptr; diff --git a/include/dpp/coro.h b/include/dpp/coro.h index 900a67e43b..4f912d823d 100644 --- a/include/dpp/coro.h +++ b/include/dpp/coro.h @@ -1,158 +1,708 @@ #ifdef DPP_CORO #pragma once -#include -#include -namespace dpp { - - /** - * @brief Shorthand for the coroutine handle's type - */ - using handle_type = std::coroutine_handle; +#if (defined(_LIBCPP_VERSION) and !defined(__cpp_impl_coroutine)) // if libc++ experimental implementation (LLVM < 14) +# define STDCORO_EXPERIMENTAL_HEADER +# define STDCORO_EXPERIMENTAL_NAMESPACE +#endif + +#ifdef STDCORO_GLIBCXX_COMPAT +# define __cpp_impl_coroutine 1 +namespace std { + namespace experimental { + using namespace std; + } +} +#endif + +#ifdef STDCORO_EXPERIMENTAL_HEADER +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { class cluster; + struct confirmation_callback_t; /** - * @brief Return type for coroutines + * @brief Implementation details for internal use only. + * + * @attention This is only meant to be used by D++ internally. Support will not be given regarding the facilities in this namespace. */ - struct task { + namespace detail { +#ifdef _DOXYGEN_ /** - * @brief Required nested promise_type for coroutines + * @brief Alias for either std or std::experimental depending on compiler and library. Used by coroutine implementation. + * + * @todo Remove and use std when all supported libraries have coroutines in it */ - using promise_type = dpp::promise; - }; + namespace std_coroutine {} +#else +# ifdef STDCORO_EXPERIMENTAL_NAMESPACE + namespace std_coroutine = std::experimental; +# else + namespace std_coroutine = std; +# endif +#endif + + /** + * @brief A task's promise type, with special logic for handling nested tasks. + */ + template + struct task_promise; + + /** + * @brief The object automatically co_await-ed at the end of a task. Ensures nested task chains are resolved, and the promise cleans up if it needs to. + */ + template + struct task_chain_final_awaiter; + + /** + * @brief Alias for std::coroutine_handle for a task_promise. + */ + template + using task_handle = detail::std_coroutine::coroutine_handle>; + } /** - * @brief Implementation of promise_type for dpp's coroutines + * @brief A coroutine task. It can be co_awaited to make nested coroutines. + * + * 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 ReturnType Return type of the coroutine. Can be void, or a complete object that supports move construction and move assignment. */ - struct promise { + template +#ifndef _DOXYGEN_ + requires std::is_same_v || (!std::is_reference_v && std::is_move_constructible_v && std::is_move_assignable_v) +#endif + class task { /** - * @brief A pointer to the cluster making the requests in the coroutine + * @brief The coroutine handle of this task. */ - cluster* bot = nullptr; + detail::task_handle handle; /** - * @brief The result of the last co_await-ed function + * @brief Promise type of this coroutine. For internal use only, do not use. */ - confirmation_callback_t callback; + friend struct detail::task_promise; /** - * @brief Construct a new promise object + * @brief Construct from a coroutine handle. Internal use only */ - promise() = default; + explicit task(detail::task_handle handle_) : handle(handle_) {} + public: /** - * @brief Construct a new promise object - * - * @param ev Base type of all events, only used to get the dpp::cluster pointer + * @brief Default constructor, creates a task not bound to a coroutine. */ - promise(const dpp::event_dispatch_t& ev) : bot(ev.from->creator) { } + task() = default; /** - * @brief Get the return object - * - * @return task dpp::task type + * @brief Copy constructor is disabled + */ + task(const task &) = delete; + + /** + * @brief Move constructor, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task(task &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + + + /** + * @brief Destructor. + * + * Destroys the handle if coroutine is done, otherwise detaches it from this thread. + * In detached mode, the handle will destroy itself at the end of the coroutine. */ - task get_return_object() { - return {}; + ~task() { + if (handle) { + auto &promise = handle.promise(); + + if (!promise.is_sync) { + std::unique_lock lock{promise.mutex}; + + if (promise.destroy) // promise in async thread checked first and skipped clean up, we do it + { + if (promise.exception && promise.exception_handler) + promise.exception_handler(promise.exception); + lock.unlock(); + handle.destroy(); + } + else + handle.promise().destroy = true; + } + else { + if (promise.exception && promise.exception_handler) + promise.exception_handler(promise.exception); + handle.destroy(); + } + } } /** - * @brief Function called when the coroutine is first suspended, never suspends - * - * @return std::suspend_never Never suspend this coroutine at the first suspend point + * @brief Copy assignment is disabled */ - std::suspend_never initial_suspend() noexcept { - return {}; + task &operator=(const task &) = delete; + + /** + * @brief Move assignment, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task &operator=(task &&other) noexcept { + handle = std::exchange(other.handle, nullptr); + return (*this); } /** - * @brief Function called when the coroutine reaches its last suspension point - * - * @return std::suspend_never Never suspend this coroutine at the final suspend point + * @brief First function called by the standard library when the task is co_await-ed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @return bool Whether not to suspend the caller or not + */ + bool await_ready() { + return handle.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 */ - std::suspend_never final_suspend() noexcept { - return {}; + template + bool await_suspend(detail::task_handle caller) noexcept { + auto &my_promise = handle.promise(); + + if (my_promise.is_sync) + return false; + my_promise.parent = caller; + caller.promise().is_sync = false; + return true; } /** - * @brief Function called when the coroutine returns nothing + * @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 ReturnType The result of the coroutine. It is the value the whole co-await expression evaluates to + */ + ReturnType await_resume(); + + /** + * @brief Function to check if the coroutine has finished its execution entirely + * + * @return bool Whether the coroutine is done. + * @see https://en.cppreference.com/w/cpp/coroutine/coroutine_handle/done */ - void return_void() noexcept {} + bool done() const noexcept { + return handle.done(); + } /** - * @brief Function called when coroutine throws a un-catch-ed exception. Does nothing + * @brief Set the exception handling function. Called when an exception is thrown but not caught + * + * @warning The exception handler must not throw. If an exception that is not caught is thrown in a detached task, the program will terminate. */ - void unhandled_exception() { - /* try { std::rethrow_exception(std::current_exception()); } */ - /* catch (const std::exception& e) { std::cout << e.what() << '\n'; } */ + task &on_exception(std::function func) { + handle.promise().exception_handler = std::move(func); + if (handle.promise().exception) + func(handle.promise().exception); + return *this; } }; + namespace detail { + /** + * @brief Awaitable returned from task_promise's final_suspend. Resumes the parent and cleans up its handle if needed + */ + template + struct task_chain_final_awaiter { + /** + * @brief Always suspend at the end of the task. This allows us to clean up and resume the parent + */ + bool await_ready() noexcept { + return (false); + } + + /* + * @brief The suspension logic of the coroutine when it finishes. Always suspend the caller, meaning cleaning up the handle is on us + * + * @param handle The handle of this coroutine + */ + void await_suspend(detail::task_handle handle) noexcept; + + /* + * @brief Function called when this object is co_awaited by the standard library at the end of final_suspend. Do nothing, return nothing + */ + void await_resume() noexcept {} + }; + /** + * @brief Base implementation of task_promise, without the logic that would depend on the return type. Meant to be inherited from + */ + struct task_promise_base { + /** + * @brief Mutex for async task destruction. + */ + std::mutex mutex{}; + + /** + * @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::exception_ptr + */ + std::exception_ptr exception = nullptr; + + /** + * @brief Whether the coroutine has async calls or not + * + * Will only ever change on the calling thread while callback mutex guards the async thread + */ + bool is_sync = true; + + /** + * @brief Whether either the task object or the promise is gone and the next one to end will clean up + */ + bool destroy = false; + + /** + * @brief Function object called when an exception is thrown from a coroutine + */ + std::function exception_handler = nullptr; + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return std::suspend_never Don't suspend, the coroutine starts immediately. + */ + std_coroutine::suspend_never initial_suspend() noexcept { + return {}; + } + + /** + * @brief Function called by the standard library when an exception is thrown and not caught in the coroutine. + * + * Stores the exception pointer to rethrow later + */ + void unhandled_exception() { + exception = std::current_exception(); + } + }; + + /** + * @brief Implementation of task_promise for non-void return type + */ + template + struct task_promise : task_promise_base { + /** + * @brief Stored return value of the coroutine. + * + * @details The main reason we use std::optional here and not ReturnType is to avoid default construction of the value so we only require ReturnType to have a move constructor, instead of both a default constructor and move assignment operator + */ + std::optional value = std::nullopt; + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(ReturnType expr) { + value = std::move(expr); + } + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return task The coroutine object + */ + task get_return_object() { + return task{task_handle::from_promise(*this)}; + } + + /** + * @brief Function called by the standard library when the coroutine reaches its last suspension point + * + * @return task_chain_final_awaiter Special object containing the chain resolution and clean-up logic. + */ + task_chain_final_awaiter final_suspend() noexcept { + return {}; + } + }; + + /** + * @brief Implementation of task_promise for void return type + */ + template <> + struct task_promise : task_promise_base { + /** + * @brief Function called by the standard library when the coroutine co_returns + * + * Does nothing but is required by the standard library. + */ + void return_void() {} + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return task The coroutine object + */ + task get_return_object() { + return task{task_handle::from_promise(*this)}; + } + + /** + * @brief Function called by the standard library when the coroutine reaches its last suspension point + * + * @return task_chain_final_awaiter Special object containing the chain resolution and clean-up logic. + */ + task_chain_final_awaiter final_suspend() noexcept { + return {}; + } + }; + + template + void detail::task_chain_final_awaiter::await_suspend(detail::task_handle handle) noexcept { + task_promise &promise = handle.promise(); + std_coroutine::coroutine_handle<> parent = promise.parent; + + if (!promise.is_sync) { + std::unique_lock lock{promise.mutex}; + + if (promise.destroy) { + if (promise.exception && promise.exception_handler) + promise.exception_handler(promise.exception); + lock.unlock(); + handle.destroy(); + } + else + promise.destroy = true; // Send the destruction back to the task + } + if (parent) + parent.resume(); + } + } + + template +#ifndef _DOXYGEN_ + requires std::is_same_v || (!std::is_reference_v && std::is_move_constructible_v && std::is_move_assignable_v) +#endif + ReturnType task::await_resume() { + if (handle.promise().exception) // If we have an exception, rethrow + std::rethrow_exception(handle.promise().exception); + if constexpr (!std::is_same_v) // If we have a return type, return it and clean up our stored value + return std::forward(*std::exchange(handle.promise().value, std::nullopt)); + } + /** - * @brief A co_await-able struct which returns the result of stored api call when co_await-ed. Meant to be opaque to the user - * - * @tparam T The type of the function (lambda if auto-generated by the php script) handling the making of api call + * @brief A co_await-able object handling an API call. + * + * This class is the return type of the dpp::cluster::co_* methods, but it can also be created manually to wrap any async call. + * + * @remark - This object's methods, other than constructors and operators, should not be called directly. It is designed to be used with coroutine keywords such as co_await. + * @remark - This object must not be co_await-ed more than once. + * @remark - The coroutine may be resumed in another thread, do not rely on thread_local variables. + * @warning This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. Please report any to GitHub issues or to the D++ Discord server. + * @tparam ReturnType The return type of the API call. Defaults to confirmation_callback_t */ - template - struct awaitable { + template + class awaitable { + /** + * @brief Ref-counted callback, contains the callback logic and manages the lifetime of the callback data over multiple threads. + */ + struct shared_callback { + /** + * @brief State of the awaitable and its callback. + */ + struct callback_state { + enum state_t { + waiting, + done, + dangling + }; + + /** + * @brief Mutex to ensure the API result isn't set at the same time the coroutine is awaited and its value is checked, or the awaitable is destroyed + */ + std::mutex mutex{}; + + /** + * @brief Number of references to this callback state. + */ + int ref_count; + + /** + * @brief State of the awaitable and the API callback + */ + state_t state = waiting; + + /** + * @brief The stored result of the API call + */ + std::optional result = std::nullopt; + + /** + * @brief Handle to the coroutine co_await-ing on this API call + * + * @see std::coroutine_handle + */ + detail::std_coroutine::coroutine_handle<> coro_handle = nullptr; + }; + + /** + * @brief Callback function. + * + * @param cback The result of the API call. + */ + void operator()(const ReturnType &cback) const { + std::unique_lock lock{get_mutex()}; + + if (state->state == callback_state::dangling) // Awaitable is gone - likely an exception killed it or it was never co_await-ed + return; + state->result = cback; + state->state = callback_state::done; + if (state->coro_handle) { + auto handle = state->coro_handle; + state->coro_handle = nullptr; + lock.unlock(); + handle.resume(); + } + } + + /** + * @brief Main constructor, allocates a new callback_state object. + */ + shared_callback() : state{new callback_state{.ref_count = 1}} {} + + /** + * @brief Copy constructor. Takes shared ownership of the callback state, increasing the reference count. + */ + shared_callback(const shared_callback &other) { + 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 Copy assignment. Takes shared ownership of the callback state, increasing the reference count. + */ + shared_callback &operator=(const shared_callback &other) noexcept { + std::lock_guard lock{other.get_mutex()}; + + 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 { + std::lock_guard lock{other.get_mutex()}; + + state = std::exchange(other.state, nullptr); + return *this; + } + + /** + * @brief Function called by the awaitable when it is destroyed when it was never co_awaited, signals to the callback to abort. + */ + void set_dangling() { + if (!state) // moved-from object + return; + std::lock_guard lock{get_mutex()}; + + if (state->state == callback_state::waiting) + state->state = callback_state::dangling; + } + + /** + * @brief Convenience function to get the shared callback state's mutex. + */ + std::mutex &get_mutex() const { + return (state->mutex); + } + + /** + * @brief Convenience function to get the shared callback state's result. + */ + std::optional &get_result() const { + return (state->result); + } + + /** + * @brief Destructor. Releases the held reference and destroys if no other references exist. + */ + ~shared_callback() { + if (!state) // Moved-from object + return; + + std::unique_lock lock{state->mutex}; + + if (state->ref_count) { + --(state->ref_count); + if (state->ref_count <= 0) {; + lock.unlock(); + delete state; + } + } + } + + callback_state *state; + }; + /** - * @brief Pointer to the nested promise object of the coroutine, used for storing and retrieving the result of an api call + * @brief Shared state of the awaitable and its callback, to be used across threads. */ - promise* p; - + shared_callback api_callback; + + public: /** - * @brief Pointer to the cluster making the api request + * @brief Construct an awaitable wrapping an object method, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param obj The object to call the method on + * @param fun The method of the object to call. Its last parameter must be a callback taking a parameter of type ReturnType + * @param args Parameters to pass to the method, excluding the callback */ - cluster* bot; + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + awaitable(Obj &&obj, Fun &&fun, Args&&... args) : api_callback{} { + std::invoke(std::forward(fun), std::forward(obj), std::forward(args)..., api_callback); + } /** - * @brief The function handling the making of request, using the cluster pointer + * @brief Construct an awaitable 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 ReturnType + * @param args Parameters to pass to the object, excluding the callback */ - T api_req; + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + awaitable(Fun &&fun, Args&&... args) : api_callback{} { + std::invoke(std::forward(fun), std::forward(args)..., api_callback); + } /** - * @brief Construct a new awaitable object - * - * @param cl pointer to the cluster making the api request - * @param api_call a function to invoke with the cluster pointer, handles the making of request + * @brief Destructor. If any callback is pending it will be aborted. + * */ - awaitable(cluster* cl, T api_call) : bot{cl}, api_req{api_call} {} + ~awaitable() { + api_callback.set_dangling(); + } /** - * @brief First function called when this object is co_await-ed, its return type tells if the coroutine should be immediately suspended + * @brief Copy constructor is disabled + */ + awaitable(const awaitable &) = 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 awaitable after this function is undefined behavior. + * @param other The awaitable object to move the data from. + */ + awaitable(awaitable &&other) noexcept = default; + + /** + * @brief Copy assignment is disabled + */ + awaitable &operator=(const awaitable &) = 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 awaitable after this function is undefined behavior. + * @param other The awaitable object to move the data from + */ + awaitable &operator=(awaitable &&other) noexcept = default; + + /** + * @brief First function called by the standard library when the object is co-awaited. * - * @return bool false, signifying immediate suspension + * Returns whether we already have the result of the API call and don't need to suspend the caller. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @return bool Whether we already have the result of the API call or not */ bool await_ready() noexcept { - return false; + std::lock_guard lock{api_callback.get_mutex()}; + + return api_callback.get_result().has_value(); } /** - * @brief Function called when the coroutine is suspended, makes the api request and queues the resumption of the suspended coroutine, storing the result in promise object - * - * @param handle the handle to the suspended coroutine + * @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 handle The handle to the coroutine co_await-ing and being suspended */ - void await_suspend(handle_type handle) { - /* p = &handle.promise(); */ - /* if (!p->bot) p->bot = bot; */ - api_req([handle](const confirmation_callback_t& cback) { handle.promise().callback = cback; handle.resume(); }); + template + bool await_suspend(detail::task_handle handle) { + std::lock_guard lock{api_callback.get_mutex()}; + + if (api_callback.get_result().has_value()) + return false; // immediately resume the coroutine as we already have the result of the api call + handle.promise().is_sync = false; + api_callback.state->coro_handle = handle; + return true; // suspend the caller, the callback will resume it } /** - * @brief Function called when the coroutine is resumed by its handle, handles the retrieval and return of result from promise object - * - * @return confirmation_callback_t the result of the api call + * @brief Function called by the standard library when the awaitable 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 ReturnType The result of the API call. */ - confirmation_callback_t await_resume() { - return p->callback; + ReturnType await_resume() { + return std::move(*api_callback.get_result()); } }; +}; +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits, Args...> { + using promise_type = dpp::detail::task_promise; }; -/* template<> */ -/* struct std::coroutine_traits { */ -/* using promise_type = dpp::promise; */ -/* }; */ #endif diff --git a/include/dpp/dispatcher.h b/include/dpp/dispatcher.h index 8119b62160..58c0643393 100644 --- a/include/dpp/dispatcher.h +++ b/include/dpp/dispatcher.h @@ -47,6 +47,10 @@ #include #include +#ifdef DPP_CORO +#include +#endif /* DPP_CORO */ + namespace dpp { /* Forward declaration */ @@ -474,6 +478,106 @@ 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 Acknowledge interaction without displaying a message to the user, + * for use with button and select menu components. + * + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_reply() const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_reply(interaction_response_type t, const message & m) const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param mt The string value to send, for simple text only messages + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_reply(interaction_response_type t, const std::string & mt) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_reply(const message & m) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param mt The string value to send, for simple text only messages + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_reply(const std::string & mt) const; + + /** + * @brief Reply to interaction with a dialog box + * + * @param mr Dialog box response to send + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_dialog(const interaction_modal_response& mr) const; + + /** + * @brief Edit the response for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_edit_response(const message & m) const; + + /** + * @brief Edit the response for this interaction + * + * @param mt The string value to send, for simple text only messages + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_edit_response(const std::string & mt) const; + + /** + * @brief Set the bot to 'thinking' state where you have up to 15 minutes to respond + * + * @param ephemeral True if the thinking state should be ephemeral + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_thinking(bool ephemeral = false) const; + + /** + * @brief Get original response message for this interaction + * + * On success the result will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_get_original_response() const; + + /** + * @brief Edit original response message for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_edit_original_response(const message & m) const; + + /** + * @brief Delete original response message for this interaction. This cannot be used on an ephemeral interaction response. + * + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::awaitable co_delete_original_response() const; +#endif /* DPP_CORO */ + /** * @brief command interaction */ @@ -932,6 +1036,10 @@ struct DPP_EXPORT message_reaction_add_t : public event_dispatch_t { * @brief message id of the message reacted upon */ snowflake message_id; + /** + * @brief ID of the user who authored the message which was reacted to (Optional) + */ + snowflake message_author_id; }; /** @brief Guild members chunk */ diff --git a/include/dpp/event_router.h b/include/dpp/event_router.h index 4f901fd394..6e2b2208b7 100644 --- a/include/dpp/event_router.h +++ b/include/dpp/event_router.h @@ -105,14 +105,18 @@ template class event_router_t { #ifdef DPP_CORO /** * @brief Container for event listeners (coroutines only) + * + * Note: keep a listener's parameter as a value type, the event passed can die while a coroutine is suspended */ - std::map> coroutine_container; + std::map(T)>> coroutine_container; #else - /** - * @brief Dummy container to keep the struct size same - */ - std::map> dummy_container; -#endif +#ifndef _DOXYGEN_ + /** + * @brief Dummy container to keep the struct size same + */ + std::map> dummy_container; +#endif /* _DOXYGEN_ */ +#endif /* DPP_CORO */ /** @@ -160,12 +164,23 @@ template class event_router_t { } }); #ifdef DPP_CORO + auto coro_exception_handler = [from = event.from](std::exception_ptr ptr) { + try { + std::rethrow_exception(ptr); + } + catch (const std::exception &exception) { + if (from && from->creator) + from->creator->log(dpp::loglevel::ll_error, std::string{"Uncaught exception in event coroutine: "} + exception.what()); + } + }; std::for_each(coroutine_container.begin(), coroutine_container.end(), [&](auto &ev) { if (!event.is_cancelled()) { - ev.second(event); + dpp::task task = ev.second(event); + + task.on_exception(coro_exception_handler); } }); -#endif +#endif /* DPP_CORO */ }; /** @@ -177,7 +192,11 @@ template class event_router_t { */ bool empty() const { std::shared_lock l(lock); +#ifdef DPP_CORO + return dispatch_container.empty() && coroutine_container.empty(); +#else return dispatch_container.empty(); +#endif /* DPP_CORO */ } /** @@ -210,7 +229,7 @@ template class event_router_t { * The lambda should follow the signature specified when declaring * the event object and should take exactly one parameter derived * from event_dispatch_t. - * + * * @param func Function lambda to attach to event * @return event_handle An event handle unique to this event, used to * detach the listener from the event later if necessary. @@ -219,17 +238,27 @@ template class event_router_t { std::unique_lock l(lock); event_handle h = next_handle++; dispatch_container.emplace(h, func); - return h; + return h; } #ifdef DPP_CORO - event_handle co_attach(std::function func) { + /** + * @brief Attach a coroutine task to the event, adding a listener. + * The coroutine should follow the signature specified when declaring + * the event object and should take exactly one parameter derived + * from event_dispatch_t. + * + * @param func Coroutine task to attack to the event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + event_handle co_attach(std::function(T)> func) { std::unique_lock l(lock); event_handle h = next_handle++; coroutine_container.emplace(h, func); - return h; + return h; } -#endif +#endif /* DPP_CORO */ /** * @brief Detach a listener from the event using a previously obtained ID. * @@ -239,7 +268,11 @@ template class event_router_t { */ bool detach(const event_handle& handle) { std::unique_lock l(lock); +#ifdef DPP_CORO + return this->dispatch_container.erase(handle) || this->coroutine_container.erase(handle); +#else return this->dispatch_container.erase(handle); +#endif /* DPP_CORO */ } }; diff --git a/include/dpp/guild.h b/include/dpp/guild.h index 13815d5127..3209e3ec26 100644 --- a/include/dpp/guild.h +++ b/include/dpp/guild.h @@ -21,6 +21,7 @@ #pragma once #include #include +#include #include #include #include @@ -382,26 +383,102 @@ class DPP_EXPORT guild_member { /** * @brief Defines a channel on a server's welcome screen */ -struct welcome_channel_t { - /// the description shown for the channel +struct DPP_EXPORT welcome_channel: public json_interface { + /// The description shown for the channel std::string description; - /// the emoji name if custom, the unicode character if standard, or null if no emoji is set + /// The emoji name if custom, the unicode character if standard, or null if no emoji is set std::string emoji_name; - /// the channel's id - snowflake channel_id = 0; - /// the emoji id, if the emoji is custom - snowflake emoji_id = 0; + /// The channel's id + snowflake channel_id; + /// The emoji id, if the emoji is custom + snowflake emoji_id; + + /** + * @brief Construct a new welcome channel object + */ + welcome_channel(); + + /** + * @brief Destroy the welcome channel object + */ + virtual ~welcome_channel() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + welcome_channel& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the channel ID of this welcome channel object + * + * @param _channel_id The channel ID to set + * @return Reference to self, so these method calls may be chained + */ + welcome_channel& set_channel_id(const snowflake _channel_id); + + /** + * @brief Set the description of this welcome channel object + * + * @param _description The description to set + * @return Reference to self, so these method calls may be chained + */ + welcome_channel& set_description(const std::string& _description); }; /** * @brief Defines a server's welcome screen */ -struct welcome_screen_t { - /// the server description shown in the welcome screen +struct DPP_EXPORT welcome_screen: public json_interface { + /// The server description shown in the welcome screen std::string description; - /// the channels shown in the welcome screen, up to 5 - std::vector welcome_channels; + /// The channels shown in the welcome screen (max 5) + std::vector welcome_channels; + + /** + * @brief Construct a new welcome screen object + */ + welcome_screen() = default; + + /** + * @brief Destroy the welcome screen object + */ + virtual ~welcome_screen() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + welcome_screen& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the server description for this welcome screen object shown in the welcome screen + * + * @param s string The server description + * @return Reference to self, so these method calls may be chained + */ + welcome_screen& set_description(const std::string& s); }; /** @@ -552,7 +629,7 @@ class DPP_EXPORT guild : public managed, public json_interface { /** Welcome screen */ - welcome_screen_t welcome_screen; + dpp::welcome_screen welcome_screen; /** Guild icon hash */ utility::iconhash icon; @@ -1053,6 +1130,223 @@ class DPP_EXPORT guild_widget { std::string build_json(bool with_id = false) const; }; +/** + * @brief The onboarding mode for the dpp::onboarding object. Defines the criteria used to satisfy Onboarding constraints that are required for enabling. + */ +enum onboarding_mode: uint8_t { + gom_default = 0, //!< Counts only Default Channels towards constraints + gom_advanced = 1, //!< Counts Default Channels and Questions towards constraints +}; + +/** + * @brief The various types of dpp::onboarding_prompt + */ +enum onboarding_prompt_type: uint8_t { + opt_multiple_choice = 0, //!< Multiple choice + opt_dropdown = 1, //!< Dropdown +}; + +/** + * @brief Various flags for dpp::onboarding_prompt + */ +enum onboarding_prompt_flags: uint8_t { + opf_single_select = 1 << 0, //!< Indicates whether users are limited to selecting one option for the prompt + opf_required = 1 << 1, //!< Indicates whether the prompt is required before a user completes the onboarding flow + opf_in_onboarding = 1 << 2, //!< Indicates whether the prompt is present in the onboarding flow. If set, the prompt will only appear in the Channels & Roles tab +}; + +/** + * @brief Represents an onboarding prompt option + */ +struct DPP_EXPORT onboarding_prompt_option: public managed, public json_interface { + std::vector channel_ids; //!< IDs for channels a member is added to when the option is selected + std::vector role_ids; //!< IDs for roles assigned to a member when the option is selected + dpp::emoji emoji; //!< Emoji of the option + std::string title; //!< Title of the option + std::string description; //!< Description of the option + + /** + * @brief Construct a new onboarding prompt option object + */ + onboarding_prompt_option(); + + /** + * @brief Destroy the onboarding prompt option object + */ + virtual ~onboarding_prompt_option() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding_prompt_option& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the emoji of this onboarding prompt option object + * + * @param _emoji The emoji to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_emoji(const dpp::emoji& _emoji); + + /** + * @brief Set the title of this onboarding prompt option object + * + * @param _title The title to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_title(const std::string& _title); + + /** + * @brief Set the description of this onboarding prompt option object + * + * @param _description The description to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_description(const std::string& _description); +}; + +/** + * @brief Represents an onboarding prompt + */ +struct DPP_EXPORT onboarding_prompt: public managed, public json_interface { + onboarding_prompt_type type; //!< Type of prompt (defaults to dpp::opt_multiple_choice) + std::vector options; //!< Options available within the prompt + std::string title; //!< Title of the prompt + uint8_t flags; //!< A set of flags built from the bitmask defined by dpp::onboarding_prompt_flags + + /** + * @brief Construct a new onboarding prompt object + */ + onboarding_prompt(); + + /** + * @brief Destroy the onboarding prompt object + */ + virtual ~onboarding_prompt() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding_prompt& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the type of this onboarding prompt object + * + * @param _type The prompt type to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt& set_type(const onboarding_prompt_type _type); + + /** + * @brief Set the title of this onboarding prompt object + * + * @param _title The title to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt& set_title(const std::string& _title); + + /** + * @brief Indicates whether users are limited to selecting one option for the prompt + * @return bool True if the users are limited to selecting one option for the prompt + */ + bool is_single_select() const; + + /** + * @brief Indicates whether the prompt is required before a user completes the onboarding flow + * @return bool True if the prompt is required before a user completes the onboarding flow + */ + bool is_required() const; + + /** + * @brief Indicates whether the prompt is present in the onboarding flow + * @return bool True if the prompt is present in the onboarding flow. False if the prompt will only appear in the Channels & Roles tab + */ + bool is_in_onboarding() const; +}; + +/** + * @brief Represents a guild's onboarding flow + */ +struct DPP_EXPORT onboarding: public json_interface { + snowflake guild_id; //!< ID of the guild this onboarding is part of + std::vector prompts; //!< Prompts shown during onboarding and in customize community + std::vector default_channel_ids; //!< Channel IDs that members get opted into automatically + onboarding_mode mode; //!< Current mode of onboarding (defaults to dpp::gom_default) + bool enabled; //!< Whether onboarding is enabled in the guild + + /** + * @brief Construct a new onboarding object + */ + onboarding(); + + /** + * @brief Destroy the onboarding object + */ + virtual ~onboarding() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set guild_id of this onboarding object + * + * @param guild_id Guild ID to set + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_guild_id(const snowflake id); + + /** + * @brief Set the mode of this onboarding object + * + * @param m onboarding_mode Mode to set + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_mode(const onboarding_mode m); + + /** + * @brief Set the enabling of this onboarding object + * + * @param is_enabled bool Whether onboarding is enabled in the guild + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_enabled(const bool is_enabled); +}; + /** * @brief helper function to deserialize a guild_member from json * diff --git a/include/dpp/message.h b/include/dpp/message.h index 5f11536f98..22fb5c7146 100644 --- a/include/dpp/message.h +++ b/include/dpp/message.h @@ -766,6 +766,14 @@ struct DPP_EXPORT reaction { ~reaction() = default; }; +/** + * @brief Bitmask flags for a dpp::attachment + */ +enum attachment_flags : uint8_t { + /// this attachment has been edited using the remix feature on mobile + a_is_remix = 1 << 2, +}; + /** * @brief Represents an attachment in a dpp::message */ @@ -794,6 +802,8 @@ struct DPP_EXPORT attachment { double duration_secs; /** base64 encoded bytearray representing a sampled waveform (currently for voice messages) */ std::string waveform; + /** Flags. Made of bits in dpp::attachment_flags */ + uint8_t flags; /** Owning message */ struct message* owner; @@ -824,6 +834,13 @@ struct DPP_EXPORT attachment { * itself has an owning cluster, this method will throw a dpp::logic_exception when called. */ void download(http_completion_event callback) const; + + /** + * @brief Returns true if remixed + * + * @return true if remixed + */ + bool is_remix() const; }; /** @@ -1523,6 +1540,13 @@ struct DPP_EXPORT message : public managed { * @return true if message is a DM */ bool is_dm() const; + + /** + * @brief Returns true if message has remixed attachment + * + * @return true if message has remixed attachment + */ + bool has_remix_attachment() const; }; /** A group of messages */ diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h index 7e027ccf18..f1d1865cdd 100644 --- a/include/dpp/permissions.h +++ b/include/dpp/permissions.h @@ -146,6 +146,25 @@ class DPP_EXPORT permission { return (value & (0 | ... | values)) == (0 | ... | values); } + /** + * @brief Check for permission flags set. It uses the Bitwise AND operator + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to check for + * + * **Example:** + * + * ```cpp + * bool is_mod = permission.has_any(dpp::p_administrator, dpp::p_ban_members); + * // Returns true if the permission bitmask contains p_administrator or p_ban_members + * ``` + * + * @return bool True if it has any the given permissions + */ + template + constexpr bool has_any(T... values) const noexcept { + return (value & (0 | ... | values)) != 0; + } + /** * @brief Add a permission with the Bitwise OR operation * @tparam T one or more uint64_t permission bits diff --git a/include/dpp/restresults.h b/include/dpp/restresults.h index 1d3640d76b..1d2d88beff 100644 --- a/include/dpp/restresults.h +++ b/include/dpp/restresults.h @@ -191,7 +191,9 @@ typedef std::variant< event_member, event_member_map, automod_rule, - automod_rule_map + automod_rule_map, + onboarding, + welcome_screen > confirmable_t; /** diff --git a/include/dpp/role.h b/include/dpp/role.h index fd16b39122..3a313ccc62 100644 --- a/include/dpp/role.h +++ b/include/dpp/role.h @@ -37,6 +37,7 @@ enum role_flags : uint8_t { 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 }; /** @@ -287,6 +288,11 @@ class DPP_EXPORT role : public managed, public json_interface { * @return bool True if the role is a linked role */ bool is_linked() const; + /** + * @brief True if the role can be selected by members in an onboarding prompt + * @return bool True if the role can be selected by members in an onboarding prompt + */ + bool is_selectable_in_prompt() const; /** * @brief True if has create instant invite permission * @note Having the administrator permission causes this method to always return true diff --git a/include/dpp/snowflake.h b/include/dpp/snowflake.h index 47e333dd1b..8f7cbad7b3 100644 --- a/include/dpp/snowflake.h +++ b/include/dpp/snowflake.h @@ -92,6 +92,16 @@ class DPP_EXPORT snowflake final { return value == 0; } + /** + * @brief Returns the stringified version of the snowflake value + * + * @return std::string string form of snowflake value + */ + inline std::string str() const + { + return std::to_string(value); + } + /** * @brief Operator less than, used for maps/unordered maps * when the snowflake is a key value. diff --git a/include/dpp/unicode_emoji.h b/include/dpp/unicode_emoji.h index b09a5bfb30..2785f8cd53 100644 --- a/include/dpp/unicode_emoji.h +++ b/include/dpp/unicode_emoji.h @@ -1,6 +1,17 @@ #pragma once -namespace dpp { namespace unicode_emoji { +namespace dpp { + +/** + * The unicode emojis in this namespace are auto-generated from https://raw.githubusercontent.com/ArkinSolomon/discord-emoji-converter/master/emojis.json + * + * If you want to use this, you have to pull the header in separately. e.g. + * ```cpp + * #include + * #include + * ``` + */ +namespace unicode_emoji { constexpr const char[] _100 = "💯"; constexpr const char[] _1234 = "🔢"; constexpr const char[] soccer = "⚽"; diff --git a/include/dpp/utility.h b/include/dpp/utility.h index 3b978c58de..45eeb5d89d 100644 --- a/include/dpp/utility.h +++ b/include/dpp/utility.h @@ -253,6 +253,13 @@ namespace dpp { */ bool DPP_EXPORT has_voice(); + /** + * @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 * diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index dc1f7f2955..e74c4e33bb 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -175,10 +175,11 @@ if(UNIX OR MSYS) endif() if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /sdl /DFD_SETSIZE=1024") if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /Od /DEBUG /Zi /sdl /DFD_SETSIZE=1024") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /Od /DEBUG /Zi") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /O2 /Oi /Oy /Gy /sdl /DFD_SETSIZE=1024") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2 /Oi /Oy /Gy") endif() if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /Zc:preprocessor") @@ -292,13 +293,52 @@ if(HAVE_PTHREAD_SETNAME_NP) endif() if(DPP_CORO) - message("-- ${Yellow}Enabled experimental coroutine support${ColourReset}") + message("-- ${Yellow}Enabled experimental coroutine feature${ColourReset}") set(CMAKE_CXX_STANDARD 20) - target_compile_features(dpp PRIVATE cxx_std_20) + target_compile_features(dpp PUBLIC cxx_std_20) if(WIN32 AND NOT MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /await:strict") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0.0) # clang >= 14 has native support + message("-- ${Yellow}Clang < 14 - attempting to detect if using libc++ or stdc++${ColourReset}") + check_cxx_source_compiles(" + #include + + int a = + #ifdef __GLIBCXX__ + 1; + #else + fgsfds; + #endif + + int main(int argc, char* argv[]) + { + return 0; + } + " IS_GLIBCXX) + if(IS_GLIBCXX) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0.0) + message(FATAL_ERROR "Clang with stdc++ and coroutines requires version 12.0.0 or above") + endif() + message("-- ${Yellow}Detected stdc++ - enabling mock std::experimental namespace${ColourReset}") + target_compile_definitions(dpp PUBLIC "STDCORO_GLIBCXX_COMPAT") + else() + message("-- ${Yellow}Detected libc++ - using ") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0.0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts") + endif() + endif() + message("-- ${Yellow}Note - coroutines in clang < 14 are experimental, upgrading is recommended") + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) + message(FATAL_ERROR "Coroutines with g++ require version 10 or above") + elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0) + message("-- ${Yellow}Note - coroutines in g++10 are experimental, upgrading to g++11 or above is recommended") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") + endif() + endif() endif() target_compile_definitions(dpp PUBLIC DPP_CORO) execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.." diff --git a/src/dpp/cluster/guild.cpp b/src/dpp/cluster/guild.cpp index f4bc8d31fd..6020a96270 100644 --- a/src/dpp/cluster/guild.cpp +++ b/src/dpp/cluster/guild.cpp @@ -150,4 +150,23 @@ void cluster::guild_sync_integration(snowflake guild_id, snowflake integration_i } +void cluster::guild_get_onboarding(snowflake guild_id, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "onboarding", m_get, "", callback); +} + +void cluster::guild_edit_onboarding(const struct onboarding& o, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(o.guild_id), "onboarding", m_put, o.build_json(), callback); +} + +void cluster::guild_get_welcome_screen(snowflake guild_id, command_completion_event_t callback) { + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "welcome-screen", m_get, "", callback); +} + +void cluster::guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled, command_completion_event_t callback) { + json j = json::parse(welcome_screen.build_json()); + j["enabled"] = enabled; + rest_request(this, API_PATH "/guilds", std::to_string(guild_id), "welcome-screen", m_patch, j.dump(), callback); +} + + }; diff --git a/src/dpp/cluster/timer.cpp b/src/dpp/cluster/timer.cpp index 816af14923..c843c04ad0 100644 --- a/src/dpp/cluster/timer.cpp +++ b/src/dpp/cluster/timer.cpp @@ -104,6 +104,17 @@ void cluster::tick_timers() { } } +#ifdef DPP_CORO +awaitable cluster::co_timer(uint64_t seconds) { + return {[this, seconds] (auto &&cb) { + start_timer([this, cb](dpp::timer handle) { + cb(handle); + stop_timer(handle); + }, seconds); + }}; +} +#endif + oneshot_timer::oneshot_timer(class cluster* cl, uint64_t duration, timer_callback_t callback) : owner(cl) { /* Create timer */ th = cl->start_timer([callback, this](dpp::timer timer_handle) { diff --git a/src/dpp/cluster_coro_calls.cpp b/src/dpp/cluster_coro_calls.cpp new file mode 100644 index 0000000000..9b0d46816a --- /dev/null +++ b/src/dpp/cluster_coro_calls.cpp @@ -0,0 +1,798 @@ +/************************************************************************************ + * + * 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_coro_struct.php. + * + * DO NOT EDIT BY HAND! + * + * To re-generate this header file re-run the script! + */ +#ifdef DPP_CORO + +#include +#include +#include +#include + +namespace dpp { + +awaitable cluster::co_global_bulk_command_create(const std::vector &commands) { + return {this, static_cast &, command_completion_event_t)>(&cluster::global_bulk_command_create), commands}; +} + +awaitable cluster::co_global_command_create(const slashcommand &s) { + return {this, static_cast(&cluster::global_command_create), s}; +} + +awaitable cluster::co_global_command_get(snowflake id) { + return {this, static_cast(&cluster::global_command_get), id}; +} + +awaitable cluster::co_global_command_delete(snowflake id) { + return {this, static_cast(&cluster::global_command_delete), id}; +} + +awaitable cluster::co_global_command_edit(const slashcommand &s) { + return {this, static_cast(&cluster::global_command_edit), s}; +} + +awaitable cluster::co_global_commands_get() { + return {this, static_cast(&cluster::global_commands_get)}; +} + +awaitable cluster::co_guild_bulk_command_create(const std::vector &commands, snowflake guild_id) { + return {this, static_cast &, snowflake, command_completion_event_t)>(&cluster::guild_bulk_command_create), commands, guild_id}; +} + +awaitable cluster::co_guild_commands_get_permissions(snowflake guild_id) { + return {this, static_cast(&cluster::guild_commands_get_permissions), guild_id}; +} + +awaitable cluster::co_guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id) { + return {this, static_cast &, snowflake, command_completion_event_t)>(&cluster::guild_bulk_command_edit_permissions), commands, guild_id}; +} + +awaitable cluster::co_guild_command_create(const slashcommand &s, snowflake guild_id) { + return {this, static_cast(&cluster::guild_command_create), s, guild_id}; +} + +awaitable cluster::co_guild_command_delete(snowflake id, snowflake guild_id) { + return {this, static_cast(&cluster::guild_command_delete), id, guild_id}; +} + +awaitable cluster::co_guild_command_edit_permissions(const slashcommand &s, snowflake guild_id) { + return {this, static_cast(&cluster::guild_command_edit_permissions), s, guild_id}; +} + +awaitable cluster::co_guild_command_get(snowflake id, snowflake guild_id) { + return {this, static_cast(&cluster::guild_command_get), id, guild_id}; +} + +awaitable cluster::co_guild_command_get_permissions(snowflake id, snowflake guild_id) { + return {this, static_cast(&cluster::guild_command_get_permissions), id, guild_id}; +} + +awaitable cluster::co_guild_command_edit(const slashcommand &s, snowflake guild_id) { + return {this, static_cast(&cluster::guild_command_edit), s, guild_id}; +} + +awaitable cluster::co_guild_commands_get(snowflake guild_id) { + return {this, static_cast(&cluster::guild_commands_get), guild_id}; +} + +awaitable cluster::co_interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r) { + return {this, static_cast(&cluster::interaction_response_create), interaction_id, token, r}; +} + +awaitable cluster::co_interaction_response_edit(const std::string &token, const message &m) { + return {this, static_cast(&cluster::interaction_response_edit), token, m}; +} + +awaitable cluster::co_interaction_response_get_original(const std::string &token) { + return {this, static_cast(&cluster::interaction_response_get_original), token}; +} + +awaitable cluster::co_interaction_followup_create(const std::string &token, const message &m) { + return {this, static_cast(&cluster::interaction_followup_create), token, m}; +} + +awaitable cluster::co_interaction_followup_edit_original(const std::string &token, const message &m) { + return {this, static_cast(&cluster::interaction_followup_edit_original), token, m}; +} + +awaitable cluster::co_interaction_followup_delete(const std::string &token) { + return {this, static_cast(&cluster::interaction_followup_delete), token}; +} + +awaitable cluster::co_interaction_followup_edit(const std::string &token, const message &m) { + return {this, static_cast(&cluster::interaction_followup_edit), token, m}; +} + +awaitable cluster::co_interaction_followup_get(const std::string &token, snowflake message_id) { + return {this, static_cast(&cluster::interaction_followup_get), token, message_id}; +} + +awaitable cluster::co_interaction_followup_get_original(const std::string &token) { + return {this, static_cast(&cluster::interaction_followup_get_original), token}; +} + +awaitable cluster::co_automod_rules_get(snowflake guild_id) { + return {this, static_cast(&cluster::automod_rules_get), guild_id}; +} + +awaitable cluster::co_automod_rule_get(snowflake guild_id, snowflake rule_id) { + return {this, static_cast(&cluster::automod_rule_get), guild_id, rule_id}; +} + +awaitable cluster::co_automod_rule_create(snowflake guild_id, const automod_rule& r) { + return {this, static_cast(&cluster::automod_rule_create), guild_id, r}; +} + +awaitable cluster::co_automod_rule_edit(snowflake guild_id, const automod_rule& r) { + return {this, static_cast(&cluster::automod_rule_edit), guild_id, r}; +} + +awaitable cluster::co_automod_rule_delete(snowflake guild_id, snowflake rule_id) { + return {this, static_cast(&cluster::automod_rule_delete), guild_id, rule_id}; +} + +awaitable cluster::co_channel_create(const class channel &c) { + return {this, static_cast(&cluster::channel_create), c}; +} + +awaitable cluster::co_channel_delete_permission(const class channel &c, snowflake overwrite_id) { + return {this, static_cast(&cluster::channel_delete_permission), c, overwrite_id}; +} + +awaitable cluster::co_channel_delete(snowflake channel_id) { + return {this, static_cast(&cluster::channel_delete), channel_id}; +} + +awaitable cluster::co_channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member) { + return {this, static_cast(&cluster::channel_edit_permissions), c, overwrite_id, allow, deny, member}; +} + +awaitable cluster::co_channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member) { + return {this, static_cast(&cluster::channel_edit_permissions), channel_id, overwrite_id, allow, deny, member}; +} + +awaitable cluster::co_channel_edit_positions(const std::vector &c) { + return {this, static_cast &, command_completion_event_t)>(&cluster::channel_edit_positions), c}; +} + +awaitable cluster::co_channel_edit(const class channel &c) { + return {this, static_cast(&cluster::channel_edit), c}; +} + +awaitable cluster::co_channel_follow_news(const class channel &c, snowflake target_channel_id) { + return {this, static_cast(&cluster::channel_follow_news), c, target_channel_id}; +} + +awaitable cluster::co_channel_get(snowflake c) { + return {this, static_cast(&cluster::channel_get), c}; +} + +awaitable cluster::co_channel_invite_create(const class channel &c, const class invite &i) { + return {this, static_cast(&cluster::channel_invite_create), c, i}; +} + +awaitable cluster::co_channel_invites_get(const class channel &c) { + return {this, static_cast(&cluster::channel_invites_get), c}; +} + +awaitable cluster::co_channel_typing(const class channel &c) { + return {this, static_cast(&cluster::channel_typing), c}; +} + +awaitable cluster::co_channel_typing(snowflake cid) { + return {this, static_cast(&cluster::channel_typing), cid}; +} + +awaitable cluster::co_channels_get(snowflake guild_id) { + return {this, static_cast(&cluster::channels_get), guild_id}; +} + +awaitable cluster::co_create_dm_channel(snowflake user_id) { + return {this, static_cast(&cluster::create_dm_channel), user_id}; +} + +awaitable cluster::co_current_user_get_dms() { + return {this, static_cast(&cluster::current_user_get_dms)}; +} + +awaitable cluster::co_direct_message_create(snowflake user_id, const message &m) { + return {this, static_cast(&cluster::direct_message_create), user_id, m}; +} + +awaitable cluster::co_gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick) { + return {this, static_cast(&cluster::gdm_add), channel_id, user_id, access_token, nick}; +} + +awaitable cluster::co_gdm_remove(snowflake channel_id, snowflake user_id) { + return {this, static_cast(&cluster::gdm_remove), channel_id, user_id}; +} + +awaitable cluster::co_guild_emoji_create(snowflake guild_id, const class emoji& newemoji) { + return {this, static_cast(&cluster::guild_emoji_create), guild_id, newemoji}; +} + +awaitable cluster::co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id) { + return {this, static_cast(&cluster::guild_emoji_delete), guild_id, emoji_id}; +} + +awaitable cluster::co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji) { + return {this, static_cast(&cluster::guild_emoji_edit), guild_id, newemoji}; +} + +awaitable cluster::co_guild_emoji_get(snowflake guild_id, snowflake emoji_id) { + return {this, static_cast(&cluster::guild_emoji_get), guild_id, emoji_id}; +} + +awaitable cluster::co_guild_emojis_get(snowflake guild_id) { + return {this, static_cast(&cluster::guild_emojis_get), guild_id}; +} + +awaitable cluster::co_get_gateway_bot() { + return {this, static_cast(&cluster::get_gateway_bot)}; +} + +awaitable cluster::co_guild_current_member_edit(snowflake guild_id, const std::string &nickname) { + return {this, static_cast(&cluster::guild_current_member_edit), guild_id, nickname}; +} + +awaitable cluster::co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit) { + return {this, static_cast(&cluster::guild_auditlog_get), guild_id, user_id, action_type, before, after, limit}; +} + +awaitable cluster::co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds) { + return {this, static_cast(&cluster::guild_ban_add), guild_id, user_id, delete_message_seconds}; +} + +awaitable cluster::co_guild_ban_delete(snowflake guild_id, snowflake user_id) { + return {this, static_cast(&cluster::guild_ban_delete), guild_id, user_id}; +} + +awaitable cluster::co_guild_create(const class guild &g) { + return {this, static_cast(&cluster::guild_create), g}; +} + +awaitable cluster::co_guild_delete(snowflake guild_id) { + return {this, static_cast(&cluster::guild_delete), guild_id}; +} + +awaitable cluster::co_guild_delete_integration(snowflake guild_id, snowflake integration_id) { + return {this, static_cast(&cluster::guild_delete_integration), guild_id, integration_id}; +} + +awaitable cluster::co_guild_edit(const class guild &g) { + return {this, static_cast(&cluster::guild_edit), g}; +} + +awaitable cluster::co_guild_edit_widget(snowflake guild_id, const class guild_widget &gw) { + return {this, static_cast(&cluster::guild_edit_widget), guild_id, gw}; +} + +awaitable cluster::co_guild_get_ban(snowflake guild_id, snowflake user_id) { + return {this, static_cast(&cluster::guild_get_ban), guild_id, user_id}; +} + +awaitable cluster::co_guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit) { + return {this, static_cast(&cluster::guild_get_bans), guild_id, before, after, limit}; +} + +awaitable cluster::co_guild_get(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get), guild_id}; +} + +awaitable cluster::co_guild_get_integrations(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_integrations), guild_id}; +} + +awaitable cluster::co_guild_get_preview(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_preview), guild_id}; +} + +awaitable cluster::co_guild_get_vanity(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_vanity), guild_id}; +} + +awaitable cluster::co_guild_get_widget(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_widget), guild_id}; +} + +awaitable cluster::co_guild_modify_integration(snowflake guild_id, const class integration &i) { + return {this, static_cast(&cluster::guild_modify_integration), guild_id, i}; +} + +awaitable cluster::co_guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo) { + return {this, static_cast(&cluster::guild_get_prune_counts), guild_id, pruneinfo}; +} + +awaitable cluster::co_guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo) { + return {this, static_cast(&cluster::guild_begin_prune), guild_id, pruneinfo}; +} + +awaitable cluster::co_guild_set_nickname(snowflake guild_id, const std::string &nickname) { + return {this, static_cast(&cluster::guild_set_nickname), guild_id, nickname}; +} + +awaitable cluster::co_guild_sync_integration(snowflake guild_id, snowflake integration_id) { + return {this, static_cast(&cluster::guild_sync_integration), guild_id, integration_id}; +} + +awaitable cluster::co_guild_get_onboarding(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_onboarding), guild_id}; +} + +awaitable cluster::co_guild_edit_onboarding(const struct onboarding& o) { + return {this, static_cast(&cluster::guild_edit_onboarding), o}; +} + +awaitable cluster::co_guild_get_welcome_screen(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_welcome_screen), guild_id}; +} + +awaitable cluster::co_guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled) { + return {this, static_cast(&cluster::guild_edit_welcome_screen), guild_id, welcome_screen, enabled}; +} + +awaitable cluster::co_guild_add_member(const guild_member& gm, const std::string &access_token) { + return {this, static_cast(&cluster::guild_add_member), gm, access_token}; +} + +awaitable cluster::co_guild_edit_member(const guild_member& gm) { + return {this, static_cast(&cluster::guild_edit_member), gm}; +} + +awaitable cluster::co_guild_get_member(snowflake guild_id, snowflake user_id) { + return {this, static_cast(&cluster::guild_get_member), guild_id, user_id}; +} + +awaitable cluster::co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake after) { + return {this, static_cast(&cluster::guild_get_members), guild_id, limit, after}; +} + +awaitable cluster::co_guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id) { + return {this, static_cast(&cluster::guild_member_add_role), guild_id, user_id, role_id}; +} + +awaitable cluster::co_guild_member_delete(snowflake guild_id, snowflake user_id) { + return {this, static_cast(&cluster::guild_member_delete), guild_id, user_id}; +} + +awaitable cluster::co_guild_member_kick(snowflake guild_id, snowflake user_id) { + return {this, static_cast(&cluster::guild_member_kick), guild_id, user_id}; +} + +awaitable cluster::co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until) { + return {this, static_cast(&cluster::guild_member_timeout), guild_id, user_id, communication_disabled_until}; +} + +awaitable cluster::co_guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id) { + return {this, static_cast(&cluster::guild_member_delete_role), guild_id, user_id, role_id}; +} + +awaitable cluster::co_guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id) { + return {this, static_cast(&cluster::guild_member_remove_role), guild_id, user_id, role_id}; +} + +awaitable cluster::co_guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id) { + return {this, static_cast(&cluster::guild_member_move), channel_id, guild_id, user_id}; +} + +awaitable cluster::co_guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit) { + return {this, static_cast(&cluster::guild_search_members), guild_id, query, limit}; +} + +awaitable cluster::co_guild_get_invites(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_invites), guild_id}; +} + +awaitable cluster::co_invite_delete(const std::string &invitecode) { + return {this, static_cast(&cluster::invite_delete), invitecode}; +} + +awaitable cluster::co_invite_get(const std::string &invite_code) { + return {this, static_cast(&cluster::invite_get), invite_code}; +} + +awaitable cluster::co_message_add_reaction(const struct message &m, const std::string &reaction) { + return {this, static_cast(&cluster::message_add_reaction), m, reaction}; +} + +awaitable cluster::co_message_add_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction) { + return {this, static_cast(&cluster::message_add_reaction), message_id, channel_id, reaction}; +} + +awaitable cluster::co_message_create(const message &m) { + return {this, static_cast(&cluster::message_create), m}; +} + +awaitable cluster::co_message_crosspost(snowflake message_id, snowflake channel_id) { + return {this, static_cast(&cluster::message_crosspost), message_id, channel_id}; +} + +awaitable cluster::co_message_delete_all_reactions(const struct message &m) { + return {this, static_cast(&cluster::message_delete_all_reactions), m}; +} + +awaitable cluster::co_message_delete_all_reactions(snowflake message_id, snowflake channel_id) { + return {this, static_cast(&cluster::message_delete_all_reactions), message_id, channel_id}; +} + +awaitable cluster::co_message_delete_bulk(const std::vector& message_ids, snowflake channel_id) { + return {this, static_cast&, snowflake, command_completion_event_t)>(&cluster::message_delete_bulk), message_ids, channel_id}; +} + +awaitable cluster::co_message_delete(snowflake message_id, snowflake channel_id) { + return {this, static_cast(&cluster::message_delete), message_id, channel_id}; +} + +awaitable cluster::co_message_delete_own_reaction(const struct message &m, const std::string &reaction) { + return {this, static_cast(&cluster::message_delete_own_reaction), m, reaction}; +} + +awaitable cluster::co_message_delete_own_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction) { + return {this, static_cast(&cluster::message_delete_own_reaction), message_id, channel_id, reaction}; +} + +awaitable cluster::co_message_delete_reaction(const struct message &m, snowflake user_id, const std::string &reaction) { + return {this, static_cast(&cluster::message_delete_reaction), m, user_id, reaction}; +} + +awaitable cluster::co_message_delete_reaction(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction) { + return {this, static_cast(&cluster::message_delete_reaction), message_id, channel_id, user_id, reaction}; +} + +awaitable cluster::co_message_delete_reaction_emoji(const struct message &m, const std::string &reaction) { + return {this, static_cast(&cluster::message_delete_reaction_emoji), m, reaction}; +} + +awaitable cluster::co_message_delete_reaction_emoji(snowflake message_id, snowflake channel_id, const std::string &reaction) { + return {this, static_cast(&cluster::message_delete_reaction_emoji), message_id, channel_id, reaction}; +} + +awaitable cluster::co_message_edit(const message &m) { + return {this, static_cast(&cluster::message_edit), m}; +} + +awaitable cluster::co_message_get(snowflake message_id, snowflake channel_id) { + return {this, static_cast(&cluster::message_get), message_id, channel_id}; +} + +awaitable cluster::co_message_get_reactions(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit) { + return {this, static_cast(&cluster::message_get_reactions), m, reaction, before, after, limit}; +} + +awaitable cluster::co_message_get_reactions(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit) { + return {this, static_cast(&cluster::message_get_reactions), message_id, channel_id, reaction, before, after, limit}; +} + +awaitable cluster::co_message_pin(snowflake channel_id, snowflake message_id) { + return {this, static_cast(&cluster::message_pin), channel_id, message_id}; +} + +awaitable cluster::co_messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit) { + return {this, static_cast(&cluster::messages_get), channel_id, around, before, after, limit}; +} + +awaitable cluster::co_message_unpin(snowflake channel_id, snowflake message_id) { + return {this, static_cast(&cluster::message_unpin), channel_id, message_id}; +} + +awaitable cluster::co_channel_pins_get(snowflake channel_id) { + return {this, static_cast(&cluster::channel_pins_get), channel_id}; +} + +awaitable cluster::co_role_create(const class role &r) { + return {this, static_cast(&cluster::role_create), r}; +} + +awaitable cluster::co_role_delete(snowflake guild_id, snowflake role_id) { + return {this, static_cast(&cluster::role_delete), guild_id, role_id}; +} + +awaitable cluster::co_role_edit(const class role &r) { + return {this, static_cast(&cluster::role_edit), r}; +} + +awaitable cluster::co_roles_edit_position(snowflake guild_id, const std::vector &roles) { + return {this, static_cast &, command_completion_event_t)>(&cluster::roles_edit_position), guild_id, roles}; +} + +awaitable cluster::co_roles_get(snowflake guild_id) { + return {this, static_cast(&cluster::roles_get), guild_id}; +} + +awaitable cluster::co_application_role_connection_get(snowflake application_id) { + return {this, static_cast(&cluster::application_role_connection_get), application_id}; +} + +awaitable cluster::co_application_role_connection_update(snowflake application_id, const std::vector &connection_metadata) { + return {this, static_cast &, command_completion_event_t)>(&cluster::application_role_connection_update), application_id, connection_metadata}; +} + +awaitable cluster::co_user_application_role_connection_get(snowflake application_id) { + return {this, static_cast(&cluster::user_application_role_connection_get), application_id}; +} + +awaitable cluster::co_user_application_role_connection_update(snowflake application_id, const application_role_connection &connection) { + return {this, static_cast(&cluster::user_application_role_connection_update), application_id, connection}; +} + +awaitable cluster::co_guild_events_get(snowflake guild_id) { + return {this, static_cast(&cluster::guild_events_get), guild_id}; +} + +awaitable cluster::co_guild_event_create(const scheduled_event& event) { + return {this, static_cast(&cluster::guild_event_create), event}; +} + +awaitable cluster::co_guild_event_delete(snowflake event_id, snowflake guild_id) { + return {this, static_cast(&cluster::guild_event_delete), event_id, guild_id}; +} + +awaitable cluster::co_guild_event_edit(const scheduled_event& event) { + return {this, static_cast(&cluster::guild_event_edit), event}; +} + +awaitable cluster::co_guild_event_get(snowflake guild_id, snowflake event_id) { + return {this, static_cast(&cluster::guild_event_get), guild_id, event_id}; +} + +awaitable cluster::co_stage_instance_create(const stage_instance& si) { + return {this, static_cast(&cluster::stage_instance_create), si}; +} + +awaitable cluster::co_stage_instance_get(const snowflake channel_id) { + return {this, static_cast(&cluster::stage_instance_get), channel_id}; +} + +awaitable cluster::co_stage_instance_edit(const stage_instance& si) { + return {this, static_cast(&cluster::stage_instance_edit), si}; +} + +awaitable cluster::co_stage_instance_delete(const snowflake channel_id) { + return {this, static_cast(&cluster::stage_instance_delete), channel_id}; +} + +awaitable cluster::co_guild_sticker_create(sticker &s) { + return {this, static_cast(&cluster::guild_sticker_create), s}; +} + +awaitable cluster::co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id) { + return {this, static_cast(&cluster::guild_sticker_delete), sticker_id, guild_id}; +} + +awaitable cluster::co_guild_sticker_get(snowflake id, snowflake guild_id) { + return {this, static_cast(&cluster::guild_sticker_get), id, guild_id}; +} + +awaitable cluster::co_guild_sticker_modify(sticker &s) { + return {this, static_cast(&cluster::guild_sticker_modify), s}; +} + +awaitable cluster::co_guild_stickers_get(snowflake guild_id) { + return {this, static_cast(&cluster::guild_stickers_get), guild_id}; +} + +awaitable cluster::co_nitro_sticker_get(snowflake id) { + return {this, static_cast(&cluster::nitro_sticker_get), id}; +} + +awaitable cluster::co_sticker_packs_get() { + return {this, static_cast(&cluster::sticker_packs_get)}; +} + +awaitable cluster::co_guild_create_from_template(const std::string &code, const std::string &name) { + return {this, static_cast(&cluster::guild_create_from_template), code, name}; +} + +awaitable cluster::co_guild_template_create(snowflake guild_id, const std::string &name, const std::string &description) { + return {this, static_cast(&cluster::guild_template_create), guild_id, name, description}; +} + +awaitable cluster::co_guild_template_delete(snowflake guild_id, const std::string &code) { + return {this, static_cast(&cluster::guild_template_delete), guild_id, code}; +} + +awaitable cluster::co_guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description) { + return {this, static_cast(&cluster::guild_template_modify), guild_id, code, name, description}; +} + +awaitable cluster::co_guild_templates_get(snowflake guild_id) { + return {this, static_cast(&cluster::guild_templates_get), guild_id}; +} + +awaitable cluster::co_guild_template_sync(snowflake guild_id, const std::string &code) { + return {this, static_cast(&cluster::guild_template_sync), guild_id, code}; +} + +awaitable cluster::co_template_get(const std::string &code) { + return {this, static_cast(&cluster::template_get), code}; +} + +awaitable cluster::co_current_user_join_thread(snowflake thread_id) { + return {this, static_cast(&cluster::current_user_join_thread), thread_id}; +} + +awaitable cluster::co_current_user_leave_thread(snowflake thread_id) { + return {this, static_cast(&cluster::current_user_leave_thread), thread_id}; +} + +awaitable cluster::co_threads_get_active(snowflake guild_id) { + return {this, static_cast(&cluster::threads_get_active), guild_id}; +} + +awaitable cluster::co_threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit) { + return {this, static_cast(&cluster::threads_get_joined_private_archived), channel_id, before_id, limit}; +} + +awaitable cluster::co_threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit) { + return {this, static_cast(&cluster::threads_get_private_archived), channel_id, before_timestamp, limit}; +} + +awaitable cluster::co_threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit) { + return {this, static_cast(&cluster::threads_get_public_archived), channel_id, before_timestamp, limit}; +} + +awaitable cluster::co_thread_member_get(const snowflake thread_id, const snowflake user_id) { + return {this, static_cast(&cluster::thread_member_get), thread_id, user_id}; +} + +awaitable cluster::co_thread_members_get(snowflake thread_id) { + return {this, static_cast(&cluster::thread_members_get), thread_id}; +} + +awaitable cluster::co_thread_create_in_forum(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags) { + return {this, static_cast, command_completion_event_t)>(&cluster::thread_create_in_forum), thread_name, channel_id, msg, auto_archive_duration, rate_limit_per_user, applied_tags}; +} + +awaitable cluster::co_thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user) { + return {this, static_cast(&cluster::thread_create), thread_name, channel_id, auto_archive_duration, thread_type, invitable, rate_limit_per_user}; +} + +awaitable cluster::co_thread_edit(const thread &t) { + return {this, static_cast(&cluster::thread_edit), t}; +} + +awaitable cluster::co_thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user) { + return {this, static_cast(&cluster::thread_create_with_message), thread_name, channel_id, message_id, auto_archive_duration, rate_limit_per_user}; +} + +awaitable cluster::co_thread_member_add(snowflake thread_id, snowflake user_id) { + return {this, static_cast(&cluster::thread_member_add), thread_id, user_id}; +} + +awaitable cluster::co_thread_member_remove(snowflake thread_id, snowflake user_id) { + return {this, static_cast(&cluster::thread_member_remove), thread_id, user_id}; +} + +awaitable cluster::co_current_user_edit(const std::string &nickname, const std::string& image_blob, const image_type type) { + return {this, static_cast(&cluster::current_user_edit), nickname, image_blob, type}; +} + +awaitable cluster::co_current_application_get() { + return {this, static_cast(&cluster::current_application_get)}; +} + +awaitable cluster::co_current_user_get() { + return {this, static_cast(&cluster::current_user_get)}; +} + +awaitable cluster::co_current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress, time_t request_to_speak_timestamp) { + return {this, static_cast(&cluster::current_user_set_voice_state), guild_id, channel_id, suppress, request_to_speak_timestamp}; +} + +awaitable cluster::co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress) { + return {this, static_cast(&cluster::user_set_voice_state), user_id, guild_id, channel_id, suppress}; +} + +awaitable cluster::co_current_user_connections_get() { + return {this, static_cast(&cluster::current_user_connections_get)}; +} + +awaitable cluster::co_current_user_get_guilds() { + return {this, static_cast(&cluster::current_user_get_guilds)}; +} + +awaitable cluster::co_current_user_leave_guild(snowflake guild_id) { + return {this, static_cast(&cluster::current_user_leave_guild), guild_id}; +} + +awaitable cluster::co_user_get(snowflake user_id) { + return {this, static_cast(&cluster::user_get), user_id}; +} + +awaitable cluster::co_user_get_cached(snowflake user_id) { + return {this, static_cast(&cluster::user_get_cached), user_id}; +} + +awaitable cluster::co_get_voice_regions() { + return {this, static_cast(&cluster::get_voice_regions)}; +} + +awaitable cluster::co_guild_get_voice_regions(snowflake guild_id) { + return {this, static_cast(&cluster::guild_get_voice_regions), guild_id}; +} + +awaitable cluster::co_create_webhook(const class webhook &w) { + return {this, static_cast(&cluster::create_webhook), w}; +} + +awaitable cluster::co_delete_webhook(snowflake webhook_id) { + return {this, static_cast(&cluster::delete_webhook), webhook_id}; +} + +awaitable cluster::co_delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id) { + return {this, static_cast(&cluster::delete_webhook_message), wh, message_id, thread_id}; +} + +awaitable cluster::co_delete_webhook_with_token(snowflake webhook_id, const std::string &token) { + return {this, static_cast(&cluster::delete_webhook_with_token), webhook_id, token}; +} + +awaitable cluster::co_edit_webhook(const class webhook& wh) { + return {this, static_cast(&cluster::edit_webhook), wh}; +} + +awaitable cluster::co_edit_webhook_message(const class webhook &wh, const struct message& m, snowflake thread_id) { + return {this, static_cast(&cluster::edit_webhook_message), wh, m, thread_id}; +} + +awaitable cluster::co_edit_webhook_with_token(const class webhook& wh) { + return {this, static_cast(&cluster::edit_webhook_with_token), wh}; +} + +awaitable cluster::co_execute_webhook(const class webhook &wh, const struct message& m, bool wait, snowflake thread_id, const std::string& thread_name) { + return {this, static_cast(&cluster::execute_webhook), wh, m, wait, thread_id, thread_name}; +} + +awaitable cluster::co_get_channel_webhooks(snowflake channel_id) { + return {this, static_cast(&cluster::get_channel_webhooks), channel_id}; +} + +awaitable cluster::co_get_guild_webhooks(snowflake guild_id) { + return {this, static_cast(&cluster::get_guild_webhooks), guild_id}; +} + +awaitable cluster::co_get_webhook(snowflake webhook_id) { + return {this, static_cast(&cluster::get_webhook), webhook_id}; +} + +awaitable cluster::co_get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id) { + return {this, static_cast(&cluster::get_webhook_message), wh, message_id, thread_id}; +} + +awaitable cluster::co_get_webhook_with_token(snowflake webhook_id, const std::string &token) { + return {this, static_cast(&cluster::get_webhook_with_token), webhook_id, token}; +} + + +}; + +/* End of auto-generated definitions */ +dpp::awaitable dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap &headers) { + return awaitable{[&](auto &&cc) { this->request(url, method, cc, postdata, mimetype, headers); }}; +} + +#endif diff --git a/src/dpp/cluster_sync_calls.cpp b/src/dpp/cluster_sync_calls.cpp index 1ff073cb7a..2b9be8b862 100644 --- a/src/dpp/cluster_sync_calls.cpp +++ b/src/dpp/cluster_sync_calls.cpp @@ -337,6 +337,22 @@ confirmation cluster::guild_sync_integration_sync(snowflake guild_id, snowflake return dpp::sync(this, static_cast(&cluster::guild_sync_integration), guild_id, integration_id); } +onboarding cluster::guild_get_onboarding_sync(snowflake guild_id) { + return dpp::sync(this, static_cast(&cluster::guild_get_onboarding), guild_id); +} + +onboarding cluster::guild_edit_onboarding_sync(const struct onboarding& o) { + return dpp::sync(this, static_cast(&cluster::guild_edit_onboarding), o); +} + +dpp::welcome_screen cluster::guild_get_welcome_screen_sync(snowflake guild_id) { + return dpp::sync(this, static_cast(&cluster::guild_get_welcome_screen), guild_id); +} + +dpp::welcome_screen cluster::guild_edit_welcome_screen_sync(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled) { + return dpp::sync(this, static_cast(&cluster::guild_edit_welcome_screen), guild_id, welcome_screen, enabled); +} + confirmation cluster::guild_add_member_sync(const guild_member& gm, const std::string &access_token) { return dpp::sync(this, static_cast(&cluster::guild_add_member), gm, access_token); } diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index f0742b5dd3..77399eaf99 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -645,7 +645,7 @@ void discord_client::disconnect_voice_internal(snowflake guild_id, bool emit_jso std::unique_lock lock(voice_mutex); auto v = connecting_voice_channels.find(guild_id); if (v != connecting_voice_channels.end()) { - log(ll_debug, "Disconnecting voice, guild: {}" + std::to_string(guild_id)); + log(ll_debug, "Disconnecting voice, guild: " + std::to_string(guild_id)); if (emit_json) { queue_message(jsonobj_to_string(json({ { "op", 4 }, diff --git a/src/dpp/dispatcher.cpp b/src/dpp/dispatcher.cpp index 7ab6b68385..cecdce02fd 100644 --- a/src/dpp/dispatcher.cpp +++ b/src/dpp/dispatcher.cpp @@ -208,6 +208,57 @@ void interaction_create_t::delete_original_response(command_completion_event_t c }); } + +#ifdef DPP_CORO +awaitable interaction_create_t::co_reply() const { + return dpp::awaitable{[this](auto &&cb) { this->reply(cb); }}; +} + +awaitable interaction_create_t::co_reply(interaction_response_type t, const message & m) const { + return dpp::awaitable{[&, this](auto &&cb) { this->reply(t, m, cb); }}; +} + +awaitable interaction_create_t::co_reply(interaction_response_type t, const std::string & mt) const { + return dpp::awaitable{[&, this](auto &&cb) { this->reply(t, mt, cb); }}; +} + +awaitable interaction_create_t::co_reply(const message & m) const { + return dpp::awaitable{[&, this](auto &&cb) { this->reply(m, cb); }}; +} + +awaitable interaction_create_t::co_reply(const std::string & mt) const { + return dpp::awaitable{[&, this](auto &&cb) { this->reply(mt, cb); }}; +} + +awaitable interaction_create_t::co_dialog(const interaction_modal_response& mr) const { + return dpp::awaitable{[&, this](auto &&cb) { this->dialog(mr, cb); }}; +} + +awaitable interaction_create_t::co_edit_response(const message & m) const { + return dpp::awaitable{[&, this](auto &&cb) { this->edit_response(m, cb); }}; +} + +awaitable interaction_create_t::co_edit_response(const std::string & mt) const { + return dpp::awaitable{[&, this](auto &&cb) { this->edit_response(mt, cb); }}; +} + +awaitable interaction_create_t::co_thinking(bool ephemeral) const { + return dpp::awaitable{[&, this](auto &&cb) { this->thinking(ephemeral, cb); }}; +} + +awaitable interaction_create_t::co_get_original_response() const { + return dpp::awaitable{[&, this](auto &&cb) { this->get_original_response(cb); }}; +} + +awaitable interaction_create_t::co_edit_original_response(const message & m) const { + return dpp::awaitable{[&, this](auto &&cb) { this->edit_original_response(m, cb); }}; +} + +awaitable interaction_create_t::co_delete_original_response() const { + return dpp::awaitable{[&, this](auto &&cb) { this->delete_original_response(cb); }}; +} +#endif /* DPP_CORO */ + command_value interaction_create_t::get_parameter(const std::string& name) const { const command_interaction ci = command.get_command_interaction(); diff --git a/src/dpp/events/message_reaction_add.cpp b/src/dpp/events/message_reaction_add.cpp index 6c4c5ae184..979a6cc698 100644 --- a/src/dpp/events/message_reaction_add.cpp +++ b/src/dpp/events/message_reaction_add.cpp @@ -48,6 +48,7 @@ void message_reaction_add::handle(discord_client* client, json &j, const std::st mra.channel_id = snowflake_not_null(&d, "channel_id"); mra.reacting_channel = dpp::find_channel(mra.channel_id); mra.message_id = snowflake_not_null(&d, "message_id"); + mra.message_author_id = snowflake_not_null(&d, "message_author_id"); mra.reacting_emoji = dpp::emoji().fill_from_json(&(d["emoji"])); if (mra.channel_id && mra.message_id) { client->creator->on_message_reaction_add.call(mra); diff --git a/src/dpp/guild.cpp b/src/dpp/guild.cpp index f557bb4841..772bb53551 100644 --- a/src/dpp/guild.cpp +++ b/src/dpp/guild.cpp @@ -160,6 +160,9 @@ void from_json(const nlohmann::json& j, guild_member& gm) { set_ts_not_null(&j, "joined_at", gm.joined_at); set_ts_not_null(&j, "premium_since", gm.premium_since); set_ts_not_null(&j, "communication_disabled_until", gm.communication_disabled_until); + /* Note: The permissions of the guild member are stored in the resolved set in the interaction event to + * reduce storage as they would be mostly empty anyway and only retrieved from interaction events + */ uint16_t flags = int16_not_null(&j, "flags"); for (auto & flag : membermap) { @@ -282,6 +285,75 @@ bool guild_member::has_bypasses_verification() const { return flags & dpp::gm_bypasses_verification; } + +welcome_channel::welcome_channel(): channel_id(0), emoji_id(0) { +} + +welcome_channel &welcome_channel::fill_from_json(nlohmann::json *j) { + channel_id = snowflake_not_null(j, "channel_id"); + description = string_not_null(j, "channel_id"); + emoji_id = snowflake_not_null(j, "emoji_id"); + emoji_name = string_not_null(j, "emoji_name"); + return *this; +} + +std::string welcome_channel::build_json(bool with_id) const { + json j; + j["channel_id"] = std::to_string(channel_id); + j["description"] = description; + if (!emoji_id.empty()) { + j["emoji_id"] = std::to_string(emoji_id); + } + if (!emoji_name.empty()) { + j["emoji_name"] = emoji_name; + } + return j.dump(); +} + +welcome_channel &welcome_channel::set_channel_id(const snowflake _channel_id) { + this->channel_id = _channel_id; + return *this; +} + +welcome_channel &welcome_channel::set_description(const std::string &_description) { + this->description = _description; + return *this; +} + +welcome_screen &welcome_screen::fill_from_json(nlohmann::json *j) { + description = string_not_null(j, "description"); + + welcome_channels.clear(); + if (j->contains("welcome_channels")) { + welcome_channels.reserve(j->at("welcome_channels").size()); + for (auto &c : j->at("welcome_channels")) { + welcome_channels.emplace_back(welcome_channel().fill_from_json(&c)); + } + } + return *this; +} + +std::string welcome_screen::build_json(bool with_id) const { + json j; + if (!description.empty()) { + j["description"] = description; + } + + if (!welcome_channels.empty()) { + j["welcome_channels"] = json::array(); + for (const auto &welcome_channel : welcome_channels) { + j["welcome_channels"].push_back(json::parse(welcome_channel.build_json())); + } + } + + return j.dump(); +} + +welcome_screen &welcome_screen::set_description(const std::string &s){ + this->description = s; + return *this; +} + bool guild::is_large() const { return this->flags & g_large; } @@ -603,17 +675,7 @@ guild& guild::fill_from_json(discord_client* shard, nlohmann::json* d) { this->nsfw_level = (guild_nsfw_level_t)int8_not_null(d, "nsfw_level"); if (d->find("welcome_screen") != d->end()) { - json& w = (*d)["welcome_screen"]; - set_string_not_null(&w, "description", welcome_screen.description); - welcome_screen.welcome_channels.reserve(w["welcome_channels"].size()); - for (auto& wc : w["welcome_channels"]) { - welcome_channel_t wchan; - set_string_not_null(&wc, "description", wchan.description); - set_snowflake_not_null(&wc, "channel_id", wchan.channel_id); - set_snowflake_not_null(&wc, "emoji_id", wchan.emoji_id); - set_string_not_null(&wc, "emoji_name", wchan.emoji_name); - welcome_screen.welcome_channels.emplace_back(wchan); - } + this->welcome_screen = dpp::welcome_screen().fill_from_json(&d->at("welcome_screen")); } set_snowflake_not_null(d, "safety_alerts_channel_id", this->safety_alerts_channel_id); @@ -865,4 +927,197 @@ guild_member find_guild_member(const snowflake guild_id, const snowflake user_id } +onboarding_prompt_option::onboarding_prompt_option(): managed(0) { +} + +onboarding_prompt::onboarding_prompt(): managed(0), type(opt_multiple_choice), flags(0) { +} + +onboarding::onboarding(): guild_id(0), mode(gom_default), enabled(false) { +} + +onboarding_prompt_option &onboarding_prompt_option::fill_from_json(nlohmann::json *j) { + this->id = snowflake_not_null(j, "id"); + if (j->contains("emoji")) { + this->emoji = dpp::emoji().fill_from_json(&j->at("emoji")); + } + this->title = string_not_null(j, "title"); + this->description = string_not_null(j, "description"); + + channel_ids.clear(); + if (j->contains("channel_ids")) { + channel_ids.reserve(j->at("channel_ids").size()); + for (auto &channel_id : j->at("channel_ids")) { + channel_ids.push_back(std::stoull(channel_id.get())); + } + } + role_ids.clear(); + if (j->contains("role_ids")) { + role_ids.reserve(j->at("role_ids").size()); + for (auto &role_id : j->at("role_ids")) { + role_ids.push_back(std::stoull(role_id.get())); + } + } + return *this; +} + +std::string onboarding_prompt_option::build_json(bool with_id) const { + json j; + j["emoji"] = json::parse(emoji.build_json()); + j["title"] = title; + if (!description.empty()) { + j["description"] = description; + } + + if (!channel_ids.empty()) { + j["channel_ids"] = json::array(); + for (const auto &channel_id : channel_ids) { + j["channel_ids"].push_back(std::to_string(channel_id)); + } + } + + if (!role_ids.empty()) { + j["role_ids"] = json::array(); + for (const auto &role_id : role_ids) { + j["role_ids"].push_back(std::to_string(role_id)); + } + } + + return j.dump(); +} + +onboarding_prompt_option &onboarding_prompt_option::set_emoji(const dpp::emoji &_emoji) { + this->emoji = _emoji; + return *this; +} + +onboarding_prompt_option &onboarding_prompt_option::set_title(const std::string &_title) { + this->title = _title; + return *this; +} + +onboarding_prompt_option &onboarding_prompt_option::set_description(const std::string &_description) { + this->description = _description; + return *this; +} + +onboarding_prompt &onboarding_prompt::fill_from_json(nlohmann::json *j) { + id = snowflake_not_null(j, "id"); + type = static_cast(int8_not_null(j, "type")); + title = string_not_null(j, "title"); + + options.clear(); + if (j->contains("options")) { + for (auto &option : j->at("options")) { + options.push_back(onboarding_prompt_option().fill_from_json(&option)); + } + } + + flags |= bool_not_null(j, "single_select") ? opf_single_select : 0; + flags |= bool_not_null(j, "required") ? opf_required : 0; + flags |= bool_not_null(j, "in_onboarding") ? opf_in_onboarding : 0; + return *this; +} + +std::string onboarding_prompt::build_json(bool with_id) const { + json j; + j["type"] = type; + j["title"] = title; + + if (!options.empty()) { + j["options"] = json::array(); + for (auto const &option : options) { + j["options"].push_back(json::parse(option.build_json())); + } + } + + j["single_select"] = is_single_select(); + j["required"] = is_required(); + j["in_onboarding"] = is_in_onboarding(); + return j.dump(); +} + +bool onboarding_prompt::is_single_select() const { + return flags & dpp::opf_single_select; +} + +bool onboarding_prompt::is_required() const { + return flags & dpp::opf_required; +} + +bool onboarding_prompt::is_in_onboarding() const { + return flags & dpp::opf_in_onboarding; +} + +onboarding_prompt &onboarding_prompt::set_type(const onboarding_prompt_type _type) { + this->type = _type; + return *this; +} + +onboarding_prompt &onboarding_prompt::set_title(const std::string& _title) { + this->title = _title; + return *this; +} + +onboarding& onboarding::fill_from_json(nlohmann::json* j) { + guild_id = snowflake_not_null(j, "guild_id"); + enabled = bool_not_null(j, "enabled"); + mode = static_cast(int8_not_null(j, "mode")); + + prompts.clear(); + if (j->contains("prompts")) { + for (auto &prompt : j->at("prompts")) { + prompts.push_back(onboarding_prompt().fill_from_json(&prompt)); + } + } + + default_channel_ids.clear(); + if (j->contains("default_channel_ids")) { + default_channel_ids.reserve(j->at("default_channel_ids").size()); + for (auto &default_channel_id : j->at("default_channel_ids")) { + default_channel_ids.push_back(std::stoull(default_channel_id.get())); + } + } + + return *this; +} + +std::string onboarding::build_json(bool with_id) const { + json j; + + if (!prompts.empty()) { + j["prompts"] = json::array(); + for (auto const &prompt : prompts) { + j["prompts"].push_back(json::parse(prompt.build_json())); + } + } + + if (!default_channel_ids.empty()) { + j["default_channel_ids"] = json::array(); + for (auto &default_channel_id : default_channel_ids) { + j["default_channel_ids"].push_back(std::to_string(default_channel_id)); + } + } + + j["enabled"] = enabled; + j["mode"] = mode; + return j.dump(); +} + +onboarding &onboarding::set_guild_id(const snowflake id) { + this->guild_id = id; + return *this; +} + +onboarding &onboarding::set_mode(const onboarding_mode m) { + this->mode = m; + return *this; +} + +onboarding &onboarding::set_enabled(const bool is_enabled) { + this->enabled = is_enabled; + return *this; +} + + }; diff --git a/src/dpp/message.cpp b/src/dpp/message.cpp index 3f4331ad05..ec6a36fa1b 100644 --- a/src/dpp/message.cpp +++ b/src/dpp/message.cpp @@ -18,6 +18,7 @@ * limitations under the License. * ************************************************************************************/ +#include #include #include #include @@ -768,6 +769,7 @@ attachment::attachment(struct message* o) , width(0) , height(0) , ephemeral(false) + , flags(0) , owner(o) { } @@ -785,6 +787,7 @@ attachment::attachment(struct message* o, json *j) : attachment(o) { this->ephemeral = bool_not_null(j, "ephemeral"); this->duration_secs = double_not_null(j, "duration_secs"); this->waveform = string_not_null(j, "waveform"); + this->flags = int8_not_null(j, "flags"); } void attachment::download(http_completion_event callback) const { @@ -797,6 +800,10 @@ void attachment::download(http_completion_event callback) const { } } +bool attachment::is_remix() const { + return flags & a_is_remix; +} + std::string message::build_json(bool with_id, bool is_interaction_response) const { /* This is the basics. once it works, expand on it. */ json j({ @@ -1117,6 +1124,15 @@ message& message::fill_from_json(json* d, cache_policy_t cp) { return *this; } +bool message::has_remix_attachment() const { + return std::any_of( + attachments.begin(), + attachments.end(), + [](const auto& a) -> bool { + return a.is_remix(); + }); +} + sticker::sticker() : managed(0), pack_id(0), type(st_standard), format_type(sf_png), available(true), guild_id(0), sort_value(0) { } diff --git a/src/dpp/role.cpp b/src/dpp/role.cpp index dc11b7772c..7739a3ef19 100644 --- a/src/dpp/role.cpp +++ b/src/dpp/role.cpp @@ -32,6 +32,11 @@ namespace dpp { using json = nlohmann::json; +/* A mapping of discord's flag values to our bitmap (they're different bit positions to fit other stuff in) */ +std::map rolemap = { + { 1 << 0, dpp::r_in_prompt }, +}; + role::role() : managed(), guild_id(0), @@ -70,6 +75,14 @@ role& role::fill_from_json(snowflake _guild_id, nlohmann::json* j) this->colour = int32_not_null(j, "color"); this->position = int8_not_null(j, "position"); this->permissions = snowflake_not_null(j, "permissions"); + + uint8_t f = int8_not_null(j, "flags"); + for (auto & flag : rolemap) { + if (f & flag.first) { + this->flags |= flag.second; + } + } + this->flags |= bool_not_null(j, "hoist") ? dpp::r_hoist : 0; this->flags |= bool_not_null(j, "managed") ? dpp::r_managed : 0; this->flags |= bool_not_null(j, "mentionable") ? dpp::r_mentionable : 0; @@ -167,6 +180,10 @@ bool role::is_linked() const { return this->flags & dpp::r_guild_connections; } +bool role::is_selectable_in_prompt() const { + return this->flags & dpp::r_in_prompt; +} + bool role::has_create_instant_invite() const { return has_administrator() || permissions.has(p_create_instant_invite); } diff --git a/src/dpp/user.cpp b/src/dpp/user.cpp index b9faa8767d..75f3c24bfa 100644 --- a/src/dpp/user.cpp +++ b/src/dpp/user.cpp @@ -127,6 +127,9 @@ std::string user::get_default_avatar_url() const { } std::string user::format_username() const { + if (!global_name.empty()) { + return global_name; + } return username + '#' + leading_zeroes(discriminator, 4); } diff --git a/src/dpp/utility.cpp b/src/dpp/utility.cpp index 8ffe07905c..3ea688099a 100644 --- a/src/dpp/utility.cpp +++ b/src/dpp/utility.cpp @@ -115,6 +115,14 @@ namespace dpp { #endif } + bool is_coro_enabled() { +#ifdef DPP_CORO + return true; +#else + return false; +#endif + } + std::string current_date_time() { #ifdef _WIN32 std::time_t curr_time = time(nullptr); diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index d4393cf9c7..14bd314ea0 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -23,9 +23,15 @@ #include #include +namespace { + /** + * @brief Thread emoji - https://www.compart.com/en/unicode/U+1F9F5 + */ + inline const std::string THREAD_EMOJI = "\xF0\x9F\xA7\xB5"; +} /* Unit tests go here */ -int main() +int main(int argc, char *argv[]) { std::string token(get_token()); @@ -33,7 +39,9 @@ int main() if (offline) { std::cout << "Running offline unit tests only.\n"; } else { - std::cout << "Running offline and online unit tests. Guild ID: " << TEST_GUILD_ID << " Text Channel ID: " << TEST_TEXT_CHANNEL_ID << " VC ID: " << TEST_VC_ID << " User ID: " << TEST_USER_ID << " Event ID: " << TEST_EVENT_ID << "\n"; + if (argc > 1 && std::find_if(argv + 1, argv + argc, [](const char *a){ return (std::strcmp(a, "full") == 0); }) != argv + argc) + extended = true; + std::cout << "Running offline and " << (extended ? "extended" : "limited") << " online unit tests. Guild ID: " << TEST_GUILD_ID << " Text Channel ID: " << TEST_TEXT_CHANNEL_ID << " VC ID: " << TEST_VC_ID << " User ID: " << TEST_USER_ID << " Event ID: " << TEST_EVENT_ID << "\n"; } std::string test_to_escape = "*** _This is a test_ ***\n```cpp\n\ @@ -800,13 +808,17 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } void test_pin() { + if (!extended) { + set_pin_tested(); + return; + } set_test("MESSAGEPIN", false); set_test("MESSAGEUNPIN", false); - bot.message_pin(channel_id, message_id, [=](const dpp::confirmation_callback_t &callback) { + bot.message_pin(channel_id, message_id, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock(mutex); if (!callback.is_error()) { set_test("MESSAGEPIN", true); - bot.message_unpin(TEST_TEXT_CHANNEL_ID, message_id, [=](const dpp::confirmation_callback_t &callback) { + bot.message_unpin(TEST_TEXT_CHANNEL_ID, message_id, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock(mutex); if (!callback.is_error()) { set_test("MESSAGEUNPIN", true); @@ -859,12 +871,13 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b bool members_tested = false; bool messages_tested = false; bool events_tested = false; + bool get_active_tested = false; uint32_t events_tested_mask = 0; uint32_t events_to_test_mask = 0; void delete_if_done() { - if (edit_tested && members_tested && messages_tested && events_tested) { + if (edit_tested && members_tested && messages_tested && events_tested && get_active_tested) { bot.channel_delete(thread_id); } } @@ -896,6 +909,15 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b delete_if_done(); } + void set_get_active_tested() + { + if (get_active_tested) { + return; + } + get_active_tested = true; + delete_if_done(); + } + void set_messages_tested() { if (messages_tested) { @@ -912,7 +934,7 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } events_tested_mask |= flag; for (uint32_t i = 1; i < EVENT_END; i <<= 1) { - if ((events_tested_mask & i) != i) + if ((events_to_test_mask & i) && (events_tested_mask & i) != i) return; } set_events_tested(); @@ -951,7 +973,7 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b set_test("THREAD_UPDATE_EVENT", false); edit.name = "edited"; edit.metadata.locked = true; - bot.thread_edit(edit, [this, id = edit.id](const dpp::confirmation_callback_t &callback) { + bot.thread_edit(edit, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock(mutex); if (!callback.is_error()) { set_test("THREAD_EDIT", true); @@ -961,17 +983,42 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } } - void test_members(const dpp::thread &thread) - { + void test_get_active(const dpp::thread &thread) + { std::lock_guard lock{mutex}; - set_test("THREAD_MEMBER_ADD", false); - set_test("THREAD_MEMBER_GET", false); - set_test("THREAD_MEMBERS_GET", false); - set_test("THREAD_MEMBER_REMOVE", false); - set_test("THREAD_MEMBERS_ADD_EVENT", false); - set_test("THREAD_MEMBERS_REMOVE_EVENT", false); + set_test("THREAD_GET_ACTIVE", false); + bot.threads_get_active(TEST_GUILD_ID, [this](const dpp::confirmation_callback_t &callback) { + std::lock_guard lock{mutex}; + if (!callback.is_error()) { + const auto &threads = callback.get(); + if (auto thread_it = threads.find(thread_id); thread_it != threads.end()) { + const auto &thread = thread_it->second.active_thread; + const auto &member = thread_it->second.bot_member; + if (thread.id == thread_id && member.has_value() && member->user_id == bot.me.id) { + set_test("THREAD_GET_ACTIVE", true); + } + } + } + set_get_active_tested(); + }); + } + + void test_members(const dpp::thread &thread) + { + std::lock_guard lock{mutex}; + if (!members_tested) { + if (!extended) { + set_members_tested(); + return; + } + set_test("THREAD_MEMBER_ADD", false); + set_test("THREAD_MEMBER_GET", false); + set_test("THREAD_MEMBERS_GET", false); + set_test("THREAD_MEMBER_REMOVE", false); + set_test("THREAD_MEMBERS_ADD_EVENT", false); + set_test("THREAD_MEMBERS_REMOVE_EVENT", false); bot.thread_member_add(thread_id, TEST_USER_ID, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock{mutex}; if (callback.is_error()) { @@ -998,34 +1045,27 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b return; } set_test("THREAD_MEMBERS_GET", true); - bot.threads_get_active(TEST_GUILD_ID, [this](const dpp::confirmation_callback_t &callback) { + bot.thread_member_remove(thread_id, TEST_USER_ID, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock{mutex}; if (!callback.is_error()) { - const auto &threads = callback.get(); - if (auto thread_it = threads.find(thread_id); thread_it != threads.end()) { - const auto &thread = thread_it->second.active_thread; - const auto &member = thread_it->second.bot_member; - if (thread.id == thread_id && member.has_value() && member->user_id == bot.me.id) { - set_test("THREAD_GET_ACTIVE", true); - } - } + set_test("THREAD_MEMBER_REMOVE", true); } - bot.thread_member_remove(thread_id, TEST_USER_ID, [this](const dpp::confirmation_callback_t &callback) { - std::lock_guard lock{mutex}; - if (!callback.is_error()) { - set_test("THREAD_MEMBER_REMOVE", true); - } - set_members_tested(); - }); + set_members_tested(); }); }); }); }); } - } + } void test_messages(const dpp::thread &thread) { + if (!extended) { + set_messages_tested(); + set_events_tested(); + return; + } + std::lock_guard lock{mutex}; set_test("THREAD_MESSAGE", false); set_test("THREAD_MESSAGE_CREATE_EVENT", false); @@ -1083,14 +1123,10 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b }); } - void confirm_message_receive() - { - std::lock_guard lock{mutex}; - } - void run(const dpp::thread &thread) { thread_id = thread.id; + test_get_active(thread); test_edit(thread); test_members(thread); test_messages(thread); @@ -1357,18 +1393,25 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } bot.invite_get(created.code, [&bot, created](const dpp::confirmation_callback_t &event) { - if (event.is_error()) return; + if (!event.is_error()) { + auto retrieved = event.get(); + if (retrieved.code == created.code && retrieved.guild_id == created.guild_id && retrieved.channel_id == created.channel_id && retrieved.inviter.id == created.inviter.id) { + if (retrieved.destination_guild.flags & dpp::g_community) + set_test("INVITE_GET", retrieved.expires_at == 0); + else + set_test("INVITE_GET", true); - auto retrieved = event.get(); - if (retrieved.code == created.code && retrieved.expires_at == 0 && retrieved.guild_id == created.guild_id && retrieved.channel_id == created.channel_id && retrieved.inviter.id == created.inviter.id) { - set_test("INVITE_GET", true); + } + else { + set_test("INVITE_GET", false); + } + } else { + set_test("INVITE_GET", false); } set_test("INVITE_DELETE_EVENT", false); - bot.invite_delete(retrieved.code, [](const dpp::confirmation_callback_t &event) { - if (!event.is_error()) { - set_test("INVITE_DELETE", true); - } + bot.invite_delete(created.code, [](const dpp::confirmation_callback_t &event) { + set_test("INVITE_DELETE", !event.is_error()); }); }); }); diff --git a/src/unittest/test.h b/src/unittest/test.h index 331002a997..317996a938 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -40,6 +40,8 @@ enum test_type_t { tt_offline, /* A test that requires discord connectivity */ tt_online, + /* A test that requires both online and full tests to be enabled */ + tt_extended }; /* Represents a test case */ @@ -75,6 +77,7 @@ extern dpp::snowflake TEST_EVENT_ID; /* True if we skip tt_online tests */ extern bool offline; +extern bool extended; /** * @brief Perform a test of a REST base API call with one parameter @@ -314,8 +317,3 @@ inline constexpr user_project_id_t get_user_snowflake; inline constexpr auto is_owner = [](auto &&user) noexcept { return get_user_snowflake(user) == TEST_USER_ID; }; - -/** - * @brief Thread emoji - https://www.compart.com/en/unicode/U+1F9F5 - */ -inline const std::string THREAD_EMOJI = "\xF0\x9F\xA7\xB5"; diff --git a/src/unittest/unittest.cpp b/src/unittest/unittest.cpp index 70c9b53788..6c909c9f77 100644 --- a/src/unittest/unittest.cpp +++ b/src/unittest/unittest.cpp @@ -33,8 +33,6 @@ std::map tests = { {"MESSAGECREATE", {tt_online, "Creation of a channel message", false, false}}, {"MESSAGEEDIT", {tt_online, "Editing a channel message", false, false}}, {"EDITEVENT", {tt_online, "Message edit event", false, false}}, - {"MESSAGEPIN", {tt_online, "Pinning a channel message", false, false}}, - {"MESSAGEUNPIN", {tt_online, "Unpinning a channel message", false, false}}, {"MESSAGEDELETE", {tt_online, "Deletion of a channel message", false, false}}, {"MESSAGERECEIVE", {tt_online, "Receipt of a created message", false, false}}, {"MESSAGEFILE", {tt_online, "Message attachment send and check", false, false}}, @@ -103,22 +101,8 @@ std::map tests = { {"THREAD_DELETE_EVENT", {tt_online, "cluster::on_thread_delete event", false, false}}, {"THREAD_EDIT", {tt_online, "cluster::thread_edit", false, false}}, {"THREAD_UPDATE_EVENT", {tt_online, "cluster::on_thread_update event", false, false}}, - {"THREAD_MEMBER_ADD", {tt_online, "cluster::thread_member_add", false, false}}, - {"THREAD_MEMBER_GET", {tt_online, "cluster::thread_member_get", false, false}}, - {"THREAD_MEMBERS_GET", {tt_online, "cluster::thread_members_get", false, false}}, - {"THREAD_MEMBER_REMOVE", {tt_online, "cluster::thread_member_remove", false, false}}, - {"THREAD_MEMBERS_ADD_EVENT", {tt_online, "cluster::on_thread_members_update event with member addition", false, false}}, - {"THREAD_MEMBERS_REMOVE_EVENT", {tt_online, "cluster::on_thread_members_update event with member removal", false, false}}, - {"THREAD_CREATE_MESSAGE", {tt_online, "cluster::thread_create_with_message", false, false}}, {"THREAD_GET_ACTIVE", {tt_online, "cluster::threads_get_active", false, false}}, - {"THREAD_MESSAGE", {tt_online, "message manipulation in thread", false, false}}, - {"THREAD_MESSAGE_CREATE_EVENT", {tt_online, "cluster::on_message_create in thread", false, false}}, - {"THREAD_MESSAGE_EDIT_EVENT", {tt_online, "cluster::on_message_edit in thread", false, false}}, - {"THREAD_MESSAGE_DELETE_EVENT", {tt_online, "cluster::on_message_delete in thread", false, false}}, - {"THREAD_MESSAGE_REACT_ADD_EVENT", {tt_online, "cluster::on_reaction_add in thread", false, false}}, - {"THREAD_MESSAGE_REACT_REMOVE_EVENT", {tt_online, "cluster::on_reaction_remove in thread", false, false}}, - {"VOICE_CHANNEL_CREATE", {tt_online, "creating a voice channel", false, false}}, {"VOICE_CHANNEL_EDIT", {tt_online, "editing the created voice channel", false, false}}, {"VOICE_CHANNEL_DELETE", {tt_online, "deleting the created voice channel", false, false}}, @@ -169,10 +153,31 @@ std::map tests = { {"INVITE_CREATE", {tt_online, "cluster::channel_invite_create", false, false}}, {"INVITE_GET", {tt_online, "cluster::invite_get", false, false}}, {"INVITE_DELETE", {tt_online, "cluster::invite_delete", false, false}}, + + /* Extended set -- Less important, skipped on the master branch due to rate limits and GitHub actions limitations*/ + /* To execute, run unittests with "full" command line argument */ + {"MESSAGEPIN", {tt_extended, "Pinning a channel message", false, false}}, + {"MESSAGEUNPIN", {tt_extended, "Unpinning a channel message", false, false}}, + + {"THREAD_MEMBER_ADD", {tt_extended, "cluster::thread_member_add", false, false}}, + {"THREAD_MEMBER_GET", {tt_extended, "cluster::thread_member_get", false, false}}, + {"THREAD_MEMBERS_GET", {tt_extended, "cluster::thread_members_get", false, false}}, + {"THREAD_MEMBER_REMOVE", {tt_extended, "cluster::thread_member_remove", false, false}}, + {"THREAD_MEMBERS_ADD_EVENT", {tt_extended, "cluster::on_thread_members_update event with member addition", false, false}}, + {"THREAD_MEMBERS_REMOVE_EVENT", {tt_extended, "cluster::on_thread_members_update event with member removal", false, false}}, + {"THREAD_CREATE_MESSAGE", {tt_extended, "cluster::thread_create_with_message", false, false}}, + + {"THREAD_MESSAGE", {tt_extended, "message manipulation in thread", false, false}}, + {"THREAD_MESSAGE_CREATE_EVENT", {tt_extended, "cluster::on_message_create in thread", false, false}}, + {"THREAD_MESSAGE_EDIT_EVENT", {tt_extended, "cluster::on_message_edit in thread", false, false}}, + {"THREAD_MESSAGE_DELETE_EVENT", {tt_extended, "cluster::on_message_delete in thread", false, false}}, + {"THREAD_MESSAGE_REACT_ADD_EVENT", {tt_extended, "cluster::on_reaction_add in thread", false, false}}, + {"THREAD_MESSAGE_REACT_REMOVE_EVENT", {tt_extended, "cluster::on_reaction_remove in thread", false, false}}, }; double start = dpp::utility::time_f(); bool offline = false; +bool extended = false; dpp::snowflake TEST_GUILD_ID = std::stoull(SAFE_GETENV("TEST_GUILD_ID")); dpp::snowflake TEST_TEXT_CHANNEL_ID = std::stoull(SAFE_GETENV("TEST_TEXT_CHANNEL_ID")); @@ -216,7 +221,7 @@ int test_summary() { std::cout << "\u001b[37;1m\n\nUNIT TEST SUMMARY\n==================\n\u001b[0m"; for (auto & t : tests) { bool test_skipped = false; - if (t.second.type == tt_online && offline) { + if ((t.second.type == tt_online && offline) || (t.second.type == tt_extended && !extended)) { skipped++; test_skipped = true; } else { @@ -232,9 +237,18 @@ int test_summary() { return failed; } +namespace { + std::string get_testdata_dir() { + char *env_var = getenv("TEST_DATA_DIR"); + + return (env_var ? env_var : "../../testdata/"); + } +} + std::vector load_test_audio() { std::vector testaudio; - std::ifstream input ("../../testdata/Robot.pcm", std::ios::in|std::ios::binary|std::ios::ate); + std::string dir = get_testdata_dir(); + std::ifstream input (dir + "Robot.pcm", std::ios::in|std::ios::binary|std::ios::ate); if (input.is_open()) { size_t testaudio_size = input.tellg(); testaudio.resize(testaudio_size); @@ -243,7 +257,7 @@ std::vector load_test_audio() { input.close(); } else { - std::cout << "ERROR: Can't load ../../testdata/Robot.pcm\n"; + std::cout << "ERROR: Can't load " + dir + "Robot.pcm\n"; exit(1); } return testaudio; @@ -251,7 +265,8 @@ std::vector load_test_audio() { std::vector load_test_image() { std::vector testimage; - std::ifstream input ("../../testdata/DPP-Logo.png", std::ios::in|std::ios::binary|std::ios::ate); + std::string dir = get_testdata_dir(); + std::ifstream input (dir + "DPP-Logo.png", std::ios::in|std::ios::binary|std::ios::ate); if (input.is_open()) { size_t testimage_size = input.tellg(); testimage.resize(testimage_size); @@ -260,7 +275,7 @@ std::vector load_test_image() { input.close(); } else { - std::cout << "ERROR: Can't load ../../testdata/DPP-Logo.png\n"; + std::cout << "ERROR: Can't load " + dir + "DPP-Logo.png\n"; exit(1); } return testimage; @@ -287,9 +302,9 @@ void wait_for_tests() { for (auto & t : tests) { if (t.second.executed == true) { executed++; - } else if (offline && t.second.type == tt_online && !t.second.executed) { + } else if (!t.second.executed && ((offline && t.second.type == tt_online) || (!extended && t.second.type == tt_extended))) { executed++; - t.second.success = true; + t.second.executed = true; std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[33mSKIPPED\u001b[0m] " << t.second.description << "\n"; } } @@ -300,4 +315,8 @@ void wait_for_tests() { std::this_thread::sleep_for(std::chrono::seconds(1)); ticks++; } + for (auto &t : tests) { + if (!t.second.executed) + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[31mTIMEOUT\u001b[0m] " << t.second.description << "\n"; + } }