diff --git a/vendor/DPP/include/dpp/cache.h b/vendor/DPP/include/dpp/cache.h index 60a341f9..457da1ae 100644 --- a/vendor/DPP/include/dpp/cache.h +++ b/vendor/DPP/include/dpp/cache.h @@ -88,7 +88,7 @@ namespace dpp { * @brief Get the container map * @warning Be sure to use cache::get_mutex() correctly if you * manipulate or iterate the map returned by this method! If you do - * not, this is not thread safe and will casue crashes! + * not, this is not thread safe and will cause crashes! * @see cache::get_mutex * * @return cache_container& A reference to the cache's container map diff --git a/vendor/DPP/include/dpp/cluster.h b/vendor/DPP/include/dpp/cluster.h index 87938927..ad0939dc 100644 --- a/vendor/DPP/include/dpp/cluster.h +++ b/vendor/DPP/include/dpp/cluster.h @@ -290,8 +290,8 @@ public: * @param shards The total number of shards on this bot. If there are multiple clusters, then (shards / clusters) actual shards will run on this cluster. * If you omit this value, the library will attempt to query the Discord API for the correct number of shards to start. * @param cluster_id The ID of this cluster, should be between 0 and MAXCLUSTERS-1 - * @param maxclusters The total number of clusters that are active, which may be on seperate processes or even separate machines. - * @param compressed Wether or not to use compression for shards on this cluster. Saves a ton of bandwidth at the cost of some CPU + * @param maxclusters The total number of clusters that are active, which may be on separate processes or even separate machines. + * @param compressed Whether or not to use compression for shards on this cluster. Saves a ton of bandwidth at the cost of some CPU * @param policy Set the user caching policy for the cluster, either lazy (only cache users/members when they message the bot) or aggressive (request whole member lists on seeing new guilds too) */ cluster(const std::string &token, uint32_t intents = i_default_intents, uint32_t shards = 0, uint32_t cluster_id = 0, uint32_t maxclusters = 1, bool compressed = true, cache_policy_t policy = {cp_aggressive, cp_aggressive, cp_aggressive}); @@ -1968,7 +1968,7 @@ public: void thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, command_completion_event_t callback = {}); /** - * @brief Create a thread with a message (Discord: ID of a thread is same as mesage ID) + * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) * * @param thread_name Name of the thread * @param channel_id Channel in which thread to create diff --git a/vendor/DPP/include/dpp/commandhandler.h b/vendor/DPP/include/dpp/commandhandler.h index b7aea158..6d3ee3e5 100644 --- a/vendor/DPP/include/dpp/commandhandler.h +++ b/vendor/DPP/include/dpp/commandhandler.h @@ -30,11 +30,29 @@ namespace dpp { +/** + * @brief dpp::resolved_user contains both a dpp::guild_member and a dpp::user. + * The user can be used to obtain in-depth user details such as if they are nitro, + * and the guild member information to check their roles on a guild etc. + * The Discord API provides both if a parameter is a user ping, + * so we offer both in a combined structure. + */ +struct CoreExport resolved_user { + /** + * @brief Holds user information + */ + dpp::user user; + /** + * @brief Holds member information + */ + dpp::guild_member member; +}; + /** * @brief Represents a received parameter. * We use variant so that multiple non-related types can be contained within. */ -typedef std::variant command_parameter; +typedef std::variant command_parameter; /** * @brief Parameter types when registering a command. @@ -102,7 +120,7 @@ typedef std::vector> parameter_registration_t /** * @brief Parameter list for a called command. - * See dpp::parameter_registration_t for an explaination as to why vector is used. + * See dpp::parameter_registration_t for an explanation as to why vector is used. */ typedef std::vector> parameter_list_t; @@ -277,9 +295,25 @@ public: * to the user if you do not emit some form of reply within 3 seconds. * * @param m message to reply with. - * @param interaction true if the reply is generated by an interaction + * @param source source of the command */ void reply(const dpp::message &m, command_source source); + + /** + * @brief Reply to a command without a message, causing the discord client + * to display "Bot name is thinking...". + * The "thinking" message will persist for a maximum of 15 minutes. + * This counts as a reply for a slash command. Slash commands will emit an + * ugly error to the user if you do not emit some form of reply within 3 + * seconds. + * + * @param source source of the command + */ + void thinking(command_source source); + + /* Easter egg */ + void thonk(command_source source); + }; }; \ No newline at end of file diff --git a/vendor/DPP/include/dpp/discord.h b/vendor/DPP/include/dpp/discord.h index d5571404..008ed3dd 100644 --- a/vendor/DPP/include/dpp/discord.h +++ b/vendor/DPP/include/dpp/discord.h @@ -186,7 +186,7 @@ namespace dpp { * @brief Convert a byte count to display value * * @param c number of bytes - * @return std::string display value suffixed with M, G, T where neccessary + * @return std::string display value suffixed with M, G, T where necessary */ std::string CoreExport bytes(uint64_t c); @@ -257,7 +257,7 @@ namespace dpp { * @param str string to return substring from * @param start start codepoint offset * @param length length in codepoints - * @return std::string Substring in UTF-8 or emtpy string if invalid UTF-8 passed in + * @return std::string Substring in UTF-8 or empty string if invalid UTF-8 passed in */ std::string CoreExport utf8substr(const std::string& str, std::string::size_type start, std::string::size_type length); }; diff --git a/vendor/DPP/include/dpp/discordvoiceclient.h b/vendor/DPP/include/dpp/discordvoiceclient.h index 5ab56a47..8116d47a 100644 --- a/vendor/DPP/include/dpp/discordvoiceclient.h +++ b/vendor/DPP/include/dpp/discordvoiceclient.h @@ -201,7 +201,7 @@ class CoreExport discord_voice_client : public websocket_client int UDPSend(const char* data, size_t length); /** - * @brief Receieve data from UDP socket immediately. + * @brief Receive data from UDP socket immediately. * * @param data data to receive * @param max_length size of data receiving buffer diff --git a/vendor/DPP/include/dpp/dispatcher.h b/vendor/DPP/include/dpp/dispatcher.h index ed95da6a..d41872fa 100644 --- a/vendor/DPP/include/dpp/dispatcher.h +++ b/vendor/DPP/include/dpp/dispatcher.h @@ -1095,7 +1095,7 @@ public: * @param event Event parameters */ std::function voice_ready; - /** @brief Event handler function pointer for voice receieve event + /** @brief Event handler function pointer for voice receive event * @param event Event parameters */ std::function voice_receive; diff --git a/vendor/DPP/include/dpp/guild.h b/vendor/DPP/include/dpp/guild.h index 6e34f187..1ffc9896 100644 --- a/vendor/DPP/include/dpp/guild.h +++ b/vendor/DPP/include/dpp/guild.h @@ -207,7 +207,7 @@ public: /** Setting for how notifications are to be delivered to users */ uint8_t default_message_notifications; - /** Wether or not explicit content filtering is enable and what setting it is */ + /** Whether or not explicit content filtering is enable and what setting it is */ uint8_t explicit_content_filter; /** If multi factor authentication is required for moderators or not */ diff --git a/vendor/DPP/include/dpp/message.h b/vendor/DPP/include/dpp/message.h index 7621cac2..c4bf2338 100644 --- a/vendor/DPP/include/dpp/message.h +++ b/vendor/DPP/include/dpp/message.h @@ -474,7 +474,7 @@ struct CoreExport embed { /** Add an embed field. Returns the embed itself so these method calls may be "chained" * @param name The name of the field * @param value The value of the field (max length 1000) - * @param is_inline Wether or not to display the field 'inline' or on its own line + * @param is_inline Whether or not to display the field 'inline' or on its own line * @return A reference to self */ embed& add_field(const std::string& name, const std::string &value, bool is_inline = false); @@ -569,6 +569,8 @@ struct CoreExport attachment { uint32_t height; /** MIME type of the attachment, if applicable */ std::string content_type; + /** Whether this attachment is ephemeral, if applicable */ + bool ephemeral; /** * @brief Constructs a new attachment object. @@ -724,7 +726,7 @@ enum message_flags { /// this message originated from a message in another channel (via Channel Following) m_is_crosspost = 1 << 1, /// do not include any embeds when serializing this message - m_supress_embeds = 1 << 2, + m_suppress_embeds = 1 << 2, /// the source message for this crosspost has been deleted (via Channel Following) m_source_message_deleted = 1 << 3, /// this message came from the urgent message system @@ -736,7 +738,7 @@ enum message_flags { }; /** - * @brief Mesage types for dpp::message::type + * @brief Message types for dpp::message::type */ enum message_type { /// Default @@ -970,9 +972,9 @@ struct CoreExport message { /** * @brief Set the allowed mentions object for pings on the message * - * @param _parse_users wether or not to parse users in the message content or embeds - * @param _parse_roles wether or not to parse roles in the message content or embeds - * @param _parse_everyone wether or not to parse everyone/here in the message content or embeds + * @param _parse_users whether or not to parse users in the message content or embeds + * @param _parse_roles whether or not to parse roles in the message content or embeds + * @param _parse_everyone whether or not to parse everyone/here in the message content or embeds * @param _replied_user if set to true and this is a reply, then ping the user we reply to * @param users list of user ids to allow pings for * @param roles list of role ids to allow pings for @@ -982,7 +984,7 @@ struct CoreExport message { /** Fill this object from json. * @param j JSON object to fill from - * @param cp Cache policy for user records, wether or not we cache users when a message is received + * @param cp Cache policy for user records, whether or not we cache users when a message is received * @return A reference to self */ message& fill_from_json(nlohmann::json* j, cache_policy_t cp = {cp_aggressive, cp_aggressive, cp_aggressive}); @@ -1012,7 +1014,7 @@ struct CoreExport message { * * @return true if embeds removed */ - bool supress_embeds() const; + bool suppress_embeds() const; /** * @brief True if source message was deleted diff --git a/vendor/DPP/include/dpp/slashcommand.h b/vendor/DPP/include/dpp/slashcommand.h index 209ea5f4..5166e72c 100644 --- a/vendor/DPP/include/dpp/slashcommand.h +++ b/vendor/DPP/include/dpp/slashcommand.h @@ -54,8 +54,10 @@ enum command_option_type : uint8_t { * @brief This type is a variant that can hold any of the potential * native data types represented by the enum above. * It is used in interactions. + * + * std::monostate indicates an invalid parameter value, e.g. an unfilled optional parameter. */ -typedef std::variant command_value; +typedef std::variant command_value; /** * @brief This struct represents choices in a multiple choice option @@ -157,8 +159,6 @@ void to_json(nlohmann::json& j, const command_option& opt); */ enum interaction_response_type { ir_pong = 1, //!< ACK a Ping - ir_acknowledge = 2, //!< DEPRECATED ACK a command without sending a message, eating the user's input - ir_channel_message = 3, //!< DEPRECATED respond with a message, eating the user's input ir_channel_message_with_source = 4, //!< respond to an interaction with a message ir_deferred_channel_message_with_source = 5, //!< ACK an interaction and edit a response later, the user sees a loading state ir_deferred_update_message = 6, //!< for components, ACK an interaction and edit the original message later; the user does not see a loading state @@ -226,11 +226,25 @@ struct CoreExport interaction_response { }; /** - * @brief Resolved snowflake ids to usernames. - * TODO: Needs implementation. Not needed something that - * functions as we have cache. + * @brief Resolved snowflake ids to users, guild members, roles and channels. */ struct CoreExport command_resolved { + /** + * @brief Resolved users + */ + std::map users; + /** + * @brief Resolved guild members + */ + std::map members; + /** + * @brief Resolved roles + */ + std::map roles; + /** + * @brief Resolved channels + */ + std::map channels; }; /** @@ -272,7 +286,6 @@ enum interaction_type { struct CoreExport command_interaction { snowflake id; //!< the ID of the invoked command std::string name; //!< the name of the invoked command - command_resolved resolved; //!< Optional: converted users + roles + channels std::vector options; //!< Optional: the params + values from the user }; @@ -326,6 +339,7 @@ public: user usr; //!< Optional: user object for the invoking user, if invoked in a DM std::string token; //!< a continuation token for responding to the interaction uint8_t version; //!< read-only property, always 1 + command_resolved resolved; //!< Resolved user/role etc /** * @brief Fill object properties from JSON @@ -513,7 +527,7 @@ public: /** * @brief Disable default permissions, command will be unusable unless - * permissions are overriden with add_permission and + * permissions are overridden with add_permission and * dpp::guild_command_edit_permissions * * @return slashcommand& reference to self for chaining of calls diff --git a/vendor/DPP/include/dpp/version.h b/vendor/DPP/include/dpp/version.h index be31cd72..59411f59 100644 --- a/vendor/DPP/include/dpp/version.h +++ b/vendor/DPP/include/dpp/version.h @@ -21,9 +21,9 @@ #pragma once #if !defined(DPP_VERSION_LONG) -#define DPP_VERSION_LONG 0x00090003 -#define DPP_VERSION_SHORT 090003 -#define DPP_VERSION_TEXT "D++ 9.0.3 (05-Sep-2021)" +#define DPP_VERSION_LONG 0x00090004 +#define DPP_VERSION_SHORT 090004 +#define DPP_VERSION_TEXT "D++ 9.0.5 (14-Sep-2021)" #define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) #define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) diff --git a/vendor/DPP/include/dpp/voicestate.h b/vendor/DPP/include/dpp/voicestate.h index 8c461ef8..2b875202 100644 --- a/vendor/DPP/include/dpp/voicestate.h +++ b/vendor/DPP/include/dpp/voicestate.h @@ -34,7 +34,7 @@ enum voicestate_flags { vs_self_deaf = 0b00001000, //!< Self Deafened vs_self_stream = 0b00010000, //!< Self Streaming vs_self_video = 0b00100000, //!< Self Video - vs_supress = 0b01000000 //!< Supression + vs_suppress = 0b01000000 //!< Suppression }; /** @@ -94,9 +94,9 @@ public: /// Return true if the user is in video bool self_video() const; - /// Return true if user is surpressed. - /// "HELP HELP I'M BEING SUPRESSED!" - bool is_supressed() const; + /// Return true if user is suppressed. + /// "HELP HELP I'M BEING SUPPRESSED!" + bool is_suppressed() const; }; /** A container of voicestates */ diff --git a/vendor/DPP/src/dpp/cluster.cpp b/vendor/DPP/src/dpp/cluster.cpp index ce3efcce..e31b21ef 100644 --- a/vendor/DPP/src/dpp/cluster.cpp +++ b/vendor/DPP/src/dpp/cluster.cpp @@ -327,7 +327,7 @@ void cluster::interaction_response_edit(const std::string &token, const message void cluster::global_command_create(slashcommand &s, command_completion_event_t callback) { - this->post_rest(API_PATH "/applications", std::to_string(me.id), "commands", m_post, s.build_json(false), [s, callback] (json &j, const http_request_completion_t& http) mutable { + this->post_rest(API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "commands", m_post, s.build_json(false), [s, callback] (json &j, const http_request_completion_t& http) mutable { if (j.contains("id")) { s.id = SnowflakeNotNull(&j, "id"); } @@ -339,7 +339,7 @@ void cluster::global_command_create(slashcommand &s, command_completion_event_t } void cluster::guild_command_create(slashcommand &s, snowflake guild_id, command_completion_event_t callback) { - this->post_rest(API_PATH "/applications", std::to_string(me.id), "guilds/" + std::to_string(guild_id) + "/commands", m_post, s.build_json(false), [s, this, guild_id, callback] (json &j, const http_request_completion_t& http) mutable { + this->post_rest(API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands", m_post, s.build_json(false), [s, this, guild_id, callback] (json &j, const http_request_completion_t& http) mutable { if (j.contains("id")) { s.id = SnowflakeNotNull(&j, "id"); } @@ -355,11 +355,14 @@ void cluster::guild_command_create(slashcommand &s, snowflake guild_id, command_ } void cluster::guild_bulk_command_create(const std::vector &commands, snowflake guild_id, command_completion_event_t callback) { + if (commands.empty()) { + return; + } json j = json::array(); for (auto & s : commands) { j.push_back(json::parse(s.build_json(false))); } - this->post_rest(API_PATH "/applications", std::to_string(me.id), "guilds/" + std::to_string(guild_id) + "/commands", m_put, j.dump(), [this, callback] (json &j, const http_request_completion_t& http) mutable { + this->post_rest(API_PATH "/applications", std::to_string(commands[0].application_id ? commands[0].application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands", m_put, j.dump(), [this, callback] (json &j, const http_request_completion_t& http) mutable { slashcommand_map slashcommands; for (auto & curr_slashcommand : j) { slashcommands[SnowflakeNotNull(&curr_slashcommand, "id")] = slashcommand().fill_from_json(&curr_slashcommand); @@ -371,11 +374,14 @@ void cluster::guild_bulk_command_create(const std::vector &command } void cluster::global_bulk_command_create(const std::vector &commands, command_completion_event_t callback) { + if (commands.empty()) { + return; + } json j = json::array(); for (auto & s : commands) { j.push_back(json::parse(s.build_json(false))); } - this->post_rest(API_PATH "/applications", std::to_string(me.id), "commands", m_put, j.dump(), [this, callback] (json &j, const http_request_completion_t& http) mutable { + this->post_rest(API_PATH "/applications", std::to_string(commands[0].application_id ? commands[0].application_id : me.id), "commands", m_put, j.dump(), [this, callback] (json &j, const http_request_completion_t& http) mutable { slashcommand_map slashcommands; for (auto & curr_slashcommand : j) { slashcommands[SnowflakeNotNull(&curr_slashcommand, "id")] = slashcommand().fill_from_json(&curr_slashcommand); @@ -387,7 +393,7 @@ void cluster::global_bulk_command_create(const std::vector &comman } void cluster::global_command_edit(const slashcommand &s, command_completion_event_t callback) { - this->post_rest(API_PATH "/applications", std::to_string(me.id), "commands/" + std::to_string(s.id), m_patch, s.build_json(true), [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "commands/" + std::to_string(s.id), m_patch, s.build_json(true), [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("confirmation", confirmation(), http)); } @@ -395,7 +401,7 @@ void cluster::global_command_edit(const slashcommand &s, command_completion_even } void cluster::guild_command_edit(const slashcommand &s, snowflake guild_id, command_completion_event_t callback) { - this->post_rest(API_PATH "/applications", std::to_string(me.id), "guilds/" + std::to_string(guild_id) + "/commands/" + std::to_string(s.id), m_patch, s.build_json(true), [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands/" + std::to_string(s.id), m_patch, s.build_json(true), [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("confirmation", confirmation(), http)); } @@ -414,7 +420,7 @@ void cluster::guild_command_edit_permissions(const slashcommand &s, snowflake gu } } - this->post_rest(API_PATH "/applications", std::to_string(me.id), "guilds/" + std::to_string(guild_id) + "/commands/" + std::to_string(s.id) + "/permissions", m_put, j.dump(), [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/applications", std::to_string(s.application_id ? s.application_id : me.id), "guilds/" + std::to_string(guild_id) + "/commands/" + std::to_string(s.id) + "/permissions", m_put, j.dump(), [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("confirmation", confirmation(), http)); } @@ -1040,7 +1046,7 @@ void cluster::guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t dele j["reason"] = reason; if (delete_message_days) j["delete_message_days"] = delete_message_days; - this->post_rest(API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, "", [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/guilds", std::to_string(guild_id), "bans/" + std::to_string(user_id), m_put, j.dump(), [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("confirmation", confirmation(), http)); } @@ -1656,7 +1662,7 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, if (thread_id) { parameters.append("&thread_id=" + std::to_string(thread_id)); } - this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(token), m_post, m.build_json(false), [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(!wh.token.empty() ? wh.token: token), m_post, m.build_json(false), [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("message", message().fill_from_json(&j), http)); } @@ -1665,7 +1671,7 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, void cluster::get_webhook_message(const class webhook &wh, command_completion_event_t callback) { - this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(token) + "/messages/@original", m_get, "", [callback](json &j, const http_request_completion_t &http){ + this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/@original", m_get, "", [callback](json &j, const http_request_completion_t &http){ if (callback){ callback(confirmation_callback_t("message", message().fill_from_json(&j), http)); } @@ -1673,7 +1679,7 @@ void cluster::get_webhook_message(const class webhook &wh, command_completion_ev } void cluster::edit_webhook_message(const class webhook &wh, const struct message& m, command_completion_event_t callback) { - this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(token) + "/messages/" + std::to_string(m.id), m_patch, m.build_json(false), [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(m.id), m_patch, m.build_json(false), [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("message", message().fill_from_json(&j), http)); } @@ -1681,7 +1687,7 @@ void cluster::edit_webhook_message(const class webhook &wh, const struct message } void cluster::delete_webhook_message(const class webhook &wh, snowflake message_id, command_completion_event_t callback) { - this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(token) + "/messages/" + std::to_string(message_id), m_delete, "", [callback](json &j, const http_request_completion_t& http) { + this->post_rest(API_PATH "/webhooks", std::to_string(wh.id), dpp::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(message_id), m_delete, "", [callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t("confirmation", confirmation(), http)); } diff --git a/vendor/DPP/src/dpp/commandhandler.cpp b/vendor/DPP/src/dpp/commandhandler.cpp index df793bff..ff9fd158 100644 --- a/vendor/DPP/src/dpp/commandhandler.cpp +++ b/vendor/DPP/src/dpp/commandhandler.cpp @@ -155,6 +155,10 @@ bool commandhandler::string_has_prefix(std::string &str) return false; } +/* Note that message based command routing relies on cache to resolve ping types (e.g. user, channel ping). + * There isn't really a way around this for many things because there is no 'resolved' member for it. + * We only get resolved information for the user issuing the command. + */ void commandhandler::route(const dpp::message& msg) { std::string msg_content = msg.content; @@ -226,8 +230,15 @@ void commandhandler::route(const dpp::message& msg) snowflake uid = from_string(x.substr(2, x.length() - 1), std::dec); user* u = dpp::find_user(uid); if (u) { - param = *u; + dpp::resolved_user m; + m.user = *u; + dpp::guild* g = dpp::find_guild(msg.guild_id); + if (g->members.find(uid) != g->members.end()) { + m.member = g->members[uid]; + } + param = m; } + } } break; @@ -270,6 +281,7 @@ void commandhandler::route(const interaction_create_t & event) * dont have prefixes at all. */ command_interaction cmd = std::get(event.command.data); + auto found_cmd = commands.find(lowercase(cmd.name)); if (found_cmd != commands.end()) { /* Command found; parse parameters */ @@ -277,57 +289,86 @@ void commandhandler::route(const interaction_create_t & event) for (auto& p : found_cmd->second.parameters) { command_parameter param; const command_value& slash_parameter = event.get_parameter(p.first); + dpp::command_resolved res = event.command.resolved; - if (p.second.optional && slash_parameter.valueless_by_exception()) { + if (p.second.optional && slash_parameter.index() == 0 /* std::monostate */) { /* Missing optional parameter, skip this */ continue; } - try { - switch (p.second.type) { - case pt_string: { - std::string s = std::get(slash_parameter); - param = s; - } - break; - case pt_role: { - snowflake rid = std::get(slash_parameter); - role* r = dpp::find_role(rid); - if (r) { - param = *r; - } - } - break; - case pt_channel: { - snowflake cid = std::get(slash_parameter); - channel* c = dpp::find_channel(cid); - if (c) { - param = *c; - } - } - break; - case pt_user: { - snowflake uid = std::get(slash_parameter); - user* u = dpp::find_user(uid); - if (u) { - param = *u; - } - } - break; - case pt_integer: { - int32_t i = std::get(slash_parameter); - param = i; - } - case pt_boolean: { - bool b = std::get(slash_parameter); - param = b; - } - break; + switch (p.second.type) { + case pt_string: { + std::string s = std::get(slash_parameter); + param = s; } - } - catch (const std::bad_variant_access& e) { - /* Missing optional parameter, skip this */ - continue; + break; + case pt_role: { + snowflake rid = std::get(slash_parameter); + role* r = dpp::find_role(rid); + if (r) { + /* Use cache if the role is in the cache */ + param = *r; + } else { + /* Otherwise use interaction resolved fields */ + if (res.roles.find(rid) != res.roles.end()) { + param = res.roles[rid]; + } + } + } + break; + case pt_channel: { + snowflake cid = std::get(slash_parameter); + channel* c = dpp::find_channel(cid); + if (c) { + /* Use cache if the channel is in the cache */ + param = *c; + } else { + /* Otherwise use interaction resolved fields */ + if (res.channels.find(cid) != res.channels.end()) { + param = res.channels[cid]; + } + } + } + break; + case pt_user: { + snowflake uid = std::get(slash_parameter); + /* TODO: Make this used resolved, not cache */ + user* u = dpp::find_user(uid); + if (u) { + /* Use the cache if the user is in the cache */ + dpp::resolved_user m; + m.user = *u; + dpp::guild* g = dpp::find_guild(event.command.guild_id); + if (g->members.find(uid) != g->members.end()) { + m.member = g->members[uid]; + } + param = m; + } else { + /* Otherwise use interaction resolved fields */ + if ( + event.command.resolved.users.find(uid) != event.command.resolved.users.end() + && + event.command.resolved.members.find(uid) != event.command.resolved.members.end() + ) { + /* Fill in both member and user info */ + dpp::resolved_user m; + m.member = res.members[uid]; + m.user = res.users[uid]; + param = m; + } + } + } + break; + case pt_integer: { + int32_t i = std::get(slash_parameter); + param = i; + } + break; + case pt_boolean: { + bool b = std::get(slash_parameter); + param = b; + } + break; } /* Add parameter to the list */ @@ -357,4 +398,20 @@ void commandhandler::reply(const dpp::message &m, command_source source) } } +void commandhandler::thinking(command_source source) +{ + dpp::message msg; + msg.content = "*"; + msg.guild_id = source.guild_id; + msg.channel_id = source.channel_id; + if (!source.command_token.empty() && source.command_id) { + owner->interaction_response_create(source.command_id, source.command_token, dpp::interaction_response(ir_deferred_channel_message_with_source, msg)); + } +} + +void commandhandler::thonk(command_source source) +{ + thinking(source); +} + }; \ No newline at end of file diff --git a/vendor/DPP/src/dpp/discordclient.cpp b/vendor/DPP/src/dpp/discordclient.cpp index fe34f898..5c73e4f4 100644 --- a/vendor/DPP/src/dpp/discordclient.cpp +++ b/vendor/DPP/src/dpp/discordclient.cpp @@ -329,7 +329,7 @@ void discord_client::Error(uint32_t errorcode) { 1003, "Endpoint received an unsupported frame" }, { 1004, "Reserved code" }, { 1005, "Expected close status, received none" }, - { 1006, "No close code frame has been receieved" }, + { 1006, "No close code frame has been received" }, { 1007, "Endpoint received inconsistent message (e.g. malformed UTF-8)" }, { 1008, "Generic error" }, { 1009, "Endpoint won't process large frame" }, diff --git a/vendor/DPP/src/dpp/discordevents.cpp b/vendor/DPP/src/dpp/discordevents.cpp index 6d770f40..c093a34a 100644 --- a/vendor/DPP/src/dpp/discordevents.cpp +++ b/vendor/DPP/src/dpp/discordevents.cpp @@ -42,7 +42,7 @@ char* strptime(const char* s, const char* f, struct tm* tm) { input.imbue(std::locale(setlocale(LC_ALL, nullptr))); input >> std::get_time(tm, f); if (input.fail()) { - return const_cast< char* >(""); + return ""; } return (char*)(s + input.tellg()); } @@ -79,7 +79,7 @@ std::string StringNotNull(const json* j, const char *keyname) { if (k != j->end()) { return !k->is_null() && k->is_string() ? k->get() : ""; } else { - return ""; + return const_cast< char* >(""); } } @@ -311,7 +311,7 @@ void discord_client::HandleEvent(const std::string &event, json &j, const std::s auto ev_iter = eventmap.find(event); if (ev_iter != eventmap.end()) { /* A handler with nullptr is silently ignored. We don't plan to make a handler for it - * so this usually some user-only thing thats crept into the API and shown to bots + * so this usually some user-only thing that's crept into the API and shown to bots * that we dont care about. */ if (ev_iter->second != nullptr) { diff --git a/vendor/DPP/src/dpp/discordvoiceclient.cpp b/vendor/DPP/src/dpp/discordvoiceclient.cpp index 2b079e2f..34049e0a 100644 --- a/vendor/DPP/src/dpp/discordvoiceclient.cpp +++ b/vendor/DPP/src/dpp/discordvoiceclient.cpp @@ -450,7 +450,7 @@ void discord_voice_client::Error(uint32_t errorcode) { 1003, "Endpoint received an unsupported frame" }, { 1004, "Reserved code" }, { 1005, "Expected close status, received none" }, - { 1006, "No close code frame has been receieved" }, + { 1006, "No close code frame has been received" }, { 1007, "Endpoint received inconsistent message (e.g. malformed UTF-8)" }, { 1008, "Generic error" }, { 1009, "Endpoint won't process large frame" }, diff --git a/vendor/DPP/src/dpp/message.cpp b/vendor/DPP/src/dpp/message.cpp index aa0da318..f42e5296 100644 --- a/vendor/DPP/src/dpp/message.cpp +++ b/vendor/DPP/src/dpp/message.cpp @@ -517,11 +517,13 @@ reaction::reaction(json* j) { emoji_name = StringNotNull(&emoji, "name"); } -attachment::attachment() { - id = 0; - size = 0; - width = 0; - height = 0; +attachment::attachment() + : id(0) + , size(0) + , width(0) + , height(0) + , ephemeral(false) +{ } attachment::attachment(json *j) : attachment() { @@ -533,6 +535,7 @@ attachment::attachment(json *j) : attachment() { this->width = Int32NotNull(j, "width"); this->height = Int32NotNull(j, "height"); this->content_type = StringNotNull(j, "content_type"); + this->ephemeral = BoolNotNull(j, "ephemeral"); } std::string message::build_json(bool with_id, bool is_interaction_response) const { @@ -553,6 +556,11 @@ std::string message::build_json(bool with_id, bool is_interaction_response) cons j["content"] = content; } + if(author != nullptr) { + /* Used for webhooks */ + j["username"] = author->username; + } + /* Populate message reference */ if (message_reference.channel_id || message_reference.guild_id || message_reference.message_id) { j["message_reference"] = json::object(); @@ -740,8 +748,8 @@ bool message::is_crosspost() const { } -bool message::supress_embeds() const { - return flags & m_supress_embeds; +bool message::suppress_embeds() const { + return flags & m_suppress_embeds; } bool message::is_source_message_deleted() const { diff --git a/vendor/DPP/src/dpp/queues.cpp b/vendor/DPP/src/dpp/queues.cpp index 405f7781..2b70e1e2 100644 --- a/vendor/DPP/src/dpp/queues.cpp +++ b/vendor/DPP/src/dpp/queues.cpp @@ -285,7 +285,7 @@ void request_queue::in_loop() auto currbucket = buckets.find(bucket.first); if (currbucket != buckets.end()) { - /* Theres a bucket for this request. Check its status. If the bucket says to wait, + /* There's a bucket for this request. Check its status. If the bucket says to wait, * skip all requests in this bucket till its ok. */ if (currbucket->second.remaining < 1) { diff --git a/vendor/DPP/src/dpp/slashcommand.cpp b/vendor/DPP/src/dpp/slashcommand.cpp index c63f2f29..f9e39ce6 100644 --- a/vendor/DPP/src/dpp/slashcommand.cpp +++ b/vendor/DPP/src/dpp/slashcommand.cpp @@ -125,6 +125,7 @@ void to_json(json& j, const slashcommand& p) { } j["default_permission"] = p.default_permission; + j["application_id"] = std::to_string(p.application_id); } std::string slashcommand::build_json(bool with_id) const { @@ -271,7 +272,6 @@ void from_json(const nlohmann::json& j, interaction& i) { SetSnowflakeNotNull(&m, "id", i.message_id); } - i.type = Int8NotNull(&j, "type"); i.token = StringNotNull(&j, "token"); i.version = Int8NotNull(&j, "version"); @@ -284,6 +284,47 @@ void from_json(const nlohmann::json& j, interaction& i) { } if (j.contains("data") && !j.at("data").is_null()) { + + const json& data = j["data"]; + + /* Deal with 'resolved' data, e.g. users, members, roles, channels */ + if (data.find("resolved") != data.end()) { + const json& d_resolved = data["resolved"]; + /* Users */ + if (d_resolved.find("users") != d_resolved.end()) { + for (auto v = d_resolved["users"].begin(); v != d_resolved["users"].end(); ++v) { + json f = *v; + dpp::snowflake id = strtoull(v.key().c_str(), nullptr, 10); + i.resolved.users[id] = dpp::user().fill_from_json(&f); + } + } + /* Roles */ + if (d_resolved.find("roles") != d_resolved.end()) { + for (auto v = d_resolved["roles"].begin(); v != d_resolved["roles"].end(); ++v) { + json f = *v; + dpp::snowflake id = strtoull(v.key().c_str(), nullptr, 10); + i.resolved.roles[id] = dpp::role().fill_from_json(i.guild_id, &f); + } + } + /* Channels */ + if (d_resolved.find("channels") != d_resolved.end()) { + for (auto v = d_resolved["channels"].begin(); v != d_resolved["channels"].end(); ++v) { + json f = *v; + dpp::snowflake id = strtoull(v.key().c_str(), nullptr, 10); + i.resolved.channels[id] = dpp::channel().fill_from_json(&f); + } + } + /* Members */ + if (d_resolved.find("members") != d_resolved.end()) { + for (auto v = d_resolved["members"].begin(); v != d_resolved["members"].end(); ++v) { + json f = *v; + dpp::snowflake id = strtoull(v.key().c_str(), nullptr, 10); + i.resolved.members[id] = dpp::guild_member().fill_from_json(&f, i.guild_id, id); + } + } + } + + if (i.type == it_application_command) { command_interaction ci; j.at("data").get_to(ci); diff --git a/vendor/DPP/src/dpp/user.cpp b/vendor/DPP/src/dpp/user.cpp index 74c70bf6..fcba0544 100644 --- a/vendor/DPP/src/dpp/user.cpp +++ b/vendor/DPP/src/dpp/user.cpp @@ -25,7 +25,7 @@ using json = nlohmann::json; -/* A mapping of discord's flag values to our bitmap (theyre different bit positions to fit other stuff in) */ +/* A mapping of discord's flag values to our bitmap (they're different bit positions to fit other stuff in) */ std::map usermap = { { 1 << 0, dpp::u_discord_employee }, { 1 << 1, dpp::u_partnered_owner }, @@ -57,7 +57,7 @@ user::~user() } std::string user::get_avatar_url() const { - /* XXX: Discord were supposed to change their CDN over to discord.com, they havent. + /* XXX: Discord were supposed to change their CDN over to discord.com, they haven't. * At some point in the future this URL *will* change! */ return fmt::format("https://cdn.discordapp.com/avatars/{}/{}{}.{}", diff --git a/vendor/DPP/src/dpp/voicestate.cpp b/vendor/DPP/src/dpp/voicestate.cpp index ddb31593..3025d5fd 100644 --- a/vendor/DPP/src/dpp/voicestate.cpp +++ b/vendor/DPP/src/dpp/voicestate.cpp @@ -51,8 +51,8 @@ voicestate& voicestate::fill_from_json(nlohmann::json* j) { flags |= vs_self_stream; if (BoolNotNull(j, "self_video")) flags |= vs_self_video; - if (BoolNotNull(j, "supress")) - flags |= vs_supress; + if (BoolNotNull(j, "suppress")) + flags |= vs_suppress; return *this; } @@ -80,8 +80,8 @@ bool voicestate::self_video() const { return flags & vs_self_video; } -bool voicestate::is_supressed() const { - return flags & vs_supress; +bool voicestate::is_suppressed() const { + return flags & vs_suppress; } std::string voicestate::build_json() const { diff --git a/vendor/DPP/src/dpp/wsclient.cpp b/vendor/DPP/src/dpp/wsclient.cpp index 21d72ac1..5433b687 100644 --- a/vendor/DPP/src/dpp/wsclient.cpp +++ b/vendor/DPP/src/dpp/wsclient.cpp @@ -304,7 +304,7 @@ void websocket_client::HandlePingPong(bool ping, const std::string &payload) { unsigned char out[MAXHEADERSIZE]; if (ping) { - /* For receving pings we echo back their payload with the type OP_PONG */ + /* For receiving pings we echo back their payload with the type OP_PONG */ size_t s = this->FillHeader(out, payload.length(), OP_PONG); std::string header((const char*)out, s); ssl_client::write(header);