diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | cmake/FindPCRE.cmake | 37 | ||||
-rw-r--r-- | include/142bot/bot.hpp | 5 | ||||
-rw-r--r-- | include/142bot/modules.hpp | 3 | ||||
-rw-r--r-- | modules/mmanager/mmanager.cpp | 107 | ||||
-rw-r--r-- | modules/reactions/reactions.cpp | 43 | ||||
-rw-r--r-- | resources/reactions.json | 3 | ||||
-rw-r--r-- | src/bot.cpp | 14 | ||||
-rw-r--r-- | src/main.cpp | 6 | ||||
-rw-r--r-- | src/meta.cpp | 9 | ||||
-rw-r--r-- | src/modules.cpp | 39 |
12 files changed, 254 insertions, 19 deletions
@@ -95,4 +95,5 @@ tags # End of https://www.toptal.com/developers/gitignore/api/cmake,c++,visualstudiocode,vim build/ -config.json
\ No newline at end of file +config.json +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b2e67c..40edb5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ FetchContent_Declare(dpp FetchContent_MakeAvailable(clog dpp) +include(cmake/FindPCRE.cmake) + find_package(fmt) string(ASCII 27 Esc) @@ -24,7 +26,7 @@ aux_source_directory("src" coresrc) add_executable(142bot ${coresrc}) include_directories("include") -target_link_libraries(142bot clog dpp fmt spdlog) +target_link_libraries(142bot clog dpp fmt spdlog pcre) set (modules_dir "modules") file(GLOB subdirlist ${modules_dir}/*) diff --git a/cmake/FindPCRE.cmake b/cmake/FindPCRE.cmake new file mode 100644 index 0000000..d6a77a5 --- /dev/null +++ b/cmake/FindPCRE.cmake @@ -0,0 +1,37 @@ +# Copyright (C) 2007-2009 LuaDist. +# Created by Peter Kapec <kapecp@gmail.com> +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Note: +# Searching headers and libraries is very simple and is NOT as powerful as scripts +# distributed with CMake, because LuaDist defines directories to search for. +# Everyone is encouraged to contact the author with improvements. Maybe this file +# becomes part of CMake distribution sometimes. + +# - Find pcre +# Find the native PCRE headers and libraries. +# +# PCRE_INCLUDE_DIRS - where to find pcre.h, etc. +# PCRE_LIBRARIES - List of libraries when using pcre. +# PCRE_FOUND - True if pcre found. + +# Look for the header file. +FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h) + +# Look for the library. +FIND_LIBRARY(PCRE_LIBRARY NAMES pcre) + +# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE. +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR) + +# Copy the results to the output variables. +IF(PCRE_FOUND) + SET(PCRE_LIBRARIES ${PCRE_LIBRARY}) + SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR}) +ELSE(PCRE_FOUND) + SET(PCRE_LIBRARIES) + SET(PCRE_INCLUDE_DIRS) +ENDIF(PCRE_FOUND) + +MARK_AS_ADVANCED(PCRE_INCLUDE_DIRS PCRE_LIBRARIES)
\ No newline at end of file diff --git a/include/142bot/bot.hpp b/include/142bot/bot.hpp index c919179..49aa956 100644 --- a/include/142bot/bot.hpp +++ b/include/142bot/bot.hpp @@ -1,3 +1,4 @@ +#include <dpp/snowflake.h> #include <dpp/user.h> #include <dpp/dpp.h> @@ -8,6 +9,7 @@ class Module; class ModuleLoader; class Bot { bool dev; + dpp::snowflake owner_id; public: class dpp::cluster * core; @@ -16,6 +18,9 @@ public: Bot(bool development, dpp::cluster* cluster); //virtual ~Bot(); + + void set_owner_id(dpp::snowflake id); + dpp::snowflake get_owner_id(); ModuleLoader* loader; diff --git a/include/142bot/modules.hpp b/include/142bot/modules.hpp index d1bb3f6..ab888d3 100644 --- a/include/142bot/modules.hpp +++ b/include/142bot/modules.hpp @@ -134,7 +134,7 @@ public: bool unload(const std::string &fname); // Unloads and then reloads a module - void reload(const std::string &fname); + bool reload(const std::string &fname); // Get list of loaded modules const ModuleMap& get_loaded_modules() const; @@ -186,6 +186,7 @@ public: virtual bool OnVoiceStateUpdate(const dpp::voice_state_update_t &obj); virtual bool OnVoiceServerUpdate(const dpp::voice_server_update_t &obj); virtual bool OnWebhooksUpdate(const dpp::webhooks_update_t &obj); + void EmbedSimple(const std::string &message, int64_t channelID); }; /* A macro that lets us simply define the entrypoint of a module by name */ diff --git a/modules/mmanager/mmanager.cpp b/modules/mmanager/mmanager.cpp index fda8b3c..36b6aae 100644 --- a/modules/mmanager/mmanager.cpp +++ b/modules/mmanager/mmanager.cpp @@ -19,14 +19,17 @@ #include <stdlib.h> #include <string> #include <142bot/modules.hpp> +#include <142bot/util.hpp> +#include <pcre.h> #include <dpp/dpp.h> +#include <fmt/format.h> class MManagerModule : public Module { double microseconds_ping; public: MManagerModule(Bot* creator, ModuleLoader* ml) : Module(creator, ml) { ml->attach({ I_OnMessage, I_OnReady }, this); - creator->core->log(dpp::ll_info, "ModuleManager online!"); + creator->core->log(dpp::ll_info, "ModuleManager online!"); } virtual std::string version() { @@ -43,7 +46,107 @@ public: } virtual bool OnMessage(const dpp::message_create_t &message, const std::string& clean_message, bool mentioned, const std::vector<std::string> &stringmentions) { - bot->core->log(dpp::ll_info, "Got message!"); + std::vector<std::string> param; + const char* pcre_error; + int pcre_error_ofs; + auto comp = pcre_compile(std::string("^sudo(\\s+(.+?))$").c_str(), PCRE_CASELESS | PCRE_MULTILINE, &pcre_error, &pcre_error_ofs, NULL); + if (!comp) { + bot->core->log(dpp::ll_error, pcre_error); + } + + int matcharr[90]; + int matchcount = pcre_exec(comp, NULL, clean_message.c_str(), clean_message.length(), 0, 0, matcharr, 90); + for (int i = 0; i < matchcount; ++i) { + param.push_back(std::string(clean_message.c_str() + matcharr[2 * i], (size_t)(matcharr[2 * i + 1] - matcharr[2 * i]))); + } + if (mentioned && matchcount > 0) { + if (message.msg.author.id == bot->get_owner_id()) { + // Tokenize + for (int i = 0; i < param.size(); i++) { + bot->core->log(dpp::ll_debug, fmt::format("{}", param[i])); + } + std::stringstream tokens(trim(param[2])); + std::string subcommand; + tokens >> subcommand; + + bot->core->log(dpp::ll_warning, fmt::format("SUDO: <{}> {}",message.msg.author.username, clean_message)); + + if (lowercase(subcommand) == "modules") { + std::stringstream s; + + // NOTE: GetModuleList's reference is safe from within a module event + const ModuleMap& modlist = bot->loader->get_loaded_modules(); + + s << "```diff" << std::endl; + s << fmt::format("╭─────────────────────────┬───────────┬────────────────────────────────────────────────╮") << std::endl; + s << fmt::format("│ Filename | Version | Description |") << std::endl; + s << fmt::format("├─────────────────────────┼───────────┼────────────────────────────────────────────────┤") << std::endl; + + for (auto mod = modlist.begin(); mod != modlist.end(); ++mod) { + s << fmt::format("│ {:23} | {:9} | {:46} |", mod->first, mod->second->version(), mod->second->description()) << std::endl; + } + s << fmt::format("╰─────────────────────────┴───────────┴────────────────────────────────────────────────╯") << std::endl; + s << "```"; + + dpp::channel* c = dpp::find_channel(message.msg.channel_id); + if (c) { + bot->core->message_create(dpp::message(c->id, s.str())); + + } + + } else if (lowercase(subcommand) == "load") { + std::string modfile; + tokens >> modfile; + if (bot->loader->load(modfile)) { + EmbedSimple("Loaded module: " + modfile, message.msg.channel_id); + } else { + EmbedSimple(std::string("Can't do that, check server logs"), message.msg.channel_id); + } + } else if (lowercase(subcommand) == "unload") { + std::string modfile; + tokens >> modfile; + if (modfile == "module_mmanager.so") { + EmbedSimple("That's the module manager, are you sure about that chief?", message.msg.channel_id); + } + + if (bot->loader->unload(modfile)) { + EmbedSimple("Unloaded module: " + modfile, message.msg.channel_id); + } else { + EmbedSimple("Can't do that, check server logs.", message.msg.channel_id); + } + } else if(lowercase(subcommand) == "reload") { + std::string modfile; + tokens >> modfile; + if (modfile == "module_mmanager.so") { + EmbedSimple("That's the module manager, are you sure about that chief?", message.msg.channel_id); + ::sleep(500); + } + + if (bot->loader->reload(modfile)) { + EmbedSimple("Reloaded module: " + modfile, message.msg.channel_id); + } else { + EmbedSimple("Can't do that, check server logs", message.msg.channel_id); + } + }else if (lowercase(subcommand) == "ping") { + dpp::channel* c = dpp::find_channel(message.msg.channel_id); + if (c) { + std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); + dpp::snowflake cid = message.msg.channel_id; + bot->core->message_create(dpp::message(message.msg.channel_id, "Pinging..."), [cid, this, start_time](const dpp::confirmation_callback_t & state) { + double microseconds_ping = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start_time).count(); + dpp::snowflake mid = (std::get<dpp::message>(state.value)).id; + this->bot->core->message_delete(mid, cid); + this->EmbedSimple(fmt::format("**Pong!** REST Response time: {:.3f} ms", microseconds_ping / 1000, 4), cid); + }); + } + } else { + EmbedSimple("Command not found.", message.msg.channel_id); + } + } else { + bot->core->log(dpp::ll_error, fmt::format("Called ModuleManager as a mortal ({})", bot->get_owner_id())); + message.reply(dpp::message("nope")); + } + } return true; } diff --git a/modules/reactions/reactions.cpp b/modules/reactions/reactions.cpp new file mode 100644 index 0000000..3818d0d --- /dev/null +++ b/modules/reactions/reactions.cpp @@ -0,0 +1,43 @@ +#include <dpp/message.h> +#include <dpp/json.h> +#include <stdlib.h> +#include <142bot/modules.hpp> +#include <fmt/format.h> + +using std::to_string; +class ReactionsModule : public Module { + std::map<std::string, std::string> reactionMap; +public: + ReactionsModule(Bot* creator, ModuleLoader* ml): Module(creator, ml) { + ml->attach({I_OnMessage}, this); + + std::ifstream f("resources/reactions.json"); + json reactions = json::parse(f); + + for(auto it = reactions.begin(); it != reactions.end(); it++) { + reactionMap.insert_or_assign(it.key(), reactions.value(it.key(), "")); + } + } + + virtual std::string version() { + return "0.1.0"; + } + + virtual std::string description() { + return "Auto-reactions based on keyword"; + } + virtual bool OnMessage(const dpp::message_create_t &message, const std::string& clean_message, bool mentioned, const std::vector<std::string> &stringmentions) { + for (auto i = reactionMap.begin(); i != reactionMap.end(); i++) { + if (clean_message.find(i->first) != std::string::npos) { + bot->core->message_add_reaction(message.msg, i->second); + bot->core->log(dpp::ll_debug, "Adding reaction based on keyword"); + } + } + + return true; + } + +}; + + +ENTRYPOINT(ReactionsModule)
\ No newline at end of file diff --git a/resources/reactions.json b/resources/reactions.json new file mode 100644 index 0000000..5d4ccaa --- /dev/null +++ b/resources/reactions.json @@ -0,0 +1,3 @@ +{ + "test": "🔧" +}
\ No newline at end of file diff --git a/src/bot.cpp b/src/bot.cpp index 1c764b7..57bf091 100644 --- a/src/bot.cpp +++ b/src/bot.cpp @@ -1,4 +1,5 @@ +#include <dpp/snowflake.h> #include <stdlib.h> #include <142bot/bot.hpp> #include <dpp/dpp.h> @@ -22,6 +23,14 @@ 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; } @@ -127,9 +136,8 @@ void Bot::onGuildUpdate(const dpp::guild_update_t &obj) } -void Bot::onMessageReactionAdd(const dpp::message_reaction_add_t &obj) -{ - +void Bot::onMessageReactionAdd(const dpp::message_reaction_add_t &obj) { + FOREACH_MOD(I_OnMessageReactionAdd, OnMessageReactionAdd(obj)); } diff --git a/src/main.cpp b/src/main.cpp index d58f474..8f3ad9a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include <spdlog/async.h> #include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/rotating_file_sink.h> +#include <string> using namespace std; @@ -29,7 +30,7 @@ int main(int argc, char const *argv[]) { log = std::make_shared<spdlog::async_logger>("logs", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); spdlog::register_logger(log); log->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [%L] [th#%t]%$ : %v"); - log->set_level(spdlog::level::level_enum::debug); + log->set_level(spdlog::level::level_enum::trace); /* Integrate spdlog logger to D++ log events */ bot.on_log([&bot, &log](const dpp::log_t & event) { @@ -59,9 +60,12 @@ int main(int argc, char const *argv[]) { Bot client(0, &bot); + client.set_owner_id(dpp::snowflake(cfg.value("owner", "00000000000"))); + /* Attach events to the Bot class methods */ bot.on_message_create(std::bind(&Bot::onMessage, &client, std::placeholders::_1)); bot.on_ready(std::bind(&Bot::onReady, &client, std::placeholders::_1)); + bot.on_message_reaction_add(std::bind(&Bot::onMessageReactionAdd, &client, std::placeholders::_1)); bot.start(dpp::st_wait); return 0; diff --git a/src/meta.cpp b/src/meta.cpp deleted file mode 100644 index 3ae58a7..0000000 --- a/src/meta.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include <dpp/dpp.h> -#include <commandhandler.h> - -using namespace std; -using namespace dpp; - -void about(const std::string &name, const parameter_list_t ¶ms, command_source) { - -}
\ No newline at end of file diff --git a/src/modules.cpp b/src/modules.cpp index efdc447..6a8d500 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -116,7 +116,7 @@ bool ModuleLoader::load(const std::string &fname) { dlclose(m.dlopen_handle); return false; } else { - bot->core->log(dpp::ll_info, fmt::format("Loaded module {}", m.mod->description())); + bot->core->log(dpp::ll_info, fmt::format("Loaded {} ({})", m.mod->description(), m.mod->version())); Modules[fname] = m; mod_map[fname] = m.mod; return true; @@ -173,6 +173,15 @@ bool ModuleLoader::unload(const std::string &fname) { } + +bool ModuleLoader::reload(const std::string &filename) +{ + /* Short-circuit evaluation here means that if Unload() returns false, + * Load() won't be called at all. + */ + return (unload(filename) && load(filename)); +} + /** * Return a given symbol name from a shared object represented by the ModuleNative value. */ @@ -410,3 +419,31 @@ bool Module::OnAllShardsReady() } + +/** + * Output a simple embed to a channel consisting just of a message. + */ +void Module::EmbedSimple(const std::string &message, int64_t channelID) +{ + std::stringstream s; + json embed_json; + + s << "{\"color\":16767488, \"description\": \"" << message << "\"}"; + + try { + embed_json = json::parse(s.str()); + } + catch (const std::exception &e) { + bot->core->log(dpp::ll_error, fmt::format("Invalid json for channel {} created by EmbedSimple: ", channelID, s.str())); + } + dpp::channel* channel = dpp::find_channel(channelID); + if (channel) { + dpp::message m; + m.channel_id = channel->id; + m.embeds.push_back(dpp::embed(&embed_json)); + bot->core->message_create(m); + + } else { + bot->core->log(dpp::ll_error, fmt::format("Invalid channel {} passed to EmbedSimple", channelID)); + } +}
\ No newline at end of file |