diff options
| author | garder500 <jeremy27.clara22@gmail.com> | 2025-04-26 15:56:16 +0200 |
|---|---|---|
| committer | garder500 <jeremy27.clara22@gmail.com> | 2025-04-26 15:56:16 +0200 |
| commit | bd508f4a3c7fc18f053696148f614851cfb0d448 (patch) | |
| tree | baa8368af27b5b313d39db90e395c332ea24f130 | |
| parent | d6a9644c909b246491a8b642c08dd8103c06fa0a (diff) | |
Ajout de la configuration de l'environnement de développement, des fichiers de projet et des utilitaires pour le bot Discord.
| -rw-r--r-- | .envrc | 5 | ||||
| -rw-r--r-- | .gitignore | 11 | ||||
| -rw-r--r-- | .vscode/settings.json | 91 | ||||
| -rw-r--r-- | bot/CMakeLists.txt | 78 | ||||
| -rw-r--r-- | bot/include/utils.cpp | 187 | ||||
| -rw-r--r-- | bot/include/utils.hpp | 62 | ||||
| -rw-r--r-- | bot/src/main.cpp | 28 | ||||
| -rw-r--r-- | devenv.lock | 103 | ||||
| -rw-r--r-- | devenv.nix | 49 | ||||
| -rw-r--r-- | devenv.yaml | 15 |
10 files changed, 629 insertions, 0 deletions
@@ -0,0 +1,5 @@ +export DIRENV_WARN_TIMEOUT=20s + +eval "$(devenv direnvrc)" + +use devenv @@ -30,3 +30,14 @@ *.exe *.out *.app + +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml +build diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0954d11 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,91 @@ +{ + "cmake.sourceDirectory": "/Users/jeremy/Documents/bot-creator-api/bot", + "files.associations": { + "map": "cpp", + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "coroutine": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ratio": "cpp", + "set": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "valarray": "cpp", + "variant": "cpp", + "vector": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "chrono": "cpp", + "compare": "cpp", + "concepts": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "random": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "stop_token": "cpp" + } +} diff --git a/bot/CMakeLists.txt b/bot/CMakeLists.txt new file mode 100644 index 0000000..5fef1c3 --- /dev/null +++ b/bot/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.15) +project(discord-bot VERSION 1.0 DESCRIPTION "A Discord bot using DPP library") + +# Required for DPP and modern C++ features +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Find required dependencies +find_package(OpenSSL REQUIRED) +find_package(ZLIB REQUIRED) + +# Configure DPP library (assuming you have it installed via Nix) +find_package(dpp REQUIRED + HINTS ${DPP_INCLUDE_DIR} # Add path if needed: /nix/store/.../include +) +file(GLOB_RECURSE SRC_FILES + include/*.cpp + include/*.hpp +) +# Add executable +add_executable(${PROJECT_NAME} + src/main.cpp + ${SRC_FILES} + # Add other source files here +) + + +# Link libraries with proper dependencies +target_link_libraries(${PROJECT_NAME} PRIVATE + dpp + OpenSSL::SSL + OpenSSL::Crypto + ZLIB::ZLIB + pthread # Required for DPP's threading +) + +# Include directories +target_include_directories(${PROJECT_NAME} PRIVATE + ${DPP_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIR} + ${Sodium_INCLUDE_DIRS} +) + +# macOS ARM64 specific fixes +if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") + target_compile_options(${PROJECT_NAME} PRIVATE + -arch arm64 + -I/opt/homebrew/opt/openssl/include # If using Homebrew + ) + target_link_options(${PROJECT_NAME} PRIVATE + -arch arm64 + -L/opt/homebrew/opt/openssl/lib # If using Homebrew + ) +elseif(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + target_compile_options(${PROJECT_NAME} PRIVATE + -arch x86_64 + -I/opt/homebrew/opt/openssl/include # If using Homebrew + ) + target_link_options(${PROJECT_NAME} PRIVATE + -arch x86_64 + -L/opt/homebrew/opt/openssl/lib # If using Homebrew + ) +elseif(UNIX AND NOT APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + target_compile_options(${PROJECT_NAME} PRIVATE + -m64 + ) + target_link_options(${PROJECT_NAME} PRIVATE + -m64 + ) +elseif(UNIX AND NOT APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") + target_compile_options(${PROJECT_NAME} PRIVATE + -marm + ) + target_link_options(${PROJECT_NAME} PRIVATE + -marm + ) +endif() diff --git a/bot/include/utils.cpp b/bot/include/utils.cpp new file mode 100644 index 0000000..6a47d42 --- /dev/null +++ b/bot/include/utils.cpp @@ -0,0 +1,187 @@ +#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::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::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::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(); + 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"] = c.name; + key_values["channelId"] = c.id.str(); + key_values["channelType"] = std::to_string(c.get_type()); + 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::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; + } + } +} diff --git a/bot/include/utils.hpp b/bot/include/utils.hpp new file mode 100644 index 0000000..19c834c --- /dev/null +++ b/bot/include/utils.hpp @@ -0,0 +1,62 @@ +// utils.hpp + +#ifndef UTILS_HPP +#define UTILS_HPP + +#include <dpp/dpp.h> +#include <dpp/nlohmann/json.hpp> +#include <map> +#include <string> +#include <regex> +#include <sstream> +#include <algorithm> +using namespace dpp; +namespace app +{ + + /** + * @brief Creates a URL for a user's avatar + * + * @param u The user object + * @return std::string The URL to the user's avatar + */ + std::string make_avatar_url(const user &u); + + /** + * @brief Creates a URL for a guild's icon + * + * @param g The guild object + * @return std::string The URL to the guild's icon + */ + std::string make_guild_icon(const guild &g); + + /** + * @brief Updates a string by replacing placeholders with values from a map + * + * @param initial The initial string with placeholders in the format ((key)) + * @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); + + /** + * @brief Processes a command option recursively and adds values to the key-value map + * + * @param event The slash command event + * @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); + + /** + * @brief Generates a map of key-value pairs from a slash command event + * + * @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); + +} // namespace dpp + +#endif // UTILS_HPP +// utils.hpp diff --git a/bot/src/main.cpp b/bot/src/main.cpp new file mode 100644 index 0000000..500170d --- /dev/null +++ b/bot/src/main.cpp @@ -0,0 +1,28 @@ +#include <dpp/dpp.h> +#include "../include/utils.hpp" +const std::string BOT_TOKEN = getenv("BOT_TOKEN"); + +int main() { + dpp::cluster bot(BOT_TOKEN); + + bot.on_log(dpp::utility::cout_logger()); + + bot.on_slashcommand([](const dpp::slashcommand_t& event) { + // let's generate the key-value map + std::map<std::string, std::string> key_values = app::generate_key_values(event); + // let's create a string to send + std::string response = "Pong! ((userName))"; + + if (event.command.get_command_name() == "ping") { + event.reply(app::update_string(response, key_values)); + } + }); + + bot.on_ready([&bot](const dpp::ready_t& event) { + if (dpp::run_once<struct register_bot_commands>()) { + bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id)); + } + }); + + bot.start(dpp::st_wait); +} diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..2388897 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,103 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1745604263, + "owner": "cachix", + "repo": "devenv", + "rev": "36807c727e743e7a00999922e7f737a0cc4e05ac", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1733328505, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1742649964, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733477122, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..4820f3a --- /dev/null +++ b/devenv.nix @@ -0,0 +1,49 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + env = { + CXXFLAGS = "-std=c++20"; + PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig"; # Critical pour trouver OpenSSL + NIX_CFLAGS_COMPILE = "-arch arm64"; + }; + + packages = with pkgs; [ + git + gcc + cmake + clang-tools + openssl.dev # Inclure les headers de développement + zlib.dev + libopus + pkg-config + (stdenv.mkDerivation { + name = "dpp"; + version = "latest"; + src = fetchFromGitHub { + owner = "brainboxdotcc"; + repo = "DPP"; + rev = "c6bcab5b4632fe35e32e63e3bc813e9e2cd2893e"; + sha256 = "sha256-IMESnvvjATgsKCDfrRY8bPxUYpiULIPFf6SToO7GbVM="; + }; + nativeBuildInputs = [ cmake pkg-config ]; + buildInputs = [ openssl zlib libopus ]; + cmakeFlags = [ + "-DCMAKE_CXX_STANDARD=20" + "-DCMAKE_BUILD_TYPE=Release" + "-DBUILD_TEST=OFF" + "-DBUILD_EXAMPLES=OFF" + ]; + }) # Nécessaire pour détecter OpenSSL + ]; + + languages.go.enable = true; + + scripts.build.exec = '' + mkdir -p bot/build + cd bot/build + cmake .. + make -j$NIX_BUILD_CORES + ''; + + # Supprimer la configuration brew inutile dans Nix +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..116a2ad --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + +# If you're using non-OSS software, you can set allowUnfree to true. +# allowUnfree: true + +# If you're willing to use a package that's vulnerable +# permittedInsecurePackages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend |
