| D++ (DPP) C++ Discord API Bot Library

Search
Loading...
Searching...
No Matches
Making simple commands

Warning
  D++ Coroutines are currently only supported by D++ on g++ 13, clang/LLVM 14, and MSVC 19.37 or above. Additionally, your program has to support C++20.

### 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 :

#include <dpp/dpp.h>
int main() {
dpp::cluster bot{"token"};
bot.on_log(dpp::utility::cout_logger());
bot.on_slashcommand([](const dpp::slashcommand_t& event) -> dpp::task<void> {
if (event.command.get_command_name() == "addemoji") {
dpp::cluster *cluster = event.owner;
// Retrieve parameter values
dpp::snowflake file_id = std::get<dpp::snowflake>(event.get_parameter("file"));
std::string emoji_name = std::get<std::string>(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 we could use event.co_reply, 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 "<bot> is thinking..." message, to wait on later so we can edit
dpp::async 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<dpp::emoji>().get_mention()); // Show the new emoji
}
}
}
});
bot.on_ready([&bot](const dpp::ready_t& event) {
if (dpp::run_once<struct register_bot_commands>()) {
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);
return 0;
}
dpp::async
A co_await-able object handling an API call in parallel with the caller.
Definition async.h:107
dpp::cluster
The cluster class represents a group of shards and a command queue for sending and receiving commands...
Definition cluster.h:89
dpp::cluster::global_command_create
void global_command_create(const slashcommand &s, command_completion_event_t callback=utility::log_error())
Create a global slash command (a bot can have a maximum of 100 of these).
dpp::cluster::co_request
async< http_request_completion_t > co_request(const std::string &url, http_method method, const std::string &postdata="", const std::string &mimetype="text/plain", const std::multimap< std::string, std::string > &headers={}, const std::string &protocol="1.1")
dpp::cluster::start
void start(start_type return_after=st_wait)
Start the cluster, connecting all its shards.
dpp::cluster::on_slashcommand
event_router_t< slashcommand_t > on_slashcommand
Called when a slash command is issued. Only dpp::ctxm_chat_input types of interaction are routed to t...
Definition cluster.h:734
dpp::cluster::on_ready
event_router_t< ready_t > on_ready
Called when a shard is connected and ready. A set of cluster::on_guild_create events will follow this...
Definition cluster.h:842
dpp::cluster::on_log
event_router_t< log_t > on_log
Called when a log message is to be written to the log. You can attach any logging system here you wis...
Definition cluster.h:692
dpp::cluster::co_guild_emoji_create
async< confirmation_callback_t > co_guild_emoji_create(snowflake guild_id, const class emoji &newemoji)
Create single emoji. You must ensure that the emoji passed contained image data using the emoji::load...
dpp::cluster::me
dpp::user me
The details of the bot user. This is assumed to be identical across all shards in the cluster....
Definition cluster.h:255
dpp::emoji
Represents an emoji for a dpp::guild.
Definition emoji.h:64
dpp::emoji::get_mention
static std::string get_mention(std::string_view name, snowflake id, bool is_animated=false)
Create a mentionable emoji.
dpp::interaction::guild_id
snowflake guild_id
Optional: the guild it was sent from.
Definition appcommand.h:1038
dpp::interaction::get_command_name
std::string get_command_name() const
Get the command name for a command interaction.
dpp::managed::id
snowflake id
Unique ID of object set by Discord. This value contains a timestamp, worker ID, internal server ID,...
Definition managed.h:39
dpp::slashcommand
Represents an application command, created by your bot either globally, or on a guild.
Definition appcommand.h:1436
dpp::snowflake
A container for a 64 bit unsigned value representing many things on discord. This value is known in d...
Definition snowflake.h:54
dpp::task
A coroutine task. It starts immediately on construction and can be co_await-ed, making it perfect for...
Definition task.h:95
dpp::utility::cout_logger
std::function< void(const dpp::log_t &)> DPP_EXPORT cout_logger()
Get a default logger that outputs to std::cout. e.g.
dpp::run_once
auto run_once()
Run some code within an if() statement only once.
Definition once.h:41
dpp::m_get
@ m_get
GET.
Definition queues.h:189
dpp::i_png
@ i_png
image/png
Definition misc-enum.h:36
dpp::co_attachment
@ co_attachment
File attachment type.
Definition appcommand.h:109
dpp::co_string
@ co_string
A string value.
Definition appcommand.h:69
dpp::st_wait
@ st_wait
Wait forever on a condition variable. The cluster will spawn threads for each shard and start() will ...
Definition cluster.h:72
dpp::attachment
Represents an attachment in a dpp::message.
Definition message.h:1366
dpp::attachment::content_type
std::string content_type
MIME type of the attachment, if applicable.
Definition message.h:1411
dpp::attachment::url
std::string url
URL which points to the attachment.
Definition message.h:1391
dpp::command_option
Each command option is a command line parameter. It can have a type (see dpp::command_option_type),...
Definition appcommand.h:208
dpp::confirmation_callback_t
The results of a REST call wrapped in a convenient struct.
Definition restresults.h:261
dpp::confirmation_callback_t::is_error
bool is_error() const
Returns true if the call resulted in an error rather than a legitimate value in the confirmation_call...
dpp::confirmation_callback_t::get_error
error_info get_error() const
Get the error_info object. The error_info object contains the details of any REST error,...
dpp::confirmation_callback_t::get
T get() const
Get the stored value via std::get.
Definition restresults.h:329
dpp::error_info::message
std::string message
Error message.
Definition restresults.h:245
dpp::http_request_completion_t
The result of any HTTP request. Contains the headers, vital rate limit figures, and returned request ...
Definition queues.h:111
dpp::http_request_completion_t::status
uint16_t status
HTTP status. e.g. 200 = OK, 404 = Not found, 429 = Rate limited, etc.
Definition queues.h:121
dpp::http_request_completion_t::body
std::string body
Reply body.
Definition queues.h:162
dpp::interaction_create_t::command
interaction command
command interaction
Definition dispatcher.h:789
dpp::interaction_create_t::get_parameter
virtual command_value get_parameter(const std::string &name) const
Get a slashcommand parameter.
dpp::ready_t
Session ready.
Definition dispatcher.h:1072
dpp::slashcommand_t
User has issued a slash command.
Definition dispatcher.h:806

### 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:

#include <dpp/dpp.h>
int main() {
dpp::cluster bot{"token"};
bot.on_log(dpp::utility::cout_logger());
bot.on_slashcommand([](const dpp::slashcommand_t& event) -> dpp::task<void> {
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<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; // Parameter is empty so user is sender
}
else if (std::holds_alternative<dpp::snowflake>(user_param)) {
user_id = std::get<dpp::snowflake>(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.owner->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<dpp::guild_member>();
}
};
// Send a "<bot> is thinking..." message, to wait on later so we can edit
dpp::async thinking = event.co_thinking(false);
// Call our coroutine defined above to retrieve the member requested
std::optional<dpp::guild_member> 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.owner->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<dpp::user_identified>().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<struct register_bot_commands>()) {
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);
return 0;
}
dpp::guild_member
Represents dpp::user membership upon a dpp::guild. This contains the user's nickname,...
Definition guild.h:407
dpp::guild
Represents a guild on Discord (AKA a server).
Definition guild.h:999
dpp::guild::members
members_container members
List of guild members.
Definition guild.h:1069
dpp::user_identified
A user with additional fields only available via the oauth2 identify scope. These are not included in...
Definition user.h:470
dpp::unicode_emoji::thinking
constexpr const char thinking[]
Definition unicode_emoji.h:1691
dpp::command_value
std::variant< std::monostate, std::string, int64_t, bool, snowflake, double > command_value
This type is a variant that can hold any of the potential native data types represented by the enum d...
Definition appcommand.h:122
dpp::co_user
@ co_user
A user snowflake id.
Definition appcommand.h:84
dpp::find_guild
DPP_EXPORT class guild * find_guild(snowflake id)
dpp::message
Represents messages sent and received on Discord.
Definition message.h:2350
D++ Library version 9.0.13 D++ Library version 9.0.12 D++ Library version 9.0.11 D++ Library version 9.0.10 D++ Library version 9.0.9 D++ Library version 9.0.8 D++ Library version 9.0.7 D++ Library version 9.0.6 D++ Library version 9.0.5 D++ Library version 9.0.4 D++ Library version 9.0.3 D++ Library version 9.0.2 D++ Library version 9.0.1 D++ Library version 9.0.0 D++ Library version 1.0.2 D++ Library version 1.0.1 D++ Library version 1.0.0