diff options
Diffstat (limited to 'bot/include')
| -rw-r--r-- | bot/include/server.cpp | 182 | ||||
| -rw-r--r-- | bot/include/utils.cpp | 38 | ||||
| -rw-r--r-- | bot/include/utils.hpp | 22 |
3 files changed, 234 insertions, 8 deletions
diff --git a/bot/include/server.cpp b/bot/include/server.cpp new file mode 100644 index 0000000..55e5df9 --- /dev/null +++ b/bot/include/server.cpp @@ -0,0 +1,182 @@ +#include <iostream> +#include <thread> +#include <mutex> +#include <vector> +#include <queue> +#include <functional> +#include <sys/socket.h> +#include <sys/un.h> +#include <poll.h> +#include <fcntl.h> +#include <unistd.h> +#include <cstring> + +#ifdef __APPLE__ +#define SOCKET_PATH_MAX 104 +#else +#define SOCKET_PATH_MAX 108 +#endif + +class UnixSocketServer { +public: + using MessageHandler = std::function<void(const std::string&)>; + + UnixSocketServer(const std::string& path) : + socket_path_(path.substr(0, SOCKET_PATH_MAX - 1)), + running_(false) {} + + ~UnixSocketServer() { stop(); } + + void start(MessageHandler handler) { + if (running_) return; + + server_fd_ = create_socket(); + setup_socket(); + + running_ = true; + server_thread_ = std::thread(&UnixSocketServer::event_loop, this, handler); + } + + void stop() { + running_ = false; + if (server_thread_.joinable()) { + server_thread_.join(); + } + cleanup(); + } + + void send(const std::string& message) { + std::lock_guard<std::mutex> lock(clients_mutex_); + for (auto& client : clients_) { + queue_message(client.fd, message); + } + } + +private: + struct Client { + int fd; + std::queue<std::string> write_queue; + }; + + int create_socket() { + int fd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) throw std::runtime_error("::socket() failed"); + return fd; + } + + void setup_socket() { + struct sockaddr_un addr = {}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socket_path_.c_str(), sizeof(addr.sun_path) - 1); + + unlink(socket_path_.c_str()); + + if (bind(server_fd_, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + close(server_fd_); + throw std::runtime_error("bind() failed"); + } + + if (listen(server_fd_, 5) == -1) { + close(server_fd_); + throw std::runtime_error("listen() failed"); + } + + set_nonblocking(server_fd_); + } + + void set_nonblocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } + + void event_loop(MessageHandler handler) { + std::vector<struct pollfd> fds; + fds.push_back({server_fd_, POLLIN, 0}); + + while (running_) { + int ready = ::poll(fds.data(), fds.size(), 250); + if (ready == -1) break; + + for (size_t i = 0; i < fds.size(); ++i) { + if (fds[i].revents & POLLIN) { + if (fds[i].fd == server_fd_) { + accept_new_connection(fds); + } else { + handle_client_input(fds[i].fd, handler); + } + } + + if (fds[i].revents & POLLOUT) { + handle_client_output(fds[i].fd); + } + } + } + } + + void accept_new_connection(std::vector<struct pollfd>& fds) { + int client_fd = accept(server_fd_, nullptr, nullptr); + if (client_fd == -1) return; + + set_nonblocking(client_fd); + fds.push_back({client_fd, POLLIN | POLLOUT, 0}); + + std::lock_guard<std::mutex> lock(clients_mutex_); + clients_.push_back({client_fd, {}}); + } + + void handle_client_input(int fd, MessageHandler handler) { + char buffer[4096]; + ssize_t count = recv(fd, buffer, sizeof(buffer), 0); + + if (count > 0) { + handler(std::string(buffer, count)); + } else { + remove_client(fd); + } + } + + void handle_client_output(int fd) { + std::lock_guard<std::mutex> lock(clients_mutex_); + for (auto& client : clients_) { + if (client.fd == fd && !client.write_queue.empty()) { + const std::string& msg = client.write_queue.front(); + ssize_t sent = ::send(fd, msg.data(), msg.size(), 0); + if (sent > 0) { + client.write_queue.pop(); + } + } + } + } + + void queue_message(int fd, const std::string& message) { + std::lock_guard<std::mutex> lock(clients_mutex_); + for (auto& client : clients_) { + if (client.fd == fd) { + client.write_queue.push(message); + return; + } + } + } + + void remove_client(int fd) { + std::lock_guard<std::mutex> lock(clients_mutex_); + clients_.erase( + std::remove_if(clients_.begin(), clients_.end(), + [fd](const Client& c) { return c.fd == fd; }), + clients_.end() + ); + close(fd); + } + + void cleanup() { + close(server_fd_); + unlink(socket_path_.c_str()); + } + + std::string socket_path_; + int server_fd_ = -1; + std::atomic<bool> running_; + std::thread server_thread_; + std::mutex clients_mutex_; + std::vector<Client> clients_; +}; diff --git a/bot/include/utils.cpp b/bot/include/utils.cpp index 6a47d42..081fd4e 100644 --- a/bot/include/utils.cpp +++ b/bot/include/utils.cpp @@ -20,7 +20,7 @@ namespace app return g.get_icon_url(1024, i_webp); } - std::string update_string(const std::string &initial, const std::map<std::string, std::string> &updates) + 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); @@ -62,12 +62,12 @@ namespace app return result; } // Forward declaration - void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::map<std::string, std::string> &kv); + 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::map<std::string, std::string> generate_key_values(const slashcommand_t &event) + std::unordered_map<std::string, std::string> generate_key_values(const slashcommand_t &event) { - std::map<std::string, std::string> key_values; + std::unordered_map<std::string, std::string> key_values; const guild *g = event.command.is_guild_interaction() ? &event.command.get_guild() : nullptr; const channel &c = event.command.get_channel(); const user &u = event.command.get_issuing_user(); @@ -98,7 +98,7 @@ namespace app } // Traite une option d'interaction récursivement - void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::map<std::string, std::string> &kv) + void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::unordered_map<std::string, std::string> &kv) { switch (option.type) { @@ -184,4 +184,32 @@ namespace app break; } } + + 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 19c834c..57f4005 100644 --- a/bot/include/utils.hpp +++ b/bot/include/utils.hpp @@ -37,7 +37,7 @@ namespace app * @param updates A map of key-value pairs to replace placeholders * @return std::string The updated string with placeholders replaced */ - std::string update_string(const std::string &initial, const std::map<std::string, std::string> &updates); + std::string update_string(const std::string &initial, const std::unordered_map<std::string, std::string> &updates); /** * @brief Processes a command option recursively and adds values to the key-value map @@ -46,7 +46,7 @@ namespace app * @param option The command option to process * @param kv The key-value map to update */ - void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::map<std::string, std::string> &kv); + void process_interaction_option(const slashcommand_t &event, const command_data_option &option, std::unordered_map<std::string, std::string> &kv); /** * @brief Generates a map of key-value pairs from a slash command event @@ -54,7 +54,23 @@ namespace app * @param event The slash command event * @return std::map<std::string, std::string> A map containing information about the command, user, guild, and options */ - std::map<std::string, std::string> generate_key_values(const slashcommand_t &event); + std::unordered_map<std::string, std::string> generate_key_values(const slashcommand_t &event); + + /** + * @brief Parses a JSON string into a JSON object + * + * @param str The JSON string to parse + * @return nlohmann::json The parsed JSON object + */ + nlohmann::json json_from_string(const std::string &str); + + /** + * @brief Converts a JSON object into a string + * + * @param j The JSON object to convert + * @return std::string The string representation of the JSON object + */ + std::string string_from_json(const nlohmann::json &j); } // namespace dpp |
