coro-simple-commands

# Making simple commands

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

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>

    intmain(){
    dpp::clusterbot{"token"};

    bot.on_log(dpp::utility::cout_logger());

    bot.on_slashcommand([](constdpp::slashcommand_t&event)->dpp::task<void>{
    if(event.command.get_command_name()=="addemoji"){
    dpp::cluster*cluster=event.owner;
    //Retrieveparametervalues
    dpp::snowflakefile_id=std::get<dpp::snowflake>(event.get_parameter("file"));
    std::stringemoji_name=std::get<std::string>(event.get_parameter("name"));

    //Gettheattachmentfromtheresolvedlist
    constdpp::attachment&attachment=event.command.get_resolved_attachment(file_id);

    //ForsimplicityforthisexampleweonlysupportPNG
    if(attachment.content_type!="image/png"){
    //Whilewecoulduseevent.co_reply,wecanjustuseevent.reply,aswewillexitthecommandanywayanddon'tneedtowaitontheresult
    event.reply("Error:type"+attachment.content_type+"notsupported");
    co_return;
    }
    //Senda"<bot>isthinking..."message,towaitonlatersowecanedit
    dpp::asyncthinking=event.co_thinking(false);

    //Downloadandco_awaittheresult
    dpp::http_request_completion_tresponse=co_awaitcluster->co_request(attachment.url,dpp::m_get);

    if(response.status!=200){//Pagedidn'tsendtheimage
    co_awaitthinking;//Waitforthethinkingresponsetoarrivesowecanedit
    event.edit_response("Error:couldnotdownloadtheattachment");
    }else{
    //Loadtheimagedatainadpp::emoji
    dpp::emojiemoji(emoji_name);
    emoji.load_image(response.body,dpp::image_type::i_png);

    //Createtheemojiandco_awaittheresponse
    dpp::confirmation_callback_tconfirmation=co_awaitcluster->co_guild_emoji_create(event.command.guild_id,emoji);

    co_awaitthinking;//Waitforthethinkingresponsetoarrivesowecanedit
    if(confirmation.is_error()){
    event.edit_response("Error:couldnotaddemoji:"+confirmation.get_error().message);
    }else{//Success
    event.edit_response("Successfullyadded"+confirmation.get<dpp::emoji>().get_mention());//Showthenewemoji
    }
    }
    }
    });

    bot.on_ready([&bot](constdpp::ready_t&event){
    if(dpp::run_once<struct register_bot_commands>()){
    dpp::slashcommandcommand("addemoji","Addanemoji",bot.me.id);
    //Addfileandnameasrequiredparameters
    command.add_option(dpp::command_option(dpp::co_attachment,"file","Selectanimage",true));
    command.add_option(dpp::command_option(dpp::co_string,"name","Nameoftheemojitoadd",true));

    bot.global_command_create(command);
    }
    });

    bot.start(dpp::st_wait);

    return0;
    }

### I heard you liked tasks

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>

    intmain(){
    dpp::clusterbot{"token"};

    bot.on_log(dpp::utility::cout_logger());

    bot.on_slashcommand([](constdpp::slashcommand_t&event)->dpp::task<void>{
    if(event.command.get_command_name()=="avatar"){
    //Makeanestedcoroutinetofetchtheguildmemberrequested,thatreturnsitasanoptional
    constexprautoresolve_member=[](constdpp::slashcommand_t&event)->dpp::task<std::optional<dpp::guild_member>>{
    constdpp::command_value&user_param=event.get_parameter("user");
    dpp::snowflakeuser_id;
    if(std::holds_alternative<std::monostate>(user_param)){
    user_id=event.command.usr.id;//Parameterisemptysouserissender
    }
    elseif(std::holds_alternative<dpp::snowflake>(user_param)){
    user_id=std::get<dpp::snowflake>(user_param);//Parameterhasauser
    }

    //Ifwehavetheguildmemberinthecommand'sresolveddata,returnit
    constauto&member_map=event.command.resolved.members;
    if(automember=member_map.find(user_id);member!=member_map.end()){
    co_returnmember->second;
    }
    //Trylookinginguildcache
    dpp::guild*guild=dpp::find_guild(event.command.guild_id);
    if(guild){
    //Lookinguild'smembercache
    if(automember=guild->members.find(user_id);member!=guild->members.end()){
    co_returnmember->second;
    }
    }
    //Finallyifeverythingelsefailed,requestAPI
    dpp::confirmation_callback_tconfirmation=co_awaitevent.owner->co_guild_get_member(event.command.guild_id,user_id);
    if(confirmation.is_error()){
    co_returnstd::nullopt;//Membernotfound,returnempty
    }else{
    co_returnconfirmation.get<dpp::guild_member>();
    }
    };

    //Senda"<bot>isthinking..."message,towaitonlatersowecanedit
    dpp::asyncthinking=event.co_thinking(false);
    //Callourcoroutinedefinedabovetoretrievethememberrequested
    std::optional<dpp::guild_member>member=co_awaitresolve_member(event);
    if(!member.has_value()){
    //Waitforthethinkingresponsetoarrivetomakesurewecanedit
    co_awaitthinking;
    event.edit_original_response(dpp::message{"Usernotfoundinthisserver!"});
    co_return;
    }

    std::stringavatar_url=member->get_avatar_url(512);
    if(avatar_url.empty()){//Memberdoesnothaveacustomavatarforthisserver,gettheiruseravatar
    dpp::confirmation_callback_tconfirmation=co_awaitevent.owner->co_user_get_cached(member->user_id);
    if(confirmation.is_error()){
    //Waitforthethinkingresponsetoarrivetomakesurewecanedit
    co_awaitthinking;
    event.edit_original_response(dpp::message{"Usernotfound!"});
    co_return;
    }
    avatar_url=confirmation.get<dpp::user_identified>().get_avatar_url(512);
    }

    //Waitforthethinkingresponsetoarrivetomakesurewecanedit
    co_awaitthinking;
    event.edit_original_response(dpp::message{avatar_url});
    }
    });

    bot.on_ready([&bot](constdpp::ready_t&event){
    if(dpp::run_once<struct register_bot_commands>()){
    dpp::slashcommandcommand("avatar","Getyouroranotheruser'savatarimage",bot.me.id);
    command.add_option(dpp::command_option(dpp::co_user,"user","Usertofetchtheavatarfrom"));

    bot.global_command_create(command);
    }
    });

    bot.start(dpp::st_wait);

    return0;
    }