스킵 구현

This commit is contained in:
2023-12-20 19:45:25 +09:00
parent 6503fd167b
commit 8a987320e0
775 changed files with 162601 additions and 135 deletions

View File

@@ -0,0 +1,9 @@
\page caching-messages Caching Messages
By default D++ does not cache messages. The example program below demonstrates how to instantiate a custom cache using dpp::cache which will allow you to cache messages and query the cache for messages by ID.
This can be adjusted to cache any type derived from dpp::managed including types you define yourself.
\note This example will cache and hold onto messages forever! In a real world situation this would be bad. If you do use this, you should use the dpp::cache::remove() method periodically to remove stale items. This is left out of this example as a learning exercise to the reader. For further reading please see the documentation of dpp::cache.
\include{cpp} cache_messages.cpp

View File

@@ -0,0 +1,87 @@
\page checking-member-permissions Checking Permissions
Of course most people do just iterate over the roles of a member to check for a permission. But there's a helper method for that: dpp::guild::base_permissions gets a member's permission taking into account the server owner and role permissions.
For total member permissions including channel overwrites use either the dpp::channel::get_user_permissions or dpp::guild::permission_overwrites method. Both do the same under the hood.
They all return a dpp::permission class, which is a wrapper around a permission bitmask containing bits of the dpp::permissions enum.
Demonstration:
```cpp
dpp::channel* c = dpp::find_channel(some_channel_id);
if (c && c->get_user_permissions(member).can(dpp::p_send_messages)) {
//...
}
```
## Permissions in Interaction Events
### Default Command Permissions
Discord's intended way to manage permissions for commands is through default member permissions. You set them using dpp::slashcommand::set_default_permissions when creating or updating a command to set the default permissions a user must have to use it. However, server administrators can then overwrite these permissions by their own restrictions.
The corresponding code to create a command with default permissions would look something like this:
```cpp
dpp::slashcommand command("ban", "Ban a member", bot.me.id);
command.set_default_permissions(dpp::p_ban_members); // set permissions that are required by default here
command.add_option(dpp::command_option(dpp::co_user, "user", "The user to ban", true));
command.add_option(dpp::command_option(dpp::co_string, "reason", "The reason for banning", true));
bot.global_command_create(command);
```
### Checking Permissions on Your Own
If you want to check permissions on your own, the easiest way to check if a member has certain permissions in interaction events is by using the dpp::interaction::get_resolved_permission function. The resolved list contains associated structures for the command and does not use the cache or require any extra API calls. Note that the permissions in the resolved set are pre-calculated by Discord and taking into account channel overwrites, roles and admin privileges. So no need to loop through roles or stuff like that.
Let's imagine the following scenario:
You have a ban command and want to make sure the issuer has the ban permission.
```cpp
bot.on_interaction_create([](const dpp::interaction_create_t& event) {
dpp::permission perms = event.command.get_resolved_permission(event.command.usr.id);
if (!perms.can(dpp::p_ban_members)) {
event.reply("You don't have the required permissions to ban someone!");
return;
}
});
```
\note When using default permissions you don't necessarily need to check the issuing user for any permissions in the interaction event as Discord handles all that for you. But if you'd sleep better...
### From Parameters
The resolved set also contains the permissions of members from command parameters.
For example, let's say you want to prohibit people from banning server admins with your ban command.
Get the user ID from the parameters and pass it to the `get_resolved_permission` method:
```cpp
bot.on_interaction_create([](const dpp::interaction_create_t& event) {
dpp::snowflake user_id = std::get<dpp::snowflake>(event.get_parameter("user"));
dpp::permission perms = event.command.get_resolved_permission(user_id);
if (perms.has(dpp::p_administrator)) {
event.reply("You can't ban Admins!");
return;
}
});
```
### The Bot's Permissions
You also might want to check if the bot itself has the ban permission before processing the command further. You can access the bot's permissions in the dpp::interaction::app_permissions field.
```cpp
bot.on_interaction_create([](const dpp::interaction_create_t& event) {
if (!event.command.app_permissions.can(dpp::p_ban_members)) {
event.reply("The bot doesn't have the required permission to ban anyone!");
return;
}
});
```

View File

@@ -0,0 +1,8 @@
\page collecting-reactions Collecting Reactions
D++ comes with many useful helper classes, but amongst these is something called dpp::collector. Collector is a template which can be specialised to automatically collect objects of a predetermined type from events for a specific interval of time. Once this time period is up, or the class is otherwise signalled, a method is called with the complete set of collected objects.
In the example below, we will use it to collect all reactions on a message.
\include{cpp} collect_reactions.cpp

View File

