Archived
1
0
Fork 0

Compare commits

...

50 commits

Author SHA1 Message Date
niansa
92c3058485 Update README.md 2021-06-21 21:34:07 +00:00
Nils
6f4d5606a6 Added README 2021-06-21 23:31:07 +02:00
Nils
5db977d9f6 Added opensource command 2021-06-21 23:12:42 +02:00
niansa
73dce15840 Redisabled CI 2021-06-05 17:06:31 +02:00
niansa
e7fe154df3 Try #1 to fix CI 2021-06-05 16:43:45 +02:00
niansa
afcab93421 Updated CDL and CDL DB 2021-06-05 16:40:08 +02:00
niansa
4ab7b39980 Reenabled CI 2021-06-05 16:38:04 +02:00
niansa
6132b8df31 Improvements in warns command 2021-06-05 16:36:43 +02:00
niansa
f31616cbb7 Updated CDL++ and CDL++ Db 2021-05-22 16:34:57 +02:00
niansa
8dca929d80 Update CMakeLists.txt 2021-05-22 14:33:00 +00:00
Nils
c98cfa01ba Forgot to use simpleEmbed() at just one more place 2021-05-21 18:28:14 +02:00
Nils
875578d6ee Use embeds even more 2021-05-21 18:20:40 +02:00
Nils
143a80cba0 Fixed policy warning 2021-05-09 18:10:58 +02:00
Nils
eef1e190a7 Fixed cdlpp.cmake 2021-05-09 18:01:56 +02:00
Nils
c205f9fa94 Allow building without CDL++ installed 2021-05-09 17:55:16 +02:00
Nils
2566265d1d Removed some legacy code 2021-05-09 17:05:41 +02:00
Nils
8329eea3e0 Another fix in CMakeLists.txt 2021-04-29 13:50:40 +02:00
Nils
67506ea201 Fixed CMakeLists.txt once again 2021-04-29 13:36:26 +02:00
Nils
a68b579b16 Added cmake option to toggle eval module 2021-04-29 13:27:52 +02:00
Nils
5f25ab401f Fixed loose cpuid dependency 2021-04-29 13:22:34 +02:00
Nils
44ee2bcb08 Added loose dependency: cpuid 2021-04-29 13:19:14 +02:00
niansa
5e6bf6ccb5 Updated for latest CDL++ 2021-04-09 13:37:48 +02:00
niansa
3afec81b3d Updated CDLPP-DB 2021-04-09 11:52:01 +02:00
niansa
35908dbfcd Documented unwarn command 2021-03-22 13:21:44 +01:00
niansa
13d2479e49 Added unwarn command 2021-03-22 13:18:23 +01:00
niansa
dcf88f4ab0 Disabled .gitlab-ci.yml 2021-03-22 09:06:59 +00:00
niansa
4d17a43c83 Copy member before trying to modify it 2021-03-22 10:04:57 +01:00
niansa
b80b5deb3a Renamed some* commands to nota* 2021-03-19 11:57:24 +01:00
niansa
e07bb7fa55 Added somex commands 2021-03-18 16:47:25 +01:00
niansa
d637773f85 Fixed warn reset on ban 2021-03-12 13:25:33 +01:00
niansa
24f051299a Updated for latest CDL 2021-03-12 13:12:07 +01:00
niansa
bf4c2fae35 Removed duplicate GUILD_BANS intent 2021-03-12 12:42:51 +01:00
niansa
86afdb6a68 Updated CDL-DB 2021-03-12 12:38:58 +01:00
niansa
6b9380a221 Fixed check constraint for WARNS 2021-03-12 12:32:50 +01:00
niansa
17de144df4 Just very minor warning system improvement 2021-03-12 12:13:11 +01:00
niansa
10ff959aee Further fixes 2021-03-12 00:57:36 +01:00
niansa
375f8940a8 Fixed last commit 2021-03-12 00:34:24 +01:00
niansa
ea6a412cf8 Added warn commands 2021-03-12 00:21:44 +01:00
niansa
5be8ee0e21 Use std::filesystem to remove files instead of c api 2021-03-08 08:15:46 +01:00
niansa
35103c5744 Fully clean up evaluation data 2021-03-08 08:07:44 +01:00
niansa
6b31eaa81b Update eval.cpp 2021-03-08 07:00:46 +00:00
niansa
a01d400b23 Added fmt library to CMakeLists.txt 2021-03-08 07:47:01 +01:00
niansa
59fb294826 CMakeLists.txt fixup 2021-03-08 07:45:42 +01:00
niansa
c4db5815b9 Portability improvements 2021-03-08 07:42:34 +01:00
niansa
80e6564a3d Added eval command 2021-03-08 00:19:31 +01:00
niansa
aca3591c39 Use system-wide library instead of using a submodule 2021-02-26 09:36:15 +01:00
niansa
cc19d7c677 Improved 8ball 2021-02-19 13:42:11 +01:00
niansa
0d00969c61 Improved random command 2021-02-19 12:57:03 +01:00
niansa
b611a3df9c Updated CDL 2021-02-19 11:59:05 +01:00
niansa
9e2cfbfcd4 Updated CDL-DB 2021-02-17 18:39:52 +01:00
23 changed files with 508 additions and 101 deletions

View file

@ -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
View file

@ -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

View file

@ -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
View 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)

View file

@ -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}};
}

View file

@ -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
};

View file

@ -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)

View file

@ -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
View 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

View file

@ -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);
}
};

View file

@ -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));
}
});
}

View file

@ -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"

View file

@ -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));
}
});
}

View file

@ -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!"));
}
}
}

View file

@ -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
View 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;

View file

@ -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
View 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()

View file

@ -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>
}

View file

@ -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`

View file

@ -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