Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

33 changed files with 28173 additions and 602 deletions

View File

@ -1,8 +0,0 @@
Checks:
'-*,
clang-analyzer-*,
modernize-*,
performance-*,
bugprone-*,
cppcoreguidelines-*,
-performance-avoid-endl'

3
.gitignore vendored
View File

@ -1,3 +1,2 @@
build/
.vscode/
.cache/
.vscode/

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.10)
project(sparkle)
#include(GoogleTest)
include(GoogleTest)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 20)
@ -10,31 +10,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(SOURCE sources/main.cpp)
set(LIBS ${CMAKE_SOURCE_DIR}/libs/)
set(INCLUDE ${CMAKE_SOURCE_DIR}/include/)
#set(TESTS ${CMAKE_SOURCE_DIR}/tests)
set(ADDITIONAL_CXX_FLAGS_DEBUG "-march=native -O0 -pipe -Wall -Wextra -Wpedantic -Wconversion -Wuninitialized -Wsign-conversion -Wshadow -fsanitize=address") #-Werror
set(ADDITIONAL_CXX_FLAGS_RELEASE "-march=native -O2 -pipe -Wall -Wextra -Wpedantic -Wconversion -Wuninitialized -Wsign-conversion -Wshadow -flto=full")
file(GLOB NETWORK_SOURCES libs/network/*.cpp)
set(TESTS ${CMAKE_SOURCE_DIR}/tests)
set(ADDITIONAL_CXX_FLAGS_DEBUG "-pipe -Wall -Wextra -O0 -fsanitize=address")
set(ADDITIONAL_CXX_FLAGS_RELEASE "-march=native -pipe -Wall -Wextra -O2 -flto")
find_package(CURL REQUIRED)
find_path(IXWEBSOCKET_INCLUDE_DIR ixwebsocket)
find_library(IXWEBSOCKET_LIBRARIES ixwebsocket)
if(CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DDEBUG)
set(CMAKE_CXX_FLAGS_DEBUG "${ADDITIONAL_CXX_FLAGS_DEBUG}")
message(STATUS "cflags: ${CMAKE_CXX_FLAGS_DEBUG}")
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
add_definitions(-DRELEASE)
set(CMAKE_CXX_FLAGS_RELEASE "${ADDITIONAL_CXX_FLAGS_RELEASE}")
message(STATUS "cflags: ${CMAKE_CXX_FLAGS_RELEASE}")
endif()
elseif(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE "${ADDITIONAL_CXX_FLAGS_RELEASE}")
message(STATUS "cflags: ${CMAKE_CXX_FLAGS_RELEASE}")
endif()
message(STATUS "Current compiler: ${CMAKE_CXX_COMPILER}")
if(NOT IXWEBSOCKET_INCLUDE_DIR OR NOT IXWEBSOCKET_LIBRARIES)
message(FATAL_ERROR "ixwebsocket not found")
@ -44,12 +28,29 @@ if(NOT CURL_INCLUDE_DIRS OR NOT CURL_LIBRARIES)
message(FATAL_ERROR "curl not found")
endif()
message(STATUS "Current compiler: ${CMAKE_CXX_COMPILER}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
endif()
add_executable(${PROJECT_NAME} ${SOURCE} ${NETWORK_SOURCES})
if(CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_CXX_FLAGS "${ADDITIONAL_CXX_FLAGS_DEBUG}")
add_definitions(-DDEBUG)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
add_definitions(-DRELEASE)
set(CMAKE_CXX_FLAGS "${ADDITIONAL_CXX_FLAGS_RELEASE}")
endif()
endif()
target_precompile_headers(${PROJECT_NAME} PRIVATE ${INCLUDE}pch.hpp)
message(STATUS "${CMAKE_BUILD_TYPE}")
add_executable(${PROJECT_NAME} ${SOURCE})
#add_library(sparkles STATIC ${SOURCE})
add_executable(tests ${TESTS}/tests.cpp)
enable_testing()
add_test(NAME NetworkManagerTest COMMAND tests)
target_include_directories(${PROJECT_NAME} PRIVATE
${LIBS}
@ -63,22 +64,15 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
${CURL_LIBRARIES}
)
#add_library(sparkles STATIC ${SOURCE})
#add_executable(tests ${TESTS}/tests.cpp)
target_include_directories(tests PRIVATE
${LIBS}
${INCLUDE}
${IXWEBSOCKET_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
)
#enable_testing()
#add_test(NAME NetworkManagerTest COMMAND tests)
#target_include_directories(tests PRIVATE
# ${LIBS}
# ${INCLUDE}
# ${IXWEBSOCKET_INCLUDE_DIR}
# ${CURL_INCLUDE_DIRS}
#)
#target_link_libraries(tests gtest
# ${IXWEBSOCKET_LIBRARIES}
# ${CURL_LIBRARIES}
# gtest_main
#)
target_link_libraries(tests gtest
${IXWEBSOCKET_LIBRARIES}
${CURL_LIBRARIES}
gtest_main
)

View File

@ -1,11 +1,12 @@
# Sparkle
Library for creating Discord bots.
## How to Build
### Required Libraries
- `curl`
- `ixwebsocket`
- `nlohmann-json`
- `cxxopts`
### Build Instructions
```sh
git clone http://applejack.ygg/fluttershy/sparkle.git && cd sparkle
@ -14,13 +15,12 @@ build/sparkle -t <BOT_TOKEN>
```
# ToDo
- [x] Websocket support
- [x] Minimal discord api support
- [x] Selfbot support
- [x] Easy to use
- [ ] Optimised for high loads (not tested)
- [ ] Full api support
- [ ] Fixes for known bugs
- [ ] Windows support
- [ ] Sharding
- [ ] Interactions
- [ ] Tests
#
![](res/fluttershy.gif)
- [ ] Tests

View File

@ -1,10 +1,10 @@
#ifndef INCLUDE_INTERFACE_HPP_
#define INCLUDE_INTERFACE_HPP_
#include <interface/Author.hpp>
#include <interface/Message.hpp>
#include <interface/Channel.hpp>
#include <interface/Embed.hpp>
#include <interface/Bot.hpp>
#include <interface/User.hpp>
#include <interface/Guild.hpp>
#ifndef INCLUDE_API_HPP_
#define INCLUDE_API_HPP_
#include <api/Author.hpp>
#include <api/Message.hpp>
#include <api/Channel.hpp>
#include <api/Embed.hpp>
#include <api/Bot.hpp>
#include <api/User.hpp>
#include <api/Guild.hpp>
#endif

View File

@ -1,7 +1,10 @@
#ifndef INCLUDE_INCLUDES_HPP_
#define INCLUDE_INCLUDES_HPP_
#include <utils/types.hpp>
#include <utils/json.hpp>
#include <utils/log.hpp>
#include <utils/enums.hpp>
#include <utils/functions.hpp>
#include <iostream>
#include <string>
#endif

5
include/net.hpp Normal file
View File

@ -0,0 +1,5 @@
#ifndef INCLUDE_NET_HPP_
#define INCLUDE_NET_HPP_
#include <gateway/Websocket.hpp>
#include <network/Network.hpp>
#endif

View File

@ -1,5 +0,0 @@
#ifndef INCLUDE_NET_HPP_
#define INCLUDE_NET_HPP_
#include <network/websocket.hpp>
#include <network/network.hpp>
#endif

View File

@ -1,12 +0,0 @@
#ifndef INCLUDE_PCH_HPP_
#define INCLUDE_PCH_HPP_
#include <iostream>
#include <string>
#include <functional>
#include <algorithm>
#include <utils/types.hpp>
#include <utils/log.hpp>
#include <utils/enums.hpp>
#include <utils/functions.hpp>
#include <nlohmann/json.hpp>
#endif

View File

@ -1,3 +0,0 @@
#ifndef API_ACTIVITY_HPP_
#define API_ACTIVITY_HPP_
#endif

34
libs/api/Author.hpp Normal file
View File

@ -0,0 +1,34 @@
#ifndef API_AUTHOR_HPP_
#define API_AUTHOR_HPP_
#include <includes.hpp>
#include <net.hpp>
class Author {
private:
nlohmann::json data;
const nlohmann::json& d;
WebSocket& web;
NetworkManager& req;
public:
const std::string channel_id, global_name, id, content, avatar, guild_id, discriminator, message_id;
bool isPinned, isBot;
Author(const nlohmann::json& data) :
data(data),
d(data["d"]),
web(WebSocket::getInstance()),
req(NetworkManager::getInstance()),
channel_id(functions::isNull(d["channel_id"])),
global_name(functions::isNull(d["author"]["global_name"])),
id(d["author"]["id"]),
content(d["content"]),
avatar(d["author"]["avatar"]),
guild_id(d["guild_id"]),
discriminator(d["author"]["discriminator"]),
message_id(d["id"]),
isPinned(d["pinned"]),
isBot(d["author"].contains("bot") ? d["author"]["bot"].get<bool>() : false) {
};
std::string send(const nlohmann::json& msg) {
return req.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/" + d["channel_id"].get<std::string>() + "/messages", msg.dump());
}
};
#endif

View File

@ -1,16 +1,21 @@
#ifndef INTERFACE_BOT_HPP_
#define INTERFACE_BOT_HPP_
#include <pch.hpp>
#ifndef API_BOT_HPP_
#define API_BOT_HPP_
#include <includes.hpp>
template<typename...Args>
class Discord {
private:
const nlohmann::json& data;
std::tuple<std::unique_ptr<Args>...> net;
nlohmann::json data;
template<unsigned long... Is>
void initializeCtx(const nlohmann::json& data, std::index_sequence<Is...>) {
net = std::make_tuple(std::make_unique<Args>(data)...);
}
public:
Discord(const nlohmann::json& data) : data(data), net(std::make_tuple(std::make_unique<Args>(data)...)) {}
Discord(const nlohmann::json& data) : data(data) {
initializeCtx(data, std::index_sequence_for<Args...>{});
}
template<class T>
_deprecated_t("Use ctx() instead")
auto& get() const {
[[deprecated("Use ctx() instead")]] auto& get() const {
return *std::get<std::unique_ptr<T>>(net);
}
const auto& ctx() const {
@ -19,7 +24,7 @@ public:
};
class Bot {
private:
const nlohmann::json& data;
nlohmann::json data;
public:
Bot(const nlohmann::json& data) : data(data) {};
std::string id() const {

View File

@ -1,14 +1,14 @@
#ifndef INTERFACE_CHANNEL_HPP_
#define INTERFACE_CHANNEL_HPP_
#include <pch.hpp>
#include <network.hpp>
#ifndef API_CHANNEL_HPP_
#define API_CHANNEL_HPP_
#include <includes.hpp>
#include <net.hpp>
class Channel {
private:
const nlohmann::json& data;
nlohmann::json data;
WebSocket& web;
NetworkManager& req;
public:
Channel(const nlohmann::json& data) : data(data), web(WebSocket::getInstance()), req(NetworkManager::getInstance()) {}
Channel(const nlohmann::json& data = "") : data(data), web(WebSocket::getInstance()), req(NetworkManager::getInstance()) {}
std::string send(const nlohmann::json& msg) {
return req.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/" + data["d"]["channel_id"].get<std::string>() + "/messages", msg.dump());
}

View File

@ -1,6 +1,6 @@
#ifndef INTERFACE_EMBED_HPP_
#define INTERFACE_EMBED_HPP_
#include <pch.hpp>
#ifndef API_EMBED_HPP_
#define API_EMBED_HPP_
#include <includes.hpp>
class EmbedBuilder {
private:
nlohmann::json embed = {

View File

@ -1,7 +1,7 @@
#ifndef INTERFACE_GUILD_HPP_
#define INTERFACE_GUILD_HPP_
#include <pch.hpp>
#include <network.hpp>
#ifndef API_GUILD_HPP_
#define API_GUILD_HPP_
#include <includes.hpp>
#include <net.hpp>
class Guild {
private:
nlohmann::json data;

View File

@ -1,18 +1,18 @@
#ifndef INTERFACE_MESSAGE_HPP_
#define INTERFACE_MESSAGE_HPP_
#include <pch.hpp>
#include <network.hpp>
#ifndef API_MESSAGE_HPP_
#define API_MESSAGE_HPP_
#include <includes.hpp>
#include <net.hpp>
class Message {
private:
const nlohmann::json& data;
nlohmann::json data;
WebSocket& web;
NetworkManager& req;
public:
Message(const nlohmann::json& json) : data(json), web(WebSocket::getInstance()), req(NetworkManager::getInstance()) {};
auto send(const std::string& id, const nlohmann::json& msg) -> std::string {
Message(const nlohmann::json& data = "") : data(data), web(WebSocket::getInstance()), req(NetworkManager::getInstance()) {};
std::string send(const std::string& id, const nlohmann::json& msg) {
return req.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/" + id + "/messages", msg.dump());
}
auto getMessages(const std::string& id, const std::string& count, const std::string& before = "") -> std::string {
std::string getMessages(const std::string& id, const std::string& count, const std::string& before = "") {
if (before.empty()) {
return req.request(HttpMethods::GET, DiscordEndpoints::details::latest + "/channels/" + id + "/messages?limit=" + count);
} else {
@ -20,19 +20,22 @@ public:
}
return "";
}
auto deleteMessage(const std::string& channel_id, const std::string& message_id) -> std::string {
std::string deleteMessage(const std::string& channel_id, const std::string& message_id) {
return req.request(HttpMethods::DELETE, DiscordEndpoints::details::latest + "/channels/" + channel_id + "/messages/" + message_id);
}
_nodiscard auto pack(const nlohmann::json& key, const std::string& value) const -> nlohmann::json {
nlohmann::json pack(const nlohmann::json& key, const std::string& value) const {
return { { key, value } };
}
struct Api {
const nlohmann::json& data;
nlohmann::json data;
NetworkManager& req;
Api(const nlohmann::json& json) : data(json), req(NetworkManager::getInstance()) {};
_nodiscard auto latency() const -> unsigned long {
Api(const nlohmann::json& json = "") : data(json), req(NetworkManager::getInstance()) {};
unsigned latency() const {
return req.getLatency();
}
};
std::string str() const {
return std::string(data);
}
};
#endif

View File

@ -1,23 +1,17 @@
#ifndef INTERFACE_USER_HPP_
#define INTERFACE_USER_HPP_
#include <pch.hpp>
#include <network.hpp>
#ifndef API_USER_HPP_
#define API_USER_HPP_
#include <includes.hpp>
#include <net.hpp>
#include <vector>
class User {
private:
const nlohmann::json& data;
nlohmann::json data;
WebSocket& web;
NetworkManager& req;
public:
User(const nlohmann::json& data) : data(data), web(WebSocket::getInstance()), req(NetworkManager::getInstance()),
isBot(false) {
auto botValue = functions::testValue<bool>(data, { "d", "user", "bot" });
if (botValue.has_value()) {
isBot = botValue.value();
}
}
bool isBot;
auto extract(const std::vector<std::string>& keys) -> nlohmann::json {
User(const nlohmann::json& data = "") : data(data), web(WebSocket::getInstance()), req(NetworkManager::getInstance()) {}
#ifdef DEBUG
nlohmann::json extract(const std::vector<std::string>& keys) {
std::vector<std::string> d = { "d" };
d.insert(d.end(), keys.begin(), keys.end());
nlohmann::json current = data;
@ -29,9 +23,17 @@ public:
}
}
return current;
};
auto me() -> std::string {
}
#endif
std::string me() {
return req.request(HttpMethods::GET, DiscordEndpoints::details::latest + "/users/@me");
}
bool isBot() const {
try {
return data["d"]["author"]["bot"];
} catch (...) {
return false;
}
}
};
#endif

141
libs/gateway/Websocket.hpp Normal file
View File

@ -0,0 +1,141 @@
#ifndef GATEWAY_WEBSOCKET_HPP_
#define GATEWAY_WEBSOCKET_HPP_
#include <includes.hpp>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h>
class EventEmitter {
private:
using eventHandlers = std::function<void(const nlohmann::json&)>;
std::unordered_map<std::string, std::list<eventHandlers>> 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 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<eventHandlers>() == handler.target<eventHandlers>();
}), vec.end());
}
}
};
class WebSocket : public EventEmitter {
private:
std::string token;
int intents;
bool isBot;
ix::WebSocket webSocket;
const nlohmann::json payload = { {"op", 1}, {"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 = 0, const bool& isBot = true) : token(token), intents(intents), isBot(isBot) {
nlohmann::json id = {
{"op", 2},
{"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, res["op"].dump() + " " + res["t"].dump());
switch (res["op"].get<int>()) {
case 10:
heartbeat_interval = res["d"]["heartbeat_interval"].get<int>();
!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 0: emit(res["t"].get<std::string>(), res); break;
}
break;
case ix::WebSocketMessageType::Error:
std::cout << msg->errorInfo.reason << std::endl;
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;
}
void start() {
while (1) std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
};
#endif

View File

@ -1,45 +0,0 @@
#ifndef INTERFACE_AUTHOR_HPP_
#define INTERFACE_AUTHOR_HPP_
#include <pch.hpp>
#include <network.hpp>
class Author {
private:
const nlohmann::json& data;
WebSocket& web;
NetworkManager& req;
public:
_deprecated_t("requires update")
struct Channel {
std::string channel_id;
std::string global_name;
std::string id;
std::string content;
std::string avatar;
std::string guild_id;
std::string discriminator;
std::string message_id;
bool isPinned;
bool isBot;
} authorData;
const std::string channel_id, global_name, id, content, avatar, guild_id, discriminator, message_id;
bool isPinned, isBot;
Author(const nlohmann::json& data)
: data(data),
web(WebSocket::getInstance()),
req(NetworkManager::getInstance()),
channel_id(functions::testValue<std::string>(data, { "d", "channel_id" }).value_or("")),
global_name(functions::testValue<std::string>(data, { "d", "author", "global_name" }).value_or("")),
id(functions::testValue<std::string>(data, { "d", "author", "id" }).value_or("")),
content(functions::testValue<std::string>(data, { "d", "content" }).value_or("")),
avatar(functions::testValue<std::string>(data, { "d", "author", "avatar" }).value_or("")),
guild_id(functions::testValue<std::string>(data, { "d", "guild_id" }).value_or("")),
discriminator(functions::testValue<std::string>(data, { "d", "author", "discriminator" }).value_or("")),
message_id(functions::testValue<std::string>(data, { "d", "id" }).value_or("")),
isPinned(functions::testValue<bool>(data, { "d", "pinned" }).value_or(false)),
isBot(functions::testValue<bool>(data, { "d", "author", "bot" }).value_or(false)) {
}
auto send(const nlohmann::json& msg) -> std::string {
return req.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/" + data["d"]["channel_id"].get<std::string>() + "/messages", msg.dump());
}
};
#endif

69
libs/network/Network.hpp Normal file
View File

@ -0,0 +1,69 @@
#ifndef TLS_NETWORK_HPP_
#define TLS_NETWORK_HPP_
#include <includes.hpp>
#include <gateway/Websocket.hpp>
#include <curl/curl.h>
class NetworkManager {
private:
CURL* curl;
CURLcode res;
WebSocket& web;
std::chrono::duration<double, std::milli> duration;
NetworkManager& operator=(const NetworkManager&) = delete;
NetworkManager& operator=(NetworkManager&&) = delete;
NetworkManager(NetworkManager&&) = delete;
NetworkManager(const NetworkManager&) = delete;
NetworkManager() : web(WebSocket::getInstance()) {
Log::create(INFO, NETWORK, "Network init");
curl_global_init(CURL_GLOBAL_DEFAULT);
if (!(curl = curl_easy_init())) {
Log::create(CRITICAL, NETWORK, "Failed to initialize CURL");
abort();
}
}
~NetworkManager() {
if (curl) {
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
static unsigned long WriteCallback(void* contents, unsigned long size, unsigned long nmemb, void* userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
public:
static NetworkManager& getInstance() {
Log::create(WARNING, NETWORK, "Instance event");
static NetworkManager instance;
return instance;
}
unsigned long getLatency() const {
return duration.count();
}
std::string request(const std::string& method, const std::string& path, const std::string& data = "") {
std::string response;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, path.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, ("Authorization: " + web.getToken()).c_str());
headers = curl_slist_append(headers, "User-Agent: DiscordBot");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);
auto start = std::chrono::steady_clock::now();
if ((res = curl_easy_perform(curl)) != 0) Log::create(ERROR, NETWORK, "curl_easy_perform() failed: " + std::string(curl_easy_strerror(res)));
curl_slist_free_all(headers);
duration = std::chrono::steady_clock::now() - start;
}
#ifdef DEBUG
Log::create(INFO, NETWORK, response);
#endif
return response;
}
};
#endif

View File

@ -1,50 +0,0 @@
#include "network.hpp"
NetworkManager::NetworkManager() : curl(curl_easy_init(), curl_easy_cleanup), res(CURLE_OK), web(WebSocket::getInstance()) {
Log::create(INFO, NETWORK, "Network init");
curl_global_init(CURL_GLOBAL_DEFAULT);
if (!curl) {
Log::create(CRITICAL, NETWORK, "Failed to initialize CURL");
abort();
}
}
NetworkManager::~NetworkManager() {
curl_global_cleanup();
}
auto NetworkManager::WriteCallback(void* contents, unsigned long size, unsigned long nmemb, void* userp) -> unsigned long {
(static_cast<std::string*>(userp))->append(static_cast<char*>(contents), size * nmemb);
return size * nmemb;
}
auto NetworkManager::getInstance() -> NetworkManager& {
Log::create(INFO, NETWORK, "Instance event");
static NetworkManager instance;
return instance;
}
auto NetworkManager::getLatency() const -> unsigned long {
return 0;
}
auto NetworkManager::request(const std::string& method, const std::string& path, const std::string& data) -> std::string {
std::string response;
if (curl) {
curl_easy_setopt(curl.get(), CURLOPT_URL, path.c_str());
curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, method.c_str());
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, data.c_str());
curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, ("Authorization: " + web.getToken()).c_str());
headers = curl_slist_append(headers, "User-Agent: DiscordBot");
curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);
res = curl_easy_perform(curl.get());
if (res != 0) {
Log::create(ERROR, NETWORK, "curl_easy_perform() failed: " + std::string(curl_easy_strerror(res)));
}
curl_slist_free_all(headers);
}
#ifdef DEBUG
Log::create(INFO, NETWORK, response);
#endif
return response;
}

View File

@ -1,23 +0,0 @@
#ifndef TLS_NETWORK_HPP_
#define TLS_NETWORK_HPP_
#include <pch.hpp>
#include <network/websocket.hpp>
#include <curl/curl.h>
class NetworkManager {
private:
std::unique_ptr<void, decltype(&curl_easy_cleanup)> curl;
CURLcode res;
WebSocket& web;
NetworkManager();
~NetworkManager();
static auto WriteCallback(void* contents, unsigned long size, unsigned long nmemb, void* userp) -> unsigned long;
public:
auto operator=(const NetworkManager&)->NetworkManager& = delete;
auto operator=(NetworkManager&&)->NetworkManager& = delete;
NetworkManager(NetworkManager&&) = delete;
NetworkManager(const NetworkManager&) = delete;
static auto getInstance() -> NetworkManager&;
[[nodiscard]] auto getLatency() const -> unsigned long;
auto request(const std::string& method, const std::string& path, const std::string& data = "") -> std::string;
};
#endif

View File

@ -1,127 +0,0 @@
#include "websocket.hpp"
void EventEmitter::emit(const std::string& event, const nlohmann::json& data) {
if (auto it = handlers.find(event); it != handlers.end()) {
std::for_each(it->second.begin(), it->second.end(), [&data](const auto& handler) {
handler(data);
});
}
}
void EventEmitter::getEvents() const {
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() const {
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
void EventEmitter::on(const std::string& event, eventHandlers handler) {
handlers[event].emplace_back(std::move(handler));
}
void EventEmitter::once(const std::string& event, const eventHandlers& handler) {
auto wrappedHandler = [this, event, handler](const nlohmann::json& data) {
handler(data);
off(event, handler);
};
handlers[event].emplace_back(std::move(wrappedHandler));
}
void EventEmitter::off(const std::string& event, eventHandlers handler) {
if (auto it = handlers.find(event); it != handlers.cend()) {
it->second.erase(std::ranges::remove_if(it->second, [&handler](const eventHandlers& h) {
return h.target<eventHandlers>() == handler.target<eventHandlers>();
}).begin(), it->second.cend());
}
}
WebSocket::WebSocket(const std::string& token, int intents, 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", 0},
{"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<int>()) + " " + res["t"].dump());
switch (res["op"].get<GatewayOpcodes>()) {
case GatewayOpcodes::Hello:
heartbeat_interval = res["d"]["heartbeat_interval"].get<int>();
if (connected) connected = true; else webSocket.send(id.dump());
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<std::string>(), res);
break;
default:
break;
}
break;
case ix::WebSocketMessageType::Error:
Log::force_create(ERROR, WEBSOCKET, msg->errorInfo.reason);
exit(-1);
break;
default:
break;
}
});
webSocket.start();
}
auto WebSocket::getInstance(const std::string& token, int intents, bool bot) -> WebSocket& {
Log::create(INFO, WEBSOCKET, "Instance event");
static WebSocket instance(token, intents, bot);
return instance;
}
WebSocket::~WebSocket() {
webSocket.close();
ix::uninitNetSystem();
}
auto WebSocket::getToken() const -> std::string {
return this->isBot ? std::string("Bot " + this->token) : this->token;
}
auto WebSocket::getIntents() const -> int {
return this->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());
}

View File

@ -1,39 +0,0 @@
#ifndef NETWORK_WEBSOCKET_HPP_
#define NETWORK_WEBSOCKET_HPP_
#include <pch.hpp>
#include <ranges>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h>
class EventEmitter {
private:
using eventHandlers = std::function<void(const nlohmann::json&)>;
std::unordered_map<std::string, std::vector<eventHandlers>> handlers;
protected:
void emit(const std::string& event, const nlohmann::json& data);
public:
void getEvents() const;
void start() const;
void on(const std::string& event, eventHandlers handler);
void once(const std::string& event, const 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(const std::string& token, int intents, bool isBot);
public:
auto operator=(const WebSocket&)->WebSocket& = delete;
auto operator=(WebSocket&&)->WebSocket& = delete;
WebSocket(WebSocket&&) = delete;
WebSocket(const WebSocket&) = delete;
_maybe_unused void sendPresenceUpdate(const char* statusType, const std::string& activityName, const int& activityType);
static auto getInstance(const std::string& token = "", int intents = 0, bool bot = true) -> WebSocket&;
~WebSocket();
auto getToken() const->std::string;
auto getIntents() const -> int;
};
#endif

2930
libs/utils/cxxopts.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,88 +1,87 @@
#ifndef UTILS_EVENTS_HPP_
#define UTILS_EVENTS_HPP_
#include <string>
struct GatewayEvents {
static inline constexpr char* APPLICATION_COMMAND_PERMISSIONS_UPDATE = "APPLICATION_COMMAND_PERMISSIONS_UPDATE";
static inline constexpr char* AUTO_MODERATION_ACTION_EXECUTION = "AUTO_MODERATION_ACTION_EXECUTION";
static inline constexpr char* AUTO_MODERATION_RULE_CREATE = "AUTO_MODERATION_RULE_CREATE";
static inline constexpr char* AUTO_MODERATION_RULE_DELETE = "AUTO_MODERATION_RULE_DELETE";
static inline constexpr char* AUTO_MODERATION_RULE_UPDATE = "AUTO_MODERATION_RULE_UPDATE";
static inline constexpr char* CHANNEL_CREATE = "CHANNEL_CREATE";
static inline constexpr char* CHANNEL_DELETE = "CHANNEL_DELETE";
static inline constexpr char* CHANNEL_PINS_UPDATE = "CHANNEL_PINS_UPDATE";
static inline constexpr char* CHANNEL_UPDATE = "CHANNEL_UPDATE";
static inline constexpr char* ENTITLEMENT_CREATE = "ENTITLEMENT_CREATE";
static inline constexpr char* ENTITLEMENT_DELETE = "ENTITLEMENT_DELETE";
static inline constexpr char* ENTITLEMENT_UPDATE = "ENTITLEMENT_UPDATE";
static inline constexpr char* GUILD_AUDIT_LOG_ENTRY_CREATE = "GUILD_AUDIT_LOG_ENTRY_CREATE";
static inline constexpr char* GUILD_BAN_ADD = "GUILD_BAN_ADD";
static inline constexpr char* GUILD_BAN_REMOVE = "GUILD_BAN_REMOVE";
static inline constexpr char* GUILD_CREATE = "GUILD_CREATE";
static inline constexpr char* GUILD_DELETE = "GUILD_DELETE";
static inline constexpr char* GUILD_EMOJIS_UPDATE = "GUILD_EMOJIS_UPDATE";
static inline constexpr char* GUILD_INTEGRATIONS_UPDATE = "GUILD_INTEGRATIONS_UPDATE";
static inline constexpr char* GUILD_MEMBER_ADD = "GUILD_MEMBER_ADD";
static inline constexpr char* GUILD_MEMBER_REMOVE = "GUILD_MEMBER_REMOVE";
static inline constexpr char* GUILD_MEMBERS_CHUNK = "GUILD_MEMBERS_CHUNK";
static inline constexpr char* GUILD_MEMBER_UPDATE = "GUILD_MEMBER_UPDATE";
static inline constexpr char* GUILD_ROLE_CREATE = "GUILD_ROLE_CREATE";
static inline constexpr char* GUILD_ROLE_DELETE = "GUILD_ROLE_DELETE";
static inline constexpr char* GUILD_ROLE_UPDATE = "GUILD_ROLE_UPDATE";
static inline constexpr char* GUILD_SCHEDULED_EVENT_CREATE = "GUILD_SCHEDULED_EVENT_CREATE";
static inline constexpr char* GUILD_SCHEDULED_EVENT_DELETE = "GUILD_SCHEDULED_EVENT_DELETE";
static inline constexpr char* GUILD_SCHEDULED_EVENT_UPDATE = "GUILD_SCHEDULED_EVENT_UPDATE";
static inline constexpr char* GUILD_SCHEDULED_EVENT_USER_REMOVE = "GUILD_SCHEDULED_EVENT_USER_REMOVE";
static inline constexpr char* GUILD_SOUNDBOARD_SOUND_CREATE = "GUILD_SOUNDBOARD_SOUND_CREATE";
static inline constexpr char* GUILD_SOUNDBOARD_SOUND_DELETE = "GUILD_SOUNDBOARD_SOUND_DELETE";
static inline constexpr char* GUILD_SOUNDBOARD_SOUNDS_UPDATE = "GUILD_SOUNDBOARD_SOUNDS_UPDATE";
static inline constexpr char* GUILD_SOUNDBOARD_SOUND_UPDATE = "GUILD_SOUNDBOARD_SOUND_UPDATE";
static inline constexpr char* SOUNDBOARD_SOUNDS = "SOUNDBOARD_SOUNDS";
static inline constexpr char* GUILD_STICKERS_UPDATE = "GUILD_STICKERS_UPDATE";
static inline constexpr char* GUILD_UPDATE = "GUILD_UPDATE";
static inline constexpr char* INTEGRATION_CREATE = "INTEGRATION_CREATE";
static inline constexpr char* INTEGRATION_DELETE = "INTEGRATION_DELETE";
static inline constexpr char* INTEGRATION_UPDATE = "INTEGRATION_UPDATE";
static inline constexpr char* INVITE_CREATE = "INVITE_CREATE";
static inline constexpr char* INVITE_DELETE = "INVITE_DELETE";
static inline constexpr char* MESSAGE_CREATE = "MESSAGE_CREATE";
static inline constexpr char* MESSAGE_DELETE = "MESSAGE_DELETE";
static inline constexpr char* MESSAGE_DELETE_BULK = "MESSAGE_DELETE_BULK";
static inline constexpr char* MESSAGE_POLL_VOTE_ADD = "MESSAGE_POLL_VOTE_ADD";
static inline constexpr char* MESSAGE_POLL_VOTE_REMOVE = "MESSAGE_POLL_VOTE_REMOVE";
static inline constexpr char* MESSAGE_REACTION_ADD = "MESSAGE_REACTION_ADD";
static inline constexpr char* MESSAGE_REACTION_REMOVE = "MESSAGE_REACTION_REMOVE";
static inline constexpr char* MESSAGE_REACTION_REMOVE_ALL = "MESSAGE_REACTION_REMOVE_ALL";
static inline constexpr char* MESSAGE_REACTION_REMOVE_EMOJI = "MESSAGE_REACTION_REMOVE_EMOJI";
static inline constexpr char* MESSAGE_UPDATE = "MESSAGE_UPDATE";
static inline constexpr char* PRESENCE_UPDATE = "PRESENCE_UPDATE";
static inline constexpr char* READY = "READY";
static inline constexpr char* RESUMED = "RESUMED";
static inline constexpr char* STAGE_INSTANCE_CREATE = "STAGE_INSTANCE_CREATE";
static inline constexpr char* STAGE_INSTANCE_DELETE = "STAGE_INSTANCE_DELETE";
static inline constexpr char* STAGE_INSTANCE_UPDATE = "STAGE_INSTANCE_UPDATE";
static inline constexpr char* SUBSCRIPTION_CREATE = "SUBSCRIPTION_CREATE";
static inline constexpr char* SUBSCRIPTION_DELETE = "SUBSCRIPTION_DELETE";
static inline constexpr char* SUBSCRIPTION_UPDATE = "SUBSCRIPTION_UPDATE";
static inline constexpr char* THREAD_CREATE = "THREAD_CREATE";
static inline constexpr char* THREAD_DELETE = "THREAD_DELETE";
static inline constexpr char* THREAD_LIST_SYNC = "THREAD_LIST_SYNC";
static inline constexpr char* THREAD_MEMBERS_UPDATE = "THREAD_MEMBERS_UPDATE";
static inline constexpr char* THREAD_MEMBER_UPDATE = "THREAD_MEMBER_UPDATE";
static inline constexpr char* THREAD_UPDATE = "THREAD_UPDATE";
static inline constexpr char* TYPING_START = "TYPING_START";
static inline constexpr char* USER_UPDATE = "USER_UPDATE";
static inline constexpr char* VOICE_CHANNEL_EFFECT_SEND = "VOICE_CHANNEL_EFFECT_SEND";
static inline constexpr char* VOICE_SERVER_UPDATE = "VOICE_SERVER_UPDATE";
static inline constexpr char* VOICE_STATE_UPDATE = "VOICE_STATE_UPDATE";
static inline constexpr char* WEBHOOKS_UPDATE = "WEBHOOKS_UPDATE";
static constexpr const char* APPLICATION_COMMAND_PERMISSIONS_UPDATE = "APPLICATION_COMMAND_PERMISSIONS_UPDATE";
static constexpr const char* AUTO_MODERATION_ACTION_EXECUTION = "AUTO_MODERATION_ACTION_EXECUTION";
static constexpr const char* AUTO_MODERATION_RULE_CREATE = "AUTO_MODERATION_RULE_CREATE";
static constexpr const char* AUTO_MODERATION_RULE_DELETE = "AUTO_MODERATION_RULE_DELETE";
static constexpr const char* AUTO_MODERATION_RULE_UPDATE = "AUTO_MODERATION_RULE_UPDATE";
static constexpr const char* CHANNEL_CREATE = "CHANNEL_CREATE";
static constexpr const char* CHANNEL_DELETE = "CHANNEL_DELETE";
static constexpr const char* CHANNEL_PINS_UPDATE = "CHANNEL_PINS_UPDATE";
static constexpr const char* CHANNEL_UPDATE = "CHANNEL_UPDATE";
static constexpr const char* ENTITLEMENT_CREATE = "ENTITLEMENT_CREATE";
static constexpr const char* ENTITLEMENT_DELETE = "ENTITLEMENT_DELETE";
static constexpr const char* ENTITLEMENT_UPDATE = "ENTITLEMENT_UPDATE";
static constexpr const char* GUILD_AUDIT_LOG_ENTRY_CREATE = "GUILD_AUDIT_LOG_ENTRY_CREATE";
static constexpr const char* GUILD_BAN_ADD = "GUILD_BAN_ADD";
static constexpr const char* GUILD_BAN_REMOVE = "GUILD_BAN_REMOVE";
static constexpr const char* GUILD_CREATE = "GUILD_CREATE";
static constexpr const char* GUILD_DELETE = "GUILD_DELETE";
static constexpr const char* GUILD_EMOJIS_UPDATE = "GUILD_EMOJIS_UPDATE";
static constexpr const char* GUILD_INTEGRATIONS_UPDATE = "GUILD_INTEGRATIONS_UPDATE";
static constexpr const char* GUILD_MEMBER_ADD = "GUILD_MEMBER_ADD";
static constexpr const char* GUILD_MEMBER_REMOVE = "GUILD_MEMBER_REMOVE";
static constexpr const char* GUILD_MEMBERS_CHUNK = "GUILD_MEMBERS_CHUNK";
static constexpr const char* GUILD_MEMBER_UPDATE = "GUILD_MEMBER_UPDATE";
static constexpr const char* GUILD_ROLE_CREATE = "GUILD_ROLE_CREATE";
static constexpr const char* GUILD_ROLE_DELETE = "GUILD_ROLE_DELETE";
static constexpr const char* GUILD_ROLE_UPDATE = "GUILD_ROLE_UPDATE";
static constexpr const char* GUILD_SCHEDULED_EVENT_CREATE = "GUILD_SCHEDULED_EVENT_CREATE";
static constexpr const char* GUILD_SCHEDULED_EVENT_DELETE = "GUILD_SCHEDULED_EVENT_DELETE";
static constexpr const char* GUILD_SCHEDULED_EVENT_UPDATE = "GUILD_SCHEDULED_EVENT_UPDATE";
static constexpr const char* GUILD_SCHEDULED_EVENT_USER_REMOVE = "GUILD_SCHEDULED_EVENT_USER_REMOVE";
static constexpr const char* GUILD_SOUNDBOARD_SOUND_CREATE = "GUILD_SOUNDBOARD_SOUND_CREATE";
static constexpr const char* GUILD_SOUNDBOARD_SOUND_DELETE = "GUILD_SOUNDBOARD_SOUND_DELETE";
static constexpr const char* GUILD_SOUNDBOARD_SOUNDS_UPDATE = "GUILD_SOUNDBOARD_SOUNDS_UPDATE";
static constexpr const char* GUILD_SOUNDBOARD_SOUND_UPDATE = "GUILD_SOUNDBOARD_SOUND_UPDATE";
static constexpr const char* SOUNDBOARD_SOUNDS = "SOUNDBOARD_SOUNDS";
static constexpr const char* GUILD_STICKERS_UPDATE = "GUILD_STICKERS_UPDATE";
static constexpr const char* GUILD_UPDATE = "GUILD_UPDATE";
static constexpr const char* INTEGRATION_CREATE = "INTEGRATION_CREATE";
static constexpr const char* INTEGRATION_DELETE = "INTEGRATION_DELETE";
static constexpr const char* INTEGRATION_UPDATE = "INTEGRATION_UPDATE";
static constexpr const char* INVITE_CREATE = "INVITE_CREATE";
static constexpr const char* INVITE_DELETE = "INVITE_DELETE";
static constexpr const char* MESSAGE_CREATE = "MESSAGE_CREATE";
static constexpr const char* MESSAGE_DELETE = "MESSAGE_DELETE";
static constexpr const char* MESSAGE_DELETE_BULK = "MESSAGE_DELETE_BULK";
static constexpr const char* MESSAGE_POLL_VOTE_ADD = "MESSAGE_POLL_VOTE_ADD";
static constexpr const char* MESSAGE_POLL_VOTE_REMOVE = "MESSAGE_POLL_VOTE_REMOVE";
static constexpr const char* MESSAGE_REACTION_ADD = "MESSAGE_REACTION_ADD";
static constexpr const char* MESSAGE_REACTION_REMOVE = "MESSAGE_REACTION_REMOVE";
static constexpr const char* MESSAGE_REACTION_REMOVE_ALL = "MESSAGE_REACTION_REMOVE_ALL";
static constexpr const char* MESSAGE_REACTION_REMOVE_EMOJI = "MESSAGE_REACTION_REMOVE_EMOJI";
static constexpr const char* MESSAGE_UPDATE = "MESSAGE_UPDATE";
static constexpr const char* PRESENCE_UPDATE = "PRESENCE_UPDATE";
static constexpr const char* READY = "READY";
static constexpr const char* RESUMED = "RESUMED";
static constexpr const char* STAGE_INSTANCE_CREATE = "STAGE_INSTANCE_CREATE";
static constexpr const char* STAGE_INSTANCE_DELETE = "STAGE_INSTANCE_DELETE";
static constexpr const char* STAGE_INSTANCE_UPDATE = "STAGE_INSTANCE_UPDATE";
static constexpr const char* SUBSCRIPTION_CREATE = "SUBSCRIPTION_CREATE";
static constexpr const char* SUBSCRIPTION_DELETE = "SUBSCRIPTION_DELETE";
static constexpr const char* SUBSCRIPTION_UPDATE = "SUBSCRIPTION_UPDATE";
static constexpr const char* THREAD_CREATE = "THREAD_CREATE";
static constexpr const char* THREAD_DELETE = "THREAD_DELETE";
static constexpr const char* THREAD_LIST_SYNC = "THREAD_LIST_SYNC";
static constexpr const char* THREAD_MEMBERS_UPDATE = "THREAD_MEMBERS_UPDATE";
static constexpr const char* THREAD_MEMBER_UPDATE = "THREAD_MEMBER_UPDATE";
static constexpr const char* THREAD_UPDATE = "THREAD_UPDATE";
static constexpr const char* TYPING_START = "TYPING_START";
static constexpr const char* USER_UPDATE = "USER_UPDATE";
static constexpr const char* VOICE_CHANNEL_EFFECT_SEND = "VOICE_CHANNEL_EFFECT_SEND";
static constexpr const char* VOICE_SERVER_UPDATE = "VOICE_SERVER_UPDATE";
static constexpr const char* VOICE_STATE_UPDATE = "VOICE_STATE_UPDATE";
static constexpr const char* WEBHOOKS_UPDATE = "WEBHOOKS_UPDATE";
};
struct HttpMethods {
static inline constexpr char* POST = "POST";
static inline constexpr char* GET = "GET";
static inline constexpr char* PATCH = "PATCH";
static inline constexpr char* DELETE = "DELETE";
static inline constexpr char* PUT = "PUT";
static inline constexpr char* OPTIONS = "OPTIONS";
static constexpr const char* POST = "POST";
static constexpr const char* GET = "GET";
static constexpr const char* PATCH = "PATCH";
static constexpr const char* DELETE = "DELETE";
static constexpr const char* PUT = "PUT";
static constexpr const char* OPTIONS = "OPTIONS";
};
struct ApiVersion {
static const inline std::string api = "/api/";
@ -104,21 +103,6 @@ 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 : unsigned char {
Playing,
Streaming,
Listening,
Watching,
Custom,
Competing
};
enum GatewayIntents {
AutoModerationConfiguration = 1048576,
AutoModerationExecution = 2097152,
@ -145,21 +129,15 @@ enum GatewayIntents {
MessageContent = 32768,
AllIntents = 131071
};
enum DiscordTime : long {
enum UserStatus {
Offline,
Online,
Idle,
AFK,
DoNotDisturb,
Invisible
};
enum DiscordTime {
Epoch = 1420070400000,
};
enum GatewayOpcodes : unsigned char {
Dispatch,
Heartbeat,
Identify,
PresenceUpdate,
VoiceStateUpdate,
Resume = 6,
Reconnect,
RequestGuildMembers,
InvalidSession,
Hello,
HeartbeatACK,
RequestSoundboardSounds = 31,
};
#endif

View File

@ -1,40 +1,8 @@
#ifndef UTILS_FUNCTIONS_HPP_
#define UTILS_FUNCTIONS_HPP_
#include <optional>
#include <string>
#include <nlohmann/json.hpp>
struct functions {
static inline auto isNull(const nlohmann::json& str) -> std::string {
static inline std::string isNull(const nlohmann::json& str) {
return str.is_null() ? str.dump() : str.get<std::string>();
}
template<typename T>
static auto testValue(const nlohmann::json& data, const std::vector<std::string>& keys) -> std::optional<T> {
const nlohmann::json* current = &data;
for (const auto& key : keys) {
if (current->contains(key)) {
current = &(*current)[key];
} else {
return std::nullopt;
}
}
if constexpr (std::is_same_v<T, bool>) {
if (current->is_boolean()) {
return current->get<bool>();
}
} else if constexpr (std::is_same_v<T, int>) {
if (current->is_number_integer()) {
return current->get<int>();
}
} else if constexpr (std::is_same_v<T, double>) {
if (current->is_number_float()) {
return current->get<double>();
}
} else if constexpr (std::is_same_v<T, std::string>) {
if (current->is_string()) {
return current->get<std::string>();
}
}
return std::nullopt;
}
};
#endif

24765
libs/utils/json.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,12 +5,11 @@
#include <iostream>
#include <iomanip>
#include <ctime>
#include <utils/types.hpp>
enum level : unsigned char { INFO, WARNING, ERROR, CRITICAL };
enum type : unsigned char { WEBSOCKET, NETWORK, API };
enum level { INFO, WARNING, ERROR, CRITICAL };
enum type { WEBSOCKET, NETWORK, API };
class Log {
public:
_maybe_unused static void create(_maybe_unused const level& lvl, _maybe_unused const type& t, _maybe_unused const std::string& message) {
[[maybe_unused]] static void create([[maybe_unused]] const level& lvl, [[maybe_unused]] const type& t, [[maybe_unused]] const std::string& message) {
#ifdef DEBUG
std::string color;
switch (lvl) {
@ -23,19 +22,8 @@ public:
std::cout << color << "[" << getCurrentTime() << "][" << str(t) << "][" << str(lvl) << "] \033[0m" << message << "\033[0m" << std::endl;
#endif
}
static void force_create(const level& lvl, const type& t, const std::string& message) {
std::string color;
switch (lvl) {
case INFO: color = "\033[34;1m"; break;
case WARNING: color = "\033[33;1m"; break;
case ERROR: color = "\033[31;1m"; break;
case CRITICAL: color = "\033[31;1;2m"; break;
default: color = "\033[0m"; break;
}
std::cout << color << "[" << getCurrentTime() << "][" << str(t) << "][" << str(lvl) << "] \033[0m" << message << "\033[0m" << '\n';
}
private:
static auto getCurrentTime() -> const std::string {
static const std::string getCurrentTime() {
std::time_t now_c = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm* timer = std::localtime(&now_c);
std::ostringstream oss;
@ -45,7 +33,7 @@ private:
<< timer->tm_sec;
return oss.str();
}
static auto str(const level& lvl) noexcept -> std::string {
static std::string str(const level& lvl) noexcept {
switch (lvl) {
case INFO: return "INFO";
case WARNING: return "WARNING";
@ -54,7 +42,7 @@ private:
default: return "UNKNOWN";
}
}
static auto str(const type& t) noexcept -> std::string {
static std::string str(const type& t) {
switch (t) {
case WEBSOCKET: return "WEBSOCKET";
case NETWORK: return "NETWORK";
@ -63,4 +51,5 @@ private:
}
}
};
#endif
#endif

View File

@ -1,7 +1,3 @@
#ifndef UTILS_TYPES_HPP_
#define UTILS_TYPES_HPP_
#define _maybe_unused [[maybe_unused]]
#define _deprecated_t(text) [[deprecated(text)]]
#define _deprecated [[deprecated]]
#define _nodiscard [[nodiscard]]
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

View File

@ -1,6 +1,6 @@
#include <utils/cxxopts.hpp>
#include <api.hpp>
#include <includes.hpp>
#include <cxxopts.hpp>
int main(int argc, char* argv[]) {
cxxopts::Options options("sparkle", "WIP discord bot library in C++");
options.add_options()
@ -14,33 +14,35 @@ int main(int argc, char* argv[]) {
std::cout << "sparkle version 0.3" << std::endl;
return 0;
}
if (result.count("help")) {
std::cout << options.help() << std::endl;
return 0;
}
if (!result.count("token")) {
if (!result.contains("token")) {
std::cout << "\033[31mError: no token provided.\033[0m" << std::endl;
std::cout << options.help() << std::endl;
return -1;
}
class WebSocket* bot = &WebSocket::getInstance(result["token"].as<std::string>(), GatewayIntents::AllIntents);
bot->once(GatewayEvents::READY, [](const Discord<User>& ctx) {
WebSocket* bot = &WebSocket::getInstance(result["token"].as<std::string>(), GatewayIntents::AllIntents);
bot->on(GatewayEvents::READY, [](const Discord<User>& ctx) {
auto& [user] = ctx.ctx();
std::cout << std::boolalpha << user->isBot << std::endl;
std::cout << nlohmann::json::parse(user->me())["username"].get<std::string>() << std::endl;
});
bot->on(GatewayEvents::MESSAGE_CREATE, [](const Discord<Message, User, Author>& msg) {
auto& [message, user, author] = msg.ctx();
const auto& [message, user, author] = msg.ctx();
if (!author->isBot) {
message->send("1334963948767940648", message->pack("content", std::to_string(user->isBot)));
message->send("939957962972229634", message->pack("content", author->channel_id));
}
});
bot->on(GatewayEvents::MESSAGE_REACTION_ADD, [](const Discord<Message>& msg) {
const auto& [message] = msg.ctx();
message->send("939957962972229634", message->pack("content", "test"));
});
bot->start();
return 0;
}

View File

@ -1,6 +1,6 @@
#include <gtest/gtest.h>
#include <includes.hpp>
#include <network.hpp>
#include <net.hpp>
TEST(NetworkManagerTest, RequestReturnsExpectedValue) {
NetworkManager& networkManager = NetworkManager::getInstance();
EXPECT_FALSE(networkManager.request(HttpMethods::POST, DiscordEndpoints::details::latest + "/channels/939957962972229634/messages", { "content", "test" }).empty());