@@ -0,0 +1,50 @@
\page cpp-eval-command-discord Making an eval Command in C++
## What is an eval command anyway?
Many times people will ask: "How do I make a command like 'eval' in C++?". For the uninitiated, an `eval` command is a command often found in interpreted languages such as JavaScript and Python, which allows the developer to pass in raw interpreter statements which are then executed within the context of the running program, without any sandboxing. Eval commands are plain **evil**, if not properly coded in.
Needless to say, this is very dangerous. If you are asking how to do this, and want to put this into your bot, we trust that you have a very good reason to do so and have considered alternatives before resorting to this. The code below is for educational purposes only and makes several assumptions:
1. This code will only operate on UNIX-like systems such as Linux (not **Darwin**).
2. It assumes you use GCC, and have `g++` installed on your server and in your `${PATH}`.
3. The program will attempt to write to the current directory.
4. No security checks will be done against the code, except for to check that it is being run by the bot's developer by snowflake id. It is entirely possible to send an `!eval exit(0);` and make the bot quit, for example, or delete files from the host operating system, if misused or misconfigured.
5. You're willing to wait a few seconds for compilation before your evaluated code runs. There isn't a way around this, as C++ is a compiled language.
To create this program you must create two files, `eval.h` and `eval.cpp`. The header file lists forward declarations of functions that you will be able to use directly within your `eval` code. As well as this the entirety of D++ will be available to the eval command via the local variable `bot`, and the entire `on_message_create` event variable via a local variable called `event`.
The evaluated code will run within its own thread, so can execute for as long as it needs (but use common sense, don't go spawning a tight `while` loop that runs forever, you'll lock a thread at 100% CPU that won't ever end!).
## Implementation details
This code operates by outputting your provided code to be evaluated into a simple boilerplate program which can be compiled to a shared object library (`.so`) file. This `.so` file is then compiled with `g++`, using the `-shared` and `-fPIC` flags. If the program can be successfully compiled, it is then loaded using `dlopen()`, and the symbol `so_exec()` searched for within it, and called. This `so_exec()` function will contain the body of the code given to the eval command. Once this has been called and it has returned, the `dlclose()` function is called to unload the library, and finally any temporary files (such as the `.so` file and its corresponding `.cpp` file) are cleaned up. Docker is definitely recommended if you code on Windows/macOS, because Docker desktop still uses a Linux VM, so your code can easily use the `.so` file and your code runs the same on your VPS (if it also uses Linux distro).
## Source code
\warning If you manage to get your system, network, or anything else harmed by use or misuse of this code, we are not responsible. Don't say we didn't warn you! Find another way to solve your problem!
### eval.h
Remember that `eval.h` contains forward-declarations of any functions you want to expose to the eval command. It is included both by the bot itself, and by any shared object files compiled for evaluation.
\include{cpp} eval.h
### eval.cpp
This is the main body of the example program.
\include{cpp} eval.cpp
## Compilation
To compile this program you must link against `libdl`. It is also critically important to include the `-rdynamic` flag. For example:
```
g++ -std=c++17 -rdynamic -oeval eval.cpp -ldpp -ldl
```
## Example usage
\image html eval_example.png

View File

@@ -0,0 +1,5 @@
\page making_a_http_request Making Arbitrary HTTP Requests Using D++
If you wish to make arbitrary HTTP(S) requests to websites and APIs, e.g. to update statistics on bot lists, you can use code similar to the code below. You may pass any arbitrary POST data:
\include{cpp} http_request.cpp

View File

@@ -0,0 +1,35 @@
\page making_threads Creating and Interacting with Threads
A new feature added to Discord recently is `Threads`, these allow you to break off a message into a different "channel", without creating a whole new channel. There are also other types of "thread channels", one example being a `forums channel`. This type of channel only contains threads, meaning you can't send messages in it so if you want to make one of them, be careful about trying to send a message in it!
In this tutorial, we'll be going through:
- How to create a thread.
- How to loop through all the active threads in a server (and sending a message in one).
- How to lock a thread (editing threads).
First, let's go through creating a thread.
\include{cpp} making_threads1.cpp
If all went well, you'll see that the bot has successfully created a thread!
\image html creating_thread.png
Now, let's cover looping through all the threads in a server. For this demonstration, we'll be picking the first thread we find in the list and sending a message in it.
\include{cpp} making_threads2.cpp
After that, you'll be able to see your bot send a message in your thread!
\image html creating_thread_2.png
Those of you who are familar with sending messages in regular channels may have also noticed that sending messages to threads is the same as sending a general message. This is because threads are basically channels with a couple more features!
Now, we're going to cover how to lock a thread! With this, you'll also learn how to edit threads in general, meaning you can go forward and learn how to change even more stuff about threads, as much as your heart desires!
\include{cpp} making_threads3.cpp
Once you've ran that, you'll see that you were successfully able to lock a thread!
\image html creating_thread_3.png

View File

@@ -0,0 +1,28 @@
\page setting_status Setting the Bot's Status
A bot status is pretty cool, and it'd be cooler if you knew how to do it! This tutorial will cover how to set the bot status to say `Playing games!`, as well as covering how to set the status to the amount of guilds every two minutes.
\note dpp::get_guild_cache requires the bot to have the guild cache enabled, if your bot has this disabled then you can't use that. Instead, you should look to use dpp::cluster::current_application_get and get the `approximate_guild_count` from dpp::application in the callback.
First, we'll cover setting the bot status to `Playing games!`.
\include{cpp} setting_status1.cpp
If all went well, your bot should now be online and say this on members list!
\image html botonlinestatus.png
If you want to make your bot show as Do Not Disturb, then you could change dpp::ps_online to dpp::ps_dnd.
You can also play around with dpp::at_game, changing it to something like dpp::at_custom or dpp::at_listening!
Now, let's cover setting the bot status to say `Playing with x guilds!` every two minutes.
\include{cpp} setting_status2.cpp
If you followed that well, your bot should now say this on members list!
\image html botonlinestatus2.png
If we then add our bot to another server and wait a bit, we'll see it updates like so:
\image html botonlinestatus3.png

View File

@@ -0,0 +1,62 @@
\page spdlog Integrating with spdlog
If you want to make your bot use spdlog, like aegis does, you can attach it to the `on_log` event. You can do this as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~cpp
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iomanip>
#include <dpp/dpp.h>
#include <fmt/format.h>
int main(int argc, char const *argv[]) {
dpp::cluster bot("token");
const std::string log_name = "mybot.log";
/* Set up spdlog logger */
std::shared_ptr<spdlog::logger> log;
spdlog::init_thread_pool(8192, 2);
std::vector<spdlog::sink_ptr> sinks;
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
auto rotating = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(log_name, 1024 * 1024 * 5, 10);
sinks.push_back(stdout_sink);
sinks.push_back(rotating);
log = std::make_shared<spdlog::async_logger>("logs", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
spdlog::register_logger(log);
log->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [%L] [th#%t]%$ : %v");
log->set_level(spdlog::level::level_enum::debug);
/* Integrate spdlog logger to D++ log events */
bot.on_log([&bot, &log](const dpp::log_t & event) {
switch (event.severity) {
case dpp::ll_trace:
log->trace("{}", event.message);
break;
case dpp::ll_debug:
log->debug("{}", event.message);
break;
case dpp::ll_info:
log->info("{}", event.message);
break;
case dpp::ll_warning:
log->warn("{}", event.message);
break;
case dpp::ll_error:
log->error("{}", event.message);
break;
case dpp::ll_critical:
default:
log->critical("{}", event.message);
break;
}
});
/* Add the rest of your events */
bot.start(dpp::st_wait);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -0,0 +1,29 @@
\page using-emojis Using Emojis
Need your bot to use an emoji? Then you've come to the right place! Here are three examples of using emojis.
\note If your bot isn't in the guild you want to use the custom emoji from, it won't work, giving you dpp::err_unknown_emoji.
First - Sending emojis. You have to use its mention, which depends on the type. If it's a default emoji, you use the corresponding character. So, for example, if you wanted to send a nerd emoji, you would use the nerd unicode character. Now, custom emoji. There are two types: static and animated. Their mentions are `<:[name]:[id]>` and `<a:[name]:[id]>`, where `[name]` means the emoji name and `[id]` is for its ID. When you send such mention, it automatically gets converted into your emoji. Here's an example of sending emojis:
\include{cpp} using_emojis1.cpp
Now, our bot will send our epic emojis!
\image html using_emojis1.png
Second - Reacting to messages. Sometimes there's something so interesting in the chat that we want to react to it. While we see the emoji we react with, for bots, it's some plain text. There are different formats for different kinds of emoji when reacting too. For unicode, it's simply its character, like when sending. For custom ones it's either `[name]:[id]` (if static) or `a:[name]:[id]` (if animated). Let's show our bot's honest reactions!
\include{cpp} using_emojis2.cpp
Yay, our bot has emotions now!
\image html using_emojis2.png
Finally, select menus. These guys are covered \ref components3 "here". They require emoji components (name, ID, animated state) to come separately. If the emoji you're using isn't animated, you don't have to specify that. If your emoji is unicode, it doesn't even have an ID, so you only put the character, since both animated state and ID are defaulted to none (false/0).
\include{cpp} using_emojis3.cpp
Yay, our context menu is now interesting!
\image html using_emojis3.png