From bdaa136e25d965a9ef7c8b9512d7b0fd1f62261f Mon Sep 17 00:00:00 2001 From: Arkrissym Date: Sat, 7 Sep 2024 23:59:05 +0200 Subject: [PATCH] handle rection add/remove events --- CMakeLists.txt | 2 +- Discord.C++/Discord.cpp | 15 +++++- Discord.C++/Discord.h | 12 +++++ Discord.C++/DiscordObject.cpp | 14 ++++-- Discord.C++/DiscordObject.h | 21 ++++++-- Discord.C++/Emoji.cpp | 20 ++++++++ Discord.C++/Emoji.h | 47 ++++++++++++++++++ Discord.C++/Message.h | 1 - Discord.C++/Reaction.cpp | 64 +++++++++++++++++++++++++ Discord.C++/Reaction.h | 90 +++++++++++++++++++++++++++++++++++ test_bot/main.cpp | 2 + 11 files changed, 274 insertions(+), 14 deletions(-) create mode 100644 Discord.C++/Emoji.cpp create mode 100644 Discord.C++/Emoji.h create mode 100644 Discord.C++/Reaction.cpp create mode 100644 Discord.C++/Reaction.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c639d7..8596f6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ target_include_directories( ${PROJECT_NAME} PRIVATE Discord.C++ ) if(MSVC) set( CMAKE_CXX_FLAGS " /MP /O2 /GL /GS /W3 /sdl /Gd /EHa " ) else(MSVC) - set( CMAKE_CXX_FLAGS " -Wall -Wextra -Ofast " ) + set( CMAKE_CXX_FLAGS " -Wall -Wextra -Wno-nan-infinity-disabled -Ofast " ) endif(MSVC) find_package(OpenSSL REQUIRED) diff --git a/Discord.C++/Discord.cpp b/Discord.C++/Discord.cpp index 20b4b7a..96b8fa3 100644 --- a/Discord.C++/Discord.cpp +++ b/Discord.C++/Discord.cpp @@ -212,8 +212,7 @@ void DiscordCPP::Discord::on_websocket_incoming_message(const json &payload) { } } -void DiscordCPP::Discord::handle_raw_event(const std::string &event_name, - const json &data) { +void DiscordCPP::Discord::handle_raw_event(const std::string &event_name, const json &data) { // https://discordapp.com/developers/docs/topics/gateway#commands-and-events-gateway-events if (event_name == "READY") { _user = new User(data.at("user"), get_token()); @@ -409,6 +408,18 @@ void DiscordCPP::Discord::handle_raw_event(const std::string &event_name, log.error("ignoring exception in on_message_delete: " + std::string(e.what())); } } + } else if (event_name == "MESSAGE_REACTION_ADD") { + try { + on_message_reaction(Reaction(data, get_token())); + } catch (const std::exception &e) { + log.error("ignoring exception in on_message_reaction: " + std::string(e.what())); + } + } else if (event_name == "MESSAGE_REACTION_REMOVE") { + try { + on_message_reaction_delete(Reaction(data, get_token())); + } catch (const std::exception &e) { + log.error("ignoring exception in on_message_reaction_remove: " + std::string(e.what())); + } } else if (event_name == "TYPING_START") { std::string channel_id = data.at("channel_id").get(); User user; diff --git a/Discord.C++/Discord.h b/Discord.C++/Discord.h index c7276e4..a0a2bac 100644 --- a/Discord.C++/Discord.h +++ b/Discord.C++/Discord.h @@ -10,6 +10,7 @@ #include "DMChannel.h" #include "DiscordObject.h" #include "Embed.h" +#include "Emoji.h" #include "Exceptions.h" #include "FFmpegAudioSource.h" #include "FileAudioSource.h" @@ -22,6 +23,7 @@ #include "Logger.h" #include "MainGateway.h" #include "Message.h" +#include "Reaction.h" #include "Threadpool.h" #include "User.h" #include "VoiceChannel.h" @@ -82,6 +84,16 @@ class Discord : public DiscordObject { */ virtual void on_message_delete(Message message) {} + /** called when a Reaction was added to Message + @param[in] reaction the reaction that has been created + */ + virtual void on_message_reaction(Reaction reaction) {} + + /** called when a Reaction was remove from a Message + @param[in] reaction the reaction that has been deleted + */ + virtual void on_message_reaction_delete(Reaction reaction) {} + /** called when a Member was banned @param[in] user the User who has been banned @param[in] guild the Guild the User has been banned from diff --git a/Discord.C++/DiscordObject.cpp b/Discord.C++/DiscordObject.cpp index 71297c8..72c19fc 100644 --- a/Discord.C++/DiscordObject.cpp +++ b/Discord.C++/DiscordObject.cpp @@ -23,7 +23,7 @@ static bool cache_manager_active = false; void manage_cache(); -DiscordCPP::DiscordObject::DiscordObject(std::string token) +DiscordCPP::BaseDiscordObject::BaseDiscordObject(std::string token) : _token(std::move(token)) { if (cache_manager_active == false) { cache_manager_active = true; @@ -31,9 +31,13 @@ DiscordCPP::DiscordObject::DiscordObject(std::string token) } } +DiscordCPP::DiscordObject::DiscordObject(std::string token) + : DiscordCPP::BaseDiscordObject::BaseDiscordObject(token) { +} + DiscordCPP::DiscordObject::DiscordObject(std::string token, std::string id) - : _token(std::move(token)), - id(std::move(id)) { + : DiscordCPP::BaseDiscordObject::BaseDiscordObject(token) { + this->id = std::move(id); } void manage_cache() { @@ -68,7 +72,7 @@ void manage_cache() { @return json::value API response @throws HTTPError */ -json DiscordCPP::DiscordObject::api_call(const std::string& url, const std::string& method, const json& data, const std::string& content_type, const bool cache) { +json DiscordCPP::BaseDiscordObject::api_call(const std::string& url, const std::string& method, const json& data, const std::string& content_type, const bool cache) { if (method == "GET" && cache == true) { for (auto& i : _cache) { if (i->at("url").get() == url) { @@ -133,7 +137,7 @@ json DiscordCPP::DiscordObject::api_call(const std::string& url, const std::stri return ret; } -DiscordCPP::DiscordObject::http_response DiscordCPP::DiscordObject::request_internal(const std::string& target, const std::string& method, const std::string& data, const std::string& content_type) { +DiscordCPP::BaseDiscordObject::http_response DiscordCPP::BaseDiscordObject::request_internal(const std::string& target, const std::string& method, const std::string& data, const std::string& content_type) { Logger("discord.object.request_internal").debug("sending message " + data + " via " + method + " to https://" + DISCORD_HOST + target); ssl::context ssl_context(ssl::context::tlsv13); diff --git a/Discord.C++/DiscordObject.h b/Discord.C++/DiscordObject.h index ff14f33..62f67a4 100644 --- a/Discord.C++/DiscordObject.h +++ b/Discord.C++/DiscordObject.h @@ -3,11 +3,12 @@ #include #include +#include "Exceptions.h" #include "static.h" namespace DiscordCPP { -class DiscordObject { +class BaseDiscordObject { private: struct http_response { unsigned int status_code; @@ -19,19 +20,29 @@ class DiscordObject { /// token to authentificate with the discord api std::string _token; - /// the id of the object - std::string id; - protected: [[nodiscard]] std::string get_token() const { return _token; } /// helper function to communicate with the http api json api_call(const std::string& url, const std::string& method = "GET", const json& data = json(), const std::string& content_type = "", const bool cache = true); + public: + DLL_EXPORT BaseDiscordObject() = default; + /// @param[in] token Discord token + DLL_EXPORT explicit BaseDiscordObject(std::string token); +}; + +class DiscordObject : public BaseDiscordObject { + private: + /// the id of the object + std::string id; + public: DLL_EXPORT DiscordObject() = default; /// @param[in] token Discord token DLL_EXPORT explicit DiscordObject(std::string token); - /// @param[in] token Discord token + /** @param[in] token Discord token + @param[in] id the object's id + */ DLL_EXPORT explicit DiscordObject(std::string token, std::string id); /// @return the id of the object diff --git a/Discord.C++/Emoji.cpp b/Discord.C++/Emoji.cpp new file mode 100644 index 0000000..df8d4c2 --- /dev/null +++ b/Discord.C++/Emoji.cpp @@ -0,0 +1,20 @@ +#include "Emoji.h" + +DiscordCPP::Emoji::Emoji(const json& data, const std::string& token) + : DiscordObject(token, data.at("id").get()) { + name = get_optional(data, "name"); + + if (has_value(data, "roles")) { + for (json role : data.at("roles")) { + role_ids.push_back(role.get()); + } + } + if (has_value(data, "data")) { + user = User(data.at("user"), get_token()); + } + + require_colons = get_or_else(data, "require_colons", false); + managed = get_or_else(data, "managed", false); + animated = get_or_else(data, "animated", false); + available = get_or_else(data, "available", true); +} diff --git a/Discord.C++/Emoji.h b/Discord.C++/Emoji.h new file mode 100644 index 0000000..f2daf05 --- /dev/null +++ b/Discord.C++/Emoji.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "DiscordObject.h" +#include "User.h" + +namespace DiscordCPP { +class Emoji : public DiscordObject { + private: + /// emoji name + std::optional name; + /// roles allowed to use this emoji + std::vector role_ids; + /// user that created this emoji + std::optional user; + /// wether this emoji must be wrapped in colons + bool require_colons; + /// wether this emoji is managed + bool managed; + /// wether this emoji is animated + bool animated; + /// whether this emoji can be used, may be false due to loss of Server Boosts + bool available; + + public: + DLL_EXPORT Emoji() = default; + DLL_EXPORT Emoji(const json& data, const std::string& token); + + /// @return emoji name + std::optional get_name() { return name; } + /// @return roles allowed to use this emoji + std::vector get_role_ids() { return role_ids; } + /// @return user that created this emoji + std::optional get_user() { return user; } + /// @return wether this emoji must be wrapped in colons + bool requires_colons() { return require_colons; } + /// @return wether this emoji is managed + bool is_managed() { return managed; } + /// @return wether this emoji is animated + bool is_animated() { return animated; } + /// @return whether this emoji can be used, may be false due to loss of Server Boosts + bool is_available() { return available; } +}; +} // namespace DiscordCPP diff --git a/Discord.C++/Message.h b/Discord.C++/Message.h index e325acc..a3e5625 100644 --- a/Discord.C++/Message.h +++ b/Discord.C++/Message.h @@ -1,5 +1,4 @@ #pragma once -#include #include "DiscordObject.h" #include "Embed.h" diff --git a/Discord.C++/Reaction.cpp b/Discord.C++/Reaction.cpp new file mode 100644 index 0000000..5f6c8c6 --- /dev/null +++ b/Discord.C++/Reaction.cpp @@ -0,0 +1,64 @@ +#include "Reaction.h" + +DiscordCPP::Reaction::Reaction(const json& data, const std::string& token) : BaseDiscordObject(token) { + user_id = data.at("user_id").get(); + channel_id = data.at("channel_id").get(); + message_id = data.at("message_id").get(); + guild_id = get_optional(data, "guild_id"); + + if (has_value(data, "member")) { + member = Member(data.at("member"), token); + } + + emoji = Emoji(data.at("emoji"), get_token()); + message_author_id = get_optional(data, "message_author_id"); + burst = get_or_else(data, "burst", false); + + if (has_value(data, "burst_colors")) { + for (json color : data.at("burst_colors")) { + burst_colors.push_back(color.get()); + } + } + + type = static_cast(data.at("type").get()); +} + +DiscordCPP::User DiscordCPP::Reaction::get_user() { + if (!user.has_value()) { + user = User(user_id, get_token()); + } + + return user.value(); +} + +DiscordCPP::TextChannel DiscordCPP::Reaction::get_channel() { + if (!channel.has_value()) { + channel = TextChannel(channel_id, get_token()); + } + + return channel.value(); +} + +DiscordCPP::Message DiscordCPP::Reaction::get_message() { + if (!message.has_value()) { + message = Message(message_id, get_token()); + } + + return message.value(); +} + +std::optional DiscordCPP::Reaction::get_guild() { + if (!guild_id.has_value() && guild_id.has_value()) { + guild = Guild(nullptr, guild_id.value(), get_token()); + } + + return guild; +} + +std::optional DiscordCPP::Reaction::get_message_author() { + if (!message_author.has_value() && message_author_id.has_value()) { + message_author = User(message_author_id.value(), get_token()); + } + + return message_author; +} diff --git a/Discord.C++/Reaction.h b/Discord.C++/Reaction.h new file mode 100644 index 0000000..6eb2a73 --- /dev/null +++ b/Discord.C++/Reaction.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +#include "DiscordObject.h" +#include "Emoji.h" +#include "Guild.h" +#include "Member.h" +#include "Message.h" +#include "TextChannel.h" +#include "Threadpool.h" +#include "User.h" + +namespace DiscordCPP { +class Reaction : public BaseDiscordObject { + public: + enum Type { + NORMAL = 0, + BURST = 1 + }; + + private: + /// id of the user + std::string user_id; + /// the user who sent the reaction + std::optional user; + /// id of the channel + std::string channel_id; + /// the channel of the message + std::optional channel; + /// id of the message + std::string message_id; + /// the message of this reaction + std::optional message; + /// id of the guild + std::optional guild_id; + /// the guild of the channel + std::optional guild; + /// the guild member who reacted + std::optional member; + /// emoji used to react + Emoji emoji; + /// id of the message author + std::optional message_author_id; + /// the message author + std::optional message_author; + /// true if this is a super reaction + bool burst; + /// colors used for super reaction im #rrggbb format + std::vector burst_colors; + /// the type of the reaction + Type type; + + public: + DLL_EXPORT Reaction(const json& data, const std::string& token); + + /// @return id of the user + std::string get_user_id() { return user_id; } + /// @return the user who sent the reaction + User get_user(); + /// @return id of the channel + std::string get_channel_id() { return channel_id; } + /// @return the channel of the message + TextChannel get_channel(); + /// @return id of the message + std::string get_message_id() { return message_id; } + /// @return the message of this reaction + Message get_message(); + /// @return id of the guild + std::optional get_guild_id() { return guild_id; } + /// @return the guild of the channel + std::optional get_guild(); + /// @return the guild member who reacted + std::optional get_member() { return member; } + /// @return emoji used to react + Emoji get_emoji() { return emoji; } + /// @return id of the message author + std::optional get_message_author_id() { return message_author_id; } + /// @return the message author + std::optional get_message_author(); + /// @return true if this is a super reaction + bool is_burst() { return burst; } + /// @return colors used for super reaction im #rrggbb format + std::vector get_burst_colors() { return burst_colors; } + /// @return the type of the reaction + Type get_type() { return type; } +}; +} // namespace DiscordCPP diff --git a/test_bot/main.cpp b/test_bot/main.cpp index d384eaf..387106d 100644 --- a/test_bot/main.cpp +++ b/test_bot/main.cpp @@ -330,6 +330,8 @@ class Client : public Discord { ", type: " + to_string(message.get_channel().get_type()) + ")."); } + // TODO: reaction add/delete + void on_interaction(Interaction interaction) override { if (interaction.get_type() != Interaction::Type::APPLICATION_COMMAND) { return;