- Warning
- D++ Coroutines are a very new feature and are currently only supported by D++ on g++ 11, clang/LLVM 14, and MSVC 19.37 or above. Additionally, D++ must be built with the CMake option DPP_CORO, and your program must both define the macro DPP_CORO and use C++20 or above. The feature is experimental and may have bugs or even crashes, please report any to GitHub Issues or to our Discord Server.
Several steps in one
- Note
- The next example assumes you are already familiar with how to use slash commands, parameters, and sending files through a command.
With coroutines, it becomes a lot easier to do several asynchronous requests for one task. As an example 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. Normally we would have to use callbacks and some sort of object keeping track of our state, but with coroutines, the function can simply pause and be resumed when we receive the response to our request :
dpp::cluster *cluster = event.from->creator;
dpp::snowflake file_id = std::get<dpp::snowflake>(event.get_parameter("file"));
std::string emoji_name = std::get<std::string>(event.get_parameter("name"));
const dpp::attachment &attachment = event.command.get_resolved_attachment(file_id);
if (attachment.content_type != "image/png") {
event.reply("Error: type " + attachment.content_type + " not supported");
co_return;
}
if (response.status != 200) {
co_await thinking;
event.edit_response("Error: could not download the attachment");
} else {
dpp::emoji emoji(emoji_name);
emoji.load_image(response.body, dpp::image_type::i_png);
dpp::confirmation_callback_t confirmation = co_await cluster->co_guild_emoji_create(event.command.guild_id, emoji);
co_await thinking;
if (confirmation.is_error()) {
event.edit_response("Error: could not add emoji: " + confirmation.get_error().message);
} else {
event.edit_response("Successfully added " + confirmation.get<dpp::emoji>().get_mention());
}
}
}
});
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_command_create(command);
}
});
return 0;
}
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.
Earlier we mentioned two other types of coroutines provided by dpp: dpp::coroutine and dpp::task. They both take their return type as a template parameter, which may be void. Both dpp::job and dpp::task start on the constructor for asynchronous execution, however only the latter can be co_await
-ed, this allows you to retrieve its return value. If a dpp::task is destroyed before it ends, it is cancelled and will stop when it is resumed from the next co_await
. dpp::coroutine also has a return value and can be co_await
-ed, however it only starts when co_await
-ing, meaning it is executed synchronously.
Here is an example of a command making use of dpp::task to retrieve the avatar of a specified user, or if missing, the sender:
constexpr auto resolve_member = [](const dpp::slashcommand_t &event) -> dpp::task<std::optional<dpp::guild_member>> {
const dpp::command_value &user_param = event.get_parameter("user");
dpp::snowflake user_id;
if (std::holds_alternative<std::monostate>(user_param)) {
user_id = event.command.usr.id;
}
else if (std::holds_alternative<dpp::snowflake>(user_param)) {
user_id = std::get<dpp::snowflake>(user_param);
}
const auto &member_map = event.command.resolved.members;
if (auto member = member_map.find(user_id); member != member_map.end()) {
co_return member->second;
}
dpp::guild *guild = dpp::find_guild(event.command.guild_id);
if (guild) {
if (auto member = guild->members.find(user_id); member != guild->members.end()) {
co_return member->second;
}
}
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;
} else {
co_return confirmation.get<dpp::guild_member>();
}
};
std::optional<dpp::guild_member> member = co_await resolve_member(event);
if (!member.has_value()) {
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()) {
event.edit_original_response(
dpp::message{
"User not found!"});
co_return;
}
}
}
});
if (dpp::run_once<struct register_bot_commands>()) {
dpp::slashcommand command(
"avatar",
"Get your or another user's avatar image", bot.me.id);
bot.global_command_create(command);
}
});
return 0;
}