mirror of
https://gitlab.com/niansa/SomeBot.git
synced 2025-03-06 20:48:26 +01:00
358 lines
18 KiB
C++
358 lines
18 KiB
C++
#include "../bot.hpp"
|
||
#include "../util.hpp"
|
||
|
||
#include <tuple>
|
||
#include <sstream>
|
||
|
||
|
||
|
||
class Levels {
|
||
Bot *bot;
|
||
|
||
void db_add_user(dpp::snowflake guild_id, dpp::snowflake user_id) {
|
||
bot->db << "INSERT OR IGNORE INTO levels (guild_id, user_id, level, xp) VALUES (?, ?, 0, 0);"
|
||
<< std::to_string(guild_id)
|
||
<< std::to_string(user_id);
|
||
}
|
||
void db_add_guild(dpp::snowflake guild_id) {
|
||
bot->db << "INSERT OR IGNORE INTO levels_guild_settings (id, enabled, embed, speed, color) VALUES (?, FALSE, TRUE, 1, 16711680);"
|
||
<< std::to_string(guild_id);
|
||
}
|
||
|
||
std::pair<unsigned, double> get_user_data(dpp::snowflake guild_id, dpp::snowflake user_id) {
|
||
std::pair<unsigned, double> fres = {0, 0.0};
|
||
bot->db << "SELECT level, xp FROM levels "
|
||
"WHERE guild_id = ? AND user_id = ?;"
|
||
<< std::to_string(guild_id)
|
||
<< std::to_string(user_id)
|
||
>> std::tie(fres.first, fres.second);
|
||
return fres;
|
||
}
|
||
void set_user_data(dpp::snowflake guild_id, dpp::snowflake user_id, unsigned level, double xp) {
|
||
bot->db << "UPDATE levels "
|
||
"SET level = ?, "
|
||
" xp = ? "
|
||
"WHERE guild_id = ? AND user_id = ?;"
|
||
<< level
|
||
<< xp
|
||
<< std::to_string(guild_id)
|
||
<< std::to_string(user_id);
|
||
}
|
||
|
||
std::tuple<bool, bool, double, unsigned> get_guild_data(dpp::snowflake guild_id) {
|
||
std::tuple<unsigned, unsigned, double, unsigned> fres;
|
||
bot->db << "SELECT enabled, embed, speed, color FROM levels_guild_settings "
|
||
"WHERE id = ?;"
|
||
<< std::to_string(guild_id)
|
||
>> std::tie(std::get<0>(fres), std::get<1>(fres), std::get<2>(fres), std::get<3>(fres));
|
||
return fres;
|
||
}
|
||
void set_guild_data(dpp::snowflake guild_id, bool enabled, double speed) {
|
||
bot->db << "UPDATE levels_guild_settings "
|
||
"SET enabled = ?, "
|
||
" speed = ? "
|
||
"WHERE id = ?;"
|
||
<< enabled
|
||
<< speed
|
||
<< std::to_string(guild_id);
|
||
}
|
||
|
||
dpp::message get_level_info_message(dpp::snowflake guild_id, dpp::snowflake target_id, bool target_is_sender, bool embed_enabled, unsigned color = 0xff0000) {
|
||
// Get level and XP
|
||
db_add_user(guild_id, target_id);
|
||
auto [level, xp] = get_user_data(guild_id, target_id);
|
||
// Get XP percentage (and solve rounding issue)
|
||
unsigned xp_percentage = xp*100.0;
|
||
if (xp_percentage == 100) {
|
||
xp_percentage = 99;
|
||
}
|
||
// Create message string
|
||
auto msg_str = "**<@"+std::to_string(target_id)+">**"+(target_is_sender?", you are":" is")+" level **"+std::to_string(level)+"** and **"+std::to_string(xp_percentage)+"%** to the next.";
|
||
// Create base message
|
||
dpp::message base_msg;
|
||
if (!target_is_sender) {
|
||
base_msg.set_flags(dpp::message_flags::m_ephemeral);
|
||
}
|
||
// Send current level
|
||
if (embed_enabled) {
|
||
dpp::embed embed;
|
||
embed.set_title("Your level")
|
||
.set_description(msg_str)
|
||
.set_color(color);
|
||
return base_msg.add_embed(embed);
|
||
} else {
|
||
return base_msg.set_content(msg_str).set_allowed_mentions(false, false, false, false, {}, {});
|
||
}
|
||
}
|
||
|
||
public:
|
||
Levels(Bot *_bot) : bot(_bot) {
|
||
bot->cluster.intents |= dpp::intents::i_guild_messages;
|
||
|
||
bot->db << "CREATE TABLE IF NOT EXISTS levels_guild_settings ("
|
||
" id TEXT PRIMARY KEY NOT NULL,"
|
||
" enabled INTEGER,"
|
||
" embed INTEGER,"
|
||
" speed REAL,"
|
||
" color INTEGER,"
|
||
" UNIQUE(id)"
|
||
");";
|
||
bot->db << "CREATE TABLE IF NOT EXISTS levels_guild_roles ("
|
||
" guild_id TEXT NOT NULL,"
|
||
" role_id TEXT NOT NULL,"
|
||
" threshold INTEGER,"
|
||
" UNIQUE(guild_id, role_id)"
|
||
");";
|
||
bot->db << "CREATE TABLE IF NOT EXISTS levels ("
|
||
" guild_id TEXT NOT NULL,"
|
||
" user_id TEXT NOT NULL,"
|
||
" level INTEGER,"
|
||
" xp REAL,"
|
||
" UNIQUE(guild_id, user_id)"
|
||
");";
|
||
|
||
bot->add_chatcommand(Bot::ChatCommand({"current_level", "level"}, "Shows your level (or someone else's)", dpp::slashcommand().add_option(dpp::command_option(dpp::command_option_type::co_user, "target", "Benutzer dessen Level du sehen möchtest", false))), [&](const dpp::slashcommand_t& event) {
|
||
// Get guild settings
|
||
db_add_guild(event.command.guild_id);
|
||
auto [enabled, embed_enabled, speed, color] = get_guild_data(event.command.guild_id);
|
||
// Make sure levels are enabled here
|
||
if (!enabled) {
|
||
event.reply(dpp::message("This server doesn't have levels enabled.").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Get target
|
||
dpp::snowflake target_id;
|
||
bool target_is_sender;
|
||
{
|
||
auto target_option = event.get_parameter("target");
|
||
if (target_option.index() == 0) {
|
||
target_id = event.command.usr.id;
|
||
target_is_sender = true;
|
||
} else {
|
||
target_id = std::get<dpp::snowflake>(target_option);
|
||
target_is_sender = target_id == event.command.usr.id;
|
||
}
|
||
}
|
||
// Get level info message and reply with it
|
||
event.reply(get_level_info_message(event.command.guild_id, target_id, target_is_sender, embed_enabled));
|
||
});
|
||
bot->add_chatcommand(Bot::ChatCommand({"levels_leaderboard", "leaderboard"}, "Shows the level leaderboard of this server"), [&](const dpp::slashcommand_t& event) {
|
||
// Get guild settings
|
||
db_add_guild(event.command.guild_id);
|
||
auto [enabled, embed_enabled, speed, color] = get_guild_data(event.command.guild_id);
|
||
// Make sure levels are enabled here
|
||
if (!enabled) {
|
||
event.reply(dpp::message("This server doesn't have levels enabled.").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Get all levels on this server sorted by highest-first
|
||
std::ostringstream leaderboard_str;
|
||
unsigned placement = 0;
|
||
bot->db << "SELECT level, user_id FROM levels "
|
||
"WHERE guild_id = ? "
|
||
"ORDER BY level DESC "
|
||
"LIMIT 10;"
|
||
<< std::to_string(event.command.guild_id)
|
||
>> [&](unsigned level, std::string user_id_str) {
|
||
if (level) {
|
||
leaderboard_str << "**" << ++placement << ".** <@" << user_id_str << ">" << " (Level *" << level << "*)" << (placement==1?" :crown:":"") << "\n";
|
||
}
|
||
};
|
||
// Create embed
|
||
dpp::embed embed;
|
||
embed.set_title("Leaderboard")
|
||
.set_description(std::move(leaderboard_str).str())
|
||
.set_color(color);
|
||
// Send result
|
||
event.reply(dpp::message().add_embed(embed));
|
||
});
|
||
bot->add_chatcommand(Bot::ChatCommand({"levels_settings"}, "Configure levels", dpp::slashcommand().add_option(dpp::command_option(dpp::command_option_type::co_boolean, "enabled", "Whether levels should be enabled", false)).add_option(dpp::command_option(dpp::command_option_type::co_boolean, "embed", "Whether levels should be shown in embeds", false)).add_option(dpp::command_option(dpp::command_option_type::co_string, "color", "Embed color of levels", false)).add_option(dpp::command_option(dpp::command_option_type::co_number, "speed", "Level speed: 0.5=half, 1.0=normal, 2.0=double, ...", false))), [&](const dpp::slashcommand_t& event) {
|
||
// Check that user has enough permissions
|
||
if (!event.command.get_guild().base_permissions(event.command.member).has(dpp::permissions::p_administrator)) {
|
||
event.reply(dpp::message("You need administration rights to use this command.").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Go on
|
||
db_add_guild(event.command.guild_id);
|
||
// Get parameters
|
||
auto opt_enabled = event.get_parameter("enabled"),
|
||
opt_embed = event.get_parameter("embed"),
|
||
opt_color = event.get_parameter("color"),
|
||
opt_speed = event.get_parameter("speed");
|
||
bool bad_color = false,
|
||
bad_speed = false;
|
||
// Update database
|
||
if (opt_enabled.index() != 0) {
|
||
bot->db << "UPDATE levels_guild_settings "
|
||
"SET enabled = ? "
|
||
"WHERE id = ?;"
|
||
<< std::get<bool>(opt_enabled)
|
||
<< std::to_string(event.command.guild_id);
|
||
}
|
||
if (opt_embed.index() != 0) {
|
||
bot->db << "UPDATE levels_guild_settings "
|
||
"SET embed = ? "
|
||
"WHERE id = ?;"
|
||
<< std::get<bool>(opt_embed)
|
||
<< std::to_string(event.command.guild_id);
|
||
}
|
||
if (opt_color.index() != 0) {
|
||
auto color = Util::str_to_color(std::get<std::string>(opt_color));
|
||
if (!(bad_color = color == -1U)) {
|
||
bot->db << "UPDATE levels_guild_settings "
|
||
"SET color = ? "
|
||
"WHERE id = ?;"
|
||
<< color
|
||
<< std::to_string(event.command.guild_id);
|
||
}
|
||
}
|
||
if (opt_speed.index() != 0) {
|
||
auto speed = std::get<double>(opt_speed);
|
||
if (!(bad_speed = speed <= 0.0 || speed > 7.0)) {
|
||
bot->db << "UPDATE levels_guild_settings "
|
||
"SET speed = ? "
|
||
"WHERE id = ?;"
|
||
<< speed
|
||
<< std::to_string(event.command.guild_id);
|
||
}
|
||
}
|
||
// Report success
|
||
event.reply(dpp::message(std::string("Done!")+
|
||
(bad_color?"\n**Warning:** I could not identify this color. Try again with a color code!":"")+
|
||
(bad_speed?"\n**Warning:** The specified speed is not allowed!":"")).set_flags(dpp::message_flags::m_ephemeral));
|
||
});
|
||
bot->add_chatcommand(Bot::ChatCommand({"levels_add_role", "level_role"}, "Add a level role", dpp::slashcommand().add_option(dpp::command_option(dpp::command_option_type::co_integer, "threshold", "Minimum level to have this role", true)).add_option(dpp::command_option(dpp::command_option_type::co_role, "role", "Role to assign", true))), [&](const dpp::slashcommand_t& event) {
|
||
// Check that user has enough permissions
|
||
if (!event.command.get_guild().base_permissions(event.command.member).has(dpp::permissions::p_manage_roles)) {
|
||
event.reply(dpp::message("You need role management rights to use this command.").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Get values
|
||
auto threshold = std::get<long>(event.get_parameter("threshold"));
|
||
auto role_id = std::get<dpp::snowflake>(event.get_parameter("role"));
|
||
// Check threshold
|
||
if (threshold <= 0) {
|
||
event.reply(dpp::message("You cannot set a role for this level!").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Insert into database
|
||
bot->db << "INSERT INTO levels_guild_roles (guild_id, role_id, threshold) VALUES (?, ?, ?);"
|
||
<< std::to_string(event.command.guild_id)
|
||
<< std::to_string(role_id)
|
||
<< threshold;
|
||
// Report success
|
||
event.reply(dpp::message("People with at least level "+std::to_string(threshold)+" are now assigned the role <@&"+std::to_string(role_id)+">!").set_allowed_mentions(true, false, false, true, {}, {}));
|
||
// Assign role where needed
|
||
bot->db << "SELECT user_id FROM levels "
|
||
"WHERE guild_id = ? AND level >= ?;"
|
||
<< std::to_string(event.command.guild_id)
|
||
<< threshold
|
||
>> [&](std::string user_id_str) {
|
||
bot->cluster.guild_member_add_role(event.command.guild_id, user_id_str, role_id);
|
||
};
|
||
});
|
||
bot->add_chatcommand(Bot::ChatCommand({"levels_remove_role", "level_role_remove"}, "Remove a level role", dpp::slashcommand().add_option(dpp::command_option(dpp::command_option_type::co_role, "role", "Role to no longer be assigned", true))), [&](const dpp::slashcommand_t& event) {
|
||
// Check that user has enough permissions
|
||
if (!event.command.get_guild().base_permissions(event.command.member).has(dpp::permissions::p_manage_roles)) {
|
||
event.reply(dpp::message("You need role management rights to use this command.").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Get values
|
||
auto role_id = std::get<dpp::snowflake>(event.get_parameter("role"));
|
||
bot->db << "DELETE FROM levels_guild_roles "
|
||
"WHERE role_id = ? AND guild_id = ?;"
|
||
<< std::to_string(role_id)
|
||
<< std::to_string(event.command.guild_id);
|
||
// Report success
|
||
event.reply(dpp::message("The role <@&"+std::to_string(role_id)+"> is now no longer a level role.").set_allowed_mentions(true, false, false, true, {}, {}));
|
||
});
|
||
|
||
bot->add_usercommand(Bot::UserCommand({"Level anzeigen"}, "Shows the level of a member"), [&](const dpp::user_context_menu_t& event) {
|
||
// Get guild settings
|
||
db_add_guild(event.command.guild_id);
|
||
auto [enabled, embed_enabled, speed, color] = get_guild_data(event.command.guild_id);
|
||
// Make sure levels are enabled here
|
||
if (!enabled) {
|
||
event.reply(dpp::message("This server doesn't have levels enabled.").set_flags(dpp::message_flags::m_ephemeral));
|
||
return;
|
||
}
|
||
// Get target
|
||
dpp::snowflake target_id = event.get_user().id;
|
||
// Get level info message and reply with it
|
||
event.reply(get_level_info_message(event.command.guild_id, target_id, target_id == event.command.usr.id, embed_enabled, color));
|
||
});
|
||
|
||
bot->cluster.on_message_create([&](const dpp::message_create_t& message) {
|
||
const auto& author = message.msg.author;
|
||
// Make sure user isn't a bot
|
||
if (author.is_bot()) {
|
||
return;
|
||
}
|
||
// Make sure guild has entry in database
|
||
db_add_guild(message.msg.guild_id);
|
||
// Get guild data
|
||
auto [enabled, embed_enabled, speed, color] = get_guild_data(message.msg.guild_id);
|
||
// Make sure leveling is enabled on guild
|
||
if (!enabled) {
|
||
return;
|
||
}
|
||
// Make sure user has entry in database
|
||
db_add_user(message.msg.guild_id, author.id);
|
||
// Get user data
|
||
auto [level, xp] = get_user_data(message.msg.guild_id, author.id);
|
||
// Increment XP
|
||
xp += (1.0/double(level+1))*0.4739*speed;
|
||
// Increment level and reset xp as needed
|
||
while (xp >= 1.0) {
|
||
xp -= 1.0;
|
||
level++;
|
||
// Notify user about levelup
|
||
std::string msg_str = "You are now level **"+std::to_string(level)+"**.";
|
||
dpp::message msg;
|
||
msg.set_channel_id(message.msg.channel_id)
|
||
.set_allowed_mentions(true, false, false, true, {}, {});
|
||
if (embed_enabled) {
|
||
dpp::embed embed;
|
||
embed.set_title("× Level up!")
|
||
.set_description(msg_str)
|
||
.set_thumbnail(message.msg.author.get_avatar_url())
|
||
.set_color(color);
|
||
msg.add_embed(embed)
|
||
.set_content(message.msg.member.get_mention());
|
||
bot->cluster.message_create(msg);
|
||
} else {
|
||
message.reply(msg.set_content(message.msg.member.get_mention()+'\n'+msg_str));
|
||
}
|
||
// Get highest level role
|
||
dpp::snowflake highest_level_role = 0;
|
||
unsigned highest_threshold = 0;
|
||
bot->db << "SELECT role_id, threshold FROM levels_guild_roles "
|
||
"WHERE guild_id = ? AND threshold <= ?;"
|
||
<< std::to_string(message.msg.guild_id)
|
||
<< level
|
||
>> [&](std::string role_id_str, unsigned threshold) {
|
||
if (threshold > highest_threshold) {
|
||
highest_threshold = threshold;
|
||
highest_level_role = role_id_str;
|
||
}
|
||
};
|
||
// Update their roles if needed
|
||
if (highest_threshold) {
|
||
bot->cluster.guild_member_add_role(message.msg.guild_id, author.id, highest_level_role, [this, role_id = highest_level_role](const dpp::confirmation_callback_t& ccb) {
|
||
// Drop role from database on error
|
||
if (ccb.is_error()) {
|
||
try {
|
||
bot->db << "DELETE FROM levels_guild_roles "
|
||
"WHERE role_id = ?;"
|
||
<< std::to_string(role_id);
|
||
} catch (...) {}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
// Update database
|
||
set_user_data(message.msg.guild_id, author.id, level, xp);
|
||
});
|
||
}
|
||
};
|
||
BOT_ADD_MODULE(Levels);
|