#include "sentry.h" #include #include #include #include <142bot/bot.hpp> #include #include <142bot/modules.hpp> #include <142bot/util.hpp> #include #include #include #include <142bot/db.hpp> #include #include namespace fs = std::filesystem; Bot::Bot(bool devel, dpp::cluster* cluster, char prefix, json &cfg) { dev = devel; this->core = cluster; this->prefix = prefix; this->cfg = cfg; std::string token = cfg.value("token", "bad-token"); this->core->log(dpp::ll_debug, "Attempting DB connection"); this->conn = db::connect(cfg.value("postgres", "postgres://localhost/142bot")); run_database_migrations(); this->loader = new ModuleLoader(this); this->loader->load_all(); // this->loader->LoadAll(); } /** * Attempts to run migrations stored in a directory local to CWD. * * Returns false on any error */ bool Bot::run_database_migrations() { sentry_value_t crumb = sentry_value_new_breadcrumb("debug", "Running database migrations"); sentry_value_set_by_key(crumb, "level", sentry_value_new_string("debug")); // Start a transaction this->core->log(dpp::ll_info, "Attempting database migrations..."); pqxx::work w(this->conn); // Get all files in ./migrations std::string path = "./migrations"; for (const auto &entry : fs::directory_iterator(path)) { std::ifstream f(entry.path()); std::ostringstream sstr; sstr << f.rdbuf(); std::string stmt = sstr.str(); this->core->log(dpp::ll_debug, fmt::format("Attempting to migrate database with statement {}", stmt)); try { w.exec0(stmt); } catch (std::exception &e) { w.abort(); this->core->log(dpp::ll_error, e.what()); return false; } } w.commit(); this->core->log(dpp::ll_info, "Done."); this->core->log(dpp::ll_info, "Preparing statements..."); create_queries(); this->core->log(dpp::ll_info, "Done"); return true; } void Bot::create_queries() { this->core->log(dpp::ll_trace, "Preparing state query"); this->conn.prepare("state", "SELECT value FROM bot_state WHERE setting=$1"); this->core->log(dpp::ll_trace, "Preparing update state query"); this->conn.prepare("update_state", "INSERT INTO bot_state (setting,value) VALUES ($1, $2) ON CONFLICT (setting) DO UPDATE SET value=EXCLUDED.value;"); } bool Bot::isDevMode() { return dev; } void Bot::set_owner_id(dpp::snowflake id) { this->owner_id = id; } dpp::snowflake Bot::get_owner_id() { return this->owner_id; } int64_t Bot::getID() { return this->user.id; } /** * Announces that the bot is online. Each shard receives one of the events. */ void Bot::onReady(const dpp::ready_t& ready) { this->user = core->me; FOREACH_MOD(I_OnReady, OnReady(ready)); } /** * Called on receipt of each message. We do our own cleanup of the message, sanitising any * mentions etc from the text before passing it along to modules. The bot's builtin ignore list * and a hard coded check against bots/webhooks and itself happen before any module calls, * and can't be overridden. */ void Bot::onMessage(const dpp::message_create_t &message) { if (!message.msg.author.id) { core->log(dpp::ll_info, fmt::format("Message dropped, no author: {}", message.msg.content)); return; } /* Ignore self, and bots */ if (message.msg.author.id != user.id && message.msg.author.is_bot() == false) { core->log(dpp::ll_debug, "Got message event"); /* Replace all mentions with raw nicknames */ bool mentioned = false; std::string mentions_removed = message.msg.content; std::vector stringmentions; for (auto m = message.msg.mentions.begin(); m != message.msg.mentions.end(); ++m) { stringmentions.push_back(std::to_string(m->first.id)); mentions_removed = ReplaceString(mentions_removed, std::string("<@") + std::to_string(m->first.id) + ">", m->first.username); mentions_removed = ReplaceString(mentions_removed, std::string("<@!") + std::to_string(m->first.id) + ">", m->first.username); if (m->first.id == user.id) { mentioned = true; } } std::string botusername = this->user.username; /* Remove bot's nickname from start of message, if it's there */ while (mentions_removed.substr(0, botusername.length()) == botusername) { mentions_removed = trim(mentions_removed.substr(botusername.length(), mentions_removed.length())); } /* Remove linefeeds, they mess with botnix */ mentions_removed = trim(mentions_removed); if (message.msg.content.starts_with(this->prefix)) { // Command core->log(dpp::ll_debug, fmt::format("Got command: {}", mentions_removed)); // Tokenize std::vector result; const char* str = message.msg.content.c_str(); do { const char *begin = str; while(*str != ' ' && *str) str++; core->log(dpp::ll_debug, std::string(begin, str)); result.push_back(std::string(begin, str)); } while (0 != *str++); core->log(dpp::ll_debug, "Attempting to call FOREACH_MOD"); FOREACH_MOD(I_OnCommand,OnCommand(message, lowercase(result[0].erase(0,1)), result)); } else { /* Call modules */ core->log(dpp::ll_debug, fmt::format("Got regular message: {}", mentions_removed)); FOREACH_MOD(I_OnMessage,OnMessage(message, mentions_removed, mentioned, stringmentions)); } } } void Bot::onChannel(const dpp::channel_create_t& channel_create) { FOREACH_MOD(I_OnChannelCreate, OnChannelCreate(channel_create)); } void Bot::onChannelDelete(const dpp::channel_delete_t& cd) { FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(cd)); } void Bot::onServerDelete(const dpp::guild_delete_t& gd) { FOREACH_MOD(I_OnGuildDelete, OnGuildDelete(gd)); } void Bot::onMember(const dpp::guild_member_add_t& gma) { FOREACH_MOD(I_OnGuildMemberAdd, OnGuildMemberAdd(gma)); } void Bot::onPresenceUpdate(const dpp::presence_update_t &pu) { FOREACH_MOD(I_OnPresenceUpdate, OnPresenceUpdate()); } void Bot::onTypingStart(const dpp::typing_start_t &obj) { } void Bot::onMessageUpdate(const dpp::message_update_t &obj) { } void Bot::onMessageDelete(const dpp::message_delete_t &obj) { } void Bot::onMessageDeleteBulk(const dpp::message_delete_bulk_t &obj) { } void Bot::onGuildUpdate(const dpp::guild_update_t &obj) { } void Bot::onMessageReactionAdd(const dpp::message_reaction_add_t &obj) { FOREACH_MOD(I_OnMessageReactionAdd, OnMessageReactionAdd(obj)); } void Bot::onMessageReactionRemove(const dpp::message_reaction_remove_t &obj) { } void Bot::onMessageReactionRemoveAll(const dpp::message_reaction_remove_all_t &obj) { } void Bot::onUserUpdate(const dpp::user_update_t &obj) { } void Bot::onResumed(const dpp::resumed_t &obj) { } void Bot::onChannelUpdate(const dpp::channel_update_t &obj) { } void Bot::onChannelPinsUpdate(const dpp::channel_pins_update_t &obj) { } void Bot::onGuildBanAdd(const dpp::guild_ban_add_t &obj) { } void Bot::onGuildBanRemove(const dpp::guild_ban_remove_t &obj) { } void Bot::onGuildEmojisUpdate(const dpp::guild_emojis_update_t &obj) { } void Bot::onGuildIntegrationsUpdate(const dpp::guild_integrations_update_t &obj) { } void Bot::onGuildMemberRemove(const dpp::guild_member_remove_t &obj) { } void Bot::onGuildMemberUpdate(const dpp::guild_member_update_t &obj) { } void Bot::onGuildMembersChunk(const dpp::guild_members_chunk_t &obj) { } void Bot::onGuildRoleCreate(const dpp::guild_role_create_t &obj) { } void Bot::onGuildRoleUpdate(const dpp::guild_role_update_t &obj) { } void Bot::onGuildRoleDelete(const dpp::guild_role_delete_t &obj) { } void Bot::onVoiceStateUpdate(const dpp::voice_state_update_t &obj) { } void Bot::onVoiceServerUpdate(const dpp::voice_server_update_t &obj) { } void Bot::onWebhooksUpdate(const dpp::webhooks_update_t &obj) { } void Bot::onSlashCommand(const dpp::slashcommand_t &event) { FOREACH_MOD(I_OnSlashCommand, OnSlashCommand(event)); } void Bot::onFormSubmit(const dpp::form_submit_t &event) { FOREACH_MOD(I_OnFormSubmit, OnFormSubmit(event)); }