diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dae984..788c9f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.10) project(sparkle) +#target_precompile_headers(${PROJECT_NAME} PRIVATE pch.h) + include(GoogleTest) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -49,11 +51,11 @@ message(STATUS "${CMAKE_BUILD_TYPE}") add_executable(${PROJECT_NAME} ${SOURCE} ${NETWORK_SOURCES} ${WEBSOCKET_SOURCES}) #add_library(sparkles STATIC ${SOURCE}) -add_executable(tests ${TESTS}/tests.cpp) +#add_executable(tests ${TESTS}/tests.cpp) -enable_testing() +#enable_testing() -add_test(NAME NetworkManagerTest COMMAND tests) +#add_test(NAME NetworkManagerTest COMMAND tests) target_include_directories(${PROJECT_NAME} PRIVATE ${LIBS} @@ -67,15 +69,15 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${CURL_LIBRARIES} ) -target_include_directories(tests PRIVATE - ${LIBS} - ${INCLUDE} - ${IXWEBSOCKET_INCLUDE_DIR} - ${CURL_INCLUDE_DIRS} -) +#target_include_directories(tests PRIVATE +# ${LIBS} +# ${INCLUDE} +# ${IXWEBSOCKET_INCLUDE_DIR} +# ${CURL_INCLUDE_DIRS} +#) -target_link_libraries(tests gtest - ${IXWEBSOCKET_LIBRARIES} - ${CURL_LIBRARIES} - gtest_main -) \ No newline at end of file +#target_link_libraries(tests gtest +# ${IXWEBSOCKET_LIBRARIES} +# ${CURL_LIBRARIES} +# gtest_main +#) \ No newline at end of file diff --git a/include/api.hpp b/include/api.hpp index c29c3e3..f8a0654 100644 --- a/include/api.hpp +++ b/include/api.hpp @@ -1,10 +1,10 @@ -#ifndef INCLUDE_API_HPP_ -#define INCLUDE_API_HPP_ -#include -#include -#include -#include -#include -#include -#include +#ifndef INCLUDE_INTERFACE_HPP_ +#define INCLUDE_INTERFACE_HPP_ +#include +#include +#include +#include +#include +#include +#include #endif \ No newline at end of file diff --git a/include/includes.hpp b/include/includes.hpp index c214431..51e648f 100644 --- a/include/includes.hpp +++ b/include/includes.hpp @@ -1,6 +1,6 @@ #ifndef INCLUDE_INCLUDES_HPP_ #define INCLUDE_INCLUDES_HPP_ -#include +//#include #include #include #include diff --git a/include/net.hpp b/include/network.hpp similarity index 67% rename from include/net.hpp rename to include/network.hpp index ec6c3e2..1c62ebd 100644 --- a/include/net.hpp +++ b/include/network.hpp @@ -1,5 +1,5 @@ #ifndef INCLUDE_NET_HPP_ #define INCLUDE_NET_HPP_ -#include +#include #include #endif \ No newline at end of file diff --git a/libs/api/Activity.hpp b/libs/api/Activity.hpp new file mode 100644 index 0000000..7ff7ec5 --- /dev/null +++ b/libs/api/Activity.hpp @@ -0,0 +1,3 @@ +#ifndef API_ACTIVITY_HPP_ +#define API_ACTIVITY_HPP_ +#endif \ No newline at end of file diff --git a/libs/gateway/websocket.hpp b/libs/gateway/websocket.hpp deleted file mode 100644 index 8e61897..0000000 --- a/libs/gateway/websocket.hpp +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef GATEWAY_WEBSOCKET_HPP_ -#define GATEWAY_WEBSOCKET_HPP_ -#include -#include -#include -class EventEmitter { -private: - using eventHandlers = std::function; - std::unordered_map> handlers; -protected: - void emit(const std::string& event, const nlohmann::json& data) { - if (handlers.find(event) != handlers.end()) { - for (const auto& handler : handlers[event]) { - handler(data); - } - } - } -public: - void getEvents() { - Log::create(CRITICAL, WEBSOCKET, "Event list"); - for (const auto& handler : handlers) { - Log::create(INFO, WEBSOCKET, handler.first); - } - Log::create(CRITICAL, WEBSOCKET, "End of list"); - } - void start() { - while (1) std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - void on(const std::string& event, eventHandlers handler) { - handlers[event].emplace_back(handler); - } - void once(const std::string& event, eventHandlers handler) { - auto wrappedHandler = [this, event, handler](const nlohmann::json& data) { - handler(data); - off(event, handler); - }; - handlers[event].emplace_back(wrappedHandler); - } - void off(const std::string& event, eventHandlers handler) { - if (handlers.find(event) != handlers.end()) { - auto& vec = handlers[event]; - vec.erase(std::remove_if(vec.begin(), vec.end(), [&handler](const eventHandlers& h) { - return h.target() == handler.target(); - }), vec.end()); - } - } -}; -class WebSocket : public EventEmitter { -private: - std::string token; - int intents; - bool isBot; - ix::WebSocket webSocket; - const nlohmann::json payload = { {"op", GatewayOpcodes::Heartbeat}, {"d", nullptr} }; - WebSocket& operator=(const WebSocket&) = delete; - WebSocket& operator=(WebSocket&&) = delete; - WebSocket(WebSocket&&) = delete; - WebSocket(const WebSocket&) = delete; - WebSocket(const std::string& token, const int& intents, const bool& isBot) : token(token), intents(intents), isBot(isBot) { - nlohmann::json id = { - {"op", GatewayOpcodes::Identify}, - {"d", { - {"token", token}, - {"intents", intents}, - {"properties", { - {"os", "linux"}, - {"browser", "firefox"}, - {"device", "firefox"} - }}, - {"compress", 1}, - {"presence", { - {"activities", nlohmann::json::array({ - { - {"name", "meow"}, - {"type", 2} - } - })}, - {"status", "idle"}, - {"since", 91879201}, - {"afk", false} - }} - }} - }; - ix::initNetSystem(); - webSocket.setUrl("wss://gateway.discord.gg/?v=10&encoding=json"); - webSocket.disableAutomaticReconnection(); - Log::create(INFO, WEBSOCKET, "ixwebsocket init"); - webSocket.setOnMessageCallback([this, res = nlohmann::json(), heartbeat_interval = 0, connected = false, id](const ix::WebSocketMessagePtr& msg) mutable { - switch (msg->type) { - case ix::WebSocketMessageType::Message: - res = nlohmann::json::parse(msg->str); - Log::create(INFO, WEBSOCKET, std::to_string(res["op"].get()) + " " + res["t"].dump()); - switch (res["op"].get()) { - case GatewayOpcodes::Hello: - heartbeat_interval = res["d"]["heartbeat_interval"].get(); - !connected ? connected = true, webSocket.send(id.dump()) : 0; - std::thread([this, &heartbeat_interval, &connected]() { - while (connected && heartbeat_interval != -1) { - Log::create(INFO, WEBSOCKET, "Heartbeat " + std::to_string(heartbeat_interval)); - std::this_thread::sleep_for(std::chrono::milliseconds(heartbeat_interval)); - webSocket.send(payload.dump()); - } - }).detach(); - break; - case GatewayOpcodes::Dispatch: - Log::force_create(INFO, WEBSOCKET, res.dump()); - emit(res["t"].get(), res); - break; - default: - break; - } - break; - case ix::WebSocketMessageType::Error: - Log::force_create(ERROR, WEBSOCKET, msg->errorInfo.reason); - exit(-1); - break; - default: - break; - } - - }); - webSocket.start(); - } -public: - [[maybe_unused]] - void sendPresenceUpdate(const int& statusType, const std::string& activityName) { - nlohmann::json prsUpdate = { - {"op", 3}, - {"d", { - {"since", 0}, - {"activities", nlohmann::json::array({{"name", activityName}, {"type", 2}})}, - {"status", statusType == 1 ? "online" : "idle"}, - {"afk", false} - }} - }; - webSocket.send(prsUpdate.dump()); - } - static WebSocket& getInstance(const std::string& token = "", const int& intents = 0, const bool& bot = true) { - Log::create(INFO, WEBSOCKET, "Instance event"); - static WebSocket instance(token, intents, bot); - return instance; - } - ~WebSocket() { - webSocket.close(); - ix::uninitNetSystem(); - } - std::string getToken() const { - return isBot ? std::string("Bot " + WebSocket::token) : WebSocket::token; - } - int getIntents() const { - return WebSocket::intents; - } -}; -#endif \ No newline at end of file diff --git a/libs/api/Author.hpp b/libs/interface/Author.hpp similarity index 93% rename from libs/api/Author.hpp rename to libs/interface/Author.hpp index b617183..bf306c1 100644 --- a/libs/api/Author.hpp +++ b/libs/interface/Author.hpp @@ -1,7 +1,7 @@ -#ifndef API_AUTHOR_HPP_ -#define API_AUTHOR_HPP_ +#ifndef INTERFACE_AUTHOR_HPP_ +#define INTERFACE_AUTHOR_HPP_ #include -#include +#include class Author { private: const nlohmann::json& data; @@ -35,7 +35,8 @@ public: discriminator(functions::testValue(data, { "d", "author", "discriminator" }).value_or("")), message_id(functions::testValue(data, { "d", "id" }).value_or("")), isPinned(functions::testValue(data, { "d", "pinned" }).value_or(false)), - isBot(functions::testValue(data, { "d", "author", "bot" }).value_or(false)) {} + isBot(functions::testValue(data, { "d", "author", "bot" }).value_or(false)) { + } std::string send(const nlohmann::json& msg) { return req.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/" + data["d"]["channel_id"].get() + "/messages", msg.dump()); } diff --git a/libs/api/Bot.hpp b/libs/interface/Bot.hpp similarity index 92% rename from libs/api/Bot.hpp rename to libs/interface/Bot.hpp index 446a19f..12fc6e7 100644 --- a/libs/api/Bot.hpp +++ b/libs/interface/Bot.hpp @@ -1,5 +1,5 @@ -#ifndef API_BOT_HPP_ -#define API_BOT_HPP_ +#ifndef INTERFACE_BOT_HPP_ +#define INTERFACE_BOT_HPP_ #include template class Discord { diff --git a/libs/api/Channel.hpp b/libs/interface/Channel.hpp similarity index 84% rename from libs/api/Channel.hpp rename to libs/interface/Channel.hpp index e92a946..6b6635f 100644 --- a/libs/api/Channel.hpp +++ b/libs/interface/Channel.hpp @@ -1,7 +1,7 @@ -#ifndef API_CHANNEL_HPP_ -#define API_CHANNEL_HPP_ +#ifndef INTERFACE_CHANNEL_HPP_ +#define INTERFACE_CHANNEL_HPP_ #include -#include +#include class Channel { private: const nlohmann::json& data; diff --git a/libs/api/Embed.hpp b/libs/interface/Embed.hpp similarity index 96% rename from libs/api/Embed.hpp rename to libs/interface/Embed.hpp index 69b26fb..9f8fa57 100644 --- a/libs/api/Embed.hpp +++ b/libs/interface/Embed.hpp @@ -1,5 +1,5 @@ -#ifndef API_EMBED_HPP_ -#define API_EMBED_HPP_ +#ifndef INTERFACE_EMBED_HPP_ +#define INTERFACE_EMBED_HPP_ #include class EmbedBuilder { private: diff --git a/libs/api/Guild.hpp b/libs/interface/Guild.hpp similarity index 70% rename from libs/api/Guild.hpp rename to libs/interface/Guild.hpp index 16351b2..7b7b249 100644 --- a/libs/api/Guild.hpp +++ b/libs/interface/Guild.hpp @@ -1,7 +1,7 @@ -#ifndef API_GUILD_HPP_ -#define API_GUILD_HPP_ +#ifndef INTERFACE_GUILD_HPP_ +#define INTERFACE_GUILD_HPP_ #include -#include +#include class Guild { private: nlohmann::json data; diff --git a/libs/api/Message.hpp b/libs/interface/Message.hpp similarity index 94% rename from libs/api/Message.hpp rename to libs/interface/Message.hpp index d773273..32f2208 100644 --- a/libs/api/Message.hpp +++ b/libs/interface/Message.hpp @@ -1,7 +1,7 @@ -#ifndef API_MESSAGE_HPP_ -#define API_MESSAGE_HPP_ +#ifndef INTERFACE_MESSAGE_HPP_ +#define INTERFACE_MESSAGE_HPP_ #include -#include +#include class Message { private: const nlohmann::json& data; diff --git a/libs/api/User.hpp b/libs/interface/User.hpp similarity index 92% rename from libs/api/User.hpp rename to libs/interface/User.hpp index aef8bfa..ec05d67 100644 --- a/libs/api/User.hpp +++ b/libs/interface/User.hpp @@ -1,7 +1,7 @@ -#ifndef API_USER_HPP_ -#define API_USER_HPP_ +#ifndef INTERFACE_USER_HPP_ +#define INTERFACE_USER_HPP_ #include -#include +#include #include class User { private: @@ -17,7 +17,6 @@ public: } } bool isBot; - /* nlohmann::json extract(const std::vector& keys) { std::vector d = { "d" }; d.insert(d.end(), keys.begin(), keys.end()); @@ -31,7 +30,6 @@ public: } return current; }; - */ std::string me() { return req.request(HttpMethods::GET, DiscordEndpoints::details::latest + "/users/@me"); } diff --git a/libs/network/network.hpp b/libs/network/network.hpp index a8ba117..38e5b87 100644 --- a/libs/network/network.hpp +++ b/libs/network/network.hpp @@ -1,7 +1,7 @@ #ifndef TLS_NETWORK_HPP_ #define TLS_NETWORK_HPP_ #include -#include +#include #include class NetworkManager { private: diff --git a/libs/network/websocket.cpp b/libs/network/websocket.cpp new file mode 100644 index 0000000..ecf68a7 --- /dev/null +++ b/libs/network/websocket.cpp @@ -0,0 +1,130 @@ +#include "websocket.hpp" +void EventEmitter::emit(const std::string& event, const nlohmann::json& data) { + if (handlers.find(event) != handlers.end()) { + for (const auto& handler : handlers[event]) { + handler(data); + } + } +} +void EventEmitter::getEvents() { + Log::create(CRITICAL, WEBSOCKET, "Event list"); + for (const auto& handler : handlers) { + Log::create(INFO, WEBSOCKET, handler.first); + } + Log::create(CRITICAL, WEBSOCKET, "End of list"); +} +void EventEmitter::start() { + while (1) std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} +void EventEmitter::on(const std::string& event, eventHandlers handler) { + handlers[event].emplace_back(handler); +} +void EventEmitter::once(const std::string& event, eventHandlers handler) { + auto wrappedHandler = [this, event, handler](const nlohmann::json& data) { + handler(data); + off(event, handler); + }; + handlers[event].emplace_back(wrappedHandler); +} +void EventEmitter::off(const std::string& event, eventHandlers handler) { + if (handlers.find(event) != handlers.end()) { + auto& vec = handlers[event]; + vec.erase(std::remove_if(vec.begin(), vec.end(), [&handler](const eventHandlers& h) { + return h.target() == handler.target(); + }), vec.end()); + } +} +WebSocket::WebSocket(const std::string& token, const int& intents, const bool& isBot) : token(token), intents(intents), isBot(isBot) { + nlohmann::json id = { + {"op", GatewayOpcodes::Identify}, + {"d", { + {"token", token}, + {"intents", intents}, + //{"shard", {{0}}}, + {"properties", { + {"os", "linux"}, + {"browser", "firefox"}, + {"device", "firefox"} + }}, + {"compress", 1}, + {"presence", { + {"activities", nlohmann::json::array({ + { + {"name", "meow"}, + {"type", 2} + } + })}, + {"status", "idle"}, + {"since", 91879201}, + {"afk", false} + }} +}} + }; + ix::initNetSystem(); + webSocket.setUrl("wss://gateway.discord.gg/?v=10&encoding=json"); + webSocket.disableAutomaticReconnection(); + Log::create(INFO, WEBSOCKET, "ixwebsocket init"); + webSocket.setOnMessageCallback([this, res = nlohmann::json(), heartbeat_interval = 0, connected = false, id](const ix::WebSocketMessagePtr& msg) mutable { + switch (msg->type) { + case ix::WebSocketMessageType::Message: + res = nlohmann::json::parse(msg->str); + Log::create(INFO, WEBSOCKET, std::to_string(res["op"].get()) + " " + res["t"].dump()); + switch (res["op"].get()) { + case GatewayOpcodes::Hello: + heartbeat_interval = res["d"]["heartbeat_interval"].get(); + !connected ? connected = true, webSocket.send(id.dump()) : 0; + std::thread([this, &heartbeat_interval, &connected]() { + while (connected && heartbeat_interval != -1) { + Log::create(INFO, WEBSOCKET, "Heartbeat " + std::to_string(heartbeat_interval)); + std::this_thread::sleep_for(std::chrono::milliseconds(heartbeat_interval)); + webSocket.send(payload.dump()); + } + }).detach(); + break; + case GatewayOpcodes::Dispatch: + Log::create(INFO, WEBSOCKET, res.dump()); + emit(res["t"].get(), res); + break; + default: + break; + } + break; + case ix::WebSocketMessageType::Error: + Log::force_create(ERROR, WEBSOCKET, msg->errorInfo.reason); + exit(-1); + break; + default: + break; + } + + }); + webSocket.start(); +} +WebSocket& WebSocket::getInstance(const std::string& token, const int& intents, const bool& bot) { + Log::create(INFO, WEBSOCKET, "Instance event"); + static WebSocket instance(token, intents, bot); + return instance; +} +WebSocket::~WebSocket() { + webSocket.close(); + ix::uninitNetSystem(); +} +std::string WebSocket::getToken() const { + return isBot ? std::string("Bot " + WebSocket::token) : WebSocket::token; +} +int WebSocket::getIntents() const { + return WebSocket::intents; +} +[[maybe_unused]] +void WebSocket::sendPresenceUpdate(const char* statusType, const std::string& activityName, const int& activityType) { + nlohmann::json prsUpdate = { + {"op", 3}, + {"d", { + {"since", 0}, + {"activities", nlohmann::json::array({{"name", activityName}, {"type", activityType}})}, + {"status", statusType}, + {"afk", false} + }} + }; + webSocket.send(prsUpdate.dump()); +} \ No newline at end of file diff --git a/libs/network/websocket.hpp b/libs/network/websocket.hpp new file mode 100644 index 0000000..5ed6842 --- /dev/null +++ b/libs/network/websocket.hpp @@ -0,0 +1,39 @@ +#ifndef NETWORK_WEBSOCKET_HPP_ +#define NETWORK_WEBSOCKET_HPP_ +#include +#include +#include +class EventEmitter { +private: + using eventHandlers = std::function; + std::unordered_map> handlers; +protected: + void emit(const std::string& event, const nlohmann::json& data); +public: + void getEvents(); + void start(); + void on(const std::string& event, eventHandlers handler); + void once(const std::string& event, eventHandlers handler); + void off(const std::string& event, eventHandlers handler); +}; +class WebSocket : public EventEmitter { +private: + std::string token; + int intents; + bool isBot; + ix::WebSocket webSocket; + const nlohmann::json payload = { {"op", GatewayOpcodes::Heartbeat}, {"d", nullptr} }; + WebSocket& operator=(const WebSocket&) = delete; + WebSocket& operator=(WebSocket&&) = delete; + WebSocket(WebSocket&&) = delete; + WebSocket(const WebSocket&) = delete; + WebSocket(const std::string& token, const int& intents, const bool& isBot); +public: + [[maybe_unused]] + void sendPresenceUpdate(const char* statusType, const std::string& activityName, const int& activityType); + static WebSocket& getInstance(const std::string& token = "", const int& intents = 0, const bool& bot = true); + ~WebSocket(); + std::string getToken() const; + int getIntents() const; +}; +#endif \ No newline at end of file diff --git a/libs/utils/enums.hpp b/libs/utils/enums.hpp index 303b02e..2b77a99 100644 --- a/libs/utils/enums.hpp +++ b/libs/utils/enums.hpp @@ -103,6 +103,21 @@ struct DiscordEndpoints { static const inline std::string latest = DiscordEndpoints::discord + ApiVersion::latest; }; }; +struct StatusType { + static inline const char* Offline = "offline"; + static inline const char* Online = "online"; + static inline const char* Idle = "idle"; + static inline const char* DND = "dnd"; + static inline const char* Invisible = "invisible"; +}; +enum ActivityType { + Playing, + Streaming, + Listening, + Watching, + Custom, + Competing +}; enum GatewayIntents { AutoModerationConfiguration = 1048576, AutoModerationExecution = 2097152, @@ -129,14 +144,6 @@ enum GatewayIntents { MessageContent = 32768, AllIntents = 131071 }; -enum UserStatus { - Offline, - Online, - Idle, - AFK, - DoNotDisturb, - Invisible -}; enum DiscordTime { Epoch = 1420070400000, }; diff --git a/sources/main.cpp b/sources/main.cpp index 551fca7..0306b26 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -36,8 +36,11 @@ int main(int argc, char* argv[]) { bot->on(GatewayEvents::MESSAGE_CREATE, [bot](const Discord& msg) { auto& [message, user, author] = msg.ctx(); - message->send("939957962972229634", message->pack("content", std::to_string(user->isBot))); - bot->getEvents(); + if (!author->isBot) { + message->send("939957962972229634", message->pack("content", std::to_string(user->isBot))); + bot->getEvents(); + bot->sendPresenceUpdate(StatusType::DND, "meow", ActivityType::Streaming); + } }); bot->start(); return 0; diff --git a/tests/tests.cpp b/tests/tests.cpp index 26863d7..9b586f2 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -1,7 +1,7 @@ /* #include #include -#include +#include TEST(NetworkManagerTest, RequestReturnsExpectedValue) { NetworkManager& networkManager = NetworkManager::getInstance(); EXPECT_FALSE(networkManager.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/939957962972229634/messages", { "content", "test" }).empty());