Compare commits
81 commits
v0.17-alph
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
92c3058485 | ||
|
6f4d5606a6 | ||
|
5db977d9f6 | ||
|
73dce15840 | ||
|
e7fe154df3 | ||
|
afcab93421 | ||
|
4ab7b39980 | ||
|
6132b8df31 | ||
|
f31616cbb7 | ||
|
8dca929d80 | ||
|
c98cfa01ba | ||
|
875578d6ee | ||
|
143a80cba0 | ||
|
eef1e190a7 | ||
|
c205f9fa94 | ||
|
2566265d1d | ||
|
8329eea3e0 | ||
|
67506ea201 | ||
|
a68b579b16 | ||
|
5f25ab401f | ||
|
44ee2bcb08 | ||
|
5e6bf6ccb5 | ||
|
3afec81b3d | ||
|
35908dbfcd | ||
|
13d2479e49 | ||
|
dcf88f4ab0 | ||
|
4d17a43c83 | ||
|
b80b5deb3a | ||
|
e07bb7fa55 | ||
|
d637773f85 | ||
|
24f051299a | ||
|
bf4c2fae35 | ||
|
86afdb6a68 | ||
|
6b9380a221 | ||
|
17de144df4 | ||
|
10ff959aee | ||
|
375f8940a8 | ||
|
ea6a412cf8 | ||
|
5be8ee0e21 | ||
|
35103c5744 | ||
|
6b31eaa81b | ||
|
a01d400b23 | ||
|
59fb294826 | ||
|
c4db5815b9 | ||
|
80e6564a3d | ||
|
aca3591c39 | ||
|
cc19d7c677 | ||
|
0d00969c61 | ||
|
b611a3df9c | ||
|
9e2cfbfcd4 | ||
|
939d91e4a4 | ||
|
493a5f6b53 | ||
|
aed3c7630c | ||
|
fe50130ae3 | ||
|
c080055aac | ||
|
c95f487278 | ||
|
58bfa3ccca | ||
|
977d1ae2f1 | ||
|
909f20628d | ||
|
bee04490ee | ||
|
2d0c07f03a | ||
|
623635ccda | ||
|
942e3c96a0 | ||
|
f2b1d70400 | ||
|
6ae1302daa | ||
|
226b1ba9b7 | ||
|
159d623a8f | ||
|
fda1e17372 | ||
|
d9574c3403 | ||
|
43635ef4ca | ||
|
c8476f8ea0 | ||
|
3f90aa5a69 | ||
|
6e9dc5480a | ||
|
729ed566e1 | ||
|
0b039cadfd | ||
|
d439c62e39 | ||
|
6318978c60 | ||
|
4ea6dbcf73 | ||
|
36d4d93de0 | ||
|
6a3e846844 | ||
|
040b876458 |
23 changed files with 884 additions and 358 deletions
|
@ -10,7 +10,7 @@ build:
|
||||||
- cp config.json.example config.json
|
- cp config.json.example config.json
|
||||||
- mkdir build
|
- mkdir build
|
||||||
- cd build
|
- cd build
|
||||||
- cmake ..
|
- cmake .. -DFORCE_SUBMODULE_CDLPP=Yes
|
||||||
- cmake .. -DCMAKE_BUILD_TYPE=Release
|
- cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||||
- make -j"$(nproc)"
|
- make -j"$(nproc)"
|
||||||
- cd ..
|
- cd ..
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
||||||
[submodule "lib/cdlpp"]
|
|
||||||
path = lib/cdlpp
|
|
||||||
url = https://gitlab.com/niansa/cdlpp.git
|
|
||||||
[submodule "lib/cdlpp-db"]
|
[submodule "lib/cdlpp-db"]
|
||||||
path = lib/cdlpp-db
|
path = lib/cdlpp-db
|
||||||
url = https://gitlab.com/niansa/cdlpp-db.git
|
url = https://gitlab.com/niansa/cdlpp-db.git
|
||||||
|
[submodule "lib/cdlpp"]
|
||||||
|
path = lib/cdlpp
|
||||||
|
url = https://gitlab.com/niansa/cdlpp.git
|
||||||
|
|
|
@ -5,37 +5,57 @@ set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
include(embed.cmake)
|
include(embed.cmake)
|
||||||
include(lib/cdlpp/use.cmake)
|
include(cdlpp.cmake)
|
||||||
include(lib/cdlpp-db/use.cmake)
|
|
||||||
|
|
||||||
cdlpp(lib/cdlpp)
|
|
||||||
cdlppdb(lib/cdlpp-db)
|
|
||||||
add_compile_definitions(COMPILER_ID="${CMAKE_CXX_COMPILER_ID}")
|
add_compile_definitions(COMPILER_ID="${CMAKE_CXX_COMPILER_ID}")
|
||||||
add_compile_definitions(COMPILER_VERSION="${CMAKE_CXX_COMPILER_VERSION}")
|
add_compile_definitions(COMPILER_VERSION="${CMAKE_CXX_COMPILER_VERSION}")
|
||||||
add_compile_definitions(COMPILER_PLATFORM="${CMAKE_CXX_PLATFORM_ID}")
|
add_compile_definitions(COMPILER_PLATFORM="${CMAKE_CXX_PLATFORM_ID}")
|
||||||
|
|
||||||
add_executable(tuxiflux
|
add_executable(tuxiflux
|
||||||
${CDLPP_SRC_FILES}
|
|
||||||
${CDLPPDB_SRC_FILES}
|
|
||||||
bot/src/cust.cpp
|
bot/src/cust.cpp
|
||||||
bot/src/help_pages.cpp
|
bot/src/help_pages.cpp
|
||||||
bot/modules/useful.cpp
|
bot/modules/useful.cpp
|
||||||
bot/modules/servermanagement.cpp
|
bot/modules/servermanagement.cpp
|
||||||
|
bot/modules/warns.cpp
|
||||||
bot/modules/misc.cpp
|
bot/modules/misc.cpp
|
||||||
bot/modules/help.cpp
|
bot/modules/help.cpp
|
||||||
bot/modules/money.cpp
|
bot/modules/money.cpp
|
||||||
bot/modules/fun.cpp
|
bot/modules/fun.cpp
|
||||||
|
bot/modules/eval.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(tuxiflux PRIVATE
|
include_directories(
|
||||||
${CDLPP_INCLUDE_DIRS}
|
|
||||||
${CDLPPDB_INCLUDE_DIRS}
|
|
||||||
bot/include/
|
bot/include/
|
||||||
|
lib/cdlpp-db/include/
|
||||||
${EMBED_OUTDIR}
|
${EMBED_OUTDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
cdlpp_libs()
|
find_package(cpuid QUIET)
|
||||||
cdlppdb_libs()
|
if (cpuid_FOUND)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
|
cpuid::cpuid)
|
||||||
|
add_compile_definitions(HAS_CPUID)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(fmt REQUIRED IMPORTED_TARGET fmt)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
|
PkgConfig::fmt cdlpp-db)
|
||||||
|
|
||||||
|
use_cdlpp(${PROJECT_NAME} lib/cdlpp)
|
||||||
|
|
||||||
|
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
|
option(ENABLE_EVAL "ENABLE_EVAL" ON)
|
||||||
|
if (${ENABLE_EVAL})
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
|
dl)
|
||||||
|
add_compile_definitions(ENABLE_EVAL)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
configure_file(config.json ./ COPYONLY)
|
configure_file(config.json ./ COPYONLY)
|
||||||
embed(help/*)
|
embed(help/*)
|
||||||
|
|
||||||
|
add_subdirectory(lib/cdlpp-db)
|
||||||
|
add_compile_definitions(WITH_CDLPPDB)
|
||||||
|
|
25
README.md
Normal file
25
README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Tuxiflux
|
||||||
|
A feature-filled free and open source Discord bot based on CDL++, Discord++ and Boost ASIO!
|
||||||
|
|
||||||
|
This bot is the first big bot written on CDL++, so in case you want to learn how to use that framework... Come on it's an open source project, just read the source code and learn!
|
||||||
|
|
||||||
|
## Default prefix
|
||||||
|
The default prefix is `t#`
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
For a list of commands, this out `help/help_1.txt`!
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
1. Install [CDL++](https://gitlab.com/niansa/cdlpp)
|
||||||
|
2. Copy `config.json.example` to `config.json` and fill it
|
||||||
|
3. Clone submodules: `git submodule update --init --depth 1 --recursive`
|
||||||
|
4. Make and chdir into build directory: `mkdir build && cd build`
|
||||||
|
5. Configure the project: `cmake ..`
|
||||||
|
6. Get everything compiled: `make -j$(nproc)`
|
||||||
|
7. Run the bot: `./tuxiflux`
|
||||||
|
8. Enjoy!
|
||||||
|
|
||||||
|
Hint: the database does not need any special setup. I can recommend [ElephantSQL](https://elephantsql.com) for simple setups.
|
||||||
|
|
||||||
|
## Living example
|
||||||
|
Here's a running instance of Tuxifan ready for daily use: [Discord Bot Invite](https://discordapp.com/api/oauth2/authorize?client_id=788310535799308288&permissions=8&scope=applications.commands%20bot)
|
|
@ -1,16 +1,25 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <cdlpp/bot.hpp>
|
||||||
|
#include <cdlpp/cdltypes-incomplete.hpp>
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include "bot.hpp"
|
|
||||||
#include "abstract/message.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
extern const std::string default_prefix;
|
extern const std::string default_prefix;
|
||||||
extern std::map<uint64_t, std::string> prefix_cache;
|
extern std::map<uint64_t, std::string> prefix_cache;
|
||||||
|
|
||||||
void in_main();
|
void in_main();
|
||||||
void on_message(CMessage msg, std::function<void (CMessage)> cb);
|
void on_message(CDL::CMessage msg, std::function<void (CDL::CMessage)> cb);
|
||||||
void on_error(const std::string& message);
|
void on_error(const std::string& message);
|
||||||
const std::string get_prefix(CChannel channel);
|
void get_prefix(CDL::CChannel channel, std::function<void (const std::string&)> cb);
|
||||||
bool is_globalchat(CChannel channel);
|
bool is_globalchat(CDL::CChannel channel);
|
||||||
|
|
||||||
|
|
||||||
|
constexpr uint32_t color_ok = 0x00ff00,
|
||||||
|
color_err = 0xff0000,
|
||||||
|
color_warn = 0xff6600;
|
||||||
|
|
||||||
|
inline nlohmann::json simpleEmbed(const std::string& msg, uint32_t color = color_ok) {
|
||||||
|
return {{"description", msg}, {"color", color}};
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ namespace db_templates {
|
||||||
enum type {
|
enum type {
|
||||||
guild,
|
guild,
|
||||||
user,
|
user,
|
||||||
_end, // Must be last one before _default
|
member,
|
||||||
|
_end [[maybe_unused]], // Must be last one before _default
|
||||||
_default = guild // Must be last one
|
_default = guild // Must be last one
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "cust.hpp"
|
||||||
|
|
||||||
#define NO_SUCH_USER ":warning: I couldn't find the user you've requested. It may not have been cached yet.\n:information_source: Try mentioning the user instead"
|
#define NO_SUCH_USER simpleEmbed(":warning: I couldn't find the user you've requested. It may not have been cached yet.\n:information_source: Try mentioning the user instead", color_err)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "bot.hpp"
|
#include <cdlpp/bot.hpp>
|
||||||
#include "abstract/channel.hpp"
|
#include <cdlpp/cdltypes-incomplete.hpp>
|
||||||
|
|
||||||
void send_badusage(CChannel channel, const std::string& command);
|
void send_badusage(CDL::CChannel channel, const std::string& command);
|
||||||
|
|
83
bot/modules/eval.cpp
Normal file
83
bot/modules/eval.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#ifdef ENABLE_EVAL
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
|
#include <boost/process.hpp>
|
||||||
|
#include <dlhandle.hpp>
|
||||||
|
#include "permassert.hpp"
|
||||||
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Eval {
|
||||||
|
static void eval(CMessage msg, CChannel channel, cmdargs& args) {
|
||||||
|
std::error_code ec;
|
||||||
|
// Only bot owner may use this
|
||||||
|
if (msg->author->id != env.settings["owner"]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check args
|
||||||
|
if (args.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate code
|
||||||
|
filesystem::remove("./codef.cpp", ec);
|
||||||
|
ofstream codef("./codef.cpp");
|
||||||
|
codef << "#include <cdlpp/cdltypes.hpp>\n"
|
||||||
|
"#include <dlhandle.hpp>\n"
|
||||||
|
"extern \"C\"\n"
|
||||||
|
"void eval_main(CDL::CMessage msg, CDL::CChannel channel) {\n"
|
||||||
|
"using namespace CDL;"
|
||||||
|
<< args[0] <<
|
||||||
|
"}";
|
||||||
|
codef.close();
|
||||||
|
// Compile code
|
||||||
|
channel->start_typing([=] (const bool) {
|
||||||
|
string libnme = "./codef_"+to_string(msg->id)+".so";
|
||||||
|
boost::process::async_system(*env.aioc, [=] (boost::system::error_code, int code) {
|
||||||
|
std::error_code ec;
|
||||||
|
filesystem::remove("./codef.cpp", ec);
|
||||||
|
// Check compiler result
|
||||||
|
if (code != 0) {
|
||||||
|
channel->send(":warning: Compilation failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Run code
|
||||||
|
try {
|
||||||
|
auto evalo = new Dlhandle(libnme);
|
||||||
|
auto fnc = evalo->get<void(CMessage, CChannel)>("eval_main");
|
||||||
|
remove(libnme.c_str()); // Yep we can safely delete the file; it's still in memory
|
||||||
|
if (not fnc) {
|
||||||
|
channel->send(":warning: Failed to find main function");
|
||||||
|
delete evalo; // Delete evalo directly
|
||||||
|
} else {
|
||||||
|
fnc(msg, channel);
|
||||||
|
// Delete evalo after a minute
|
||||||
|
/*auto t = new boost::asio::deadline_timer(*env.aioc, boost::posix_time::minutes(1));
|
||||||
|
t->async_wait([=] (const boost::system::error_code&) {
|
||||||
|
delete t;
|
||||||
|
delete evalo
|
||||||
|
});*/ // We could do this to not have memory leaks but I do not expect the owner to execute that many expressions anyways
|
||||||
|
}
|
||||||
|
} catch (Dlhandle::Exception&) {
|
||||||
|
channel->send(":warning: Loading failed");
|
||||||
|
}
|
||||||
|
}, "gcc -std=c++17 ./codef.cpp -shared -fPIC -l cdlpp -o "+libnme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
Eval() {
|
||||||
|
using namespace CDL;
|
||||||
|
// Commands
|
||||||
|
register_command("eval", eval, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static Eval eval;
|
||||||
|
#endif
|
|
@ -1,13 +1,13 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include "generic_msgs.h"
|
#include "generic_msgs.h"
|
||||||
#include "help.hpp"
|
#include "help.hpp"
|
||||||
#include "cdltypes.hpp"
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
class Fun {
|
class Fun {
|
||||||
|
@ -22,25 +22,21 @@ class Fun {
|
||||||
try {
|
try {
|
||||||
min = stoi(args[0]);
|
min = stoi(args[0]);
|
||||||
max = stoi(args[1]);
|
max = stoi(args[1]);
|
||||||
} catch (std::exception&) {
|
} catch (exception&) {
|
||||||
channel->send(":warning: **Both arguments** have to be numbers in range!");
|
channel->send_embed(simpleEmbed(":warning: **Both arguments** have to be numbers in range!", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check numbers
|
// Check numbers
|
||||||
if (min > max) {
|
if (min > max) {
|
||||||
// Swap numbers if they are the wrong way around
|
// Swap numbers if they are the wrong way around
|
||||||
int tmp = max;
|
swap(min, max);
|
||||||
max = min;
|
|
||||||
min = tmp;
|
|
||||||
}
|
}
|
||||||
// Initialise generator
|
// Initialise generator
|
||||||
mt19937 gen(msg->id);
|
mt19937 gen(msg->id);
|
||||||
uniform_int_distribution<> distr(min, max);
|
uniform_int_distribution<> distr(min, max);
|
||||||
// Send result
|
// Send result
|
||||||
auto rndno = distr(gen);
|
auto rndno = distr(gen);
|
||||||
channel->send(":game_die:...", [rndno] (CMessage msg) {
|
channel->send_embed(simpleEmbed(":game_die: **"+to_string(rndno)+"**"));
|
||||||
msg->edit(":game_die: **"+to_string(rndno)+"**");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void eightball(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
static void eightball(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||||
|
@ -53,13 +49,25 @@ class Fun {
|
||||||
send_badusage(channel, "8ball");
|
send_badusage(channel, "8ball");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Get "random" answer
|
|
||||||
mt19937 gen(msg->id);
|
|
||||||
uniform_int_distribution<> distr(0, static_cast<int>(answers.size() - 1));
|
|
||||||
// Send answer
|
// Send answer
|
||||||
channel->send(":crystal_ball: "+answers[static_cast<uint64_t>(distr(gen))]);
|
channel->send_embed(simpleEmbed(":crystal_ball: "+answers[msg->id % (answers.size() - 1)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tpdne(CMessage msg, CChannel channel, CDL::cmdargs&) {
|
||||||
|
channel->send("https://thispersondoesnotexist.com/image?"+to_string(msg->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcdne(CMessage msg, CChannel channel, CDL::cmdargs&) {
|
||||||
|
channel->send("https://thiscatdoesnotexist.com/?"+to_string(msg->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trdne(CMessage msg, CChannel channel, CDL::cmdargs&) {
|
||||||
|
channel->send("https://thisrentaldoesnotexist.com/img-new/hero.jpg?"+to_string(msg->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void thdne(CMessage msg, CChannel channel, CDL::cmdargs&) {
|
||||||
|
channel->send("https://thishorsedoesnotexist.com/?"+to_string(msg->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -68,6 +76,10 @@ public:
|
||||||
// Commands
|
// Commands
|
||||||
register_command("random", random, 2);
|
register_command("random", random, 2);
|
||||||
register_command("8ball", eightball, 1);
|
register_command("8ball", eightball, 1);
|
||||||
|
register_command("notaperson", tpdne, NO_ARGS);
|
||||||
|
register_command("notacat", tcdne, NO_ARGS);
|
||||||
|
register_command("notahorse", thdne, NO_ARGS);
|
||||||
|
register_command("notarental", trdne, NO_ARGS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,128 +2,133 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "extras.hpp"
|
#include <cdlpp/extras.hpp>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
#include "help_pages.hpp"
|
#include "help_pages.hpp"
|
||||||
#include "cdltypes.hpp"
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
static json syntaxes = json::parse(help_files.find("syntaxes")->second);
|
static json syntaxes = json::parse(help_files.find("syntaxes")->second);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void send_badusage(CChannel channel, const string& command) {
|
void send_badusage(CChannel channel, const string& command) {
|
||||||
// Get prefix
|
|
||||||
auto prefix = get_prefix(channel);
|
|
||||||
// Check if any usable syntax is listed to command
|
// Check if any usable syntax is listed to command
|
||||||
if (not syntaxes.contains(command)) {
|
if (not syntaxes.contains(command)) {
|
||||||
channel->send(":warning: Incorrect command usage");
|
channel->send_embed(simpleEmbed(":warning: Incorrect command usage", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto syntax = syntaxes[command];
|
auto syntax = syntaxes[command];
|
||||||
// Check if badusage text exists
|
// Get prefix
|
||||||
if (syntax.contains("badusage_text")) {
|
get_prefix(channel, [=] (const std::string& prefix) {
|
||||||
channel->send(fmt::format(string(syntax["badusage_text"]), prefix+command, string(syntax["usage"])));
|
// Check if badusage text exists
|
||||||
}
|
if (syntax.contains("badusage_text")) {
|
||||||
// Alternatively send usage
|
channel->send_embed(simpleEmbed(fmt::format(string(syntax["badusage_text"]), prefix+command, string(syntax["usage"])), color_err));
|
||||||
else {
|
}
|
||||||
string x = fmt::format(":warning: Syntax: `{0} {1}`'", prefix+command, string(syntax["usage"]));
|
// Alternatively send usage
|
||||||
channel->send(x);
|
else {
|
||||||
}
|
string x = fmt::format(":warning: Syntax: `{0} {1}`", prefix+command, string(syntax["usage"]));
|
||||||
|
channel->send_embed(simpleEmbed(x, color_err));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Help {
|
class Help {
|
||||||
static void help(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
static void help(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||||
string help_name;
|
// Get prefix
|
||||||
string helpfile_name;
|
get_prefix(channel, [=] (const std::string& prefix) {
|
||||||
bool help_mainpage = true;
|
string help_name;
|
||||||
bool not_found = false;
|
string helpfile_name;
|
||||||
bool from_template = false;
|
bool help_mainpage = true;
|
||||||
// If no arguments given, default help page
|
bool not_found = false;
|
||||||
if (args.empty()) {
|
bool from_template = false;
|
||||||
help_name = "1";
|
// If no arguments given, default help page
|
||||||
} else {
|
if (args.empty()) {
|
||||||
help_name = args[0];
|
help_name = "1";
|
||||||
}
|
} else {
|
||||||
// The name of the file might be equal to the name of the page passed
|
help_name = args[0];
|
||||||
helpfile_name = help_name;
|
|
||||||
send_help:
|
|
||||||
// Check if help page is a main page
|
|
||||||
help_mainpage = extras::is_digits(helpfile_name);
|
|
||||||
// Find given help page
|
|
||||||
auto help_file = help_files.find("help_"+helpfile_name);
|
|
||||||
if (help_file == help_files.end()) {
|
|
||||||
// Check if notfound page exists
|
|
||||||
if (not_found) {
|
|
||||||
// Error page couldn't be found
|
|
||||||
channel->send(":warning: Error page couldn't be found");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Check if syntax is listed
|
// The name of the file might be equal to the name of the page passed
|
||||||
if (syntaxes.contains(help_name)) {
|
helpfile_name = help_name;
|
||||||
helpfile_name = "syntaxtmpl";
|
send_help:
|
||||||
from_template = true;
|
// Check if help page is a main page
|
||||||
|
help_mainpage = extras::is_digits(helpfile_name);
|
||||||
|
// Find given help page
|
||||||
|
auto help_file = help_files.find("help_"+helpfile_name);
|
||||||
|
if (help_file == help_files.end()) {
|
||||||
|
// Check if notfound page exists
|
||||||
|
if (not_found) {
|
||||||
|
// Error page couldn't be found
|
||||||
|
channel->send(":warning: Error page couldn't be found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check if syntax is listed
|
||||||
|
if (syntaxes.contains(help_name)) {
|
||||||
|
helpfile_name = "syntaxtmpl";
|
||||||
|
from_template = true;
|
||||||
|
goto send_help;
|
||||||
|
}
|
||||||
|
// Send notfound page
|
||||||
|
helpfile_name = "notfound";
|
||||||
|
not_found = true;
|
||||||
goto send_help;
|
goto send_help;
|
||||||
}
|
}
|
||||||
// Send notfound page
|
const string &help_text = help_file->second;
|
||||||
helpfile_name = "notfound";
|
// Send text
|
||||||
not_found = true;
|
json embed;
|
||||||
goto send_help;
|
{
|
||||||
}
|
// Formats in 2 different ways depending on the value of not_foud
|
||||||
const string &help_text = help_file->second;
|
if (not_found) {
|
||||||
// Send text
|
// Page not found
|
||||||
json embed;
|
string syntax_list;
|
||||||
{
|
// Build list of syntaxes string
|
||||||
// Formats in 2 different ways depending on the value of not_foud
|
if (not syntaxes.empty()) {
|
||||||
if (not_found) {
|
ostringstream syntax_list_builder;
|
||||||
// Page not found
|
syntax_list_builder << "__Command syntax pages available__\n•";
|
||||||
string syntax_list;
|
for (const auto& [name, value] : syntaxes.items()) {
|
||||||
// Build list of syntaxes string
|
if (not (value.contains("unlisted") and value["unlisted"].get<bool>())) {
|
||||||
if (not syntaxes.empty()) {
|
syntax_list_builder << " `" << string(name) << "`,";
|
||||||
ostringstream syntax_list_builder;
|
}
|
||||||
syntax_list_builder << "__Command syntax pages available__\n•";
|
|
||||||
for (const auto& [name, value] : syntaxes.items()) {
|
|
||||||
if (not (value.contains("unlisted") and value["unlisted"].get<bool>())) {
|
|
||||||
syntax_list_builder << " `" << string(name) << "`,";
|
|
||||||
}
|
}
|
||||||
|
syntax_list = syntax_list_builder.str();
|
||||||
|
syntax_list.pop_back(); // Remove trailing ','
|
||||||
}
|
}
|
||||||
syntax_list = syntax_list_builder.str();
|
// Generate list of available syntax pages
|
||||||
syntax_list.pop_back(); // Remove trailing ','
|
embed["description"] = fmt::format(help_text, help_name, syntax_list);
|
||||||
}
|
|
||||||
// Generate list of available syntax pages
|
|
||||||
embed["description"] = fmt::format(help_text, help_name, syntax_list);
|
|
||||||
} else {
|
|
||||||
// Check if templating is enabled
|
|
||||||
if (from_template) {
|
|
||||||
ostringstream examples;
|
|
||||||
auto syntax = syntaxes[help_name];
|
|
||||||
// Build examples string
|
|
||||||
if (syntax.contains("examples")) {
|
|
||||||
examples << "**Examples:**\n";
|
|
||||||
for (const auto& example : syntax["examples"].items()) {
|
|
||||||
examples << fmt::format("• `{0}{1} {2}`\n", get_prefix(channel), help_name, string(example.value()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Apply template
|
|
||||||
embed["description"] = fmt::format(help_text, help_name, string(syntax["usage"]), examples.str());
|
|
||||||
} else if (help_mainpage) {
|
|
||||||
embed["description"] = fmt::format(help_text, get_prefix(channel), extras::get_bot_invite_link(), string(env.settings["supportdiscord"]));
|
|
||||||
} else {
|
} else {
|
||||||
embed["description"] = help_text;
|
// Check if templating is enabled
|
||||||
|
if (from_template) {
|
||||||
|
ostringstream examples;
|
||||||
|
auto syntax = syntaxes[help_name];
|
||||||
|
// Build examples string
|
||||||
|
if (syntax.contains("examples")) {
|
||||||
|
examples << "**Examples:**\n";
|
||||||
|
for (const auto& example : syntax["examples"].items()) {
|
||||||
|
examples << fmt::format("• `{0}{1} {2}`\n", prefix, help_name, string(example.value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Apply template
|
||||||
|
embed["description"] = fmt::format(help_text, help_name, string(syntax["usage"]), examples.str());
|
||||||
|
} else if (help_mainpage) {
|
||||||
|
embed["description"] = fmt::format(help_text, prefix, extras::get_bot_invite_link(), string(env.settings["supportdiscord"]));
|
||||||
|
} else {
|
||||||
|
embed["description"] = help_text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The rest of the embed can differ too
|
||||||
|
if (help_mainpage) {
|
||||||
|
embed["thumbnail"]["url"] = extras::get_avatar_url(env.self);
|
||||||
|
embed["footer"]["icon_url"] = extras::get_avatar_url(msg->author);
|
||||||
|
embed["footer"]["text"] = "Use "+prefix+"help <command> to see its syntax";
|
||||||
|
embed["color"] = 0x0000ff; // Blue
|
||||||
|
} else {
|
||||||
|
embed["thumbnail"]["url"] = extras::get_avatar_url(msg->author);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The rest of the embed can differ too
|
channel->send_embed(embed);
|
||||||
if (help_mainpage) {
|
});
|
||||||
embed["thumbnail"]["url"] = extras::get_avatar_url(env.self);
|
|
||||||
embed["footer"]["icon_url"] = extras::get_avatar_url(msg->author);
|
|
||||||
embed["footer"]["text"] = "Use "+get_prefix(channel)+"help <command> to see its syntax";
|
|
||||||
embed["color"] = 0x0000ff; // Blue
|
|
||||||
} else {
|
|
||||||
embed["thumbnail"]["url"] = extras::get_avatar_url(msg->author);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channel->send_embed(embed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,16 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#ifdef HAS_CPUID
|
||||||
|
# include <libcpuid.h>
|
||||||
|
#endif
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
#include "help.hpp"
|
#include "help.hpp"
|
||||||
#include "generic_msgs.h"
|
#include "generic_msgs.h"
|
||||||
#include "permassert.hpp"
|
#include "permassert.hpp"
|
||||||
#include "cdltypes.hpp"
|
#include <cdlpp/cdltypes.hpp>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
class Misc {
|
class Misc {
|
||||||
|
@ -17,7 +20,7 @@ class Misc {
|
||||||
timer::time_point startup_time;
|
timer::time_point startup_time;
|
||||||
|
|
||||||
|
|
||||||
static void invite(CMessage , CChannel channel, CDL::cmdargs&) {
|
static void invite(CMessage, CChannel channel, CDL::cmdargs&) {
|
||||||
json embed = {
|
json embed = {
|
||||||
{"description", ":speech_balloon: You can invite me using [this]("+extras::get_bot_invite_link()+") link"},
|
{"description", ":speech_balloon: You can invite me using [this]("+extras::get_bot_invite_link()+") link"},
|
||||||
{"color", 0xffffff}
|
{"color", 0xffffff}
|
||||||
|
@ -51,18 +54,22 @@ class Misc {
|
||||||
// TODO
|
// TODO
|
||||||
};
|
};
|
||||||
// Create channel
|
// Create channel
|
||||||
Channel newchannel({});
|
using namespace Permissions;
|
||||||
{
|
BaseChannel newchannel = {
|
||||||
newchannel.name = "tuxiflux-globalchat";
|
.name = "tuxiflux-globalchat",
|
||||||
using namespace Permissions;
|
.overwrites = {
|
||||||
newchannel.overwrites[env.self->id] = {
|
{
|
||||||
env.self->id,
|
env.self->id,
|
||||||
PermissionOverwrite::member,
|
{
|
||||||
VIEW_CHANNEL | SEND_MESSAGES | MANAGE_MESSAGES,
|
.id = env.self->id,
|
||||||
0
|
.type = PermissionOverwrite::member,
|
||||||
};
|
.allow = VIEW_CHANNEL | SEND_MESSAGES | MANAGE_MESSAGES,
|
||||||
}
|
.deny = 0
|
||||||
server->create_channel(&newchannel, [channel] (CChannel newchannel) {
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
server->create_channel(newchannel, [channel] (CChannel newchannel) {
|
||||||
if (newchannel) {
|
if (newchannel) {
|
||||||
channel->send(":speech_balloon: "+newchannel->get_mention()+" was created and set up. Have fun!");
|
channel->send(":speech_balloon: "+newchannel->get_mention()+" was created and set up. Have fun!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,30 +83,51 @@ class Misc {
|
||||||
// Get Uptime in hours
|
// Get Uptime in hours
|
||||||
timer::duration uptime = timer::now() - startup_time;
|
timer::duration uptime = timer::now() - startup_time;
|
||||||
auto uptime_hours = uptime.count() / 1000000000 / 60 / 60;
|
auto uptime_hours = uptime.count() / 1000000000 / 60 / 60;
|
||||||
// Get mem usage string
|
// Get prefix
|
||||||
ostringstream mem_use;
|
get_prefix(channel, [=] (const std::string& prefix) {
|
||||||
# ifdef extras_get_mem_supported
|
# ifdef extras_get_mem_supported
|
||||||
|
// Get mem usage string
|
||||||
|
ostringstream mem_use;
|
||||||
mem_use << "Memory usage • **" << extras::get_mem::used() << "** MB / **" << extras::get_mem::total() << "** MB\n";
|
mem_use << "Memory usage • **" << extras::get_mem::used() << "** MB / **" << extras::get_mem::total() << "** MB\n";
|
||||||
# endif
|
# endif
|
||||||
// Build info text
|
# ifdef HAS_CPUID
|
||||||
ostringstream text;
|
// Get CPU data
|
||||||
text << "Online since • **" << uptime_hours << "** hours\n"
|
string cpu_name;
|
||||||
"\n" <<
|
if (cpuid_present()) {
|
||||||
mem_use.str() <<
|
cpu_raw_data_t cpuid_raw;
|
||||||
"C++ version • **" << __cplusplus << "**\n"
|
cpuid_get_raw_data(&cpuid_raw);
|
||||||
"Boost version • **" BOOST_LIB_VERSION "**\n"
|
cpu_id_t cpuid;
|
||||||
"Compiler • **" COMPILER_ID " " COMPILER_VERSION " (" COMPILER_PLATFORM ")**\n"
|
cpu_identify(&cpuid_raw, &cpuid);
|
||||||
"\n"
|
cpu_name = cpuid.brand_str;
|
||||||
"Servers • **" << cache::guild_cache.size() << "**\n"
|
} else {
|
||||||
"\n"
|
cpu_name = "Unknown";
|
||||||
"Prefix in chat • `" << get_prefix(channel) << '`';
|
}
|
||||||
// Create embed
|
# endif
|
||||||
json embed = {
|
// Build info text
|
||||||
{"description", text.str()},
|
ostringstream text;
|
||||||
{"thumbnail", {{"url", extras::get_avatar_url(env.self)}}}
|
text << "Online since • **" << uptime_hours << "** hours\n"
|
||||||
};
|
"\n" <<
|
||||||
// Send embed
|
# ifdef extras_get_mem_supported
|
||||||
channel->send_embed(embed);
|
mem_use.str() <<
|
||||||
|
# endif
|
||||||
|
# ifdef HAS_CPUID
|
||||||
|
"CPU • **" << cpu_name << "**\n"
|
||||||
|
# endif
|
||||||
|
"C++ version • **" << __cplusplus << "**\n"
|
||||||
|
"Boost version • **" BOOST_LIB_VERSION "**\n"
|
||||||
|
"Compiler • **" COMPILER_ID " " COMPILER_VERSION " (" COMPILER_PLATFORM ")**\n"
|
||||||
|
"\n"
|
||||||
|
"Servers • **" << cache::guild_cache.size() << "**\n"
|
||||||
|
"\n"
|
||||||
|
"Prefix in chat • `" << prefix << '`';
|
||||||
|
// Create embed
|
||||||
|
json embed = {
|
||||||
|
{"description", text.str()},
|
||||||
|
{"thumbnail", {{"url", extras::get_avatar_url(env.self)}}}
|
||||||
|
};
|
||||||
|
// Send embed
|
||||||
|
channel->send_embed(embed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gc_autorules(CChannel channel) {
|
static void gc_autorules(CChannel channel) {
|
||||||
|
@ -145,7 +173,7 @@ class Misc {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Set blacklisted boolean
|
// Set blacklisted boolean
|
||||||
env.db->update(to_dbid(user_id), "GC_BLACKLISTED", newval, db_templates::user);
|
env.db->update(to_dbid(user_id), "GC_BLACKLISTED", newval, nullptr, db_templates::user);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void restart(CMessage msg, CChannel channel, CDL::cmdargs&) {
|
static void restart(CMessage msg, CChannel channel, CDL::cmdargs&) {
|
||||||
|
@ -175,12 +203,12 @@ public:
|
||||||
register_command("invite", invite, NO_ARGS);
|
register_command("invite", invite, NO_ARGS);
|
||||||
register_command("setup", setup, 1);
|
register_command("setup", setup, 1);
|
||||||
register_command("gc_blacklist", gc_blacklist, 2);
|
register_command("gc_blacklist", gc_blacklist, 2);
|
||||||
|
register_command("gc_ban", gc_blacklist, 2);
|
||||||
register_command("restart", restart, NO_ARGS);
|
register_command("restart", restart, NO_ARGS);
|
||||||
register_command("reload", reload, NO_ARGS);
|
register_command("reload", reload, NO_ARGS);
|
||||||
cdl_register_nonstatic_command("about", about, NO_ARGS);
|
cdl_register_nonstatic_command("about", about, NO_ARGS);
|
||||||
// Events
|
// Events
|
||||||
intents::channel_create.push_back(gc_autorules);
|
intents::channel_create.push_back(gc_autorules);
|
||||||
//intents::channel_update.push_back(gc_autorules);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include "generic_msgs.h"
|
#include "generic_msgs.h"
|
||||||
#include "help.hpp"
|
#include "help.hpp"
|
||||||
#include "cdltypes.hpp"
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
class Money {
|
class Money {
|
||||||
# define mod_money(user_id, amount) env.db->update(to_dbid(user_id), "BALANCE = BALANCE + ("+amount+")", db_templates::user)
|
# define mod_money(user_id, cb, amount) env.db->update(to_dbid(user_id), "BALANCE = BALANCE + ("+amount+")", cb, db_templates::user)
|
||||||
# define get_money(user_id) env.db->get(to_dbid(user_id), "BALANCE", db_templates::user)
|
# define get_money(user_id, cb) env.db->get<int>(to_dbid(user_id), "BALANCE", cb, db_templates::user)
|
||||||
|
|
||||||
|
|
||||||
static void balance(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
static void balance(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||||
|
@ -29,15 +29,16 @@ class Money {
|
||||||
}
|
}
|
||||||
// Check if target user == bot
|
// Check if target user == bot
|
||||||
if (user_id == env.self->id) {
|
if (user_id == env.self->id) {
|
||||||
channel->send(":warning: Sadly, I am not allowed to have money :confused:");
|
channel->send_embed(simpleEmbed(":warning: Sadly, I am not allowed to have money :confused:", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetch::user(user_id, [channel] (CUser user) {
|
fetch::user(user_id, [channel] (CUser user) {
|
||||||
if (not user) return;
|
if (not user) return;
|
||||||
// Get values
|
// Get values
|
||||||
auto balance = get_money(user->id).as<int32_t>();
|
get_money(user->id, [=] (auto amount) {
|
||||||
// Send
|
// Send
|
||||||
channel->send("**"+user->get_full_name()+"** currently has **"+to_string(balance)+"** :dollar:");
|
channel->send_embed(simpleEmbed("**"+user->get_full_name()+"** currently has **"+to_string(amount)+"** :dollar:"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,23 +48,30 @@ class Money {
|
||||||
time(&time_raw);
|
time(&time_raw);
|
||||||
tm *time = gmtime(&time_raw);
|
tm *time = gmtime(&time_raw);
|
||||||
// Get last day command was executed
|
// Get last day command was executed
|
||||||
auto last_yday = env.db->get(to_dbid(msg->author->id), "LAST_DAILY", db_templates::user);
|
env.db->get<int>(to_dbid(msg->author->id), "LAST_DAILY", [=] (auto last_daily) {
|
||||||
// Verify that next day was reached
|
// Verify that next day was reached
|
||||||
if (last_yday.as<int>() == time->tm_yday) {
|
if (last_daily == time->tm_yday) {
|
||||||
channel->send(":warning: You can't get your daily money twice a day\n"
|
channel->send_embed(simpleEmbed(":warning: You can't get your daily money twice a day\n"
|
||||||
":information_source: Please note that I'm running in GMT :wink:");
|
":information_source: Please note that I'm running in GMT :wink:", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Update database
|
// Update database
|
||||||
int32_t daily_money = env.settings["daily_money"];
|
int32_t daily_money = env.settings["daily_money"];
|
||||||
env.db->update(to_dbid(msg->author->id), "LAST_DAILY", time->tm_yday, db_templates::user);
|
env.db->update(to_dbid(msg->author->id), "LAST_DAILY", time->tm_yday, nullptr, db_templates::user);
|
||||||
mod_money(msg->author->id, to_string(daily_money));
|
mod_money(msg->author->id, [=] (const bool error) {
|
||||||
// Get new balance
|
if (error) {
|
||||||
auto new_value = get_money(msg->author->id).as<int32_t>();
|
|
||||||
int32_t old_value = new_value - daily_money;
|
} else {
|
||||||
// Send result
|
// Get new balance
|
||||||
channel->send(":dollar: You've received your daily money!\n"
|
get_money(msg->author->id, [=] (auto new_value) {
|
||||||
"**"+to_string(old_value)+"** + **"+to_string(daily_money)+"** = **"+to_string(new_value)+"**");
|
auto old_value = new_value - daily_money;
|
||||||
|
// Send result
|
||||||
|
channel->send_embed(simpleEmbed(":dollar: You've received your daily money!\n"
|
||||||
|
"**"+to_string(old_value)+"** + **"+to_string(daily_money)+"** = **"+to_string(new_value)+"**"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, to_string(daily_money));
|
||||||
|
}, db_templates::user);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pay(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
static void pay(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||||
|
@ -81,10 +89,10 @@ class Money {
|
||||||
}
|
}
|
||||||
// Check target user
|
// Check target user
|
||||||
if (target_user_id == env.self->id) {
|
if (target_user_id == env.self->id) {
|
||||||
channel->send(":warning: Why do you want to give me money?");
|
channel->send_embed(simpleEmbed(":warning: Why do you want to give me money?", color_err));
|
||||||
return;
|
return;
|
||||||
} else if (target_user_id == msg->author->id) {
|
} else if (target_user_id == msg->author->id) {
|
||||||
channel->send(":warning: That's yourself lol");
|
channel->send_embed(simpleEmbed(":warning: That's yourself lol", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check if specified user ID actually exists and is fetchable
|
// Check if specified user ID actually exists and is fetchable
|
||||||
|
@ -96,19 +104,19 @@ class Money {
|
||||||
}
|
}
|
||||||
// Check if number is clean
|
// Check if number is clean
|
||||||
if (not extras::is_digits(amount_str)) {
|
if (not extras::is_digits(amount_str)) {
|
||||||
channel->send(":warning: The amount of money you've specified isn't valid :thinking:");
|
channel->send_embed(simpleEmbed(":warning: The amount of money you've specified isn't valid :thinking:", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Pay out
|
// Pay out
|
||||||
try {
|
mod_money(msg->author->id, [=] (const bool error) {
|
||||||
mod_money(msg->author->id, string("-")+amount_str);
|
if (error) {
|
||||||
} catch (std::exception&) {
|
channel->send_embed(simpleEmbed(":warning: I wasn't able to pay out... Are you sure you've got enough money?", color_err));
|
||||||
channel->send(":warning: I wasn't able to pay out... Are you sure you've got enough money?");
|
} else {
|
||||||
return;
|
mod_money(target_user->id, nullptr, amount_str);
|
||||||
}
|
// Report success
|
||||||
mod_money(target_user->id, amount_str);
|
channel->send_embed(simpleEmbed("You've sent **"+amount_str+"** :dollar: to **"+target_user->get_full_name()+"**"));
|
||||||
// Report success
|
}
|
||||||
channel->send("You've sent **"+amount_str+"** :dollar: to **"+target_user->get_full_name()+"**");
|
}, string("-")+amount_str);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,32 +130,29 @@ class Money {
|
||||||
auto insert_str = to_string(insert);
|
auto insert_str = to_string(insert);
|
||||||
// Check if insert is in range
|
// Check if insert is in range
|
||||||
if (not (insert != 0 and insert < 100)){
|
if (not (insert != 0 and insert < 100)){
|
||||||
channel->send(":warning: **"+insert_str+"** is not > **0** and < **100**!");
|
channel->send_embed(simpleEmbed(":warning: **"+insert_str+"** is not > **0** and < **100**!", color_err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check if user has enough money
|
// Check if user has enough money
|
||||||
if (get_money(msg->author->id).as<int32_t>() < insert) {
|
get_money(msg->author->id, [=] (auto amount) {
|
||||||
channel->send(":warning: Excuse me, but you don't have enough money to do that!");
|
if (amount < insert) {
|
||||||
return;
|
channel->send_embed(simpleEmbed(":warning: Excuse me, but you don't have enough money to do that!", color_err));
|
||||||
}
|
return;
|
||||||
// Get "random" value
|
}
|
||||||
bool lost = msg->id % 4 == 0;
|
// Get "random" value
|
||||||
if (insert > 10) {
|
bool lost = msg->id % 4 == 0;
|
||||||
lost =! lost;
|
if (insert > 10) {
|
||||||
}
|
lost =! lost;
|
||||||
// Negate insert value if lost
|
}
|
||||||
if (lost) {
|
// Do database transaction
|
||||||
insert = -insert;
|
mod_money(msg->author->id, nullptr, std::string(lost?"-":"")+insert_str);
|
||||||
insert_str.insert(0, 1, '-');
|
// Report value of won
|
||||||
}
|
if (lost) {
|
||||||
// Do database transaction
|
channel->send_embed(simpleEmbed(":thumbsdown: You've lost **"+insert_str+"** :dollar:", color_warn));
|
||||||
mod_money(msg->author->id, insert_str);
|
} else {
|
||||||
// Report value of won
|
channel->send_embed(simpleEmbed(":thumbsup: You've won **"+insert_str+"** :dollar:", color_ok));
|
||||||
if (lost) {
|
}
|
||||||
channel->send(":thumbsdown: You've lost **"+insert_str+"** :dollar:");
|
});
|
||||||
} else {
|
|
||||||
channel->send(":thumbsup: You've won **"+insert_str+"** :dollar:");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include "permassert.hpp"
|
#include "permassert.hpp"
|
||||||
#include "help.hpp"
|
#include "help.hpp"
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
#include "generic_msgs.h"
|
#include "generic_msgs.h"
|
||||||
#include "cdltypes.hpp"
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
class Basic {
|
class Basic {
|
||||||
|
@ -16,7 +16,9 @@ class Basic {
|
||||||
// Check arguments
|
// Check arguments
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
// Get prefix
|
// Get prefix
|
||||||
channel->send(":paperclips: The individual prefix for **"+server->name+"** is `"+get_prefix(channel)+'`');
|
get_prefix(channel, [=] (const std::string& prefix) {
|
||||||
|
channel->send_embed(simpleEmbed(":paperclips: The individual prefix for **"+server->name+"** is `"+prefix+'`'));
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check permissions
|
// Check permissions
|
||||||
|
@ -33,29 +35,37 @@ class Basic {
|
||||||
prefix_cache[server->id] = newprefix;
|
prefix_cache[server->id] = newprefix;
|
||||||
// Inform about success
|
// Inform about success
|
||||||
if (reset) {
|
if (reset) {
|
||||||
channel->send(":paperclips: Your individual prefix has been reset for your server!");
|
channel->send_embed(simpleEmbed(":paperclips: Your individual prefix has been reset for your server!"));
|
||||||
} else {
|
} else {
|
||||||
channel->send(":paperclips: Your individual prefix for your server was changed to **"+newprefix+"**.");
|
channel->send_embed(simpleEmbed(":paperclips: Your individual prefix for your server was changed to **"+newprefix+"**."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static string welcomedm_format(CGuild guild, CUser user, const string& message) {
|
inline static string welcomedm_format(CGuild guild, CUser user, const string& message) {
|
||||||
return fmt::format(message, user->username, guild->name);
|
return fmt::format(message, user->username, guild->name);
|
||||||
}
|
}
|
||||||
inline static string welcomedm_get(CGuild guild, CUser user) {
|
static void welcomedm_get(CGuild guild, CUser user, std::function<void (const std::string& str)> cb) {
|
||||||
return welcomedm_format(guild, user, env.db->get(to_dbid(guild->id), "WELCOMEDM", db_templates::guild).as<string>());
|
env.db->get<string>(to_dbid(guild->id), "WELCOMEDM", [=] (auto welcomedm_str) {
|
||||||
|
if (not (welcomedm_str.empty() or welcomedm_str == "none")) {
|
||||||
|
cb(welcomedm_format(guild, user, welcomedm_str));
|
||||||
|
} else {
|
||||||
|
cb("none");
|
||||||
|
}
|
||||||
|
}, db_templates::guild);
|
||||||
}
|
}
|
||||||
inline static void welcomedm_set(CGuild guild, const string& message) {
|
inline static void welcomedm_set(CGuild guild, const string& message) {
|
||||||
env.db->update(to_dbid(guild->id), "WELCOMEDM", message, db_templates::guild);
|
env.db->update(to_dbid(guild->id), "WELCOMEDM", message, nullptr, db_templates::guild);
|
||||||
}
|
}
|
||||||
inline static void welcomedm_send(CMember member) {
|
inline static void welcomedm_send(CMember member) {
|
||||||
member->get_user([member] (CUser user) {
|
member->get_user([member] (CUser user) {
|
||||||
string welcomedm_msg = welcomedm_get(member->guild, user);
|
welcomedm_get(member->guild, user, [=] (const std::string& welcomedm_msg) {
|
||||||
if (welcomedm_msg != "none") {
|
if (welcomedm_msg != "none") {
|
||||||
user->get_dm([welcomedm_msg] (CChannel channel) {
|
user->get_dm([welcomedm_msg] (CChannel channel) {
|
||||||
channel->send(welcomedm_msg);
|
if (not channel) return;
|
||||||
});
|
channel->send(welcomedm_msg);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static void welcomedm(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
static void welcomedm(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||||
|
@ -64,7 +74,9 @@ class Basic {
|
||||||
// Check args
|
// Check args
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
// Return current welcomedm
|
// Return current welcomedm
|
||||||
channel->send(welcomedm_get(channel->get_guild(), msg->author));
|
welcomedm_get(channel->get_guild(), msg->author, [=] (const std::string& welcomedm_msg) {
|
||||||
|
channel->send(welcomedm_msg);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Set welcomedm
|
// Set welcomedm
|
||||||
string &newmsg = args[0];
|
string &newmsg = args[0];
|
||||||
|
@ -76,7 +88,7 @@ class Basic {
|
||||||
});
|
});
|
||||||
welcomedm_set(server, newmsg);
|
welcomedm_set(server, newmsg);
|
||||||
} catch (fmt::format_error& e) {
|
} catch (fmt::format_error& e) {
|
||||||
channel->send(":warning: Failed to set welcome DM: "+string(e.what()));
|
channel->send_embed(simpleEmbed(":warning: Failed to set welcome DM: "+string(e.what()), color_err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,13 +114,13 @@ class Basic {
|
||||||
auto reason = args[1];
|
auto reason = args[1];
|
||||||
auto cb = [channel, id_str, id_int, reason, permanent] (const bool error) {
|
auto cb = [channel, id_str, id_int, reason, permanent] (const bool error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
channel->send(":warning: That didn't work. Please make sure I have the permission to ban (Use the `setup check` command) "
|
channel->send_embed(simpleEmbed(":warning: That didn't work. Please make sure I have the permission to ban (Use the `setup check` command) "
|
||||||
"and that the user you've specified actually exists.");
|
"and that the user you've specified actually exists.", color_err));
|
||||||
} else {
|
} else {
|
||||||
// If user is in cache, show its full name; else mention
|
// If user is in cache, show its full name; else mention
|
||||||
auto user = cache::get_user(id_int);
|
auto user = cache::get_user(id_int);
|
||||||
auto identifier = user?user->get_full_name():"<@"+id_str+">";
|
auto identifier = user?user->get_full_name():"<@"+id_str+">";
|
||||||
channel->send(":smiling_imp: **"+identifier+"** was "+string(permanent?"banned":"kicked")+". (**"+reason+"**)");
|
channel->send_embed(simpleEmbed(":smiling_imp: **"+identifier+"** was "+string(permanent?"banned":"kicked")+". (**"+reason+"**)"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (permanent) {
|
if (permanent) {
|
||||||
|
@ -144,9 +156,9 @@ class Basic {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (res->second->nick.empty()) {
|
if (res->second->nick.empty()) {
|
||||||
channel->send(":information_source: **"+args[0]+"** does not have a nickname");
|
channel->send_embed(simpleEmbed(":information_source: **"+args[0]+"** does not have a nickname"));
|
||||||
} else {
|
} else {
|
||||||
channel->send(":information_source: The nickname of **"+args[0]+"** is **"+res->second->nick+"**");
|
channel->send_embed(simpleEmbed(":information_source: The nickname of **"+args[0]+"** is **"+res->second->nick+"**"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,19 +175,95 @@ class Basic {
|
||||||
// Perform change
|
// Perform change
|
||||||
channel->get_guild()->set_nick(user_id, newnick, [args, newnick, channel] (const bool error) {
|
channel->get_guild()->set_nick(user_id, newnick, [args, newnick, channel] (const bool error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
channel->send(":warning: I couldn't change their nickname.\n"
|
channel->send_embed(simpleEmbed(":warning: I couldn't change their nickname.\n"
|
||||||
":information_source: Do I have enough permissions? Is the nick even valid?");
|
":information_source: Do I have enough permissions? Is the nick even valid?", color_err));
|
||||||
} else {
|
} else {
|
||||||
if (newnick.empty()) {
|
if (newnick.empty()) {
|
||||||
channel->send(":information_source: The nickname of **"+args[0]+"** was removed.");
|
channel->send_embed(simpleEmbed(":information_source: The nickname of **"+args[0]+"** was removed."));
|
||||||
} else {
|
} else {
|
||||||
channel->send(":information_source: The nickname of **"+args[0]+"** was changed to **"+newnick+"**");
|
channel->send_embed(simpleEmbed(":information_source: The nickname of **"+args[0]+"** was changed to **"+newnick+"**"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<Role*> autoroles_get(CGuild guild, const std::string& autoroles_str) {
|
||||||
|
std::vector<Role*> fres;
|
||||||
|
if (not autoroles_str.empty()) {
|
||||||
|
auto autorole_strs = extras::strsplit(autoroles_str, ',');
|
||||||
|
for (const auto& autorole_str : autorole_strs) {
|
||||||
|
auto res = guild->roles.find(std::stoul(autorole_str));
|
||||||
|
if (res != guild->roles.end()) {
|
||||||
|
if (not res->second->managed) {
|
||||||
|
fres.push_back(res->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fres;
|
||||||
|
}
|
||||||
|
static void autoroles_assign(CMember member) {
|
||||||
|
env.db->get<string>(to_dbid(member->guild->id), "AUTOROLES", [=] (auto autoroles_str) {
|
||||||
|
auto member_cpy = *member;
|
||||||
|
// Parse and assign them
|
||||||
|
auto autoroles = autoroles_get(member_cpy.guild, autoroles_str);
|
||||||
|
if (not autoroles.empty()) {
|
||||||
|
for (const auto& autorole : autoroles) {
|
||||||
|
member_cpy.roles.push_back(autorole->id);
|
||||||
|
}
|
||||||
|
member_cpy.commit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static void autoroles(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||||
|
gchannelassert(channel);
|
||||||
|
permassert(msg, channel, Permissions::MANAGE_ROLES);
|
||||||
|
auto guild = channel->get_guild();
|
||||||
|
// Check args
|
||||||
|
if (args.empty()) {
|
||||||
|
// Send current autoroles
|
||||||
|
env.db->get<string>(to_dbid(guild->id), "AUTOROLES", [=] (auto autoroles_str) {
|
||||||
|
auto autoroles = autoroles_get(guild, autoroles_str);
|
||||||
|
std::ostringstream autoroles_strs;
|
||||||
|
if (autoroles.empty()) {
|
||||||
|
autoroles_strs << "**none**";
|
||||||
|
} else {
|
||||||
|
for (const auto& autorole : autoroles) {
|
||||||
|
autoroles_strs << autorole->get_mention() << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
channel->send_embed({
|
||||||
|
{"title", "Current autoroles"},
|
||||||
|
{"description", autoroles_strs.str()}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Set autoroles
|
||||||
|
std::string autoroles_str;
|
||||||
|
{
|
||||||
|
// Compose string
|
||||||
|
std::ostringstream autoroles_strs;
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
autoroles_strs << arg << ',';
|
||||||
|
}
|
||||||
|
autoroles_str = autoroles_strs.str();
|
||||||
|
autoroles_str.pop_back();
|
||||||
|
}
|
||||||
|
// Check
|
||||||
|
std::vector<Role*> autoroles;
|
||||||
|
try {
|
||||||
|
autoroles = autoroles_get(guild, autoroles_str);
|
||||||
|
} catch (std::invalid_argument&) {}
|
||||||
|
if (autoroles.size() != args.size()) {
|
||||||
|
channel->send_embed(simpleEmbed(":warning: Not all roles could be located", color_err));
|
||||||
|
} else {
|
||||||
|
env.db->update(to_dbid(guild->id), "AUTOROLES", autoroles_str);
|
||||||
|
channel->send_embed(simpleEmbed("Alright, done!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Basic() {
|
Basic() {
|
||||||
|
@ -186,8 +274,10 @@ public:
|
||||||
register_command("ban", ban, 2);
|
register_command("ban", ban, 2);
|
||||||
register_command("kick", kick, 2);
|
register_command("kick", kick, 2);
|
||||||
register_command("nick", nick, 2);
|
register_command("nick", nick, 2);
|
||||||
|
register_command("autoroles", autoroles, INF_ARGS);
|
||||||
// Events
|
// Events
|
||||||
intents::guild_member_add.push_back(welcomedm_send);
|
intents::guild_member_add.push_back(welcomedm_send);
|
||||||
|
intents::guild_member_add.push_back(autoroles_assign);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <array> // TODO: remove when no longer needed
|
#include <array> // TODO: remove when no longer needed
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
#include "generic_msgs.h"
|
#include "generic_msgs.h"
|
||||||
#include "help.hpp"
|
#include "help.hpp"
|
||||||
#include "permassert.hpp"
|
#include "permassert.hpp"
|
||||||
#include "cdltypes.hpp"
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Botinfo {
|
class Useful {
|
||||||
static void serverinfo_roles(CChannel channel) {
|
static void serverinfo_roles(CChannel channel) {
|
||||||
channel->get_guild([channel] (CGuild server) {
|
channel->get_guild([channel] (CGuild server) {
|
||||||
// Get info text
|
// Get info text
|
||||||
|
@ -66,7 +67,6 @@ class Botinfo {
|
||||||
server->get_bans([args, channel, server, server_owner] (const std::unordered_map<uint64_t, Ban*>& server_bans) {
|
server->get_bans([args, channel, server, server_owner] (const std::unordered_map<uint64_t, Ban*>& server_bans) {
|
||||||
ostringstream server_features;
|
ostringstream server_features;
|
||||||
auto server_avatar_url = extras::get_avatar_url(server);
|
auto server_avatar_url = extras::get_avatar_url(server);
|
||||||
auto server_creation_date = "not yet implemented";
|
|
||||||
size_t server_members = 0,
|
size_t server_members = 0,
|
||||||
server_bots = 0,
|
server_bots = 0,
|
||||||
server_humans = 0,
|
server_humans = 0,
|
||||||
|
@ -100,54 +100,53 @@ class Botinfo {
|
||||||
if (server->features.size() == 0) {
|
if (server->features.size() == 0) {
|
||||||
server_features << "**none**";
|
server_features << "**none**";
|
||||||
} else {
|
} else {
|
||||||
for (auto& feature : server->features) {
|
for (auto feature : server->features) {
|
||||||
server_features << feature_get_nice(feature) << ", ";
|
server_features << feature_get_nice(feature) << ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto server_featuresstr = server_features.str();
|
||||||
|
|
||||||
// Get info text
|
// Get info text
|
||||||
string prefix = get_prefix(channel);
|
get_prefix(channel, [=] (const std::string& prefix) {
|
||||||
ostringstream info_text;
|
ostringstream info_text;
|
||||||
info_text << "**Owner**\n"
|
info_text << "**Owner**\n"
|
||||||
"• " << server_owner->get_full_name() << " (`" << server_owner->id << "`)\n"
|
"• " << server_owner->get_full_name() << " (`" << server_owner->id << "`)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Server region**\n"
|
"**Server region**\n"
|
||||||
"• " << server->region << "\n"
|
"• " << server->region << "\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Members**\n"
|
"**Members**\n"
|
||||||
"• **" << server_members << "** members, thereof **" << server_bots << "** bots and **" << server_humans << "** humans\n"
|
"• **" << server_members << "** members, thereof **" << server_bots << "** bots and **" << server_humans << "** humans\n"
|
||||||
"• **" << server_bans.size() << "** users banned\n"
|
"• **" << server_bans.size() << "** users banned\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Channels**\n"
|
"**Channels**\n"
|
||||||
"• **" << server_textchannels << "** text channels\n"
|
"• **" << server_textchannels << "** text channels\n"
|
||||||
"• **" << server_voicechannels << "** voice channels\n"
|
"• **" << server_voicechannels << "** voice channels\n"
|
||||||
"• **" << server_categories << "** categories\n"
|
"• **" << server_categories << "** categories\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Serverboost**\n"
|
"**Serverboost**\n"
|
||||||
"• Level: **" << server->premium_tier << "**\n"
|
"• Level: **" << server->premium_tier << "**\n"
|
||||||
"• Boosts: **" << server->premium_subscription_count << "**\n"
|
"• Boosts: **" << server->premium_subscription_count << "**\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Features**\n"
|
"**Features**\n"
|
||||||
"• " << server_features.str() << "\n"
|
"• " << server_featuresstr << "\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Server created**\n"
|
"**Emojis**\n"
|
||||||
"• " << server_creation_date << "\n"
|
"• Please use `" << prefix << "serverinfo emojis`\n"
|
||||||
"\n"
|
"\n"
|
||||||
"**Emojis**\n"
|
"**Role**\n"
|
||||||
"• Please use `" << prefix << "serverinfo emojis`\n"
|
"• Please use `" << prefix << "serverinfo roles`";
|
||||||
"\n"
|
|
||||||
"**Role**\n"
|
|
||||||
"• Please use `" << prefix << "serverinfo roles`";
|
|
||||||
|
|
||||||
// Send info text
|
// Send info text
|
||||||
json embed = {
|
json embed = {
|
||||||
{"title", server->name + " (" + to_string(server->id) + ')'},
|
{"title", server->name + " (" + to_string(server->id) + ')'},
|
||||||
{"description", info_text.str()},
|
{"description", info_text.str()},
|
||||||
};
|
};
|
||||||
if (not server_avatar_url.empty()) {
|
if (not server_avatar_url.empty()) {
|
||||||
embed["thumbnail"]["url"] = server_avatar_url;
|
embed["thumbnail"]["url"] = server_avatar_url;
|
||||||
}
|
}
|
||||||
channel->send_embed(embed);
|
channel->send_embed(embed);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -179,17 +178,32 @@ class Botinfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void opensource(CMessage, CChannel channel, CDL::cmdargs&) {
|
||||||
|
channel->send_embed(simpleEmbed("**Tuxiflux** is [FOSS](https://gitlab.com/niansa/tuxiflux) (**f**ree **o**pen **s**ource **s**oftware).\n"
|
||||||
|
"This means that you are free to view, modify, and redistribute the source code under the terms of the MIT license.\n"
|
||||||
|
"\n"
|
||||||
|
"Make your code open source today!\n"
|
||||||
|
" - Worried about people stealing the code? If that happens, be proud! Bad code doesn't get stolen, good code does!\n"
|
||||||
|
" - Your code style is so bad that you don't want anyone to see it? No problem! People might even help you improve it!\n"
|
||||||
|
" - Show people your open source project! If it's interesting and/or well documented enough, some will help!\n"
|
||||||
|
" - Help others - without doing anything! People learn a lot just by playing around with other open source projects!\n"
|
||||||
|
"Btw, [Fosshost](https://fosshost.org) offers free hosting for open source projects that need it!\n"
|
||||||
|
"\n"
|
||||||
|
" -> Didn't convince you? Read [this](https://www.codeproject.com/articles/5410/why-open-source) nice article!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Botinfo() {
|
Useful() {
|
||||||
using namespace CDL;
|
using namespace CDL;
|
||||||
// Commands
|
// Commands
|
||||||
register_command("serverinfo", serverinfo, 1);
|
register_command("serverinfo", serverinfo, 1);
|
||||||
register_command("avatar", avatar, 1);
|
register_command("avatar", avatar, 1);
|
||||||
|
register_command("opensource", opensource, 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static Botinfo botinfo;
|
static Useful botinfo;
|
||||||
|
|
165
bot/modules/warns.cpp
Normal file
165
bot/modules/warns.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
|
#include "help.hpp"
|
||||||
|
#include "generic_msgs.h"
|
||||||
|
#include "permassert.hpp"
|
||||||
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Warns {
|
||||||
|
inline static string get_db_identifier(uint64_t user_id, uint64_t guild_id) {
|
||||||
|
return to_dbid(user_id)+' '+to_dbid(guild_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warns(CMessage msg, CChannel channel, cmdargs& args) {
|
||||||
|
auto guild = channel->get_guild();
|
||||||
|
// Get target member
|
||||||
|
uint64_t target_user;
|
||||||
|
if (args.size() == 1) {
|
||||||
|
target_user = extras::find_user_id(guild, args[0]);
|
||||||
|
// Check if member was found
|
||||||
|
if (not target_user) {
|
||||||
|
channel->send(NO_SUCH_USER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target_user = msg->author->id;
|
||||||
|
}
|
||||||
|
auto identifier = get_db_identifier(target_user, guild->id);
|
||||||
|
// Get amount of warnings
|
||||||
|
env.db->get<int>(identifier, "WARNS", [=] (int warns_a) {
|
||||||
|
// Check if member has any warnings
|
||||||
|
if (warns_a) {
|
||||||
|
// Create list of warnings in heap
|
||||||
|
auto warnings = make_shared<vector<string>>();
|
||||||
|
warnings->reserve(warns_a);
|
||||||
|
// Copy amount of warnings to heap
|
||||||
|
auto hwarns = make_shared<int>(warns_a);
|
||||||
|
// Define functionm that shows the result
|
||||||
|
auto show_warns = [=] () {
|
||||||
|
// Get user
|
||||||
|
fetch::user(target_user, [=] (CUser target) {
|
||||||
|
// Check if user could be found
|
||||||
|
if (not target_user) {
|
||||||
|
channel->send(NO_SUCH_USER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate string
|
||||||
|
ostringstream text;
|
||||||
|
for (const auto& reason : *warnings) {
|
||||||
|
text << " - " << reason << '\n';
|
||||||
|
}
|
||||||
|
// Send as embed
|
||||||
|
channel->send_embed({
|
||||||
|
{"title", "Warnings to "+target->get_full_name()},
|
||||||
|
{"description", text.str()}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Define reader for next warning
|
||||||
|
auto next_warn = make_shared<function<void ()>>();
|
||||||
|
*next_warn = [=] () {
|
||||||
|
if ((*hwarns)--) {
|
||||||
|
env.db->get<string>(identifier, "WARN"+to_string(*hwarns), [=] (auto warnstr) {
|
||||||
|
warnings->push_back(warnstr);
|
||||||
|
(*next_warn)();
|
||||||
|
}, db_templates::member);
|
||||||
|
} else {
|
||||||
|
// Show warnings
|
||||||
|
show_warns();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(*next_warn)();
|
||||||
|
} else {
|
||||||
|
channel->send_embed(simpleEmbed(":warning: This user has no warnings!", color_warn));
|
||||||
|
}
|
||||||
|
}, db_templates::member);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warn(CMessage msg, CChannel channel, cmdargs& args) {
|
||||||
|
auto guild = channel->get_guild();
|
||||||
|
// Check permissions
|
||||||
|
permassert(msg, channel, Permissions::BAN_MEMBERS);
|
||||||
|
// Check arguments
|
||||||
|
if (args.size() != 2) {
|
||||||
|
send_badusage(channel, "warn");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check the mentioned user
|
||||||
|
auto target_user = extras::find_user_id(guild, args[0]);
|
||||||
|
// Check if user was found
|
||||||
|
if (not target_user) {
|
||||||
|
channel->send(NO_SUCH_USER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check how many time the user has been warned already
|
||||||
|
auto identifier = get_db_identifier(target_user, guild->id);
|
||||||
|
env.db->get<int>(identifier, "WARNS", [=] (auto warns_a) {
|
||||||
|
// Increase amount of warns, add warning and check if 3 warnings were reached
|
||||||
|
if (++warns_a <= 3) {
|
||||||
|
// Set reason
|
||||||
|
env.db->update(identifier, "WARN"+to_string(warns_a-1), args[1], [=] (const bool) {
|
||||||
|
// Update counter
|
||||||
|
env.db->update(identifier, "WARNS", warns_a, [=] (const bool) {
|
||||||
|
// Return list of warnings
|
||||||
|
cmdargs fakeargs = {to_string(target_user)};
|
||||||
|
warns(nullptr, channel, fakeargs);
|
||||||
|
// Ban the member if required
|
||||||
|
if (warns_a == 3) {
|
||||||
|
guild->ban(target_user, "3 warnings");
|
||||||
|
channel->send(args[0]+" was **banned**!");
|
||||||
|
}
|
||||||
|
}, db_templates::member);
|
||||||
|
}, db_templates::member);
|
||||||
|
} else {
|
||||||
|
channel->send_embed(simpleEmbed(":warning: The user already has 3 warnings!", color_err));
|
||||||
|
}
|
||||||
|
}, db_templates::member);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unwarn(CMessage msg, CChannel channel, cmdargs& args) {
|
||||||
|
auto guild = channel->get_guild();
|
||||||
|
// Check permissions
|
||||||
|
permassert(msg, channel, Permissions::BAN_MEMBERS);
|
||||||
|
// Check arguments
|
||||||
|
if (args.size() != 1) {
|
||||||
|
send_badusage(channel, "unwarn");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check the mentioned user
|
||||||
|
auto target_user = extras::find_user_id(guild, args[0]);
|
||||||
|
// Check if user was found
|
||||||
|
if (not target_user) {
|
||||||
|
channel->send(NO_SUCH_USER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Decrease warn counter
|
||||||
|
env.db->update(get_db_identifier(target_user, guild->id), "WARNS = WARNS - 1", [=] (const bool error) {
|
||||||
|
if (not error) {
|
||||||
|
channel->send_embed(simpleEmbed("This members last warning was removed"));
|
||||||
|
} else {
|
||||||
|
channel->send_embed(simpleEmbed(":warning: This member does not yet have any warnings", color_err));
|
||||||
|
}
|
||||||
|
}, db_templates::member);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
Warns() {
|
||||||
|
register_command("warns", warns, 1);
|
||||||
|
register_command("warn", warn, 2);
|
||||||
|
register_command("unwarn", unwarn, 1);
|
||||||
|
|
||||||
|
intents::guild_ban_remove.push_back([] (CGuild server, const Ban& ban) {
|
||||||
|
// Reset warnings for the unbanned user
|
||||||
|
env.db->update(get_db_identifier(ban.user->id, server->id), "WARNS", 0, nullptr, db_templates::member);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static Warns warns;
|
116
bot/src/cust.cpp
116
bot/src/cust.cpp
|
@ -3,11 +3,12 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <cdlpp/cdltypes.hpp>
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include "db_templates.hpp"
|
#include "db_templates.hpp"
|
||||||
#include "cdltypes.hpp"
|
|
||||||
#include "cust.hpp"
|
#include "cust.hpp"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace CDL;
|
||||||
|
|
||||||
const string default_prefix = "t#";
|
const string default_prefix = "t#";
|
||||||
std::map<uint64_t, std::string> prefix_cache;
|
std::map<uint64_t, std::string> prefix_cache;
|
||||||
|
@ -18,12 +19,17 @@ static const std::vector<std::string> get_table_tmpl(db_templates::type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case guild:
|
case guild:
|
||||||
return {"PREFIX TEXT ",
|
return {"PREFIX TEXT ",
|
||||||
"WELCOMEDM TEXT "};
|
"WELCOMEDM TEXT ",
|
||||||
|
"AUTOROLES TEXT "};
|
||||||
case user:
|
case user:
|
||||||
return {"BALANCE INT CHECK(BALANCE > -1) ",
|
return {"BALANCE INT CHECK(BALANCE > -1) ",
|
||||||
"LAST_DAILY INT ",
|
"LAST_DAILY INT ",
|
||||||
"GC_BLACKLISTED BOOLEAN "};
|
"GC_BLACKLISTED BOOLEAN "};
|
||||||
case _end: break;
|
case member:
|
||||||
|
return {"WARN0 TEXT ",
|
||||||
|
"WARN1 TEXT ",
|
||||||
|
"WARN2 TEXT ",
|
||||||
|
"WARNS INT CHECK(WARNS >= 0) "};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -33,15 +39,22 @@ static const std::map<std::string, std::string> get_record_tmpl(db_templates::ty
|
||||||
case guild:
|
case guild:
|
||||||
return {
|
return {
|
||||||
{"PREFIX", default_prefix},
|
{"PREFIX", default_prefix},
|
||||||
{"WELCOMEDM", "none"}
|
{"WELCOMEDM", ""},
|
||||||
|
{"AUTOROLES", ""}
|
||||||
};
|
};
|
||||||
case user:
|
case user:
|
||||||
return {
|
return {
|
||||||
{"BALANCE", env.settings.contains("start_money")?std::to_string(env.settings["start_money"].get<uint32_t>()):"0"},
|
{"BALANCE", env.settings.contains("start_money")?std::to_string(env.settings["start_money"].get<uint32_t>()):"0"},
|
||||||
{"LAST_DAILY", "-1"},
|
{"LAST_DAILY", "-1"},
|
||||||
{"GC_BLACKLISTED", "false"}
|
{"GC_BLACKLISTED", "false"},
|
||||||
};
|
};
|
||||||
case _end: break;
|
case member:
|
||||||
|
return {
|
||||||
|
{"WARN0", ""},
|
||||||
|
{"WARN1", ""},
|
||||||
|
{"WARN2", ""},
|
||||||
|
{"WARNS", "0"},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -51,21 +64,22 @@ void in_main() {
|
||||||
env.db = new Database(get_table_tmpl, get_record_tmpl);
|
env.db = new Database(get_table_tmpl, get_record_tmpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string get_prefix(CChannel channel) {
|
void get_prefix(CChannel channel, std::function<void (const std::string&)> cb) {
|
||||||
auto server_id = channel->guild_id;
|
auto server_id = channel->guild_id;
|
||||||
if (not server_id) {
|
if (not server_id) {
|
||||||
return default_prefix;
|
cb(default_prefix);
|
||||||
} else {
|
} else {
|
||||||
// Try cache
|
// Try cache
|
||||||
auto res = prefix_cache.find(server_id);
|
auto res = prefix_cache.find(server_id);
|
||||||
if (res != prefix_cache.end()) {
|
if (res != prefix_cache.end()) {
|
||||||
// Cache entry found, return
|
// Cache entry found, return
|
||||||
return res->second;
|
cb(res->second);
|
||||||
} else {
|
} else {
|
||||||
// Cache entry not found, fetch and create
|
// Cache entry not found, fetch and create
|
||||||
auto prefix = env.db->get(to_dbid(server_id), "prefix").as<std::string>();
|
env.db->get<std::string>(to_dbid(server_id), "prefix", [=] (auto prefix) {
|
||||||
prefix_cache[server_id] = prefix;
|
prefix_cache[server_id] = prefix;
|
||||||
return prefix;
|
cb(prefix);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,39 +102,46 @@ void on_message(CMessage msg, std::function<void (CMessage)> cb) {
|
||||||
cb(msg);
|
cb(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check if message has no content or user is blacklisted
|
// Check if message has no content
|
||||||
if (msg->content.empty() or env.db->get(to_dbid(msg->author->id), "GC_BLACKLISTED", db_templates::user).as<bool>()) {
|
if (msg->content.empty()) {
|
||||||
msg->remove();
|
msg->remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Generate embed
|
// Check if user is blacklisted
|
||||||
nlohmann::json embed;
|
env.db->get<bool>(to_dbid(msg->author->id), "GC_BLACKLISTED", [=] (auto blacklisted) {
|
||||||
{
|
if (blacklisted) {
|
||||||
auto &user = msg->author;
|
msg->remove();
|
||||||
embed["description"] = msg->content;
|
return;
|
||||||
embed["author"]["name"] = user->get_full_name();
|
}
|
||||||
embed["author"]["icon_url"] = extras::get_avatar_url(user);
|
// Generate embed
|
||||||
embed["footer"]["text"] = "Server • "+server->name+" | ID • "+to_string(user->id);
|
nlohmann::json embed;
|
||||||
// Get color
|
{
|
||||||
uint32_t color = 0x696969; // Grey
|
auto &user = msg->author;
|
||||||
for (const auto& teammember : env.settings["team"].items()) {
|
embed["description"] = msg->content;
|
||||||
if (teammember.value() == user->id) {
|
embed["author"]["name"] = user->get_full_name();
|
||||||
// User is team member
|
embed["author"]["icon_url"] = extras::get_avatar_url(user);
|
||||||
color = 0x40e0d0; // Turquoise
|
embed["footer"]["text"] = "Server • "+server->name+" | ID • "+to_string(user->id);
|
||||||
|
// Get color
|
||||||
|
uint32_t color = 0x696969; // Grey
|
||||||
|
for (const auto& teammember : env.settings["team"].items()) {
|
||||||
|
if (teammember.value() == user->id) {
|
||||||
|
// User is team member
|
||||||
|
color = 0x40e0d0; // Turquoise
|
||||||
|
}
|
||||||
|
}
|
||||||
|
embed["color"] = color;
|
||||||
|
}
|
||||||
|
// Delete original message
|
||||||
|
msg->remove();
|
||||||
|
// Distribute message around servers
|
||||||
|
for (const auto& [guild_id, guild] : cache::guild_cache) {
|
||||||
|
for (const auto& [channel_id, channel] : guild->channels) {
|
||||||
|
if (is_globalchat(channel)) {
|
||||||
|
channel->send_embed(embed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
embed["color"] = color;
|
}, db_templates::user);
|
||||||
}
|
|
||||||
// Delete original message
|
|
||||||
msg->remove();
|
|
||||||
// Distribute message around servers
|
|
||||||
for (const auto& [guild_id, guild] : cache::guild_cache) {
|
|
||||||
for (const auto& [channel_id, channel] : guild->channels) {
|
|
||||||
if (is_globalchat(channel)) {
|
|
||||||
channel->send_embed(embed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -141,8 +162,6 @@ void on_error(const std::string& message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
using namespace CDL;
|
|
||||||
|
|
||||||
// Set handlers
|
// Set handlers
|
||||||
handlers::get_prefix = get_prefix;
|
handlers::get_prefix = get_prefix;
|
||||||
handlers::in_main = in_main;
|
handlers::in_main = in_main;
|
||||||
|
@ -154,17 +173,20 @@ int main(int argc, char **argv) {
|
||||||
auto scroller = new PresenceScroller();
|
auto scroller = new PresenceScroller();
|
||||||
scroller->presences.push_back([] () {
|
scroller->presences.push_back([] () {
|
||||||
Presence fres;
|
Presence fres;
|
||||||
fres.activity.type = ActivityType::playing;
|
fres.activities.push_back(Activity(
|
||||||
fres.activity.text = "on "+std::to_string(cache::guild_cache.size())+" servers";
|
"on "+std::to_string(cache::guild_cache.size())+" servers",
|
||||||
|
ActivityType::playing
|
||||||
|
));
|
||||||
return fres;
|
return fres;
|
||||||
});
|
});
|
||||||
scroller->presences.push_back([] () {
|
scroller->presences.push_back([] () {
|
||||||
Presence fres;
|
Presence fres;
|
||||||
fres.activity.type = ActivityType::listening;
|
fres.activities.push_back(Activity(
|
||||||
fres.activity.text = "to "+std::to_string(cache::user_cache.size())+" users";
|
std::to_string(cache::user_cache.size())+" users",
|
||||||
|
ActivityType::listening
|
||||||
|
));
|
||||||
return fres;
|
return fres;
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set cache props
|
// Set cache props
|
||||||
|
|
19
cdlpp.cmake
Normal file
19
cdlpp.cmake
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
function(use_cdlpp target cdlpp_src_dir)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(cdlpp IMPORTED_TARGET cdlpp)
|
||||||
|
|
||||||
|
option(FORCE_SUBMODULE_CDLPP "Use CDLPP from submodule even if installed in system" No)
|
||||||
|
if(cdlpp_FOUND AND NOT FORCE_SUBMODULE_CDLPP)
|
||||||
|
set(CDLPP_LIB PkgConfig::cdlpp)
|
||||||
|
else()
|
||||||
|
execute_process(
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
COMMAND git submodule update --init --recursive --depth 1
|
||||||
|
)
|
||||||
|
add_subdirectory(${cdlpp_src_dir})
|
||||||
|
set(CDLPP_LIB "cdlpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
|
${CDLPP_LIB})
|
||||||
|
endfunction()
|
|
@ -6,6 +6,7 @@
|
||||||
"team": [
|
"team": [
|
||||||
<team member IDs as integers>
|
<team member IDs as integers>
|
||||||
],
|
],
|
||||||
|
"owner": <owner user ID>,
|
||||||
"start_money": <how much initial money the users receive; ommit to set to 0>,
|
"start_money": <how much initial money the users receive; ommit to set to 0>,
|
||||||
"daily_money": <how much daily money the users can receive; ommit to disable daily money>
|
"daily_money": <how much daily money the users can receive; ommit to disable daily money>
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,16 @@ Page **1/1**
|
||||||
• You can call up a page with `{0}help [page]`
|
• You can call up a page with `{0}help [page]`
|
||||||
|
|
||||||
:gear: **Useful**
|
:gear: **Useful**
|
||||||
• `serverinfo`, `avatar`
|
• `serverinfo`, `avatar`, `opensource`
|
||||||
|
|
||||||
:dollar: **Money**
|
:dollar: **Money**
|
||||||
• `balance/bal`, `pay`, `daily`, `bet`
|
• `balance/bal`, `pay`, `daily`, `bet`
|
||||||
|
|
||||||
:tools: **Servermanagement**
|
:tools: **Servermanagement**
|
||||||
• `kick`, `ban`, `prefix`, `nick`, `welcomedm`
|
• `kick`, `ban`, `warn`, `unwarn`, `warns`, `prefix`, `nick`, `welcomedm`, `autoroles`
|
||||||
|
|
||||||
:smile: **Fun**
|
:smile: **Fun**
|
||||||
• `random`, `8ball`
|
• `random`, `8ball`, `notaperson`, `notacat`, `notahorse`, `notarental`
|
||||||
|
|
||||||
:speech_balloon: **Misc**
|
:speech_balloon: **Misc**
|
||||||
• `about`, `setup`, `invite`
|
• `about`, `setup`, `invite`
|
||||||
|
|
|
@ -38,6 +38,26 @@
|
||||||
"@EvilOne Scammer"
|
"@EvilOne Scammer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"warn": {
|
||||||
|
"badusage_text": ":warning: I need to know who to warn.",
|
||||||
|
"usage": "<@member (mention)> <reason>",
|
||||||
|
"examples": [
|
||||||
|
"@EvilOne Scammer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unwarn": {
|
||||||
|
"badusage_text": ":warning: I need to know who to unwarn.",
|
||||||
|
"usage": "<@member (mention)>",
|
||||||
|
"examples": [
|
||||||
|
"@GoodOne"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"warns": {
|
||||||
|
"usage": "<@member (mention)>",
|
||||||
|
"examples": [
|
||||||
|
"@tuxifan"
|
||||||
|
]
|
||||||
|
},
|
||||||
"balance": {
|
"balance": {
|
||||||
"usage": "[user]",
|
"usage": "[user]",
|
||||||
"examples": [
|
"examples": [
|
||||||
|
@ -87,5 +107,11 @@
|
||||||
"examples": [
|
"examples": [
|
||||||
"**Welcome {0} to {1}!** ..."
|
"**Welcome {0} to {1}!** ..."
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"autoroles": {
|
||||||
|
"usage": "[roles IDs]",
|
||||||
|
"examples": [
|
||||||
|
"802881774598356992 802881832017723403"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a104ec0a69f8bc7b80f3ae703e7851d8bbb14e01
|
Subproject commit 1ffb9581d40547bee82e193691c3eedb84746347
|
|
@ -1 +1 @@
|
||||||
Subproject commit bb4a7f27ca79cbedc3d49e5cb28922f33b1fd5b0
|
Subproject commit ac425fd277f5e7c20951d102e391c229cbe499b8
|
Reference in a new issue