From f0eff6cefabdbbb9f6a53fee0d5df5b48172f44e Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Tue, 3 Oct 2023 11:14:41 +0100 Subject: [PATCH 1/9] ci: changes to labeler, stopped labeler from changing every push. (#913) --- .github/labeler.yml | 10 +++++++--- .github/workflows/labeler.yml | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 3624bdd64f..62a6ca852e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -25,7 +25,11 @@ packaging: - '**Dockerfile' submodules: - '**.gitmodules' +- '**doxygen-awesome-css/**' # Ideally, nobody should be touching this, but it's here just in-case. github_actions: -- '**/.github/labeler.yml' -- '**/.github/dependabot.yml' -- '**/.github/workflows/**' +- '**.github/labeler.yml' +- '**.github/dependabot.yml' +- '**.github/workflows/**' +code: +- '**src/**' +- '**include/**' diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 474fe553fe..78fca54a49 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,7 @@ name: "Pull Request Labeler" on: -- pull_request_target + pull_request: + types: [opened, reopened] permissions: contents: read From 409c2c3cf395981a52786f12a20152d14a47998d Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Tue, 3 Oct 2023 13:35:10 +0100 Subject: [PATCH 2/9] docs: corrected freebsd.md and openbsd.md (#914) Co-authored-by: Jakub 'Eremiell' Marek --- docpages/building/freebsd.md | 64 ++++++++++++++++++++++++++---------- docpages/building/openbsd.md | 27 +++++---------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/docpages/building/freebsd.md b/docpages/building/freebsd.md index 53788acf3c..f8ca1a87e1 100644 --- a/docpages/building/freebsd.md +++ b/docpages/building/freebsd.md @@ -1,46 +1,74 @@ \page buildfreebsd Building on FreeBSD +\note This page assumes you are the root user. If you are not, start the package install commands with `sudo`, along with `make install`. You will need `sudo` installed if you are not the root user. + ## 1. Toolchain -This project uses CMake. Install it with `pkg install cmake` -## 2. Install External Dependencies -Your FreeBSD base system should have all the required dependencies installed by default. +Since the project uses `CMake`, you'll need to install it! If you don't have it, you can do the following: + +```bash +pkg install cmake +``` + +## 2. Install Voice Dependencies (Optional) + +If you wish to use voice support, you'll need to install opus and libsodium: + +First, you need to install opus. +```bash +cd /usr/ports/audio/opus +make && make install +``` -For voice support, additional dependencies are required +Then, you need to install libsodium. - pkg install libsodium opus pkgconf +```bash +cd /usr/ports/security/libsodium +make && make install +``` ## 3. Build Source Code - cmake -B ./build - cmake --build ./build -j8 +```bash +cmake -B ./build +cmake --build ./build -j8 +``` Replace the number after `-j` with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library. -## 4. Install globally +## 4. Install Globally - cd build; make install +```bash +cd build +make install +``` -## 5. Installation to a different directory +## 5. Installation to a Different Directory (Optional) If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`: - cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install +```bash +cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install +``` -Then once the build is complete, run `make install` to install to the location you specified. +Then once the build is complete, run `sudo make install` to install to the location you specified. -## 6. Using the library +## 6. Using the Library Once installed, you can make use of the library in standalone programs simply by including it and linking to it: - clang++ -std=c++17 -ldpp mydppbot.cpp -o dppbot +```bash +clang++ -std=c++17 -L/usr/local/lib -I/usr/local/include -ldpp bot.cpp -o dppbot +``` The important flags in this command-line are: - * `-std=c++17` - Required to compile the headers - * `-ldpp` - Link to libdpp.dylib - * `mydppbot.cpp` - Your source code - * `dppbot` - The name of the executable to make +* `-std=c++17` - Required to compile the headers +* `-L/usr/local/lib` - Required to tell the linker where libdpp is located. +* `-I/usr/local/include` - Required to tell the linker where dpp headers are located. +* `-ldpp` - Link to `libdpp.so`. +* `bot.cpp` - Your source code. +* `-o dppbot` - The name of the executable to make. \include{doc} install_prebuilt_footer.dox diff --git a/docpages/building/openbsd.md b/docpages/building/openbsd.md index 02d954949e..a289d75f13 100644 --- a/docpages/building/openbsd.md +++ b/docpages/building/openbsd.md @@ -1,6 +1,9 @@ \page buildopenbsd Building on OpenBSD +\note This page assumes you are the root user. If you are not, start the package install commands with `doas`, along with `make install`. + ## 1. Toolchain + Since the project uses `CMake`, you'll need to install it! If you don't have it, you can do the following: ```bash @@ -8,6 +11,7 @@ pkg_add cmake ``` ## 2. Install Voice Dependencies (Optional) + If you wish to use voice support, you'll need to do the following: ```bash @@ -23,13 +27,13 @@ cmake --build ./build -j8 Replace the number after `-j` with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library. -## 2. Install globally +## 4. Install Globally ```bash -cd build; sudo make install +cd build; make install ``` -## 3. Installation to a different directory +## 5. Installation to a Different Directory If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`: @@ -39,21 +43,8 @@ cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install Then once the build is complete, run `make install` to install to the location you specified. -## 4. Using the library - -Once installed to the `/usr/local` directory, you can make use of the library in standalone programs simply by including it and linking to it: - -```bash -clang++ -std=c++17 mydppbot.cpp -o dppbot -ldpp -``` - -The important flags in this command-line are: - - * `-std=c++17` - Required to compile the headers - * `-ldpp` - Link to libdpp.dylib - * `mydppbot.cpp` - Your source code - * `dppbot` - The name of the executable to make +## 6. Using the Library -\include{doc} install_prebuilt_footer.dox +Once installed to the `/usr/local` directory, you can make use of the library in CMake, without linking to a folder! You can't use this with `clang++`, nor `g++`, as OpenBSD seems to be broken on this end, so your only option from here is to use CMake. This isn't a bad thing, as we recommend people to use CMake anyways! **Have fun!** From 0dee598cc4a913d715c52c796076d0a44ee7c7a8 Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Tue, 3 Oct 2023 13:53:39 +0100 Subject: [PATCH 3/9] docs: merged all changes to make_a_bot from #820 (#915) Co-authored-by: Jakub 'Eremiell' Marek --- docpages/make_a_bot/meson.md | 53 ++++++++++++++++++------------- docpages/make_a_bot/replit.md | 25 ++++++++------- docpages/make_a_bot/token.md | 15 +++------ docpages/make_a_bot/windows_vs.md | 23 +++++++------- 4 files changed, 62 insertions(+), 54 deletions(-) diff --git a/docpages/make_a_bot/meson.md b/docpages/make_a_bot/meson.md index db307bf7f4..29b93a1afc 100644 --- a/docpages/make_a_bot/meson.md +++ b/docpages/make_a_bot/meson.md @@ -1,37 +1,42 @@ -\page buildmeson Build a Discord Bot using Meson +\page buildmeson Build a Discord Bot Using Meson ## 1. Toolchain -Before compiling, you will need to install `meson` on your system. -To be sure that `meson` is installed, you can type the following command: +Before compiling, you will need to install `meson` on your system. To be sure that `meson` is installed, you can type the following command: - $ meson --version - 0.63.2 +```bash +meson --version +0.63.2 +``` ## 2. Create a Meson project -In an empty directory. +First, you'll need to go ahead and create an empty directory, we'll call it `meson-project`. - - your project/ +Then, run this command: -run the command +```bash +meson init -l cpp +``` - $ meson init -l cpp +## 3. Configuring Your Meson Project -## 3. Configuring your Meson project +Add the following line after the `project()` line in your `meson.build` file. -add the following line after the `project()` line in your `meson.build` file. - - dpp = dependency('dpp') +```yml +dpp = dependency('dpp') +``` -add the following line in the executable section of your `meson.build` file. +Add the following line in the executable section of your `meson.build` file. - dependencies: [dpp] +```yml +dependencies: [dpp] +``` -change the `cpp_std` value in the `project()` to `c++17` +Change the `cpp_std` value in the `project()` to `c++17`. Your `meson.build` should look like this: your meson.build should look like this. -~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~yml project('discord-bot', 'cpp', version : '0.1', default_options : ['warning_level=3', @@ -39,19 +44,23 @@ project('discord-bot', 'cpp', dpp = dependency('dpp') - exe = executable('discord', 'discord_bot.cpp', install : true, dependencies: [dpp]) test('basic', exe) - ~~~~~~~~~~~~~~ Meson automatically generates a cpp for your project. And a test suite. ## 4. Building -To build a meson project run +To build a Meson project, run the following: + +```bash +meson setup builddir +meson compile -C builddir +``` + +Now, your Meson project should be all setup! - $ meson setup builddir - $ meson compile -C builddir \ No newline at end of file +**Have fun!** diff --git a/docpages/make_a_bot/replit.md b/docpages/make_a_bot/replit.md index e3eae1d7d0..feb0e7ea82 100644 --- a/docpages/make_a_bot/replit.md +++ b/docpages/make_a_bot/replit.md @@ -1,27 +1,29 @@ -\page building-a-cpp-discord-bot-in-repl Creating a Discord bot in Replit +\page building-a-cpp-discord-bot-in-repl Creating a Discord Bot in Replit -@note There is a premade repl, ready for use which was built using the steps above. If you wish to use this repl simply [visit this github repository](https://github.com/alanlichen/dpp-on-repl) and click the "Run on Replit" button. Then, follow the steps in the README file. +\warning Be aware, Replit changes frequently, meaning DPP may not always work. It is out of our control and you simply have to hope that they change it again. If this is an inconvenience, we recommend you obtain some affordable non-free hosting. -To build a D++ bot in a Replit instance, follow these steps. These steps are slightly more convoluted than installing D++ into a standard container as we don't have access to root in the conventional way or write access to any files outside of our home directory in a repl. This guide sidesteps the issue by locally extracting a libdpp deb file installer, and referencing the local dependencies from the command-line. +\note There is a premade repl, ready for use, which was built using the steps below. If you wish to use this repl simply [visit this GitHub repository](https://github.com/alanlichen/dpp-on-repl) and click the "Run on Replit" button. Then, follow the steps in the README file. -1. Use wget, or the upload button, to get the precompiled x64 release into your repl as a file, e.g. `wget -O libdpp.deb https://dl.dpp.dev/latest` +To build a D++ bot in a Replit instance, follow these steps. These steps are slightly more convoluted than installing D++ into a standard container as we don't have access to root in the conventional way or write access to any files outside of our home directory in a repl. This guide sidesteps the issue by locally extracting a `libdpp` deb file installer, and referencing the local dependencies from the command-line. + +1. Use wget, or the upload button, to get the precompiled x64 release into your repl as a file, e.g. `wget -O libdpp.deb https://dl.dpp.dev/latest`, 2. Extract this deb file using `dpkg`: -``` +```bash dpkg -x libdpp.deb . ``` 3. Compile your bot, note that you should be sure to include the `pthread` library explicitly and reference the extracted dpp installation you just put into the repl: -``` +```bash g++ -o bot main.cpp -ldpp -lpthread -L./usr/lib -I./usr/include -std=c++17 ``` 4. Run your bot! Note that you will need to set `LD_PRELOAD` to reference `libdpp.so` as it will be located in `$HOME` and not `/usr/lib`: -``` +```bash LD_PRELOAD=./usr/lib/libdpp.so ./bot ``` Now that your bot is running, you have to keep it online. Replit automatically puts repls to sleep after some time, so you will need to ping a webserver. Unfortunately, Replit is sometimes limiting, and this is one of the only free workarounds to this issue. 1. Start a http server. This can be through any webserver, but as a simple solution, use python's built in http.server: -``` +```bash python3 -m http.server ``` 2. Create an `index.html` file with anything inside it for the server to serve. @@ -29,14 +31,15 @@ python3 -m http.server 4. After verifying your account, click "Add New Monitor". + For Monitor Type, select "HTTP(s)" + In Friendly Name, put the name of your bot -+ For your url, copy the url of the new website that repl is serving for you ++ For your URL, copy the URL of the new website that repl is serving for you + Select any alert contacts you want, then click "Create Monitor" + Here is an example of a possible uptimerobot configuration: \image html uptimerobot.png ## Troubleshooting -If the bot fails to start and instead you receive an error message about being banned from the Discord API, there is little to be done about this. These bans are temporary but because Replit is a shared platform, you share an IP address with many thousands of bots, some abusive and some badly written. This will happen often and is outside of the control of yourself and us. However, you can try to migitate this by typing `kill 1` in the shell. This is not guaranteed to work, and you might need to try it a few times. If it still does not work, then we recommend instead you obtain some affordable non-free hosting instead. +- If the bot fails to start and instead you receive an error message about being banned from the Discord API, there is little to be done about this. These bans are temporary but because Replit is a shared platform, you share an IP address with many thousands of bots, some abusive and some badly written. This will happen often and is outside of the control of yourself and us. However, you can try to mitigate this by typing `kill 1` in the shell. This is not guaranteed to work, and you might need to try it a few times. If it still does not work, then we recommend you obtain some affordable non-free hosting instead. -If your bot continues to fall asleep even though you have a server, we advise you to double check that no errors are happening, and if the server is being pinged. If that still does not work, we again recommend you to obtain some affordable non-free hosting. +- If your bot continues to fall asleep even though you have a server, we advise you to double check that no errors are happening, and if the server is being pinged. If that still does not work then, again, we recommend you obtain some affordable non-free hosting. diff --git a/docpages/make_a_bot/token.md b/docpages/make_a_bot/token.md index 9f60f408d6..f25912f5f1 100644 --- a/docpages/make_a_bot/token.md +++ b/docpages/make_a_bot/token.md @@ -1,8 +1,8 @@ \page creating-a-bot-application Creating a Bot Token -Before you start coding, you need to create and register your bot in the Discord developer portal. You can then add this bot to your Discord-server. +Before you start coding, you need to create and register your bot in the Discord developer portal. You can then add this bot to your Discord server. -## Creating a new bot +## Creating a New Bot To create a new application, take the steps as follows: @@ -11,35 +11,30 @@ To create a new application, take the steps as follows: \image html create_application_confirm_popup.png In this example we named it "D++ Test Bot". 3. Move on by click the "Bot" tab in the left-hand side of the screen. Now click the "Add Bot" button on the right and confirm that you want to add the bot to your application. - \image html create_application_add_bot.png - On the resulting screen, you’ll note a page with information regarding your new bot. You can edit your bot name, description, and avatar here if you want to. If you wish to read the message content from messages, you need to enable the message content intent in the "Privileged Gateway Intents" section. - \image html create_application_bot_overview.png - In this panel, you can get your bot token by clicking "Reset Token". A bot token looks like this: `OTAyOTMxODU1NTU1MzE3ODUw.YXlm0g.9oYCt-XHXVH_z9qAytzmVRzKWTg` \warning **Do not share this token** with anybody! If you ever somehow compromise your current bot token or see your bot in danger, you can regenerate the token in the panel. -## Adding the bot to your server +## Adding the Bot to Your Server Once you've created your bot in the discord developer portal, you may wonder: + > Where is my bot now, I can't see him on my server?! That's because you've created a bot application, but it's not on any server right now. So, to invite the bot to your server, you must create an invitation URL. -1. go again into the [Applications page](https://discord.com/developers/applications) and click on your bot. +1. Go again into the [Applications page](https://discord.com/developers/applications) and click on your bot. 2. Go to the "OAuth2" tab and click on the subpage "URL Generator". \image html create_application_navigate_to_url_generator.png 3. Select the `bot` scope. If your bot uses slash commands, also select `applications.commands`. You can read more about scopes and which you need for your application [here](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes). 4. Choose the permissions required for your bot to function in the "Bot Permissions" section. 5. Copy and paste the resulting URL in your browser. Choose a server to invite the bot to, and click "Authorize". - \note For bots with elevated permissions, Discord enforces two-factor authentication on the bot owner's account when added to servers that have server-wide 2FA enabled. ## Troubleshooting - Stuck? You can find us on the [official discord server](https://discord.gg/dpp) - ask away! We don't bite! - diff --git a/docpages/make_a_bot/windows_vs.md b/docpages/make_a_bot/windows_vs.md index af228badda..92c6bc7759 100644 --- a/docpages/make_a_bot/windows_vs.md +++ b/docpages/make_a_bot/windows_vs.md @@ -1,20 +1,21 @@ -\page build-a-discord-bot-windows-visual-studio Building a discord bot in Windows using Visual Studio +\page build-a-discord-bot-windows-visual-studio Building a Discord Bot on Windows Using Visual Studio To create a basic bot using **Visual Studio 2019** or **Visual Studio 2022**, follow the steps below to create a *working skeleton project you can build upon*. +If you prefer a video tutorial, you can watch [this video](https://youtu.be/JGqaQ9nH5sk) to see how to run your bot, otherwise, keep reading! + 1. Make sure you have Visual Studio 2019 or 2022. Community, Professional or Enterprise work fine. These instructions are not for Visual Studio Code. You can [download the correct version here](https://visualstudio.microsoft.com/downloads/). Note that older versions of Visual Studio will not work as they do not support enough of the C++17 standard. 2. Clone the [template project](https://github.com/brainboxdotcc/windows-bot-template/). Be sure to clone the entire project and not just copy and paste the `.cpp` file. -3. Double click on the `MyBot.sln` file in the folder you just cloned - \image html vsproj_1.png -4. Add your bot token (see \ref creating-a-bot-application) and guild ID to the example program - \image html vsproj_2.png -5. Click "Local windows debugger" to compile and run your bot! - \image html vsproj_3.png +3. Double click on the `MyBot.sln` file in the folder you just cloned: +\image html vsproj_1.png +4. Add your bot token (see \ref creating-a-bot-application) and guild ID to the example program: +\image html vsproj_2.png +5. Click "Local Windows debugger" to compile and run your bot! +\image html vsproj_3.png 6. Observe the build output. There may be warnings, but so long as the build output ends with "1 succeeded" then the process has worked. You may now run your bot! - \image html vsproj_14.png +\image html vsproj_14.png ## Troubleshooting -- If you get an error that a dll is missing (e.g. `dpp.dll` or `opus.dll`) when starting your bot, then simply copy all dlls from the **bin** directory of where you extracted the DPP zip file to, into the same directory where your bot's executable is. You only need to do this once. There should be several of these dll files: `dpp.dll`, `zlib.dll`, `openssl.dll` and `libcrypto.dll` (or similarly named SSL related files), `libsodium.dll` and `opus.dll`. Note the template project does this for you, so you should never encounter this issue. -- Stuck? You can find us on the [official discord server](https://discord.gg/dpp) - ask away! We don't bite! - +- If you get an error that a DLL is missing (e.g. `dpp.dll` or `opus.dll`) when starting your bot, then simply copy all DLLs from the **bin** directory of where you cloned the D++ repository to, into the same directory where your bot's executable is. You only need to do this once. There should be several of these DLL files: `dpp.dll`, `zlib.dll`, `openssl.dll` and `libcrypto.dll` (or similarly named SSL related files), `libsodium.dll` and `opus.dll`. Note the template project does this for you, so you should never encounter this issue. +- Stuck? You can find us on the [official Discord server](https://discord.gg/dpp) - ask away! We don't bite! From 2079cceb80dccd623bb91e91264964a557c13b56 Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Wed, 4 Oct 2023 11:03:03 +0100 Subject: [PATCH 4/9] ci: fixed an issue with labeler failing (#917) --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 78fca54a49..6413f80436 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,6 @@ name: "Pull Request Labeler" on: - pull_request: + pull_request_target: types: [opened, reopened] permissions: From dba877cc8b4ac7b049688d78c228956cea6eb216 Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Wed, 4 Oct 2023 11:08:50 +0100 Subject: [PATCH 5/9] docs: embedded video into windows_vs.md (#916) --- docpages/make_a_bot/windows_vs.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docpages/make_a_bot/windows_vs.md b/docpages/make_a_bot/windows_vs.md index 92c6bc7759..6726ab7cec 100644 --- a/docpages/make_a_bot/windows_vs.md +++ b/docpages/make_a_bot/windows_vs.md @@ -2,7 +2,17 @@ To create a basic bot using **Visual Studio 2019** or **Visual Studio 2022**, follow the steps below to create a *working skeleton project you can build upon*. -If you prefer a video tutorial, you can watch [this video](https://youtu.be/JGqaQ9nH5sk) to see how to run your bot, otherwise, keep reading! +If you prefer a video tutorial, you can watch the video below! Otherwise, scroll past and keep reading! + +## Video Tutorial + +\htmlonly + + + +\endhtmlonly + +## Text Tutorial 1. Make sure you have Visual Studio 2019 or 2022. Community, Professional or Enterprise work fine. These instructions are not for Visual Studio Code. You can [download the correct version here](https://visualstudio.microsoft.com/downloads/). Note that older versions of Visual Studio will not work as they do not support enough of the C++17 standard. 2. Clone the [template project](https://github.com/brainboxdotcc/windows-bot-template/). Be sure to clone the entire project and not just copy and paste the `.cpp` file. From 3d293bae147caa4451ca514024e58ea51073c43b Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Wed, 4 Oct 2023 11:24:58 +0100 Subject: [PATCH 6/9] feat: added an error for caps in command_option. (#918) --- src/dpp/slashcommand.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dpp/slashcommand.cpp b/src/dpp/slashcommand.cpp index 336e43e831..9832eb28f4 100644 --- a/src/dpp/slashcommand.cpp +++ b/src/dpp/slashcommand.cpp @@ -333,6 +333,9 @@ command_option_choice &command_option_choice::fill_from_json(nlohmann::json *j) command_option::command_option(command_option_type t, const std::string &n, const std::string &d, bool r) : type(t), name(n), description(d), required(r), autocomplete(false) { + if (std::any_of(n.begin(), n.end(), [](unsigned char c){ return std::isupper(c); })) { + throw dpp::logic_exception("Command options can not contain capital letters in the name of the option."); + } } command_option& command_option::add_choice(const command_option_choice &o) From fa4fae314d1f6f6c5d37f879e8dbb840452e3f86 Mon Sep 17 00:00:00 2001 From: "Craig Edwards (Brain)" Date: Wed, 4 Oct 2023 11:25:13 +0100 Subject: [PATCH 7/9] ci: cancel concurrent test docs examples (#919) --- .github/workflows/test-docs-examples.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-docs-examples.yml b/.github/workflows/test-docs-examples.yml index 042e08d584..3a4d0a6688 100644 --- a/.github/workflows/test-docs-examples.yml +++ b/.github/workflows/test-docs-examples.yml @@ -19,7 +19,10 @@ jobs: test_docs_examples: name: Test build examples runs-on: ubuntu-22.04 - + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + steps: - name: Harden Runner uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1 From 47604525672a1d83aede223f35efcb494844b88a Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Wed, 4 Oct 2023 15:59:48 +0100 Subject: [PATCH 8/9] style: removed blank lines in dtemplate (#920) --- src/dpp/dtemplate.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dpp/dtemplate.cpp b/src/dpp/dtemplate.cpp index 0d9cacfd78..ebbb3eb7b5 100644 --- a/src/dpp/dtemplate.cpp +++ b/src/dpp/dtemplate.cpp @@ -23,8 +23,6 @@ #include #include - - namespace dpp { using json = nlohmann::json; @@ -33,7 +31,6 @@ dtemplate::dtemplate() : code(""), name(""), description(""), usage_count(0), cr { } - dtemplate& dtemplate::fill_from_json(nlohmann::json* j) { code = string_not_null(j, "code"); name = string_not_null(j, "name"); From 7bc16193886a848152205a923be1728530321686 Mon Sep 17 00:00:00 2001 From: "Craig Edwards (Brain)" Date: Wed, 4 Oct 2023 18:14:59 +0100 Subject: [PATCH 9/9] feat: improved human readable error messages to default logger, with unit test (#921) --- .vscode/settings.json | 3 +- include/dpp/restresults.h | 8 +++ src/dpp/cluster/confirmation.cpp | 74 +++++++++++++++++++++---- src/dpp/utility.cpp | 4 +- src/unittest/test.cpp | 93 ++++++++++++++++++++++++++++++++ src/unittest/test.h | 1 + 6 files changed, 171 insertions(+), 12 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f00d7bf8ee..21ab674e7d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -79,6 +79,7 @@ "coroutine": "cpp", "numbers": "cpp", "semaphore": "cpp", - "stop_token": "cpp" + "stop_token": "cpp", + "charconv": "cpp" } } \ No newline at end of file diff --git a/include/dpp/restresults.h b/include/dpp/restresults.h index 0691b41869..d6ad8bfa57 100644 --- a/include/dpp/restresults.h +++ b/include/dpp/restresults.h @@ -216,6 +216,10 @@ struct DPP_EXPORT error_detail { * @brief Error reason (full message) */ std::string reason; + /** + * @brief Object field index + */ + int index = 0; }; /** @@ -235,6 +239,10 @@ struct DPP_EXPORT error_info { * @brief Field specific error descriptions */ std::vector errors; + /** + * @brief Human readable error message constructed from the above + */ + std::string human_readable; }; /** diff --git a/src/dpp/cluster/confirmation.cpp b/src/dpp/cluster/confirmation.cpp index b2744f8d8f..41cc6915a1 100644 --- a/src/dpp/cluster/confirmation.cpp +++ b/src/dpp/cluster/confirmation.cpp @@ -82,34 +82,55 @@ error_info confirmation_callback_t::get_error() const { json& errors = j["errors"]; for (auto obj = errors.begin(); obj != errors.end(); ++obj) { - if (obj->find("0") != obj->end()) { + /* Arrays in the error report are numerically indexed with a number in a string. Ugh. */ + if (isdigit(*(obj.key().c_str()))) { /* An array of error messages */ + int array_index = std::atoll(obj.key().c_str()); for (auto index = obj->begin(); index != obj->end(); ++index) { if (index->find("_errors") != index->end()) { + /* A single object where one or more fields generated an error */ for (auto errordetails = (*index)["_errors"].begin(); errordetails != (*index)["_errors"].end(); ++errordetails) { error_detail detail; detail.code = (*errordetails)["code"].get(); detail.reason = (*errordetails)["message"].get(); detail.object.clear(); detail.field = obj.key(); + detail.index = array_index; e.errors.emplace_back(detail); } } else { + /* An object where one or more fields within it generated an error, e.g. slash command */ for (auto fields = index->begin(); fields != index->end(); ++fields) { - for (auto errordetails = (*fields)["_errors"].begin(); errordetails != (*fields)["_errors"].end(); ++errordetails) { - error_detail detail; - detail.code = (*errordetails)["code"].get(); - detail.reason = (*errordetails)["message"].get(); - detail.field = fields.key(); - detail.object = obj.key(); - e.errors.emplace_back(detail); + if (fields->find("_errors") != fields->end()) { + for (auto errordetails = (*fields)["_errors"].begin(); errordetails != (*fields)["_errors"].end(); ++errordetails) { + error_detail detail; + detail.code = (*errordetails)["code"].get(); + detail.reason = (*errordetails)["message"].get(); + detail.field = fields.key(); + detail.object = obj.key(); + detail.index = array_index; + e.errors.emplace_back(detail); + } + } else { + /* An array of objects where one or more generated an error, e.g. slash command bulk registration */ + for (auto fields2 = fields->begin(); fields2 != fields->end(); ++fields2) { + for (auto errordetails = (*fields2)["_errors"].begin(); errordetails != (*fields2)["_errors"].end(); ++errordetails) { + error_detail detail; + detail.code = (*errordetails)["code"].get(); + detail.reason = (*errordetails)["message"].get(); + detail.field = index.key() + "[" + fields.key() + "]." + fields2.key(); + detail.object = obj.key(); + detail.index = array_index; + e.errors.emplace_back(detail); + } + } } } } } } else if (obj->find("_errors") != obj->end()) { - /* An object of error messages */ + /* An object of error messages (rare) */ e.errors.reserve((*obj)["_errors"].size()); for (auto errordetails = (*obj)["_errors"].begin(); errordetails != (*obj)["_errors"].end(); ++errordetails) { error_detail detail; @@ -117,8 +138,43 @@ error_info confirmation_callback_t::get_error() const { detail.reason = (*errordetails)["message"].get(); detail.object.clear(); detail.field = obj.key(); + detail.index = 0; e.errors.emplace_back(detail); } + } else { + /* An object that has a subobject with errors */ + for (auto index = obj->begin(); index != obj->end(); ++index) { + int array_index = std::atoll(index.key().c_str()); + for (auto index2 = index->begin(); index2 != index->end(); ++index2) { + if (index2->find("_errors") != index2->end()) { + /* A single object where one or more fields generated an error */ + for (auto errordetails = (*index2)["_errors"].begin(); errordetails != (*index2)["_errors"].end(); ++errordetails) { + error_detail detail; + detail.code = (*errordetails)["code"].get(); + detail.reason = (*errordetails)["message"].get(); + detail.object = obj.key(); + detail.field = index2.key(); + detail.index = array_index; + e.errors.emplace_back(detail); + } + } + } + } + } + } + + e.human_readable = std::to_string(e.code) + ": " + e.message; + std::string prefix = e.errors.size() == 1 ? " " : "\n\t"; + for (const auto& error : e.errors) { + if (error.object.empty()) { + /* A singular field with an error in an unnamed object */ + e.human_readable += prefix + "- " + error.field + ": " + error.reason + " (" + error.code + ")"; + } else if (isdigit(*(error.object.c_str()))) { + /* An unnamed array of objects where one or more generated an error, e.g. slash command bulk registration */ + e.human_readable += prefix + "- [" + error.object + "]." + error.field + ": " + error.reason + " (" + error.code + ")"; + } else { + /* A named array of objects whre a field in the object has an error */ + e.human_readable += prefix + "- " + error.object + "[" + std::to_string(error.index) + "]." + error.field + ": " + error.reason + " (" + error.code + ")"; } } diff --git a/src/dpp/utility.cpp b/src/dpp/utility.cpp index 3c526df04c..1ca4751ca3 100644 --- a/src/dpp/utility.cpp +++ b/src/dpp/utility.cpp @@ -499,10 +499,10 @@ namespace dpp { return [](const dpp::confirmation_callback_t& detail) { if (detail.is_error()) { if (detail.bot) { + error_info e = detail.get_error(); detail.bot->log( dpp::ll_error, - "Error " + std::to_string(detail.get_error().code) + " [" + - detail.get_error().message + "] on API request, returned content was: " + detail.http_info.body + "Error: " + e.human_readable ); } } diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 8173c6842c..9c82ee7863 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -58,6 +58,99 @@ Markdown lol ||spoiler|| ~~strikethrough~~ `small *code* block`\n"; set_test(COMPARISON, u1 == u2 && u1 != u3); + set_test(ERRORS, false); + + /* Prepare a confirmation_callback_t in error state (400) */ + dpp::confirmation_callback_t error_test; + bool error_message_success = false; + error_test.http_info.status = 400; + + error_test.http_info.body = "{\ + \"message\": \"Invalid Form Body\",\ + \"code\": 50035,\ + \"errors\": {\ + \"options\": {\ + \"0\": {\ + \"name\": {\ + \"_errors\": [\ + {\ + \"code\": \"STRING_TYPE_REGEX\",\ + \"message\": \"String value did not match validation regex.\"\ + },\ + {\ + \"code\": \"APPLICATION_COMMAND_INVALID_NAME\",\ + \"message\": \"Command name is invalid\"\ + }\ + ]\ + }\ + }\ + }\ + }\ + }"; + error_message_success = (error_test.get_error().human_readable == "50035: Invalid Form Body\n\t- options[0].name: String value did not match validation regex. (STRING_TYPE_REGEX)\n\t- options[0].name: Command name is invalid (APPLICATION_COMMAND_INVALID_NAME)"); + + error_test.http_info.body = "{\ + \"message\": \"Invalid Form Body\",\ + \"code\": 50035,\ + \"errors\": {\ + \"type\": {\ + \"_errors\": [\ + {\ + \"code\": \"BASE_TYPE_CHOICES\",\ + \"message\": \"Value must be one of {4, 5, 9, 10, 11}.\"\ + }\ + ]\ + }\ + }\ + }"; + error_message_success = (error_message_success && error_test.get_error().human_readable == "50035: Invalid Form Body - type: Value must be one of {4, 5, 9, 10, 11}. (BASE_TYPE_CHOICES)"); + + error_test.http_info.body = "{\ + \"message\": \"Unknown Guild\",\ + \"code\": 10004\ + }"; + error_message_success = (error_message_success && error_test.get_error().human_readable == "10004: Unknown Guild"); + + error_test.http_info.body = "{\ + \"message\": \"Invalid Form Body\",\ + \"code\": 50035,\ + \"errors\": {\ + \"allowed_mentions\": {\ + \"_errors\": [\ + {\ + \"code\": \"MESSAGE_ALLOWED_MENTIONS_PARSE_EXCLUSIVE\",\ + \"message\": \"parse:[\\\"users\\\"] and users: [ids...] are mutually exclusive.\"\ + }\ + ]\ + }\ + }\ + }"; + error_message_success = (error_message_success && error_test.get_error().human_readable == "50035: Invalid Form Body - allowed_mentions: parse:[\"users\"] and users: [ids...] are mutually exclusive. (MESSAGE_ALLOWED_MENTIONS_PARSE_EXCLUSIVE)"); + + error_test.http_info.body = "{\ + \"message\": \"Invalid Form Body\",\ + \"code\": 50035,\ + \"errors\": {\ + \"1\": {\ + \"options\": {\ + \"1\": {\ + \"description\": {\ + \"_errors\": [\ + {\ + \"code\": \"BASE_TYPE_BAD_LENGTH\",\ + \"message\": \"Must be between 1 and 100 in length.\"\ + }\ + ]\ + }\ + }\ + }\ + }\ + }\ + }"; + error_message_success = (error_message_success && error_test.get_error().human_readable == "50035: Invalid Form Body - [1].options[1].description: Must be between 1 and 100 in length. (BASE_TYPE_BAD_LENGTH)"); + + set_test(ERRORS, error_message_success); + set_test(MD_ESC_1, false); set_test(MD_ESC_2, false); std::string escaped1 = dpp::utility::markdown_escape(test_to_escape); diff --git a/src/unittest/test.h b/src/unittest/test.h index c3b295ae45..df6a98d34a 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -144,6 +144,7 @@ DPP_TEST(CHANNELTYPES, "channel type flags", tf_online); DPP_TEST(FORUM_CREATION, "create a forum channel", tf_online); DPP_TEST(FORUM_CHANNEL_GET, "retrieve the created forum channel", tf_online); DPP_TEST(FORUM_CHANNEL_DELETE, "delete the created forum channel", tf_online); +DPP_TEST(ERRORS, "Human readable error translation", tf_offline); DPP_TEST(GUILD_BAN_CREATE, "cluster::guild_ban_add ban three deleted discord accounts", tf_online); DPP_TEST(GUILD_BAN_GET, "cluster::guild_get_ban getting one of the banned accounts", tf_online);