summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgarder500 <jeremy27.clara22@gmail.com>2025-04-26 15:56:16 +0200
committergarder500 <jeremy27.clara22@gmail.com>2025-04-26 15:56:16 +0200
commitbd508f4a3c7fc18f053696148f614851cfb0d448 (patch)
treebaa8368af27b5b313d39db90e395c332ea24f130
parentd6a9644c909b246491a8b642c08dd8103c06fa0a (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--.envrc5
-rw-r--r--.gitignore11
-rw-r--r--.vscode/settings.json91
-rw-r--r--bot/CMakeLists.txt78
-rw-r--r--bot/include/utils.cpp187
-rw-r--r--bot/include/utils.hpp62
-rw-r--r--bot/src/main.cpp28
-rw-r--r--devenv.lock103
-rw-r--r--devenv.nix49
-rw-r--r--devenv.yaml15
10 files changed, 629 insertions, 0 deletions
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..30da14f
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,5 @@
+export DIRENV_WARN_TIMEOUT=20s
+
+eval "$(devenv direnvrc)"
+
+use devenv
diff --git a/.gitignore b/.gitignore
index 259148f..ead8303 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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