Compare commits
50 commits
v0.19.2-al
...
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 |
23 changed files with 508 additions and 101 deletions
|
@ -10,7 +10,7 @@ build:
|
|||
- cp config.json.example config.json
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- cmake .. -DFORCE_SUBMODULE_CDLPP=Yes
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
- make -j"$(nproc)"
|
||||
- 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"]
|
||||
path = lib/cdlpp-db
|
||||
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)
|
||||
|
||||
include(embed.cmake)
|
||||
include(lib/cdlpp/use.cmake)
|
||||
include(lib/cdlpp-db/use.cmake)
|
||||
include(cdlpp.cmake)
|
||||
|
||||
cdlpp(lib/cdlpp)
|
||||
cdlppdb(lib/cdlpp-db)
|
||||
add_compile_definitions(COMPILER_ID="${CMAKE_CXX_COMPILER_ID}")
|
||||
add_compile_definitions(COMPILER_VERSION="${CMAKE_CXX_COMPILER_VERSION}")
|
||||
add_compile_definitions(COMPILER_PLATFORM="${CMAKE_CXX_PLATFORM_ID}")
|
||||
|
||||
add_executable(tuxiflux
|
||||
${CDLPP_SRC_FILES}
|
||||
${CDLPPDB_SRC_FILES}
|
||||
bot/src/cust.cpp
|
||||
bot/src/help_pages.cpp
|
||||
bot/modules/useful.cpp
|
||||
bot/modules/servermanagement.cpp
|
||||
bot/modules/warns.cpp
|
||||
bot/modules/misc.cpp
|
||||
bot/modules/help.cpp
|
||||
bot/modules/money.cpp
|
||||
bot/modules/fun.cpp
|
||||
bot/modules/eval.cpp
|
||||
)
|
||||
|
||||
target_include_directories(tuxiflux PRIVATE
|
||||
${CDLPP_INCLUDE_DIRS}
|
||||
${CDLPPDB_INCLUDE_DIRS}
|
||||
include_directories(
|
||||
bot/include/
|
||||
lib/cdlpp-db/include/
|
||||
${EMBED_OUTDIR}
|
||||
)
|
||||
|
||||
cdlpp_libs()
|
||||
cdlppdb_libs()
|
||||
find_package(cpuid QUIET)
|
||||
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)
|
||||
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,9 +1,9 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <cdlpp/bot.hpp>
|
||||
#include <cdlpp/cdltypes-incomplete.hpp>
|
||||
#include "database.hpp"
|
||||
#include "bot.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
|
||||
|
||||
extern const std::string default_prefix;
|
||||
|
@ -14,3 +14,12 @@ void on_message(CDL::CMessage msg, std::function<void (CDL::CMessage)> cb);
|
|||
void on_error(const std::string& message);
|
||||
void get_prefix(CDL::CChannel channel, std::function<void (const std::string&)> cb);
|
||||
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,6 +4,7 @@ namespace db_templates {
|
|||
enum type {
|
||||
guild,
|
||||
user,
|
||||
member,
|
||||
_end [[maybe_unused]], // Must be last one before _default
|
||||
_default = guild // Must be last one
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#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
|
||||
#include "bot.hpp"
|
||||
#include "abstract/channel.hpp"
|
||||
#include <cdlpp/bot.hpp>
|
||||
#include <cdlpp/cdltypes-incomplete.hpp>
|
||||
|
||||
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,11 +1,11 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
#include "cust.hpp"
|
||||
#include "database.hpp"
|
||||
#include "generic_msgs.h"
|
||||
#include "help.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
||||
|
@ -23,7 +23,7 @@ class Fun {
|
|||
min = stoi(args[0]);
|
||||
max = stoi(args[1]);
|
||||
} 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;
|
||||
}
|
||||
// Check numbers
|
||||
|
@ -36,9 +36,7 @@ class Fun {
|
|||
uniform_int_distribution<> distr(min, max);
|
||||
// Send result
|
||||
auto rndno = distr(gen);
|
||||
channel->send(":game_die:...", [rndno] (CMessage msg) {
|
||||
msg->edit(":game_die: **"+to_string(rndno)+"**");
|
||||
});
|
||||
channel->send_embed(simpleEmbed(":game_die: **"+to_string(rndno)+"**"));
|
||||
}
|
||||
|
||||
static void eightball(CMessage msg, CChannel channel, CDL::cmdargs& args) {
|
||||
|
@ -51,13 +49,25 @@ class Fun {
|
|||
send_badusage(channel, "8ball");
|
||||
return;
|
||||
}
|
||||
// Get "random" answer
|
||||
mt19937 gen(msg->id);
|
||||
uniform_int_distribution<> distr(0, static_cast<int>(answers.size() - 1));
|
||||
// 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:
|
||||
|
@ -66,6 +76,10 @@ public:
|
|||
// Commands
|
||||
register_command("random", random, 2);
|
||||
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,10 +2,10 @@
|
|||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <fmt/format.h>
|
||||
#include "extras.hpp"
|
||||
#include <cdlpp/extras.hpp>
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
#include "cust.hpp"
|
||||
#include "help_pages.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
||||
|
@ -16,7 +16,7 @@ static json syntaxes = json::parse(help_files.find("syntaxes")->second);
|
|||
void send_badusage(CChannel channel, const string& command) {
|
||||
// Check if any usable syntax is listed to command
|
||||
if (not syntaxes.contains(command)) {
|
||||
channel->send(":warning: Incorrect command usage");
|
||||
channel->send_embed(simpleEmbed(":warning: Incorrect command usage", color_err));
|
||||
return;
|
||||
}
|
||||
auto syntax = syntaxes[command];
|
||||
|
@ -24,12 +24,12 @@ void send_badusage(CChannel channel, const string& command) {
|
|||
get_prefix(channel, [=] (const std::string& prefix) {
|
||||
// Check if badusage text exists
|
||||
if (syntax.contains("badusage_text")) {
|
||||
channel->send(fmt::format(string(syntax["badusage_text"]), prefix+command, string(syntax["usage"])));
|
||||
channel->send_embed(simpleEmbed(fmt::format(string(syntax["badusage_text"]), prefix+command, string(syntax["usage"])), color_err));
|
||||
}
|
||||
// Alternatively send usage
|
||||
else {
|
||||
string x = fmt::format(":warning: Syntax: `{0} {1}`", prefix+command, string(syntax["usage"]));
|
||||
channel->send(x);
|
||||
channel->send_embed(simpleEmbed(x, color_err));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
#include <ostream>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#ifdef HAS_CPUID
|
||||
# include <libcpuid.h>
|
||||
#endif
|
||||
#include "cust.hpp"
|
||||
#include "help.hpp"
|
||||
#include "generic_msgs.h"
|
||||
#include "permassert.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
||||
|
@ -17,7 +20,7 @@ class Misc {
|
|||
timer::time_point startup_time;
|
||||
|
||||
|
||||
static void invite(CMessage , CChannel channel, CDL::cmdargs&) {
|
||||
static void invite(CMessage, CChannel channel, CDL::cmdargs&) {
|
||||
json embed = {
|
||||
{"description", ":speech_balloon: You can invite me using [this]("+extras::get_bot_invite_link()+") link"},
|
||||
{"color", 0xffffff}
|
||||
|
@ -51,17 +54,21 @@ class Misc {
|
|||
// TODO
|
||||
};
|
||||
// Create channel
|
||||
Channel newchannel({});
|
||||
{
|
||||
newchannel.name = "tuxiflux-globalchat";
|
||||
using namespace Permissions;
|
||||
newchannel.overwrites[env.self->id] = {
|
||||
env.self->id,
|
||||
PermissionOverwrite::member,
|
||||
VIEW_CHANNEL | SEND_MESSAGES | MANAGE_MESSAGES,
|
||||
0
|
||||
};
|
||||
}
|
||||
using namespace Permissions;
|
||||
BaseChannel newchannel = {
|
||||
.name = "tuxiflux-globalchat",
|
||||
.overwrites = {
|
||||
{
|
||||
env.self->id,
|
||||
{
|
||||
.id = env.self->id,
|
||||
.type = PermissionOverwrite::member,
|
||||
.allow = VIEW_CHANNEL | SEND_MESSAGES | MANAGE_MESSAGES,
|
||||
.deny = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
server->create_channel(newchannel, [channel] (CChannel newchannel) {
|
||||
if (newchannel) {
|
||||
channel->send(":speech_balloon: "+newchannel->get_mention()+" was created and set up. Have fun!");
|
||||
|
@ -78,16 +85,34 @@ class Misc {
|
|||
auto uptime_hours = uptime.count() / 1000000000 / 60 / 60;
|
||||
// Get prefix
|
||||
get_prefix(channel, [=] (const std::string& prefix) {
|
||||
# ifdef extras_get_mem_supported
|
||||
// Get mem usage string
|
||||
ostringstream mem_use;
|
||||
# ifdef extras_get_mem_supported
|
||||
mem_use << "Memory usage • **" << extras::get_mem::used() << "** MB / **" << extras::get_mem::total() << "** MB\n";
|
||||
# endif
|
||||
mem_use << "Memory usage • **" << extras::get_mem::used() << "** MB / **" << extras::get_mem::total() << "** MB\n";
|
||||
# endif
|
||||
# ifdef HAS_CPUID
|
||||
// Get CPU data
|
||||
string cpu_name;
|
||||
if (cpuid_present()) {
|
||||
cpu_raw_data_t cpuid_raw;
|
||||
cpuid_get_raw_data(&cpuid_raw);
|
||||
cpu_id_t cpuid;
|
||||
cpu_identify(&cpuid_raw, &cpuid);
|
||||
cpu_name = cpuid.brand_str;
|
||||
} else {
|
||||
cpu_name = "Unknown";
|
||||
}
|
||||
# endif
|
||||
// Build info text
|
||||
ostringstream text;
|
||||
text << "Online since • **" << uptime_hours << "** hours\n"
|
||||
"\n" <<
|
||||
# ifdef extras_get_mem_supported
|
||||
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"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <ctime>
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
#include "cust.hpp"
|
||||
#include "database.hpp"
|
||||
#include "generic_msgs.h"
|
||||
#include "help.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
||||
|
@ -29,7 +29,7 @@ class Money {
|
|||
}
|
||||
// Check if target user == bot
|
||||
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;
|
||||
}
|
||||
fetch::user(user_id, [channel] (CUser user) {
|
||||
|
@ -37,7 +37,7 @@ class Money {
|
|||
// Get values
|
||||
get_money(user->id, [=] (auto amount) {
|
||||
// Send
|
||||
channel->send("**"+user->get_full_name()+"** currently has **"+to_string(amount)+"** :dollar:");
|
||||
channel->send_embed(simpleEmbed("**"+user->get_full_name()+"** currently has **"+to_string(amount)+"** :dollar:"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ class Money {
|
|||
env.db->get<int>(to_dbid(msg->author->id), "LAST_DAILY", [=] (auto last_daily) {
|
||||
// Verify that next day was reached
|
||||
if (last_daily == time->tm_yday) {
|
||||
channel->send(":warning: You can't get your daily money twice a day\n"
|
||||
":information_source: Please note that I'm running in GMT :wink:");
|
||||
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:", color_err));
|
||||
return;
|
||||
}
|
||||
// Update database
|
||||
|
@ -66,8 +66,8 @@ class Money {
|
|||
get_money(msg->author->id, [=] (auto new_value) {
|
||||
auto old_value = new_value - daily_money;
|
||||
// Send result
|
||||
channel->send(":dollar: You've received your daily money!\n"
|
||||
"**"+to_string(old_value)+"** + **"+to_string(daily_money)+"** = **"+to_string(new_value)+"**");
|
||||
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));
|
||||
|
@ -89,10 +89,10 @@ class Money {
|
|||
}
|
||||
// Check target user
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
// Check if specified user ID actually exists and is fetchable
|
||||
|
@ -104,17 +104,17 @@ class Money {
|
|||
}
|
||||
// Check if number is clean
|
||||
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;
|
||||
}
|
||||
// Pay out
|
||||
mod_money(msg->author->id, [=] (const bool error) {
|
||||
if (error) {
|
||||
channel->send(":warning: I wasn't able to pay out... Are you sure you've got enough money?");
|
||||
channel->send_embed(simpleEmbed(":warning: I wasn't able to pay out... Are you sure you've got enough money?", color_err));
|
||||
} else {
|
||||
mod_money(target_user->id, nullptr, amount_str);
|
||||
// Report success
|
||||
channel->send("You've sent **"+amount_str+"** :dollar: to **"+target_user->get_full_name()+"**");
|
||||
channel->send_embed(simpleEmbed("You've sent **"+amount_str+"** :dollar: to **"+target_user->get_full_name()+"**"));
|
||||
}
|
||||
}, string("-")+amount_str);
|
||||
});
|
||||
|
@ -130,13 +130,13 @@ class Money {
|
|||
auto insert_str = to_string(insert);
|
||||
// Check if insert is in range
|
||||
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;
|
||||
}
|
||||
// Check if user has enough money
|
||||
get_money(msg->author->id, [=] (auto amount) {
|
||||
if (amount < insert) {
|
||||
channel->send(":warning: Excuse me, but you don't have enough money to do that!");
|
||||
channel->send_embed(simpleEmbed(":warning: Excuse me, but you don't have enough money to do that!", color_err));
|
||||
return;
|
||||
}
|
||||
// Get "random" value
|
||||
|
@ -148,9 +148,9 @@ class Money {
|
|||
mod_money(msg->author->id, nullptr, std::string(lost?"-":"")+insert_str);
|
||||
// Report value of won
|
||||
if (lost) {
|
||||
channel->send(":thumbsdown: You've lost **"+insert_str+"** :dollar:");
|
||||
channel->send_embed(simpleEmbed(":thumbsdown: You've lost **"+insert_str+"** :dollar:", color_warn));
|
||||
} else {
|
||||
channel->send(":thumbsup: You've won **"+insert_str+"** :dollar:");
|
||||
channel->send_embed(simpleEmbed(":thumbsup: You've won **"+insert_str+"** :dollar:", color_ok));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#include <fmt/format.h>
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
#include "database.hpp"
|
||||
#include "permassert.hpp"
|
||||
#include "help.hpp"
|
||||
#include "cust.hpp"
|
||||
#include "generic_msgs.h"
|
||||
#include "cdltypes.hpp"
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
||||
|
@ -17,7 +17,7 @@ class Basic {
|
|||
if (args.empty()) {
|
||||
// Get prefix
|
||||
get_prefix(channel, [=] (const std::string& prefix) {
|
||||
channel->send(":paperclips: The individual prefix for **"+server->name+"** is `"+prefix+'`');
|
||||
channel->send_embed(simpleEmbed(":paperclips: The individual prefix for **"+server->name+"** is `"+prefix+'`'));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -35,9 +35,9 @@ class Basic {
|
|||
prefix_cache[server->id] = newprefix;
|
||||
// Inform about success
|
||||
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 {
|
||||
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+"**."));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ class Basic {
|
|||
});
|
||||
welcomedm_set(server, newmsg);
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,13 +114,13 @@ class Basic {
|
|||
auto reason = args[1];
|
||||
auto cb = [channel, id_str, id_int, reason, permanent] (const bool error) {
|
||||
if (error) {
|
||||
channel->send(":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.");
|
||||
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.", color_err));
|
||||
} else {
|
||||
// If user is in cache, show its full name; else mention
|
||||
auto user = cache::get_user(id_int);
|
||||
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) {
|
||||
|
@ -156,9 +156,9 @@ class Basic {
|
|||
return;
|
||||
} else {
|
||||
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 {
|
||||
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 {
|
||||
|
@ -175,13 +175,13 @@ class Basic {
|
|||
// Perform change
|
||||
channel->get_guild()->set_nick(user_id, newnick, [args, newnick, channel] (const bool error) {
|
||||
if (error) {
|
||||
channel->send(":warning: I couldn't change their nickname.\n"
|
||||
":information_source: Do I have enough permissions? Is the nick even valid?");
|
||||
channel->send_embed(simpleEmbed(":warning: I couldn't change their nickname.\n"
|
||||
":information_source: Do I have enough permissions? Is the nick even valid?", color_err));
|
||||
} else {
|
||||
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 {
|
||||
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+"**"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -205,13 +205,14 @@ class Basic {
|
|||
}
|
||||
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->guild, autoroles_str);
|
||||
auto autoroles = autoroles_get(member_cpy.guild, autoroles_str);
|
||||
if (not autoroles.empty()) {
|
||||
for (const auto& autorole : autoroles) {
|
||||
member->roles.push_back(autorole->id);
|
||||
member_cpy.roles.push_back(autorole->id);
|
||||
}
|
||||
member->commit();
|
||||
member_cpy.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -255,10 +256,10 @@ class Basic {
|
|||
autoroles = autoroles_get(guild, autoroles_str);
|
||||
} catch (std::invalid_argument&) {}
|
||||
if (autoroles.size() != args.size()) {
|
||||
channel->send(":warning: Not all roles could be located");
|
||||
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("Alright, done!");
|
||||
channel->send_embed(simpleEmbed("Alright, done!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#include <string>
|
||||
#include <ostream>
|
||||
#include <array> // TODO: remove when no longer needed
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
#include "cust.hpp"
|
||||
#include "generic_msgs.h"
|
||||
#include "help.hpp"
|
||||
#include "permassert.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
||||
|
||||
|
||||
class Botinfo {
|
||||
class Useful {
|
||||
static void serverinfo_roles(CChannel channel) {
|
||||
channel->get_guild([channel] (CGuild server) {
|
||||
// Get info text
|
||||
|
@ -67,7 +67,6 @@ class Botinfo {
|
|||
server->get_bans([args, channel, server, server_owner] (const std::unordered_map<uint64_t, Ban*>& server_bans) {
|
||||
ostringstream server_features;
|
||||
auto server_avatar_url = extras::get_avatar_url(server);
|
||||
auto server_creation_date = "not yet implemented";
|
||||
size_t server_members = 0,
|
||||
server_bots = 0,
|
||||
server_humans = 0,
|
||||
|
@ -132,9 +131,6 @@ class Botinfo {
|
|||
"**Features**\n"
|
||||
"• " << server_featuresstr << "\n"
|
||||
"\n"
|
||||
"**Server created**\n"
|
||||
"• " << server_creation_date << "\n"
|
||||
"\n"
|
||||
"**Emojis**\n"
|
||||
"• Please use `" << prefix << "serverinfo emojis`\n"
|
||||
"\n"
|
||||
|
@ -182,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:
|
||||
Botinfo() {
|
||||
Useful() {
|
||||
using namespace CDL;
|
||||
// Commands
|
||||
register_command("serverinfo", serverinfo, 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;
|
|
@ -3,9 +3,9 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <cdlpp/cdltypes.hpp>
|
||||
#include "database.hpp"
|
||||
#include "db_templates.hpp"
|
||||
#include "cdltypes.hpp"
|
||||
#include "cust.hpp"
|
||||
using namespace std;
|
||||
using namespace CDL;
|
||||
|
@ -25,6 +25,11 @@ static const std::vector<std::string> get_table_tmpl(db_templates::type type) {
|
|||
return {"BALANCE INT CHECK(BALANCE > -1) ",
|
||||
"LAST_DAILY INT ",
|
||||
"GC_BLACKLISTED BOOLEAN "};
|
||||
case member:
|
||||
return {"WARN0 TEXT ",
|
||||
"WARN1 TEXT ",
|
||||
"WARN2 TEXT ",
|
||||
"WARNS INT CHECK(WARNS >= 0) "};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -41,15 +46,22 @@ static const std::map<std::string, std::string> get_record_tmpl(db_templates::ty
|
|||
return {
|
||||
{"BALANCE", env.settings.contains("start_money")?std::to_string(env.settings["start_money"].get<uint32_t>()):"0"},
|
||||
{"LAST_DAILY", "-1"},
|
||||
{"GC_BLACKLISTED", "false"}
|
||||
{"GC_BLACKLISTED", "false"},
|
||||
};
|
||||
case member:
|
||||
return {
|
||||
{"WARN0", ""},
|
||||
{"WARN1", ""},
|
||||
{"WARN2", ""},
|
||||
{"WARNS", "0"},
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void in_main() {
|
||||
// Initialise database client
|
||||
env.db = new Database(*env.aioc, get_table_tmpl, get_record_tmpl);
|
||||
env.db = new Database(get_table_tmpl, get_record_tmpl);
|
||||
}
|
||||
|
||||
void get_prefix(CChannel channel, std::function<void (const std::string&)> cb) {
|
||||
|
|
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 member IDs as integers>
|
||||
],
|
||||
"owner": <owner user ID>,
|
||||
"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>
|
||||
}
|
||||
|
|
|
@ -2,16 +2,16 @@ Page **1/1**
|
|||
• You can call up a page with `{0}help [page]`
|
||||
|
||||
:gear: **Useful**
|
||||
• `serverinfo`, `avatar`
|
||||
• `serverinfo`, `avatar`, `opensource`
|
||||
|
||||
:dollar: **Money**
|
||||
• `balance/bal`, `pay`, `daily`, `bet`
|
||||
|
||||
:tools: **Servermanagement**
|
||||
• `kick`, `ban`, `prefix`, `nick`, `welcomedm`, `autoroles`
|
||||
• `kick`, `ban`, `warn`, `unwarn`, `warns`, `prefix`, `nick`, `welcomedm`, `autoroles`
|
||||
|
||||
:smile: **Fun**
|
||||
• `random`, `8ball`
|
||||
• `random`, `8ball`, `notaperson`, `notacat`, `notahorse`, `notarental`
|
||||
|
||||
:speech_balloon: **Misc**
|
||||
• `about`, `setup`, `invite`
|
||||
|
|
|
@ -38,6 +38,26 @@
|
|||
"@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": {
|
||||
"usage": "[user]",
|
||||
"examples": [
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f2a8cf3eb1d1b489bdfe59484414c65e2a3d0cc2
|
||||
Subproject commit 1ffb9581d40547bee82e193691c3eedb84746347
|
|
@ -1 +1 @@
|
|||
Subproject commit d605e9e32f70bd08a143cb8f4ba6b1c0225a989a
|
||||
Subproject commit ac425fd277f5e7c20951d102e391c229cbe499b8
|
Reference in a new issue