diff options
| author | soler_j <soler_j@etna-alternance.net> | 2025-04-30 22:30:54 +0200 |
|---|---|---|
| committer | soler_j <soler_j@etna-alternance.net> | 2025-04-30 22:30:54 +0200 |
| commit | 976c7a7466ef2852607a83ead4e0ed93550742d8 (patch) | |
| tree | f3a6996d9b2e283b2501cf1d8b8f0bb82a21a967 /bot/include | |
| parent | e557ff8f867d777332ab397ba5b3b6be57767972 (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.hpp | 3 | ||||
| -rw-r--r-- | bot/include/http_webhook_server.cpp | 175 | ||||
| -rw-r--r-- | bot/include/http_webhook_server.hpp | 2 | ||||
| -rw-r--r-- | bot/include/utils.cpp | 302 | ||||
| -rw-r--r-- | bot/include/utils.hpp | 4 |
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 |
