summaryrefslogtreecommitdiff
path: root/bot/include
diff options
context:
space:
mode:
authorsoler_j <soler_j@etna-alternance.net>2025-04-30 22:30:54 +0200
committersoler_j <soler_j@etna-alternance.net>2025-04-30 22:30:54 +0200
commit976c7a7466ef2852607a83ead4e0ed93550742d8 (patch)
treef3a6996d9b2e283b2501cf1d8b8f0bb82a21a967 /bot/include
parente557ff8f867d777332ab397ba5b3b6be57767972 (diff)
Refactor project structure and remove unused files
- Updated CMakeLists.txt to include source files from the 'src' directory instead of 'include'. - Deleted 'http_webhook_server.cpp' and 'utils.cpp' files as they were no longer needed. - Added 'handle_actions.hpp' and 'handle_actions.cpp' to manage slash command actions. - Implemented 'http_webhook_server.cpp' to handle HTTP webhook server functionality. - Updated 'utils.hpp' to include necessary headers and declarations. - Refactored 'main.cpp' to include 'handle_actions.hpp' and updated slash command handling logic. - Enhanced 'utils.cpp' with utility functions for handling user and guild data.
Diffstat (limited to 'bot/include')
-rw-r--r--bot/include/handle_actions.hpp3
-rw-r--r--bot/include/http_webhook_server.cpp175
-rw-r--r--bot/include/http_webhook_server.hpp2
-rw-r--r--bot/include/utils.cpp302
-rw-r--r--bot/include/utils.hpp4
5 files changed, 7 insertions, 479 deletions
diff --git a/bot/include/handle_actions.hpp b/bot/include/handle_actions.hpp
new file mode 100644
index 0000000..107e93d
--- /dev/null
+++ b/bot/include/handle_actions.hpp
@@ -0,0 +1,3 @@
+#include <dpp/dpp.h>
+
+dpp::task<bool> handle_actions(const dpp::slashcommand_t& event, const nlohmann::json& actions, const std::unordered_map<std::string, std::string>& key_values);
diff --git a/bot/include/http_webhook_server.cpp b/bot/include/http_webhook_server.cpp
deleted file mode 100644
index 84815b7..0000000
--- a/bot/include/http_webhook_server.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "http_webhook_server.hpp"
-#include <fcntl.h>
-#include <unistd.h>
-#include <cstring>
-#include <algorithm>
-
-HttpWebhookServer::HttpWebhookServer(uint16_t port, Handler handler)
- : port(port), request_handler(handler) {
- setupSocket();
- setupEpoll();
-}
-
-HttpWebhookServer::~HttpWebhookServer() {
- stop();
- if (server_fd != -1) ::close(server_fd);
- if (epoll_fd != -1) ::close(epoll_fd);
-}
-
-void HttpWebhookServer::setupSocket() {
- server_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
- if (server_fd == -1) throw std::system_error(errno, std::generic_category());
-
- int opt = 1;
- setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-
- sockaddr_in addr{};
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- addr.sin_port = htons(port);
-
- if (::bind(server_fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
- throw std::system_error(errno, std::generic_category());
-
- if (::listen(server_fd, 128) == -1)
- throw std::system_error(errno, std::generic_category());
-}
-
-void HttpWebhookServer::setupEpoll() {
- epoll_fd = ::epoll_create1(0);
- if (epoll_fd == -1)
- throw std::system_error(errno, std::generic_category());
-
- epoll_event event{};
- event.events = EPOLLIN | EPOLLET;
- event.data.fd = server_fd;
-
- if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1)
- throw std::system_error(errno, std::generic_category());
-}
-
-void HttpWebhookServer::start() {
- running = true;
- epoll_event events[64];
-
- while (running) {
- int nfds = ::epoll_wait(epoll_fd, events, 64, -1);
- if (nfds == -1 && errno != EINTR)
- throw std::system_error(errno, std::generic_category());
-
- for (int i = 0; i < nfds; ++i) {
- if (events[i].data.fd == server_fd) {
- while (true) {
- sockaddr_in client_addr{};
- socklen_t client_len = sizeof(client_addr);
- int client_fd = ::accept4(server_fd, (sockaddr*)&client_addr, &client_len, SOCK_NONBLOCK);
- if (client_fd == -1) break;
-
- epoll_event event{};
- event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
- event.data.fd = client_fd;
- ::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
- clients[client_fd] = ClientContext{};
- }
- } else {
- handleClient(events[i].data.fd);
- }
- }
- }
-}
-
-void HttpWebhookServer::stop() {
- running = false;
-}
-
-void HttpWebhookServer::closeClient(int fd) {
- ::epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
- ::close(fd);
- clients.erase(fd);
-}
-
-void HttpWebhookServer::handleClient(int fd) {
- char buffer[4096];
- ssize_t count = ::recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT);
-
- if (count > 0) {
- auto& ctx = clients[fd];
- ctx.input_buffer.append(buffer, count);
-
- if (ctx.input_buffer.find("\r\n\r\n") != std::string::npos) {
- HttpRequest req;
- parseHttpRequest(ctx, req);
-
- HttpResponse res = request_handler(req);
- buildHttpResponse(res, ctx.output_buffer);
-
- epoll_event event{};
- event.events = EPOLLOUT | EPOLLET | EPOLLRDHUP;
- event.data.fd = fd;
- ::epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event);
- }
- }
- else if (count == 0 || (count == -1 && errno != EAGAIN)) {
- closeClient(fd);
- return;
- }
-
- if (!clients.count(fd)) return; // Client déjà fermé plus haut
-
- auto& ctx = clients[fd];
- if (!ctx.output_buffer.empty()) {
- ssize_t sent = ::send(fd,
- ctx.output_buffer.data() + ctx.bytes_written,
- ctx.output_buffer.size() - ctx.bytes_written,
- MSG_DONTWAIT);
-
- if (sent > 0) ctx.bytes_written += sent;
- if (ctx.bytes_written == ctx.output_buffer.size()) {
- closeClient(fd);
- }
- }
-}
-
-void HttpWebhookServer::parseHttpRequest(ClientContext& ctx, HttpRequest& req) {
- std::istringstream stream(ctx.input_buffer);
- std::string line;
-
- std::getline(stream, line);
- std::istringstream req_line(line);
- req_line >> req.method >> req.path;
-
- while (std::getline(stream, line) && line != "\r") {
- size_t colon = line.find(':');
- if (colon != std::string::npos) {
- std::string key = line.substr(0, colon);
- std::string value = line.substr(colon + 1);
- key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
- value.erase(0, value.find_first_not_of(" \t\r\n"));
- value.erase(value.find_last_not_of(" \t\r\n") + 1);
- req.headers[key] = value;
- }
- }
-
- size_t header_end = ctx.input_buffer.find("\r\n\r\n");
- if (header_end != std::string::npos) {
- req.body = ctx.input_buffer.substr(header_end + 4);
- if (req.headers.count("Content-Length")) {
- size_t content_length = std::stoul(req.headers["Content-Length"]);
- req.body = req.body.substr(0, content_length);
- }
- }
-}
-
-void HttpWebhookServer::buildHttpResponse(const HttpResponse& res, std::string& output) {
- output = "HTTP/1.1 " + std::to_string(res.status_code) + " OK\r\n";
-
- for (const auto& [key, value] : res.headers) {
- output += key + ": " + value + "\r\n";
- }
-
- if (!res.headers.count("Content-Length")) {
- output += "Content-Length: " + std::to_string(res.body.size()) + "\r\n";
- }
-
- output += "\r\n" + res.body;
-}
diff --git a/bot/include/http_webhook_server.hpp b/bot/include/http_webhook_server.hpp
index f840a16..a42829f 100644
--- a/bot/include/http_webhook_server.hpp
+++ b/bot/include/http_webhook_server.hpp
@@ -1,3 +1,5 @@
+#pragma once
+
#include <sys/epoll.h>
#include <netinet/in.h>
#include <functional>
diff --git a/bot/include/utils.cpp b/bot/include/utils.cpp
deleted file mode 100644
index 712a156..0000000
--- a/bot/include/utils.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-#include <dpp/dpp.h>
-#include <dpp/nlohmann/json.hpp>
-#include <map>
-#include <string>
-#include <regex>
-#include <sstream>
-#include <algorithm>
-
-using namespace dpp;
-namespace app
-{
- // Helpers
- std::string make_avatar_url(const user &u)
- {
- return u.avatar.to_string().empty() ? u.get_default_avatar_url() : u.get_avatar_url(1024, i_webp, true);
- }
-
- std::string make_guild_icon(const guild &g)
- {
- return g.get_icon_url(1024, i_webp);
- }
-
- std::string update_string(const std::string &initial, const std::unordered_map<std::string, std::string> &updates)
- {
- static const std::regex placeholderRegex(R"(\(\((.*?)\)\))", std::regex::icase);
-
- std::string result;
- std::sregex_iterator it(initial.begin(), initial.end(), placeholderRegex);
- std::sregex_iterator end;
-
- size_t last_pos = 0;
- for (; it != end; ++it)
- {
- const auto &match = *it;
- result.append(initial, last_pos, match.position() - last_pos);
-
- std::string content = match[1].str();
- std::vector<std::string> keys;
- std::stringstream ss(content);
- std::string key;
- bool replaced = false;
-
- while (std::getline(ss, key, '|'))
- {
- key = trim(key);
- auto found = updates.find(key);
- if (found != updates.end())
- {
- result.append(found->second);
- replaced = true;
- break;
- }
- }
- if (!replaced)
- {
- // Aucune clé trouvée : chaîne vide
- }
-
- last_pos = match.position() + match.length();
- }
- result.append(initial, last_pos, std::string::npos);
- return result;
- }
- // Forward declaration
- void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::unordered_map<std::string, std::string> &kv);
-
- // Génère la map clé/valeur
- std::unordered_map<std::string, std::string> generate_key_values(const slashcommand_t &event)
- {
- std::unordered_map<std::string, std::string> key_values;
- const guild *g = event.command.is_guild_interaction() ? &event.command.get_guild() : nullptr;
- const channel *channel_ptr = event.command.is_guild_interaction() ? &event.command.get_channel() : nullptr;
- const user &u = event.command.get_issuing_user();
- key_values["commandName"] = event.command.get_command_name();
- key_values["commandId"] = event.command.id.str();
- key_values["commandType"] = std::to_string(event.command.type);
- key_values["userName"] = u.username;
- key_values["userId"] = u.id.str();
- key_values["userAvatar"] = make_avatar_url(u);
- key_values["guildName"] = g ? g->name : "DM";
- key_values["channelName"] = channel_ptr ? channel_ptr->name : "DM";
- key_values["channelId"] = channel_ptr ? channel_ptr->id.str() : "0";
- key_values["channelType"] = channel_ptr ? std::to_string(channel_ptr->get_type()) : "0";
- key_values["guildId"] = g ? g->id.str() : "0";
- key_values["guildIcon"] = g ? make_guild_icon(*g) : "";
- key_values["guildCount"] = g ? std::to_string(g->member_count) : "0";
- key_values["guildOwner"] = g ? g->owner_id.str() : "0";
- key_values["guildCreatedAt"] = g ? std::to_string(g->get_creation_time()) : "0";
- key_values["guildBoostTier"] = g ? std::to_string(g->premium_tier) : "0";
- key_values["guildBoostCount"] = g ? std::to_string(g->premium_subscription_count) : "0";
-
- // Options de commande
- for (const auto &option : event.command.get_command_interaction().options)
- {
- process_interaction_option(event, option, key_values);
- }
- return key_values;
- }
-
- // Traite une option d'interaction récursivement
- void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::unordered_map<std::string, std::string> &kv)
- {
- switch (option.type)
- {
- case co_sub_command:
- case co_sub_command_group:
- for (const auto &subopt : option.options)
- {
- process_interaction_option(event, subopt, kv);
- }
- break;
- case co_user:
- {
- snowflake user_id = std::get<snowflake>(option.value);
- auto user_ptr = event.command.get_resolved_user(user_id);
- const user &u = user_ptr;
- kv["opts." + option.name] = u.username;
- kv["opts." + option.name + ".id"] = u.id.str();
- kv["opts." + option.name + ".avatar"] = make_avatar_url(u);
- kv["opts." + option.name + ".discriminator"] = std::to_string(u.discriminator);
- kv["opts." + option.name + ".bot"] = u.is_bot() ? "true" : "false";
- kv["opts." + option.name + ".created_at"] = std::to_string(u.get_creation_time());
- }
- break;
- case co_channel:
- {
- snowflake chan_id = std::get<snowflake>(option.value);
- auto chan_ptr = event.command.get_resolved_channel(chan_id);
- const channel &c = chan_ptr;
- kv["opts." + option.name] = c.name;
- kv["opts." + option.name + ".id"] = c.id.str();
- kv["opts." + option.name + ".type"] = std::to_string(c.get_type());
- kv["opts." + option.name + ".created_at"] = std::to_string(c.get_creation_time());
- }
- break;
- case co_role:
- {
- snowflake role_id = std::get<snowflake>(option.value);
- auto role_ptr = event.command.get_resolved_role(role_id);
- const role &r = role_ptr;
- kv["opts." + option.name] = r.name;
- kv["opts." + option.name + ".id"] = r.id.str();
- kv["opts." + option.name + ".color"] = std::to_string(r.colour);
- kv["opts." + option.name + ".hoist"] = r.is_hoisted() ? "true" : "false";
- kv["opts." + option.name + ".position"] = std::to_string(r.position);
- }
- break;
- case co_mentionable:
- {
- snowflake mentionable_id = std::get<snowflake>(option.value);
- auto member_ptr = event.command.get_resolved_member(mentionable_id);
- const user &u = *member_ptr.get_user();
- kv["opts." + option.name] = u.username;
- kv["opts." + option.name + ".id"] = u.id.str();
- kv["opts." + option.name + ".avatar"] = make_avatar_url(u);
- kv["opts." + option.name + ".discriminator"] = std::to_string(u.discriminator);
- kv["opts." + option.name + ".bot"] = u.is_bot() ? "true" : "false";
- kv["opts." + option.name + ".created_at"] = std::to_string(u.get_creation_time());
- kv["opts." + option.name + ".nick"] = member_ptr.get_nickname();
- kv["opts." + option.name + ".joined_at"] = std::to_string(member_ptr.joined_at);
- }
- break;
- case co_string:
- kv["opts." + option.name] = std::get<std::string>(option.value);
- break;
- case co_integer:
- kv["opts." + option.name] = std::to_string(std::get<int64_t>(option.value));
- break;
- case co_boolean:
- kv["opts." + option.name] = std::get<bool>(option.value) ? "true" : "false";
- break;
- case co_number:
- kv["opts." + option.name] = std::to_string(std::get<double>(option.value));
- break;
- case co_attachment:
- {
- snowflake attachment_id = std::get<snowflake>(option.value);
- auto att_ptr = event.command.get_resolved_attachment(attachment_id);
- kv["opts." + option.name] = att_ptr.url;
- kv["opts." + option.name + ".id"] = att_ptr.id.str();
- kv["opts." + option.name + ".filename"] = att_ptr.filename;
- kv["opts." + option.name + ".size"] = std::to_string(att_ptr.size);
- }
- break;
- }
- }
-
- bool handle_actions(const slashcommand_t &event, const nlohmann::json &actions, const std::unordered_map<std::string, std::string> &key_values, dpp::cluster &bot)
- {
- // Actions are a list of Objects
- if (actions.is_array())
- {
- for (const auto &action : actions)
- {
- if (action.contains("type"))
- {
- std::string action_type = action["type"];
- if (action_type == "delete_messages" && event.command.is_guild_interaction())
- {
-
- std::string error = "Amount must be between 1 and 100";
- if (action.contains("error"))
- {
- error = action["error"].get<std::string>();
- }
- // let's retrieve the current channel
- const dpp::channel *channel_ptr = &event.command.get_channel();
- int amount = 0;
- if (action.contains("amount"))
- {
- amount = action["amount"];
- // let's retrieve the amount of messages to delete
- if (amount < 0 || amount > 100)
- {
- event.reply(error);
- return false;
- }
- }
- else if (action.contains("depend_on"))
- {
- std::string depend_on = action["depend_on"];
- auto it = key_values.find(depend_on);
- if (it != key_values.end())
- {
- std::string depend_on_value = it->second;
-
- // let's convert the depend_on_value to an int
- int amount = std::stoi(depend_on_value);
- if (amount < 0 || amount > 100)
- {
- event.reply(error);
- return false;
- }
- }
- }
-
- if (amount > 0)
- {
-
- bot.messages_get(event.command.channel_id, 0, 0, 0, amount, [&event, &bot, &error, &channel_ptr](const dpp::confirmation_callback_t &callback)
- {
- if (callback.is_error())
- {
- event.reply(error);
- return false;
- }
-
- auto messages = callback.get<dpp::message_map>();
-
- std::vector<snowflake> msg_ids;
-
- for (const auto &msg : messages)
- {
- // let's check if the message is older than 2 weeks
- if (std::chrono::system_clock::from_time_t(msg.second.get_creation_time()) < std::chrono::system_clock::now() - std::chrono::hours(24 * 14))
- {
- // delete the message
- msg_ids.push_back(msg.second.id);
- }
- }
-
- if (!msg_ids.empty())
- {
- bot.co_message_delete_bulk(msg_ids, channel_ptr->id);
- } });
- }
- }
- }
- }
- }
-
- return true;
- }
-
- nlohmann::json json_from_string(const std::string &str)
- {
- nlohmann::json j;
- try
- {
- j = nlohmann::json::parse(str);
- }
- catch (const nlohmann::json::parse_error &e)
- {
- std::cerr << "JSON parse error: " << e.what() << std::endl;
- }
- return j;
- }
-
- std::string string_from_json(const nlohmann::json &j)
- {
- std::string str;
- try
- {
- str = j.dump();
- }
- catch (const nlohmann::json::exception &e)
- {
- std::cerr << "JSON exception: " << e.what() << std::endl;
- }
- return str;
- }
-}
diff --git a/bot/include/utils.hpp b/bot/include/utils.hpp
index 3052ecf..820fd01 100644
--- a/bot/include/utils.hpp
+++ b/bot/include/utils.hpp
@@ -1,5 +1,5 @@
// utils.hpp
-
+#pragma once
#ifndef UTILS_HPP
#define UTILS_HPP
@@ -63,7 +63,7 @@ namespace app
* @param actions The JSON object containing actions to be handled
* @param key_values The map of key-value pairs to be used in the actions
*/
- bool handle_actions(const slashcommand_t &event, const nlohmann::json &actions, const std::unordered_map<std::string, std::string> &key_values, dpp::cluster &bot);
+ bool handle_actions(const slashcommand_t &event, const nlohmann::json &actions, const std::unordered_map<std::string, std::string> &key_values);
/**
* @brief Parses a JSON string into a JSON object