From c6755d7ed4287d71c392a098e97bfa8003a4dcf7 Mon Sep 17 00:00:00 2001 From: Xminent Date: Fri, 3 Nov 2023 02:46:56 -0500 Subject: [PATCH 01/24] fix: :bug: fix GetProcAddress invocation (windows); add in-class initializers; suppress std::getenv warning --- CMakeLists.txt | 10 ++++++---- commands/Fun/8ball.cpp | 11 +++-------- include/saber/commands.hpp | 6 +++--- include/saber/library.hpp | 2 +- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6872158..23c2018 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,11 @@ file(GLOB_RECURSE sources CONFIGURE_DEPENDS "include/*.hpp" "src/*.cpp") add_executable(${PROJECT_NAME} ${sources}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) + +if(WIN32) + target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + target_include_directories( ${PROJECT_NAME} PUBLIC $ $ @@ -66,12 +71,9 @@ foreach(fullcmdname ${commands}) target_include_directories( cmd_${cmdname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ) - target_link_libraries(cmd_${cmdname} PRIVATE ekizu) + target_link_libraries(cmd_${cmdname} PRIVATE ekizu spdlog::spdlog) endforeach(fullcmdname) if(${PROJECT_NAME}_INSTALL) install(TARGETS ${PROJECT_NAME} DESTINATION bin) endif() - -target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=thread) -target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=thread) diff --git a/commands/Fun/8ball.cpp b/commands/Fun/8ball.cpp index e38e4fd..6bf713f 100644 --- a/commands/Fun/8ball.cpp +++ b/commands/Fun/8ball.cpp @@ -1,4 +1,3 @@ -#include #include #include @@ -12,7 +11,7 @@ struct Eightball : Command { DIRNAME, true, {}, - false, + {}, {}, {}, { "eight-ball", "eightball" }, @@ -21,14 +20,10 @@ struct Eightball : Command { { ekizu::Permissions::SendMessages, ekizu::Permissions::EmbedLinks }, {}, - false, - false, - 3000, - {}, {}, {}, - {}, - {} }) + 3000, + }) { } diff --git a/include/saber/commands.hpp b/include/saber/commands.hpp index e6a51de..e818a88 100644 --- a/include/saber/commands.hpp +++ b/include/saber/commands.hpp @@ -62,11 +62,11 @@ struct CommandOptions { bool nsfw{}; bool owner_only{}; uint32_t cooldown{ 3000 }; - std::vector examples; - std::string subcommands; + std::vector examples{}; + std::string subcommands{}; bool activity{}; bool voice_only{}; - std::string category; + std::string category{}; }; struct Command { diff --git a/include/saber/library.hpp b/include/saber/library.hpp index a64278c..67c8967 100644 --- a/include/saber/library.hpp +++ b/include/saber/library.hpp @@ -36,7 +36,7 @@ struct Library { } #ifdef _WIN32 - auto *ptr = ::GetProcAddress(name.data(), nullptr); + auto *ptr = ::GetProcAddress(m_handle, name.data()); #else auto *ptr = dlsym(m_handle, name.data()); #endif From 6b63c3bbd6f2eda9a8506e62e6253f01c30ba894 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 12:20:55 -0600 Subject: [PATCH 02/24] fix: :bug: link commands to saber library to inherit utility methods --- CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23c2018..67e5bbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,21 +46,24 @@ cpmfindpackage( ) file(GLOB_RECURSE sources CONFIGURE_DEPENDS "include/*.hpp" "src/*.cpp") +list(FILTER sources EXCLUDE REGEX ".*main\\.cpp$") -add_executable(${PROJECT_NAME} ${sources}) +add_library(${PROJECT_NAME}_lib ${sources}) +add_executable(${PROJECT_NAME} src/main.cpp) -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) +target_compile_features(${PROJECT_NAME}_lib PUBLIC cxx_std_17) if(WIN32) target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) endif() target_include_directories( - ${PROJECT_NAME} PUBLIC $ - $ + ${PROJECT_NAME}_lib PUBLIC $ + $ ) -target_link_libraries(${PROJECT_NAME} PRIVATE ekizu spdlog::spdlog) +target_link_libraries(${PROJECT_NAME}_lib PUBLIC ekizu spdlog::spdlog) +target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib) file(GLOB_RECURSE commands CONFIGURE_DEPENDS commands/*.cpp) @@ -68,12 +71,9 @@ foreach(fullcmdname ${commands}) get_filename_component(cmdname ${fullcmdname} NAME_WE) message(STATUS "Found command:'cmd_${cmdname}'") add_library(cmd_${cmdname} SHARED ${fullcmdname}) - target_include_directories( - cmd_${cmdname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include - ) - target_link_libraries(cmd_${cmdname} PRIVATE ekizu spdlog::spdlog) + target_link_libraries(cmd_${cmdname} PRIVATE ${PROJECT_NAME}_lib) endforeach(fullcmdname) if(${PROJECT_NAME}_INSTALL) install(TARGETS ${PROJECT_NAME} DESTINATION bin) -endif() +endif() \ No newline at end of file From e27f285d6ae05be968f7a6ad80b560fa2256fccb Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 12:30:40 -0600 Subject: [PATCH 03/24] style: :art: update .clang-format --- .clang-format | 121 ++++--------------------------------- include/saber/commands.hpp | 83 ++++++++++++------------- include/saber/library.hpp | 23 +++---- include/saber/saber.hpp | 20 ++++-- include/saber/util.hpp | 10 ++- src/commands.cpp | 106 ++++++++++++++------------------ src/library.cpp | 31 ++++------ src/main.cpp | 5 +- src/saber.cpp | 61 +++++++++---------- src/util.cpp | 34 ++++------- 10 files changed, 180 insertions(+), 314 deletions(-) diff --git a/.clang-format b/.clang-format index 371fedc..3f98c96 100644 --- a/.clang-format +++ b/.clang-format @@ -1,113 +1,14 @@ ---- -AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Left -AlignOperands: true -AlignTrailingComments: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: false -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: true - AfterNamespace: true - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BreakBeforeInheritanceComma: false -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeComma -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: false +BasedOnStyle: Google +IndentWidth: 4 ColumnLimit: 80 -CommentPragmas: "^ IWYU pragma:" -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 8 -ContinuationIndentWidth: 8 -Cpp11BracedListStyle: false -DerivePointerAlignment: false -DisableFormat: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false - -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH - -IncludeBlocks: Preserve -IncludeCategories: - - Regex: ".*" - Priority: 1 -IncludeIsMainRegex: "(Test)?$" -IndentCaseLabels: false -IndentGotoLabels: false -IndentPPDirectives: None -IndentWidth: 8 -IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: "" -MacroBlockEnd: "" -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 8 -ObjCSpaceAfterProperty: true -ObjCSpaceBeforeProtocolList: true - -# Taken from git's rules -PenaltyBreakAssignment: 10 -PenaltyBreakBeforeFirstCallParameter: 30 -PenaltyBreakComment: 10 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakString: 10 -PenaltyExcessCharacter: 100 -PenaltyReturnTypeOnItsOwnLine: 60 - -PointerAlignment: Right -ReflowComments: false +TabWidth: 4 +UseTab: Always +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortLambdasOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true SortIncludes: true SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatementsExceptForEachMacros -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp03 -TabWidth: 8 -UseTab: Always +PenaltyIndentedWhitespace: 1 diff --git a/include/saber/commands.hpp b/include/saber/commands.hpp index e818a88..cf3ddc3 100644 --- a/include/saber/commands.hpp +++ b/include/saber/commands.hpp @@ -11,8 +11,7 @@ #include #include -namespace saber -{ +namespace saber { struct Command; struct Saber; @@ -28,27 +27,29 @@ struct CommandLoader { void load_all(); void process_commands(const ekizu::Message &message); void unload(const std::string &name); + void get_commands( + ekizu::FunctionView > &)>) + const; - private: + private: mutable std::mutex m_mtx; Saber *m_parent{}; std::unordered_map commands; std::unordered_map > command_map; std::unordered_map > alias_map; - std::unordered_map > - slash_commands; + std::unordered_map > slash_commands; std::unordered_map > user_commands; std::unordered_map< std::string, - std::unordered_map > + std::unordered_map > cooldowns; }; struct CommandOptions { std::string name; std::string dirname; - bool enabled{ true }; + bool enabled{true}; bool init{}; bool guild_only{}; bool slash{}; @@ -61,7 +62,7 @@ struct CommandOptions { std::vector bot_permissions; bool nsfw{}; bool owner_only{}; - uint32_t cooldown{ 3000 }; + uint32_t cooldown{3000}; std::vector examples{}; std::string subcommands{}; bool activity{}; @@ -71,14 +72,12 @@ struct CommandOptions { struct Command { Command(Saber *instigator, CommandOptions options_) - : bot{ instigator } - , options{ std::move(options_) } - { + : bot{instigator}, options{std::move(options_)} { // Determine category based on the name of the folder the command is in. - options.category = !options.dirname.empty() ? options.dirname : - "Other"; + options.category = !options.dirname.empty() ? options.dirname : "Other"; - // If the command is a slash command, make sure to make the appropriate JSON data. + // If the command is a slash command, make sure to make the appropriate + // JSON data. if (options.slash) { // slash_data.name = options.name; @@ -102,53 +101,49 @@ struct Command { virtual void setup() = 0; /// Method reserved for message commands' execution. virtual void execute(const ekizu::Message &message, - const std::vector &args) = 0; + const std::vector &args) = 0; Saber *bot{}; CommandOptions options; }; #if defined(__linux__) || defined(__APPLE__) -#define COMMAND_ALLOC(name) \ - extern "C" { \ - __attribute__((visibility("default"))) Command * \ - init_command(Saber *parent) \ - { \ - return new name(parent); \ - } \ +#define COMMAND_ALLOC(name) \ + extern "C" { \ + __attribute__((visibility("default"))) Command *init_command( \ + Saber *parent) { \ + return new name(parent); \ + } \ } #elif defined(_WIN32) -#define COMMAND_ALLOC(name) \ - extern "C" { \ - __declspec(dllexport) Command *init_command(Saber *parent) \ - { \ - return new name(parent); \ - } \ +#define COMMAND_ALLOC(name) \ + extern "C" { \ + __declspec(dllexport) Command *init_command(Saber *parent) { \ + return new name(parent); \ + } \ } #endif #if defined(__linux__) || defined(__APPLE__) -#define COMMAND_FREE(name) \ - extern "C" { \ - __attribute__((visibility("default"))) void \ - free_command(Command *command) \ - { \ - delete command; \ - } \ - } -#elif defined(_WIN32) -#define COMMAND_FREE(name) \ +#define COMMAND_FREE(name) \ extern "C" { \ - __declspec(dllexport) void free_command(Command *command) \ - { \ - delete command; \ + __attribute__((visibility("default"))) void free_command( \ + Command *command) { \ + delete command; \ } \ } +#elif defined(_WIN32) +#define COMMAND_FREE(name) \ + extern "C" { \ + __declspec(dllexport) void free_command(Command *command) { \ + delete command; \ + } \ + } #endif // I miss javascript #define DIRNAME \ std::filesystem::path(__FILE__).parent_path().filename().string() -} // namespace saber +} // namespace saber -#endif // SABER_COMMANDS_HPP +#endif // SABER_COMMANDS_HPP diff --git a/include/saber/library.hpp b/include/saber/library.hpp index 67c8967..031c669 100644 --- a/include/saber/library.hpp +++ b/include/saber/library.hpp @@ -6,7 +6,7 @@ #include #ifdef _WIN32 -#define _WINSOCKAPI_ // stops windows.h including winsock.h +#define _WINSOCKAPI_ // stops windows.h including winsock.h #include using dlopen_handle_t = HMODULE; #else @@ -14,13 +14,14 @@ using dlopen_handle_t = HMODULE; using dlopen_handle_t = void *; #endif -namespace saber -{ -template using Result = ekizu::Result; +namespace saber { +template +using Result = ekizu::Result; /** - * @brief Simple cross-platform wrapper for platform-specific dlopen()-like functions. Supports loading instances of - * objects with supporting allocater and deallocater functions. + * @brief Simple cross-platform wrapper for platform-specific dlopen()-like + * functions. Supports loading instances of objects with supporting allocater + * and deallocater functions. * * @tparam T The type of object to load and manage. * @tparam Args The types of the arguments for the allocater. @@ -28,8 +29,8 @@ template using Result = ekizu::Result; struct Library { static Result create(std::string_view path); - template Result get(std::string_view name) const - { + template + Result get(std::string_view name) const { if (m_handle == nullptr) { return tl::make_unexpected( std::make_error_code(std::errc::bad_address)); @@ -55,12 +56,12 @@ struct Library { Library &operator=(Library &&) noexcept; ~Library(); - private: + private: Library(dlopen_handle_t handle); /// Our handle to the shared library. dlopen_handle_t m_handle{}; }; -} // namespace saber +} // namespace saber -#endif // SABER_LIBRARY_HPP +#endif // SABER_LIBRARY_HPP diff --git a/include/saber/saber.hpp b/include/saber/saber.hpp index c0c59cf..3761be8 100644 --- a/include/saber/saber.hpp +++ b/include/saber/saber.hpp @@ -1,14 +1,17 @@ #ifndef SABER_SABER_HPP #define SABER_SABER_HPP +#include +#include + #include #include #include -#include -#include -namespace saber -{ +#include "ekizu/current_user.hpp" +#include "ekizu/snowflake.hpp" + +namespace saber { struct Saber { Saber(std::string_view token); @@ -19,8 +22,13 @@ struct Saber { CommandLoader commands; ekizu::HttpClient http; std::shared_ptr logger; + ekizu::LruCache messages_cache{500}; + ekizu::Snowflake owner_id{155780111197536256}; + std::string prefix{">"}; ekizu::Shard shard; + ekizu::CurrentUser user; + ekizu::LruCache users_cache{500}; }; -} // namespace saber +} // namespace saber -#endif // SABER_SABER_HPP +#endif // SABER_SABER_HPP diff --git a/include/saber/util.hpp b/include/saber/util.hpp index 4d9f319..8a15a7f 100644 --- a/include/saber/util.hpp +++ b/include/saber/util.hpp @@ -6,12 +6,10 @@ #include #include -namespace saber::util -{ +namespace saber::util { template T get_random_number(T begin = (std::numeric_limits::min)(), - T end = (std::numeric_limits::max)()) -{ + T end = (std::numeric_limits::max)()) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution dis(begin, end); @@ -22,6 +20,6 @@ std::vector split(std::string_view s, std::string_view delimiter); std::string <rim(std::string &s); std::string &rtrim(std::string &s); std::string &trim(std::string &s); -} // namespace saber::util +} // namespace saber::util -#endif // SABER_UTIL_HPP +#endif // SABER_UTIL_HPP diff --git a/src/commands.cpp b/src/commands.cpp index e5e3958..13e6e0c 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -2,56 +2,44 @@ #include #include -namespace -{ +namespace { #ifdef _WIN32 -constexpr std::string_view LIBRARY_EXTENSION{ ".dll" }; +constexpr std::string_view LIBRARY_EXTENSION{".dll"}; #elif __linux__ -constexpr std::string_view LIBRARY_EXTENSION{ ".so" }; +constexpr std::string_view LIBRARY_EXTENSION{".so"}; #elif __APPLE__ -constexpr std::string_view LIBRARY_EXTENSION{ ".dylib" }; +constexpr std::string_view LIBRARY_EXTENSION{".dylib"}; #endif -} // namespace +} // namespace -namespace saber -{ -CommandLoader::CommandLoader(Saber *parent) - : m_parent{ parent } -{ -} +namespace saber { +CommandLoader::CommandLoader(Saber *parent) : m_parent{parent} {} -void CommandLoader::load(std::string_view path) -{ +void CommandLoader::load(std::string_view path) { auto library = Library::create(path); if (!library) { + m_parent->logger->error( + "Failed to load command {}: {}", path, library.error().message()); return; } auto init_command = library->get("init_command"); auto free_command = library->get("free_command"); - if (!init_command || !free_command) { - return; - } + if (!init_command || !free_command) { return; } - std::scoped_lock lk{ m_mtx }; + std::scoped_lock lk{m_mtx}; auto *command_ptr = (*init_command)(m_parent); - if (command_ptr == nullptr) { - return; - } + if (command_ptr == nullptr) { return; } - if (command_ptr->options.init) { - command_ptr->setup(); - } + if (command_ptr->options.init) { command_ptr->setup(); } const auto &command_name = command_ptr->options.name; const std::shared_ptr loader{ - command_ptr, - [dealloc = *free_command](Command *ptr) { dealloc(ptr); } - }; + command_ptr, [dealloc = *free_command](Command *ptr) { dealloc(ptr); }}; commands.insert_or_assign(command_name, std::move(*library)); command_map.insert_or_assign(command_name, loader); @@ -71,66 +59,53 @@ void CommandLoader::load(std::string_view path) m_parent->logger->info("Loaded command {}", command_name); } -void CommandLoader::load_all() -{ +void CommandLoader::load_all() { namespace fs = std::filesystem; for (const auto &file : fs::directory_iterator(".")) { const auto filename = file.path().filename().string(); if (filename.find("cmd_") != std::string::npos && - (filename.size() >= LIBRARY_EXTENSION.size() && - filename.compare(filename.size() - LIBRARY_EXTENSION.size(), - LIBRARY_EXTENSION.size(), - LIBRARY_EXTENSION) == 0)) { - load(fs::absolute(file.path()) - .lexically_normal() - .string()); + (filename.size() >= LIBRARY_EXTENSION.size() && + filename.compare( + filename.size() - LIBRARY_EXTENSION.size(), + LIBRARY_EXTENSION.size(), LIBRARY_EXTENSION) == 0)) { + load(fs::absolute(file.path()).lexically_normal().string()); } } m_parent->logger->info("Loaded {} commands", commands.size()); } -void CommandLoader::process_commands(const ekizu::Message &message) -{ - if (message.author.bot) { - return; - } +void CommandLoader::process_commands(const ekizu::Message &message) { + if (message.author.bot) { return; } - auto content = message.content; + auto content = message.content.substr(m_parent->prefix.size()); auto args = util::split(util::trim(content), " "); - if (args.empty()) { - return; - } + if (args.empty()) { return; } auto command_name = std::move(args.front()); args.erase(args.begin()); - std::transform(command_name.begin(), command_name.end(), - command_name.begin(), [](uint8_t c) { - return static_cast(std::tolower(c)); - }); + std::transform( + command_name.begin(), command_name.end(), command_name.begin(), + [](uint8_t c) { return static_cast(std::tolower(c)); }); - std::unique_lock lk{ m_mtx }; + std::unique_lock lk{m_mtx}; - if (command_map.find(command_name) == command_map.end()) { - return; - } + if (command_map.find(command_name) == command_map.end()) { return; } - // NOTE: I'm seeing a case in which the commands will need the lock so it should be unlocked here. i.e. an unload command or something. + // NOTE: I'm seeing a case in which the commands will need the lock so it + // should be unlocked here. i.e. an unload command or something. lk.unlock(); command_map.at(command_name)->execute(message, args); } -void CommandLoader::unload(const std::string &name) -{ - std::scoped_lock lk{ m_mtx }; +void CommandLoader::unload(const std::string &name) { + std::scoped_lock lk{m_mtx}; - if (commands.find(name) == commands.end()) { - return; - } + if (commands.find(name) == commands.end()) { return; } const auto command = std::move(command_map.at(name)); @@ -144,4 +119,13 @@ void CommandLoader::unload(const std::string &name) user_commands.erase(name); m_parent->logger->info("Unloaded command {}", name); } -} // namespace saber \ No newline at end of file + +void CommandLoader::get_commands( + ekizu::FunctionView > &)> + cb) const { + std::scoped_lock lk{m_mtx}; + + cb(command_map); +} +} // namespace saber \ No newline at end of file diff --git a/src/library.cpp b/src/library.cpp index 849c94d..92813f8 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1,9 +1,9 @@ #include -namespace saber -{ -Result Library::create(std::string_view path) -{ +#include "fmt/core.h" + +namespace saber { +Result Library::create(std::string_view path) { if (path.empty()) { return tl::make_unexpected( std::make_error_code(std::errc::bad_address)); @@ -16,25 +16,21 @@ Result Library::create(std::string_view path) #endif if (handle == nullptr) { + fmt::print("Failed to load library: {}\n", dlerror()); return tl::make_unexpected( std::make_error_code(std::errc::bad_address)); } - return Library{ handle }; + return Library{handle}; } -Library::Library(dlopen_handle_t handle) - : m_handle{ handle } -{ -} +Library::Library(dlopen_handle_t handle) : m_handle{handle} {} -Library::Library(Library &&other) noexcept : m_handle{ other.m_handle } -{ +Library::Library(Library &&other) noexcept : m_handle{other.m_handle} { other.m_handle = nullptr; } -Library &Library::operator=(Library &&other) noexcept -{ +Library &Library::operator=(Library &&other) noexcept { if (this != &other) { m_handle = other.m_handle; other.m_handle = nullptr; @@ -43,11 +39,8 @@ Library &Library::operator=(Library &&other) noexcept return *this; } -Library::~Library() -{ - if (m_handle == nullptr) { - return; - } +Library::~Library() { + if (m_handle == nullptr) { return; } #ifdef _WIN32 FreeLibrary(m_handle); @@ -55,4 +48,4 @@ Library::~Library() dlclose(m_handle); #endif } -} // namespace saber +} // namespace saber diff --git a/src/main.cpp b/src/main.cpp index 5fcbd79..502a628 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,7 @@ #include -int main() -{ - auto saber = saber::Saber{ std::getenv("DISCORD_TOKEN") }; +int main() { + auto saber = saber::Saber{std::getenv("DISCORD_TOKEN")}; saber.run(); } diff --git a/src/saber.cpp b/src/saber.cpp index 6539d29..5d8e768 100644 --- a/src/saber.cpp +++ b/src/saber.cpp @@ -1,51 +1,48 @@ #include -namespace -{ -template struct overload : Func... { +namespace { +template +struct overload : Func... { using Func::operator()...; }; -template overload(Func...) -> overload; -} // namespace +template +overload(Func...) -> overload; +} // namespace -namespace saber -{ +namespace saber { Saber::Saber(std::string_view token) - : commands{ this } - , http{ token } - , shard{ ekizu::ShardId::ONE, token, ekizu::Intents::AllIntents } -{ + : commands{this}, + http{token}, + shard{ekizu::ShardId::ONE, token, ekizu::Intents::AllIntents} { logger = spdlog::stdout_color_mt("logger"); spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v"); spdlog::set_level(spdlog::level::debug); } -void Saber::run() -{ +void Saber::run() { commands.load_all(); (void)shard.run([this](const ekizu::Event &ev) { handle_event(ev); }); } -void Saber::handle_event(ekizu::Event ev) -{ +void Saber::handle_event(ekizu::Event ev) { std::visit( - overload{ [this](const ekizu::Ready &r) { - const auto &user = r.user; - - logger->info("Logged in as {}", user.username); - bot_id = user.id; - logger->info("API version: {}", r.v); - logger->info("Guilds: {}", r.guilds.size()); - }, - [this](const ekizu::MessageCreate &m) { - commands.process_commands(m.message); - }, - [this](ekizu::Resumed) { logger->info("Resumed"); }, - [this](const auto &e) { - logger->warn("Unhandled event: {}", - typeid(e).name()); - } }, + overload{[this](const ekizu::Ready &r) { + user = r.user; + logger->info("Logged in as {}", user.username); + bot_id = user.id; + logger->info("API version: {}", r.v); + logger->info("Guilds: {}", r.guilds.size()); + }, + [this](const ekizu::MessageCreate &m) { + messages_cache.put(m.message.id, m.message); + users_cache.put(m.message.author.id, m.message.author); + commands.process_commands(m.message); + }, + [this](ekizu::Resumed) { logger->info("Resumed"); }, + [this](const auto &e) { + logger->warn("Unhandled event: {}", typeid(e).name()); + }}, ev); } -} // namespace saber +} // namespace saber diff --git a/src/util.cpp b/src/util.cpp index f6d6495..d59104a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,10 +1,8 @@ #include #include -namespace saber::util -{ -std::vector split(std::string_view s, std::string_view delimiter) -{ +namespace saber::util { +std::vector split(std::string_view s, std::string_view delimiter) { std::vector ret; size_t pos{}; size_t prev{}; @@ -18,30 +16,22 @@ std::vector split(std::string_view s, std::string_view delimiter) return ret; } -std::string <rim(std::string &s) -{ - s.erase(s.begin(), - std::find_if(s.begin(), s.end(), [](unsigned char ch) { - return std::isspace(ch) == 0; - })); +std::string <rim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return std::isspace(ch) == 0; + })); return s; } -std::string &rtrim(std::string &s) -{ +std::string &rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), - [](unsigned char ch) { - return std::isspace(ch) == 0; - }) - .base(), - s.end()); + [](unsigned char ch) { return std::isspace(ch) == 0; }) + .base(), + s.end()); return s; } -std::string &trim(std::string &s) -{ - return ltrim(rtrim(s)); -} -} // namespace saber::util \ No newline at end of file +std::string &trim(std::string &s) { return ltrim(rtrim(s)); } +} // namespace saber::util \ No newline at end of file From a28557e187e994961edd8e1a96cb3baf2cb07023 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 13:16:11 -0600 Subject: [PATCH 04/24] ci: :construction_worker: add ci --- .github/workflows/ci.yml | 159 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b523c44 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,159 @@ +name: CI + +on: + push: + branches: + - dev + paths: + - "**ci.yml" + - "**.hpp" + - "**.cpp" + - "**CMakeLists.txt" + pull_request: + branches: + - dev + paths: + - "**ci.yml" + - "**.hpp" + - "**.cpp" + - "**CMakeLists.txt" + +jobs: + build: + strategy: + fail-fast: false + matrix: + config: + - { + os: ubuntu-latest, + arch: x64, + binary_path: "${{ github.workspace }}/build/saber", + output_name: "saber-linux-x64", + } + - { + os: windows-2022, + arch: x64, + binary_path: "${{ github.workspace }}/build/Release/saber.exe", + output_name: "saber-windows-x64.exe", + } + - { + os: windows-2022, + arch: x86, + binary_path: "${{ github.workspace }}/build/Release/saber.exe", + output_name: "saber-windows-x86.exe", + } + + name: build-${{ matrix.config.os }}-${{ matrix.config.arch }} + runs-on: ${{ matrix.config.os }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y cmake g++ libssl-dev ninja-build rpm zlib1g-dev + + - name: Add MSBuild to PATH (Windows) + if: runner.os == 'Windows' + uses: microsoft/setup-msbuild@v1.1 + + - name: Configure CMake (Linux) + if: runner.os == 'Linux' + run: cmake -S . -B build -G Ninja + + - name: Configure CMake (Windows x64) + if: runner.os == 'Windows' && matrix.config.arch == 'x64' + run: cmake -S . -B build -G "Visual Studio 17 2022" -A x64 -T host=x64 + + - name: Configure CMake (Windows x86) + if: runner.os == 'Windows' && matrix.config.arch == 'x86' + run: cmake -S . -B build -D net_FORCE_BUILD_OPENSSL=ON -G "Visual Studio 17 2022" -A Win32 -T host=x86 + + - name: Build + run: cmake --build build --config Release + + - name: Upload Binary + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.config.output_name }} + path: ${{ matrix.config.binary_path }} + + release: + needs: build + strategy: + fail-fast: false + matrix: + config: + - { os: ubuntu-latest, arch: x64, binary_name: saber-linux-x64 } + - { + os: windows-latest, + arch: x64, + binary_name: saber-windows-x64.exe, + } + - { + os: windows-latest, + arch: x86, + binary_name: saber-windows-x86.exe, + } + + name: release-${{ matrix.config.os }}-${{ matrix.config.arch }} + runs-on: ${{ matrix.config.os }} + if: github.event_name == 'push' + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Get Next Version + id: semver + uses: ietf-tools/semver-action@v1 + with: + token: ${{ github.token }} + branch: dev + + - name: Create Draft Release + uses: ncipollo/release-action@v1.12.0 + with: + prerelease: true + draft: false + commit: ${{ github.sha }} + tag: ${{ steps.semver.outputs.next }}-dev + name: ${{ steps.semver.outputs.next }}-dev + body: "*pending*" + token: ${{ github.token }} + + - name: Update CHANGELOG + id: changelog + uses: requarks/changelog-action@v1 + with: + token: ${{ github.token }} + tag: ${{ steps.semver.outputs.next }}-dev + writeToFile: false + + - name: Create Release + uses: ncipollo/release-action@v1.12.0 + with: + prerelease: true + allowUpdates: true + draft: false + makeLatest: true + commit: ${{ github.sha }} + tag: ${{ steps.semver.outputs.next }}-dev + name: ${{ steps.semver.outputs.next }}-dev + body: ${{ steps.changelog.outputs.changes }} + token: ${{ github.token }} + + - name: Download binaries + uses: actions/download-artifact@v3 + with: + name: ${{ matrix.config.binary_name }} + + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ github.token }} + file: ${{ matrix.config.binary_name }} + tag: ${{ steps.semver.outputs.next }}-dev From 67053978d7a1807e4124cb29ef90c4f953771d45 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 13:28:18 -0600 Subject: [PATCH 05/24] fix: :bug: fix dlerror being used in windows context --- src/library.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/library.cpp b/src/library.cpp index 92813f8..e372a25 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1,6 +1,6 @@ -#include +#include -#include "fmt/core.h" +#include namespace saber { Result Library::create(std::string_view path) { @@ -11,12 +11,14 @@ Result Library::create(std::string_view path) { #ifdef _WIN32 auto *handle = LoadLibrary(path.data()); + const auto last_error = GetLastError(); #else auto *handle = dlopen(path.data(), RTLD_NOW | RTLD_LOCAL); + const auto *last_error = dlerror(); #endif if (handle == nullptr) { - fmt::print("Failed to load library: {}\n", dlerror()); + fmt::println("Failed to load library: {}", last_error); return tl::make_unexpected( std::make_error_code(std::errc::bad_address)); } From aa7287a031ce62cd6985d1be348bc2f91a94a012 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 13:28:49 -0600 Subject: [PATCH 06/24] ci: :construction_worker: use g++-12 for ubuntu runner --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b523c44..34aa59d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y cmake g++ libssl-dev ninja-build rpm zlib1g-dev + sudo apt-get install -y cmake g++-12 libssl-dev ninja-build rpm zlib1g-dev - name: Add MSBuild to PATH (Windows) if: runner.os == 'Windows' From e48b16673448a30e44d002cf126385d28e049b22 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 13:40:27 -0600 Subject: [PATCH 07/24] ci: :construction_worker: use ubuntu-22.04 runner and g++-10; edit artifact search path for windows --- .github/workflows/ci.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34aa59d..5199424 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,22 +25,22 @@ jobs: matrix: config: - { - os: ubuntu-latest, + os: ubuntu-22.04, arch: x64, - binary_path: "${{ github.workspace }}/build/saber", - output_name: "saber-linux-x64", + binary_path: saber, + output_name: saber-linux-x64, } - { os: windows-2022, arch: x64, - binary_path: "${{ github.workspace }}/build/Release/saber.exe", - output_name: "saber-windows-x64.exe", + binary_path: Release/saber.exe, + output_name: saber-windows-x64.exe, } - { os: windows-2022, arch: x86, - binary_path: "${{ github.workspace }}/build/Release/saber.exe", - output_name: "saber-windows-x86.exe", + binary_path: Release/saber.exe, + output_name: saber-windows-x86.exe, } name: build-${{ matrix.config.os }}-${{ matrix.config.arch }} @@ -54,7 +54,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y cmake g++-12 libssl-dev ninja-build rpm zlib1g-dev + sudo apt-get install -y cmake g++-10 libssl-dev ninja-build rpm zlib1g-dev - name: Add MSBuild to PATH (Windows) if: runner.os == 'Windows' @@ -79,7 +79,8 @@ jobs: uses: actions/upload-artifact@v3 with: name: ${{ matrix.config.output_name }} - path: ${{ matrix.config.binary_path }} + path: ./build/${matrix.config.binary_path} + if-no-files-found: error release: needs: build From 589b12237efd65991fbdcdf3704b6add9b31e349 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 13:49:43 -0600 Subject: [PATCH 08/24] ci: :bug: fix var usage --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5199424..dc57a74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: ${{ matrix.config.output_name }} - path: ./build/${matrix.config.binary_path} + path: ./build/${{ matrix.config.binary_path }} if-no-files-found: error release: From 313c7ef05bebb878851e8639c707801047354d08 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 14:58:52 -0600 Subject: [PATCH 09/24] feat: :sparkles: add commands; add ekizu debug logging --- commands/Fun/beep.cpp | 44 +++++ commands/Fun/countdown.cpp | 58 +++++++ commands/Fun/css.cpp | 48 ++++++ commands/Fun/hype.cpp | 64 ++++++++ commands/Fun/meme.cpp | 77 +++++++++ commands/Fun/praise.cpp | 47 ++++++ commands/Fun/xkcd.cpp | 75 +++++++++ commands/Misc/about.cpp | 93 +++++++++++ commands/Misc/help.cpp | 323 +++++++++++++++++++++++++++++++++++++ include/saber/saber.hpp | 5 +- src/saber.cpp | 1 + 11 files changed, 831 insertions(+), 4 deletions(-) create mode 100644 commands/Fun/beep.cpp create mode 100644 commands/Fun/countdown.cpp create mode 100644 commands/Fun/css.cpp create mode 100644 commands/Fun/hype.cpp create mode 100644 commands/Fun/meme.cpp create mode 100644 commands/Fun/praise.cpp create mode 100644 commands/Fun/xkcd.cpp create mode 100644 commands/Misc/about.cpp create mode 100644 commands/Misc/help.cpp diff --git a/commands/Fun/beep.cpp b/commands/Fun/beep.cpp new file mode 100644 index 0000000..179bcaa --- /dev/null +++ b/commands/Fun/beep.cpp @@ -0,0 +1,44 @@ +#include +#include + +using namespace saber; + +struct Beep : Command { + explicit Beep(Saber *creator) + : Command(creator, CommandOptions{ + "beep", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "beep", + "Replies with boop.", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 3000, + }) + { + } + + void setup() override + { + } + + void + execute(const ekizu::Message &message, + [[maybe_unused]] const std::vector &args) override + { + (void)bot->http.create_message(message.channel_id) + .content("Boop!") + .send(); + } +}; + +COMMAND_ALLOC(Beep) +COMMAND_FREE(Beep) diff --git a/commands/Fun/countdown.cpp b/commands/Fun/countdown.cpp new file mode 100644 index 0000000..0c4b769 --- /dev/null +++ b/commands/Fun/countdown.cpp @@ -0,0 +1,58 @@ +#include +#include + +using namespace saber; + +struct Countdown : Command { + explicit Countdown(Saber *creator) + : Command(creator, CommandOptions{ + "countdown", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "countdown", + "Start counting down from 5.", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 0, + }) + { + } + + void setup() override + { + } + + void execute(const ekizu::Message &message, + const std::vector &args) override + { + if (!args.empty()) { + return; + } + + const std::vector countdown{ "five", "four", + "three", "two", + "one" }; + + for (const std::string &num : countdown) { + (void)bot->http.create_message(message.channel_id) + .content("**:" + num + ":**") + .send(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + (void)bot->http.create_message(message.channel_id) + .content("**:ok:** DING DING DING") + .send(); + } +}; + +COMMAND_ALLOC(Countdown) +COMMAND_FREE(Countdown) diff --git a/commands/Fun/css.cpp b/commands/Fun/css.cpp new file mode 100644 index 0000000..516bab9 --- /dev/null +++ b/commands/Fun/css.cpp @@ -0,0 +1,48 @@ +#include +#include + +using namespace saber; + +struct CSS : Command { + explicit CSS(Saber *creator) + : Command(creator, CommandOptions{ + "css", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "css", + "", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 0, + }) + { + } + + void setup() override + { + } + + void execute(const ekizu::Message &message, + const std::vector &args) override + { + if (!args.empty()) { + return; + } + + (void)bot->http.create_message(message.channel_id) + .content( + "https://media2.giphy.com/media/yYSSBtDgbbRzq/giphy.gif?cid=ecf05e47ckbtzm84p629vw655dbua1qzaiw8tl46ejp4f0xj&ep=v1_gifs_search&rid=giphy.gif&ct=g") + .send(); + } +}; + +COMMAND_ALLOC(CSS) +COMMAND_FREE(CSS) diff --git a/commands/Fun/hype.cpp b/commands/Fun/hype.cpp new file mode 100644 index 0000000..62ff8d4 --- /dev/null +++ b/commands/Fun/hype.cpp @@ -0,0 +1,64 @@ +#include +#include + +using namespace saber; + +struct Hype : Command { + explicit Hype(Saber *creator) + : Command(creator, CommandOptions{ + "hype", + DIRNAME, + true, + {}, + {}, + {}, + {}, + { "hypu", "train" }, + "hype", + "", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 0, + }) + { + } + + void setup() override + { + } + + void execute(const ekizu::Message &message, + const std::vector &args) override + { + if (!args.empty()) { + return; + } + + const std::string &selectedHype = + hypu[util::get_random_number(0, + hypu.size() - 1)]; + std::string msg = ":train2: CHOO CHOO " + selectedHype; + + (void)bot->http.create_message(message.channel_id) + .content(msg) + .send(); + } + + private: + std::vector hypu{ + "https://cdn.discordapp.com/attachments/102817255661772800/219514281136357376/tumblr_nr6ndeEpus1u21ng6o1_540.gif", + "https://cdn.discordapp.com/attachments/102817255661772800/219518372839161859/tumblr_n1h2afSbCu1ttmhgqo1_500.gif", + "https://gfycat.com/HairyFloweryBarebirdbat", + "https://i.imgur.com/PFAQSLA.gif", + "https://abload.de/img/ezgif-32008219442iq0i.gif", + "https://i.imgur.com/vOVwq5o.jpg", + "https://i.imgur.com/Ki12X4j.jpg", + "https://media.giphy.com/media/b1o4elYH8Tqjm/giphy.gif" + }; +}; + +COMMAND_ALLOC(Hype) +COMMAND_FREE(Hype) diff --git a/commands/Fun/meme.cpp b/commands/Fun/meme.cpp new file mode 100644 index 0000000..aec9e17 --- /dev/null +++ b/commands/Fun/meme.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +using namespace saber; + +struct Meme : Command { + explicit Meme(Saber *creator) + : Command(creator, + CommandOptions{ + "meme", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "meme", + "Displays a random meme from the `memes`, `dankmemes`, or `me_irl` subreddits.", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 0, + }) + { + } + + void setup() override + { + } + + void + execute(const ekizu::Message &message, + [[maybe_unused]] const std::vector &args) override + { + fetch_meme(message); + } + + void fetch_meme(const ekizu::Message &message) + { + auto res = net::http::get("https://meme-api.com/gimme"); + + if (!res) { + return; + } + + const auto json = + nlohmann::json::parse(res->body, nullptr, false); + + if (json.is_discarded() || !json.is_object()) { + return; + } + + auto embed = + ekizu::EmbedBuilder{} + .set_title(json["title"]) + .set_description(fmt::format( + "By: **{}** | {}", + json["author"].get(), + json["postLink"].get())) + .set_image({ + json["url"].get(), + }) + .build(); + + (void)bot->http.create_message(message.channel_id) + .embeds({ std::move(embed) }) + .send(); + } +}; + +COMMAND_ALLOC(Meme) +COMMAND_FREE(Meme) diff --git a/commands/Fun/praise.cpp b/commands/Fun/praise.cpp new file mode 100644 index 0000000..ef4dbc4 --- /dev/null +++ b/commands/Fun/praise.cpp @@ -0,0 +1,47 @@ +#include +#include + +using namespace saber; + +struct Praise : Command { + explicit Praise(Saber *creator) + : Command(creator, CommandOptions{ + "praise", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "praise", + "", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 0, + }) + { + } + + void setup() override + { + } + + void execute(const ekizu::Message &message, + const std::vector &args) override + { + if (!args.empty()) { + return; + } + + (void)bot->http.create_message(message.channel_id) + .content("https://i.imgur.com/K8ySn3e.gif") + .send(); + } +}; + +COMMAND_ALLOC(Praise) +COMMAND_FREE(Praise) diff --git a/commands/Fun/xkcd.cpp b/commands/Fun/xkcd.cpp new file mode 100644 index 0000000..78c3d43 --- /dev/null +++ b/commands/Fun/xkcd.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +using namespace saber; + +struct XKCD : Command { + explicit XKCD(Saber *creator) + : Command( + creator, + CommandOptions{ + "xkcd", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "xkcd", + "", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 0, + }) {} + + void setup() override {} + + void execute(const ekizu::Message &message, + const std::vector &args) override { + fetchXKCD(message, args); + } + + private: + std::string api_url{"https://xkcd.com{}info.0.json"}; + + void fetchXKCD(const ekizu::Message &message, + [[maybe_unused]] const std::vector &args) { + const auto res = + net::http::get(api_url.replace(api_url.find("{}"), 2, "/")); + + if (!res || res->status_code != net::HttpStatus::Ok) { + bot->logger->error("Error while fetching XKCD data"); + (void)bot->http.create_message(message.channel_id) + .content("There was an error. Please try again.") + .send(); + return; + } + + const auto json = nlohmann::json::parse(res->body, nullptr, false); + + if (json.is_discarded() || !json.is_object()) { + bot->logger->error("Error parsing XKCD data"); + (void)bot->http.create_message(message.channel_id) + .content("There was an error. Please try again.") + .send(); + return; + } + + const auto comic_url = fmt::format("https://xkcd.com/{}", json["num"]); + const auto msg = fmt::format( + "**{}**\n{}\nAlt Text:```{}```XKCD Link: <{}>", + json["safe_title"].get(), + json["img"].get(), json["alt"].get(), + comic_url); + + (void)bot->http.create_message(message.channel_id).content(msg).send(); + } +}; + +COMMAND_ALLOC(XKCD) +COMMAND_FREE(XKCD) diff --git a/commands/Misc/about.cpp b/commands/Misc/about.cpp new file mode 100644 index 0000000..c88231c --- /dev/null +++ b/commands/Misc/about.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +using namespace saber; + +struct About : Command { + explicit About(Saber *creator) + : Command( + creator, + CommandOptions{ + "about", + DIRNAME, + true, + true, + false, + {}, + {}, + {}, + "about", + "Info about me.", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 2000, + }) {} + + void setup() override { + bot->logger->info("Setting up About command"); + + bot->http.get_user(bot->owner_id) + .send() + .map([this](const ekizu::User &owner) { + bot->logger->info( + "Fetched owner: {} from the API", owner.username); + bot->users_cache.put(owner.id, owner); + bot->http.get_current_user().send().map( + [this, &owner](const ekizu::User &user) { + bot->logger->info( + "Fetched our own user: {} from the API", + user.username); + bot->users_cache.put(user.id, user); + + about_embed = + ekizu::EmbedBuilder{} + .set_title(fmt::format( + "🔥About :: Yamato | ID :: {}", user.id)) + .set_description( + "Yamato is a simple bot that was built for " + "my " + "personal Discord server. From providing " + "7DS " + "game " + "updates to moderating your own server, " + "this " + "is " + "the bot for you! I am glad you enjoy my " + "bot " + "and " + "hope you enjoy your stay 💖.") + .set_thumbnail({user.display_avatar_url()}) + .add_fields({ + {"Info\nOwner", + fmt::format("{}", bot->owner_id), true}, + }) + .set_footer( + {fmt::format("Made by {}", owner.username), + owner.display_avatar_url()}) + .build(); + + bot->logger->info("About command setup complete"); + }); + }); + } + + void execute( + const ekizu::Message &message, + [[maybe_unused]] const std::vector &args) override { + if (!about_embed) { return; } + + (void)bot->http.create_message(message.channel_id) + .embeds({*about_embed}) + .send(); + } + + private: + std::optional about_embed; +}; + +COMMAND_ALLOC(About) +COMMAND_FREE(About) diff --git a/commands/Misc/help.cpp b/commands/Misc/help.cpp new file mode 100644 index 0000000..aee3ce8 --- /dev/null +++ b/commands/Misc/help.cpp @@ -0,0 +1,323 @@ +#include +#include +#include + +// template +// std::vector filter(const std::vector &v, Predicate p) +// { +// std::vector result{}; +// result.reserve(v.size()); +// std::copy_if(v.begin(), v.end(), std::back_inserter(result), p); +// return result; +// } + +// // function template equivalent to filter but for unordered_maps. takes an unordered_map and a predicate. returns a new +// // unordered_map with all the elements that satisfy the predicate. +// template +// std::unordered_map +// filter(const std::unordered_map &v, Predicate p) +// { +// std::unordered_map result{}; +// for (const auto &pair : v) { +// if (p(pair)) { +// result.emplace(pair); +// } +// } +// return result; +// } + +// // function template equivalent to map but for unordered_maps. takes an unordered_map and a function. returns an array +// // of the results of the function. +// template +// std::vector map(const std::unordered_map &v, +// std::function &)> f) +// { +// std::vector result{}; +// result.reserve(v.size()); +// for (const auto &pair : v) { +// result.emplace_back(f(pair)); +// } +// return result; +// } + +// template +// std::vector map(const std::vector &v, std::function func) +// { +// std::vector result{}; +// result.reserve(v.size()); +// for (const auto &elem : v) { +// result.emplace_back(func(elem)); +// } +// return result; +// } + +// // to_lower function for strings. +// std::string to_lower(std::string_view str) +// { +// std::string result{ str }; +// std::transform(result.begin(), result.end(), result.begin(), +// [](uint8_t c) { +// return static_cast(std::tolower(c)); +// }); +// return result; +// } + +using namespace saber; + +struct Help : Command { + struct CategoricCommand { + std::string category{}; + std::vector commands{}; + }; + + explicit Help(Saber *creator) + : Command(creator, + CommandOptions{ "help", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "help", + "", + { ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks }, + {}, + {}, + {}, + 3000 }) + { + } + + void setup() override + { + } + + void execute(const ekizu::Message &message, + const std::vector &args) override + { + // if (args.empty()) { + // return create_help_menu(message); + // } + + return get_command_help(message, args[0]); + } + + void get_command_help(const ekizu::Message &message, + const std::string &command) + { + bot->commands.get_commands([this, &command, + &message](const std::unordered_map< + std::string, + std::shared_ptr > + &commands) { + if (commands.find(command) == commands.end()) { + (void)bot->http + .create_message(message.channel_id) + .content("Command not found.") + .send(); + return; + } + + const auto &cmd = commands.at(command); + auto builder = ekizu::EmbedBuilder{}.set_title( + cmd->options.name); + + if (!cmd->options.description.empty()) { + builder.set_description( + cmd->options.description); + } + + if (!cmd->options.examples.empty()) { + std::string examples{}; + examples.reserve((bot->prefix.length() + 3) * + cmd->options.examples.size()); + for (const auto &example : + cmd->options.examples) { + examples += fmt::format("`{}{}`\n", + bot->prefix, + example); + } + + builder.add_field({ "❯ Examples", examples }); + } + + if (!cmd->options.usage.empty()) { + builder.add_field( + { "❯ Usage", + fmt::format("`{}{}`", bot->prefix, + cmd->options.usage) }); + } + + if (const auto &aliases = cmd->options.aliases; + !aliases.empty()) { + const auto aliases_str = std::accumulate( + std::next(aliases.begin()), + aliases.end(), + fmt::format("`{}`", aliases[0]), + [](const auto &a, const auto &b) { + return fmt::format("{} | `{}`", + a, b); + }); + + builder.add_field({ "❯ Aliases", aliases_str }); + } + + builder.add_field( + { "❯ Cooldown", + fmt::format("{}ms", cmd->options.cooldown) }); + builder.add_field( + { "❯ Legend", + fmt::format("`<> required, [] optional`") }); + builder.set_footer( + { fmt::format("Prefix: {}", bot->prefix) }); + + (void)bot->http.create_message(message.channel_id) + .embeds({ builder.build() }) + .send(); + }); + } + + // void create_help_menu(const ekizu::Message &message) const + // { + // if (!message.channel()) { + // return; + // } + // const auto &commands = bot->command_loader.get_commands(); + // // Create a set of all the categories. + // std::vector categories{}; + // { + // std::unordered_set categories_set{}; + // std::transform( + // commands.begin(), commands.end(), + // std::inserter(categories_set, + // categories_set.end()), + // [](const auto &pair) { + // return pair.second->options.category; + // }); + // std::copy(categories_set.begin(), categories_set.end(), + // std::back_inserter(categories)); + // } + + // // Emojis for the categories. + // static const std::unordered_map emojis{ + // { "music", "🎵" }, { "fun", "🎉" }, + // { "hentai", "🔞" }, { "moderation", "🔨" }, + // { "owner", "👑" }, { "templates", "📷" }, + // { "steam", "🎮" }, { "guild", "🏠" }, + // { "misc", "❓" }, { "activity", "👷" }, + // }; + + // const auto formatted_categories = map( + // categories, + // std::function([&commands](const std::string &category) { + // const auto filtered_cmds = filter( + // commands, + // [&category](const auto &pair) { + // return pair.second->options + // .category == + // category; + // }); + + // const auto cmds = map( + // filtered_cmds, + // std::function( + // [](const std::pair< + // std::string, + // std::shared_ptr > + // &pair) { + // return pair.first; + // })); + + // return CategoricCommand{ category, cmds }; + // })); + + // const auto embed = ekizu::Embed().set_description( + // "Please choose a category."); + + // const auto components = [&formatted_categories](bool disabled) { + // // Create a new MessageComponent, being an ActionRow, who is disabled if disabled is true. + // return ekizu::ActionRow().add_component( + // ekizu::SelectMenu() + // .set_custom_id("helpMenu") + // .set_placeholder( + // "Please select a category.") + // .set_disabled(disabled) + // .add_options(map( + // formatted_categories, + // std::function([](const CategoricCommand + // &cmd) { + // return ekizu::SelectOptions{ + // .label = + // cmd.category, + // .value = to_lower( + // cmd.category), + // .description = fmt::format( + // "Commands from {} category.", + // cmd.category), + // .emoji = + // ekizu::PartialEmoji{ + // {}, + // emojis.at(to_lower( + // cmd.category)) }, + // }; + // })))); + // }; + + // // const auto& author = message.author(); + + // const auto initial_message = message.reply({ + // .embeds = { { embed } }, + // .components = { { components(false) } }, + // }); + + // auto &channel = message.channel(); + // auto &collector = + // channel.add_message_component_listener("helpMenu"); + + // collector.on_add.add_listener([this, + // fmt_categories = std::move( + // formatted_categories)]( + // ekizu::Interaction + // &interaction) { + // const auto category = interaction.data.values[0]; + // const auto formatted_category = *std::find_if( + // fmt_categories.begin(), fmt_categories.end(), + // [&category](const auto &cmd) { + // return to_lower(cmd.category) == + // category; + // }); + // const auto category_embed = + // ekizu::Embed() + // .set_title(fmt::format("{} commands.", + // category)) + // .set_description(fmt::format( + // "Here are the commands you can use.")) + // .add_fields(map( + // formatted_category.commands, + // std::function([this](const std::string + // &cmd) { + // return ekizu::EmbedField{ + // .name = cmd, + // .value = fmt::format( + // "`{}{}`", + // bot->prefix, + // cmd), + // }; + // }))); + + // interaction.update( + // { .embeds = { { category_embed } } }); + // }); + + // collector.on_remove.add_listener([initial_message, components] { + // initial_message->edit( + // { .components = { { components(true) } } }); + // }); + // } +}; + +COMMAND_ALLOC(Help) +COMMAND_FREE(Help) diff --git a/include/saber/saber.hpp b/include/saber/saber.hpp index 3761be8..e1c478a 100644 --- a/include/saber/saber.hpp +++ b/include/saber/saber.hpp @@ -8,12 +8,9 @@ #include #include -#include "ekizu/current_user.hpp" -#include "ekizu/snowflake.hpp" - namespace saber { struct Saber { - Saber(std::string_view token); + explicit Saber(std::string_view token); void run(); void handle_event(ekizu::Event ev); diff --git a/src/saber.cpp b/src/saber.cpp index 5d8e768..1622e12 100644 --- a/src/saber.cpp +++ b/src/saber.cpp @@ -39,6 +39,7 @@ void Saber::handle_event(ekizu::Event ev) { users_cache.put(m.message.author.id, m.message.author); commands.process_commands(m.message); }, + [this](const ekizu::Log &l) { logger->debug(l.message); }, [this](ekizu::Resumed) { logger->info("Resumed"); }, [this](const auto &e) { logger->warn("Unhandled event: {}", typeid(e).name()); From e3212ad9b258e032056f6fba0aace41644cc86b3 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 15:44:09 -0600 Subject: [PATCH 10/24] ci: :construction_worker: use installed gcc version; update to g++-12 --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc57a74..e629f79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y cmake g++-10 libssl-dev ninja-build rpm zlib1g-dev + sudo apt-get install -y cmake g++-12 libssl-dev ninja-build rpm zlib1g-dev - name: Add MSBuild to PATH (Windows) if: runner.os == 'Windows' @@ -63,6 +63,8 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: cmake -S . -B build -G Ninja + env: + CXX: g++-12 - name: Configure CMake (Windows x64) if: runner.os == 'Windows' && matrix.config.arch == 'x64' From c4d13fa25692982d4816a335075da976d6e0d991 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 16:27:35 -0600 Subject: [PATCH 11/24] fix: :bug: add header for failing windows builds; clean up help command --- commands/Misc/help.cpp | 369 +++++++++-------------------------------- 1 file changed, 75 insertions(+), 294 deletions(-) diff --git a/commands/Misc/help.cpp b/commands/Misc/help.cpp index aee3ce8..a7b38ea 100644 --- a/commands/Misc/help.cpp +++ b/commands/Misc/help.cpp @@ -1,322 +1,103 @@ #include +#include #include #include -// template -// std::vector filter(const std::vector &v, Predicate p) -// { -// std::vector result{}; -// result.reserve(v.size()); -// std::copy_if(v.begin(), v.end(), std::back_inserter(result), p); -// return result; -// } - -// // function template equivalent to filter but for unordered_maps. takes an unordered_map and a predicate. returns a new -// // unordered_map with all the elements that satisfy the predicate. -// template -// std::unordered_map -// filter(const std::unordered_map &v, Predicate p) -// { -// std::unordered_map result{}; -// for (const auto &pair : v) { -// if (p(pair)) { -// result.emplace(pair); -// } -// } -// return result; -// } - -// // function template equivalent to map but for unordered_maps. takes an unordered_map and a function. returns an array -// // of the results of the function. -// template -// std::vector map(const std::unordered_map &v, -// std::function &)> f) -// { -// std::vector result{}; -// result.reserve(v.size()); -// for (const auto &pair : v) { -// result.emplace_back(f(pair)); -// } -// return result; -// } - -// template -// std::vector map(const std::vector &v, std::function func) -// { -// std::vector result{}; -// result.reserve(v.size()); -// for (const auto &elem : v) { -// result.emplace_back(func(elem)); -// } -// return result; -// } - -// // to_lower function for strings. -// std::string to_lower(std::string_view str) -// { -// std::string result{ str }; -// std::transform(result.begin(), result.end(), result.begin(), -// [](uint8_t c) { -// return static_cast(std::tolower(c)); -// }); -// return result; -// } - using namespace saber; struct Help : Command { - struct CategoricCommand { - std::string category{}; - std::vector commands{}; - }; - explicit Help(Saber *creator) - : Command(creator, - CommandOptions{ "help", - DIRNAME, - true, - {}, - {}, - {}, - {}, - {}, - "help", - "", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, - {}, - {}, - {}, - 3000 }) - { - } - - void setup() override - { - } + : Command( + creator, + CommandOptions{ + "help", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "help", + "", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 3000}) {} + + void setup() override {} void execute(const ekizu::Message &message, - const std::vector &args) override - { - // if (args.empty()) { - // return create_help_menu(message); - // } + const std::vector &args) override { + // TODO: Implement help menu when no args are given. return get_command_help(message, args[0]); } void get_command_help(const ekizu::Message &message, - const std::string &command) - { - bot->commands.get_commands([this, &command, - &message](const std::unordered_map< - std::string, - std::shared_ptr > - &commands) { - if (commands.find(command) == commands.end()) { - (void)bot->http - .create_message(message.channel_id) - .content("Command not found.") - .send(); - return; - } - - const auto &cmd = commands.at(command); - auto builder = ekizu::EmbedBuilder{}.set_title( - cmd->options.name); - - if (!cmd->options.description.empty()) { - builder.set_description( - cmd->options.description); - } - - if (!cmd->options.examples.empty()) { - std::string examples{}; - examples.reserve((bot->prefix.length() + 3) * - cmd->options.examples.size()); - for (const auto &example : - cmd->options.examples) { - examples += fmt::format("`{}{}`\n", - bot->prefix, - example); + const std::string &command) { + bot->commands.get_commands( + [this, &command, &message]( + const std::unordered_map > + &commands) { + if (commands.find(command) == commands.end()) { + (void)bot->http.create_message(message.channel_id) + .content("Command not found.") + .send(); + return; } - builder.add_field({ "❯ Examples", examples }); - } - - if (!cmd->options.usage.empty()) { - builder.add_field( - { "❯ Usage", - fmt::format("`{}{}`", bot->prefix, - cmd->options.usage) }); - } - - if (const auto &aliases = cmd->options.aliases; - !aliases.empty()) { - const auto aliases_str = std::accumulate( - std::next(aliases.begin()), - aliases.end(), - fmt::format("`{}`", aliases[0]), - [](const auto &a, const auto &b) { - return fmt::format("{} | `{}`", - a, b); - }); - - builder.add_field({ "❯ Aliases", aliases_str }); - } + const auto &cmd = commands.at(command); + auto builder = + ekizu::EmbedBuilder{}.set_title(cmd->options.name); - builder.add_field( - { "❯ Cooldown", - fmt::format("{}ms", cmd->options.cooldown) }); - builder.add_field( - { "❯ Legend", - fmt::format("`<> required, [] optional`") }); - builder.set_footer( - { fmt::format("Prefix: {}", bot->prefix) }); - - (void)bot->http.create_message(message.channel_id) - .embeds({ builder.build() }) - .send(); - }); - } - - // void create_help_menu(const ekizu::Message &message) const - // { - // if (!message.channel()) { - // return; - // } - // const auto &commands = bot->command_loader.get_commands(); - // // Create a set of all the categories. - // std::vector categories{}; - // { - // std::unordered_set categories_set{}; - // std::transform( - // commands.begin(), commands.end(), - // std::inserter(categories_set, - // categories_set.end()), - // [](const auto &pair) { - // return pair.second->options.category; - // }); - // std::copy(categories_set.begin(), categories_set.end(), - // std::back_inserter(categories)); - // } - - // // Emojis for the categories. - // static const std::unordered_map emojis{ - // { "music", "🎵" }, { "fun", "🎉" }, - // { "hentai", "🔞" }, { "moderation", "🔨" }, - // { "owner", "👑" }, { "templates", "📷" }, - // { "steam", "🎮" }, { "guild", "🏠" }, - // { "misc", "❓" }, { "activity", "👷" }, - // }; - - // const auto formatted_categories = map( - // categories, - // std::function([&commands](const std::string &category) { - // const auto filtered_cmds = filter( - // commands, - // [&category](const auto &pair) { - // return pair.second->options - // .category == - // category; - // }); - - // const auto cmds = map( - // filtered_cmds, - // std::function( - // [](const std::pair< - // std::string, - // std::shared_ptr > - // &pair) { - // return pair.first; - // })); - - // return CategoricCommand{ category, cmds }; - // })); - - // const auto embed = ekizu::Embed().set_description( - // "Please choose a category."); + if (!cmd->options.description.empty()) { + builder.set_description(cmd->options.description); + } - // const auto components = [&formatted_categories](bool disabled) { - // // Create a new MessageComponent, being an ActionRow, who is disabled if disabled is true. - // return ekizu::ActionRow().add_component( - // ekizu::SelectMenu() - // .set_custom_id("helpMenu") - // .set_placeholder( - // "Please select a category.") - // .set_disabled(disabled) - // .add_options(map( - // formatted_categories, - // std::function([](const CategoricCommand - // &cmd) { - // return ekizu::SelectOptions{ - // .label = - // cmd.category, - // .value = to_lower( - // cmd.category), - // .description = fmt::format( - // "Commands from {} category.", - // cmd.category), - // .emoji = - // ekizu::PartialEmoji{ - // {}, - // emojis.at(to_lower( - // cmd.category)) }, - // }; - // })))); - // }; + if (!cmd->options.examples.empty()) { + std::string examples{}; + examples.reserve((bot->prefix.length() + 3) * + cmd->options.examples.size()); + for (const auto &example : cmd->options.examples) { + examples += + fmt::format("`{}{}`\n", bot->prefix, example); + } - // // const auto& author = message.author(); + builder.add_field({"❯ Examples", examples}); + } - // const auto initial_message = message.reply({ - // .embeds = { { embed } }, - // .components = { { components(false) } }, - // }); + if (!cmd->options.usage.empty()) { + builder.add_field( + {"❯ Usage", fmt::format("`{}{}`", bot->prefix, + cmd->options.usage)}); + } - // auto &channel = message.channel(); - // auto &collector = - // channel.add_message_component_listener("helpMenu"); + if (const auto &aliases = cmd->options.aliases; + !aliases.empty()) { + const auto aliases_str = std::accumulate( + std::next(aliases.begin()), aliases.end(), + fmt::format("`{}`", aliases[0]), + [](const auto &a, const auto &b) { + return fmt::format("{} | `{}`", a, b); + }); - // collector.on_add.add_listener([this, - // fmt_categories = std::move( - // formatted_categories)]( - // ekizu::Interaction - // &interaction) { - // const auto category = interaction.data.values[0]; - // const auto formatted_category = *std::find_if( - // fmt_categories.begin(), fmt_categories.end(), - // [&category](const auto &cmd) { - // return to_lower(cmd.category) == - // category; - // }); - // const auto category_embed = - // ekizu::Embed() - // .set_title(fmt::format("{} commands.", - // category)) - // .set_description(fmt::format( - // "Here are the commands you can use.")) - // .add_fields(map( - // formatted_category.commands, - // std::function([this](const std::string - // &cmd) { - // return ekizu::EmbedField{ - // .name = cmd, - // .value = fmt::format( - // "`{}{}`", - // bot->prefix, - // cmd), - // }; - // }))); + builder.add_field({"❯ Aliases", aliases_str}); + } - // interaction.update( - // { .embeds = { { category_embed } } }); - // }); + builder.add_field( + {"❯ Cooldown", fmt::format("{}ms", cmd->options.cooldown)}); + builder.add_field( + {"❯ Legend", fmt::format("`<> required, [] optional`")}); + builder.set_footer({fmt::format("Prefix: `{}`", bot->prefix)}); - // collector.on_remove.add_listener([initial_message, components] { - // initial_message->edit( - // { .components = { { components(true) } } }); - // }); - // } + (void)bot->http.create_message(message.channel_id) + .embeds({builder.build()}) + .send(); + }); + } }; COMMAND_ALLOC(Help) From 50c41ea9d6c1270de1884bd6b9046bda2965b9b7 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 17:11:55 -0600 Subject: [PATCH 12/24] fix: :bug: fix unknown symbol on windows; edit COMMAND_ALLOC and COMMAND_FREE macros to forward declare struct; reformat --- commands/Fun/8ball.cpp | 22 +++++------- commands/Fun/beep.cpp | 51 +++++++++++++-------------- commands/Fun/countdown.cpp | 58 ++++++++++++++----------------- commands/Fun/css.cpp | 56 +++++++++++++++--------------- commands/Fun/hype.cpp | 71 +++++++++++++++++--------------------- commands/Fun/meme.cpp | 44 +++++++++-------------- commands/Fun/praise.cpp | 51 ++++++++++++--------------- include/saber/commands.hpp | 2 ++ 8 files changed, 160 insertions(+), 195 deletions(-) diff --git a/commands/Fun/8ball.cpp b/commands/Fun/8ball.cpp index 6bf713f..eb1749c 100644 --- a/commands/Fun/8ball.cpp +++ b/commands/Fun/8ball.cpp @@ -5,7 +5,8 @@ using namespace saber; struct Eightball : Command { explicit Eightball(Saber *creator) - : Command(creator, + : Command( + creator, CommandOptions{ "8ball", DIRNAME, @@ -14,26 +15,21 @@ struct Eightball : Command { {}, {}, {}, - { "eight-ball", "eightball" }, + {"eight-ball", "eightball"}, "8ball ", "Asks the Magic 8-Ball for some psychic wisdom.", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, {}, {}, {}, 3000, - }) - { - } + }) {} - void setup() override - { - } + void setup() override {} void execute(const ekizu::Message &message, - const std::vector &args) override - { + const std::vector &args) override { if (args.empty()) { (void)bot->http.create_message(message.channel_id) .content("You need to ask a question!") @@ -49,7 +45,7 @@ struct Eightball : Command { .send(); } - private: + private: std::vector responses{ "It is certain.", "It is decidedly so.", diff --git a/commands/Fun/beep.cpp b/commands/Fun/beep.cpp index 179bcaa..414303d 100644 --- a/commands/Fun/beep.cpp +++ b/commands/Fun/beep.cpp @@ -5,35 +5,32 @@ using namespace saber; struct Beep : Command { explicit Beep(Saber *creator) - : Command(creator, CommandOptions{ - "beep", - DIRNAME, - true, - {}, - {}, - {}, - {}, - {}, - "beep", - "Replies with boop.", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, - {}, - {}, - {}, - 3000, - }) - { - } + : Command( + creator, + CommandOptions{ + "beep", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "beep", + "Replies with boop.", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 3000, + }) {} - void setup() override - { - } + void setup() override {} - void - execute(const ekizu::Message &message, - [[maybe_unused]] const std::vector &args) override - { + void execute( + const ekizu::Message &message, + [[maybe_unused]] const std::vector &args) override { (void)bot->http.create_message(message.channel_id) .content("Boop!") .send(); diff --git a/commands/Fun/countdown.cpp b/commands/Fun/countdown.cpp index 0c4b769..404c581 100644 --- a/commands/Fun/countdown.cpp +++ b/commands/Fun/countdown.cpp @@ -5,41 +5,35 @@ using namespace saber; struct Countdown : Command { explicit Countdown(Saber *creator) - : Command(creator, CommandOptions{ - "countdown", - DIRNAME, - true, - {}, - {}, - {}, - {}, - {}, - "countdown", - "Start counting down from 5.", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, - {}, - {}, - {}, - 0, - }) - { - } - - void setup() override - { - } + : Command( + creator, + CommandOptions{ + "countdown", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "countdown", + "Start counting down from 5.", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 0, + }) {} + + void setup() override {} void execute(const ekizu::Message &message, - const std::vector &args) override - { - if (!args.empty()) { - return; - } + const std::vector &args) override { + if (!args.empty()) { return; } - const std::vector countdown{ "five", "four", - "three", "two", - "one" }; + const std::vector countdown{ + "five", "four", "three", "two", "one"}; for (const std::string &num : countdown) { (void)bot->http.create_message(message.channel_id) diff --git a/commands/Fun/css.cpp b/commands/Fun/css.cpp index 516bab9..19b2ad7 100644 --- a/commands/Fun/css.cpp +++ b/commands/Fun/css.cpp @@ -5,41 +5,39 @@ using namespace saber; struct CSS : Command { explicit CSS(Saber *creator) - : Command(creator, CommandOptions{ - "css", - DIRNAME, - true, - {}, - {}, - {}, - {}, - {}, - "css", - "", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, - {}, - {}, - {}, - 0, - }) - { - } + : Command( + creator, + CommandOptions{ + "css", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "css", + "", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 0, + }) {} - void setup() override - { - } + void setup() override {} void execute(const ekizu::Message &message, - const std::vector &args) override - { - if (!args.empty()) { - return; - } + const std::vector &args) override { + if (!args.empty()) { return; } (void)bot->http.create_message(message.channel_id) .content( - "https://media2.giphy.com/media/yYSSBtDgbbRzq/giphy.gif?cid=ecf05e47ckbtzm84p629vw655dbua1qzaiw8tl46ejp4f0xj&ep=v1_gifs_search&rid=giphy.gif&ct=g") + "https://media2.giphy.com/media/yYSSBtDgbbRzq/" + "giphy.gif?cid=" + "ecf05e47ckbtzm84p629vw655dbua1qzaiw8tl46ejp4f0xj&ep=v1_gifs_" + "search&rid=giphy.gif&ct=g") .send(); } }; diff --git a/commands/Fun/hype.cpp b/commands/Fun/hype.cpp index 62ff8d4..d7c2cff 100644 --- a/commands/Fun/hype.cpp +++ b/commands/Fun/hype.cpp @@ -5,59 +5,52 @@ using namespace saber; struct Hype : Command { explicit Hype(Saber *creator) - : Command(creator, CommandOptions{ - "hype", - DIRNAME, - true, - {}, - {}, - {}, - {}, - { "hypu", "train" }, - "hype", - "", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, - {}, - {}, - {}, - 0, - }) - { - } - - void setup() override - { - } + : Command( + creator, + CommandOptions{ + "hype", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {"hypu", "train"}, + "hype", + "", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 0, + }) {} + + void setup() override {} void execute(const ekizu::Message &message, - const std::vector &args) override - { - if (!args.empty()) { - return; - } + const std::vector &args) override { + if (!args.empty()) { return; } const std::string &selectedHype = - hypu[util::get_random_number(0, - hypu.size() - 1)]; + hypu[util::get_random_number(0, hypu.size() - 1)]; std::string msg = ":train2: CHOO CHOO " + selectedHype; - (void)bot->http.create_message(message.channel_id) - .content(msg) - .send(); + (void)bot->http.create_message(message.channel_id).content(msg).send(); } - private: + private: std::vector hypu{ - "https://cdn.discordapp.com/attachments/102817255661772800/219514281136357376/tumblr_nr6ndeEpus1u21ng6o1_540.gif", - "https://cdn.discordapp.com/attachments/102817255661772800/219518372839161859/tumblr_n1h2afSbCu1ttmhgqo1_500.gif", + "https://cdn.discordapp.com/attachments/102817255661772800/" + "219514281136357376/tumblr_nr6ndeEpus1u21ng6o1_540.gif", + "https://cdn.discordapp.com/attachments/102817255661772800/" + "219518372839161859/tumblr_n1h2afSbCu1ttmhgqo1_500.gif", "https://gfycat.com/HairyFloweryBarebirdbat", "https://i.imgur.com/PFAQSLA.gif", "https://abload.de/img/ezgif-32008219442iq0i.gif", "https://i.imgur.com/vOVwq5o.jpg", "https://i.imgur.com/Ki12X4j.jpg", - "https://media.giphy.com/media/b1o4elYH8Tqjm/giphy.gif" - }; + "https://media.giphy.com/media/b1o4elYH8Tqjm/giphy.gif"}; }; COMMAND_ALLOC(Hype) diff --git a/commands/Fun/meme.cpp b/commands/Fun/meme.cpp index aec9e17..3095b00 100644 --- a/commands/Fun/meme.cpp +++ b/commands/Fun/meme.cpp @@ -7,7 +7,8 @@ using namespace saber; struct Meme : Command { explicit Meme(Saber *creator) - : Command(creator, + : Command( + creator, CommandOptions{ "meme", DIRNAME, @@ -18,49 +19,38 @@ struct Meme : Command { {}, {}, "meme", - "Displays a random meme from the `memes`, `dankmemes`, or `me_irl` subreddits.", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, + "Displays a random meme from the `memes`, `dankmemes`, or " + "`me_irl` subreddits.", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, {}, {}, {}, 0, - }) - { - } + }) {} - void setup() override - { - } + void setup() override {} - void - execute(const ekizu::Message &message, - [[maybe_unused]] const std::vector &args) override - { + void execute( + const ekizu::Message &message, + [[maybe_unused]] const std::vector &args) override { fetch_meme(message); } - void fetch_meme(const ekizu::Message &message) - { + void fetch_meme(const ekizu::Message &message) { auto res = net::http::get("https://meme-api.com/gimme"); - if (!res) { - return; - } + if (!res) { return; } - const auto json = - nlohmann::json::parse(res->body, nullptr, false); + const auto json = nlohmann::json::parse(res->body, nullptr, false); - if (json.is_discarded() || !json.is_object()) { - return; - } + if (json.is_discarded() || !json.is_object()) { return; } auto embed = ekizu::EmbedBuilder{} .set_title(json["title"]) .set_description(fmt::format( - "By: **{}** | {}", - json["author"].get(), + "By: **{}** | {}", json["author"].get(), json["postLink"].get())) .set_image({ json["url"].get(), @@ -68,7 +58,7 @@ struct Meme : Command { .build(); (void)bot->http.create_message(message.channel_id) - .embeds({ std::move(embed) }) + .embeds({std::move(embed)}) .send(); } }; diff --git a/commands/Fun/praise.cpp b/commands/Fun/praise.cpp index ef4dbc4..955651b 100644 --- a/commands/Fun/praise.cpp +++ b/commands/Fun/praise.cpp @@ -5,37 +5,32 @@ using namespace saber; struct Praise : Command { explicit Praise(Saber *creator) - : Command(creator, CommandOptions{ - "praise", - DIRNAME, - true, - {}, - {}, - {}, - {}, - {}, - "praise", - "", - { ekizu::Permissions::SendMessages, - ekizu::Permissions::EmbedLinks }, - {}, - {}, - {}, - 0, - }) - { - } + : Command( + creator, + CommandOptions{ + "praise", + DIRNAME, + true, + {}, + {}, + {}, + {}, + {}, + "praise", + "", + {ekizu::Permissions::SendMessages, + ekizu::Permissions::EmbedLinks}, + {}, + {}, + {}, + 0, + }) {} - void setup() override - { - } + void setup() override {} void execute(const ekizu::Message &message, - const std::vector &args) override - { - if (!args.empty()) { - return; - } + const std::vector &args) override { + if (!args.empty()) { return; } (void)bot->http.create_message(message.channel_id) .content("https://i.imgur.com/K8ySn3e.gif") diff --git a/include/saber/commands.hpp b/include/saber/commands.hpp index cf3ddc3..30eefed 100644 --- a/include/saber/commands.hpp +++ b/include/saber/commands.hpp @@ -117,6 +117,7 @@ struct Command { } #elif defined(_WIN32) #define COMMAND_ALLOC(name) \ + struct name; \ extern "C" { \ __declspec(dllexport) Command *init_command(Saber *parent) { \ return new name(parent); \ @@ -134,6 +135,7 @@ struct Command { } #elif defined(_WIN32) #define COMMAND_FREE(name) \ + struct name; \ extern "C" { \ __declspec(dllexport) void free_command(Command *command) { \ delete command; \ From 3ba82f72b5a6198f42f66f5eddd75e945ec0897e Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 17:45:30 -0600 Subject: [PATCH 13/24] fix: :bug: rename beep command to ping; add saber namespace in case it is not using'd Never name a struct Beep - Windows --- commands/Fun/{beep.cpp => ping.cpp} | 16 ++++++------- include/saber/commands.hpp | 36 ++++++++++++++--------------- 2 files changed, 25 insertions(+), 27 deletions(-) rename commands/Fun/{beep.cpp => ping.cpp} (76%) diff --git a/commands/Fun/beep.cpp b/commands/Fun/ping.cpp similarity index 76% rename from commands/Fun/beep.cpp rename to commands/Fun/ping.cpp index 414303d..183768c 100644 --- a/commands/Fun/beep.cpp +++ b/commands/Fun/ping.cpp @@ -3,12 +3,12 @@ using namespace saber; -struct Beep : Command { - explicit Beep(Saber *creator) +struct Ping : Command { + explicit Ping(Saber *creator) : Command( creator, CommandOptions{ - "beep", + "ping", DIRNAME, true, {}, @@ -16,8 +16,8 @@ struct Beep : Command { {}, {}, {}, - "beep", - "Replies with boop.", + "ping", + "Replies with pong.", {ekizu::Permissions::SendMessages, ekizu::Permissions::EmbedLinks}, {}, @@ -32,10 +32,10 @@ struct Beep : Command { const ekizu::Message &message, [[maybe_unused]] const std::vector &args) override { (void)bot->http.create_message(message.channel_id) - .content("Boop!") + .content("Pong!") .send(); } }; -COMMAND_ALLOC(Beep) -COMMAND_FREE(Beep) +COMMAND_ALLOC(Ping) +COMMAND_FREE(Ping) diff --git a/include/saber/commands.hpp b/include/saber/commands.hpp index 30eefed..51b3691 100644 --- a/include/saber/commands.hpp +++ b/include/saber/commands.hpp @@ -108,20 +108,19 @@ struct Command { }; #if defined(__linux__) || defined(__APPLE__) -#define COMMAND_ALLOC(name) \ - extern "C" { \ - __attribute__((visibility("default"))) Command *init_command( \ - Saber *parent) { \ - return new name(parent); \ - } \ +#define COMMAND_ALLOC(name) \ + extern "C" { \ + __attribute__((visibility("default"))) saber::Command *init_command( \ + saber::Saber *parent) { \ + return new name(parent); \ + } \ } #elif defined(_WIN32) -#define COMMAND_ALLOC(name) \ - struct name; \ - extern "C" { \ - __declspec(dllexport) Command *init_command(Saber *parent) { \ - return new name(parent); \ - } \ +#define COMMAND_ALLOC(name) \ + extern "C" { \ + __declspec(dllexport) saber::Command *init_command(saber::Saber *parent) { \ + return new name(parent); \ + } \ } #endif @@ -129,17 +128,16 @@ struct Command { #define COMMAND_FREE(name) \ extern "C" { \ __attribute__((visibility("default"))) void free_command( \ - Command *command) { \ + saber::Command *command) { \ delete command; \ } \ } #elif defined(_WIN32) -#define COMMAND_FREE(name) \ - struct name; \ - extern "C" { \ - __declspec(dllexport) void free_command(Command *command) { \ - delete command; \ - } \ +#define COMMAND_FREE(name) \ + extern "C" { \ + __declspec(dllexport) void free_command(saber::Command *command) { \ + delete command; \ + } \ } #endif From 5c261e8b5ffacee7c45b60da130e069045ddbef7 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 17:56:59 -0600 Subject: [PATCH 14/24] ci: :green_heart: use same runners as build job --- .github/workflows/ci.yml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e629f79..a59d10c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,17 +90,9 @@ jobs: fail-fast: false matrix: config: - - { os: ubuntu-latest, arch: x64, binary_name: saber-linux-x64 } - - { - os: windows-latest, - arch: x64, - binary_name: saber-windows-x64.exe, - } - - { - os: windows-latest, - arch: x86, - binary_name: saber-windows-x86.exe, - } + - { os: ubuntu-22.04, arch: x64, binary_name: saber-linux-x64 } + - { os: windows-2022, arch: x64, binary_name: saber-windows-x64.exe } + - { os: windows-2022, arch: x86, binary_name: saber-windows-x86.exe } name: release-${{ matrix.config.os }}-${{ matrix.config.arch }} runs-on: ${{ matrix.config.os }} From 580956e17df201efa6f9368bfdedee99e5066857 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 18:28:05 -0600 Subject: [PATCH 15/24] ci: :green_heart: all matrix jobs must pass; release should only happen once; split release creation and artifact upload into 2 jobs --- .github/workflows/ci.yml | 44 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a59d10c..bdd5753 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ on: jobs: build: strategy: - fail-fast: false + fail-fast: true matrix: config: - { @@ -86,16 +86,7 @@ jobs: release: needs: build - strategy: - fail-fast: false - matrix: - config: - - { os: ubuntu-22.04, arch: x64, binary_name: saber-linux-x64 } - - { os: windows-2022, arch: x64, binary_name: saber-windows-x64.exe } - - { os: windows-2022, arch: x86, binary_name: saber-windows-x86.exe } - - name: release-${{ matrix.config.os }}-${{ matrix.config.arch }} - runs-on: ${{ matrix.config.os }} + runs-on: ubuntu-22.04 if: github.event_name == 'push' steps: @@ -141,14 +132,35 @@ jobs: body: ${{ steps.changelog.outputs.changes }} token: ${{ github.token }} - - name: Download binaries + upload: + strategy: + fail-fast: true + matrix: + artifact: + ["saber-linux-x64", "saber-windows-x64.exe", "saber-windows-x86.exe"] + + needs: release + name: upload-${{ matrix.artifact }} + runs-on: ubuntu-22.04 + if: github.event_name == 'push' + + steps: + - name: Get Current Version + id: semver + uses: ietf-tools/semver-action@v1 + with: + token: ${{ github.token }} + branch: dev + noVersionBumpBehavior: silent + + - name: Download artifact uses: actions/download-artifact@v3 with: - name: ${{ matrix.config.binary_name }} + name: ${{ matrix.artifact }} - - name: Upload binaries to release + - name: Upload artifact to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ github.token }} - file: ${{ matrix.config.binary_name }} - tag: ${{ steps.semver.outputs.next }}-dev + file: ${{ matrix.artifact }} + tag: ${{ steps.semver.outputs.current }}-dev From 55bbff2f4a46977a30a80ecab6d7958bccbf077b Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 18:41:49 -0600 Subject: [PATCH 16/24] ci: :green_heart: pass next tag to upload job --- .github/workflows/ci.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdd5753..ac7c704 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,6 +132,9 @@ jobs: body: ${{ steps.changelog.outputs.changes }} token: ${{ github.token }} + outputs: + next: ${{ steps.semver.outputs.next }} + upload: strategy: fail-fast: true @@ -145,14 +148,6 @@ jobs: if: github.event_name == 'push' steps: - - name: Get Current Version - id: semver - uses: ietf-tools/semver-action@v1 - with: - token: ${{ github.token }} - branch: dev - noVersionBumpBehavior: silent - - name: Download artifact uses: actions/download-artifact@v3 with: @@ -163,4 +158,4 @@ jobs: with: repo_token: ${{ github.token }} file: ${{ matrix.artifact }} - tag: ${{ steps.semver.outputs.current }}-dev + tag: ${{ needs.release.outputs.next }}-dev From 984692bd456b99f87fa315d8cfcb6cc74d25008d Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 18:57:48 -0600 Subject: [PATCH 17/24] ci: :green_heart: fix artifact not being found --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac7c704..bf17c46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,6 +149,7 @@ jobs: steps: - name: Download artifact + id: download uses: actions/download-artifact@v3 with: name: ${{ matrix.artifact }} @@ -157,5 +158,6 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ github.token }} - file: ${{ matrix.artifact }} + file: ${{ steps.download.outputs.download-path }}/${{ matrix.artifact }} + asset_name: ${{ matrix.artifact }} tag: ${{ needs.release.outputs.next }}-dev From 846b180fcb6cbca67359053b5eb40d41b1496bed Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 19:10:10 -0600 Subject: [PATCH 18/24] ci: :green_heart: specify download path --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf17c46..79171f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,6 +153,7 @@ jobs: uses: actions/download-artifact@v3 with: name: ${{ matrix.artifact }} + path: ${{ github.workspace }}/artifacts - name: Upload artifact to release uses: svenstaro/upload-release-action@v2 From 96e184d9c02d06ac9ca2b580aed2910437e7e9d8 Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 19:23:51 -0600 Subject: [PATCH 19/24] ci: :green_heart: maybe download-path is the artifact? --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79171f2..b6d1d03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,12 +153,11 @@ jobs: uses: actions/download-artifact@v3 with: name: ${{ matrix.artifact }} - path: ${{ github.workspace }}/artifacts - name: Upload artifact to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ github.token }} - file: ${{ steps.download.outputs.download-path }}/${{ matrix.artifact }} + file: ${{ steps.download.outputs.download-path }} asset_name: ${{ matrix.artifact }} tag: ${{ needs.release.outputs.next }}-dev From b120762ac18e117f6fb506aaa4ac33712641713b Mon Sep 17 00:00:00 2001 From: Xminent Date: Sun, 10 Dec 2023 21:54:32 -0600 Subject: [PATCH 20/24] ci: :construction_worker: add binary rename step; remove matrix requirement for upload job --- .github/workflows/ci.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6d1d03..096b8f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,11 +77,19 @@ jobs: - name: Build run: cmake --build build --config Release + - name: Move binary (Linux) + if: runner.os == 'Linux' + run: mv build/${{ matrix.config.binary_path }} build/${{ matrix.config.output_name }} + + - name: Move binary (Windows) + if: runner.os == 'Windows' + run: Move-Item build/${{ matrix.config.binary_path }} build/${{ matrix.config.output_name }} + - name: Upload Binary uses: actions/upload-artifact@v3 with: name: ${{ matrix.config.output_name }} - path: ./build/${{ matrix.config.binary_path }} + path: ./build/${{ matrix.config.output_name }} if-no-files-found: error release: @@ -136,28 +144,20 @@ jobs: next: ${{ steps.semver.outputs.next }} upload: - strategy: - fail-fast: true - matrix: - artifact: - ["saber-linux-x64", "saber-windows-x64.exe", "saber-windows-x86.exe"] - needs: release - name: upload-${{ matrix.artifact }} runs-on: ubuntu-22.04 if: github.event_name == 'push' steps: - - name: Download artifact - id: download + - name: Download all artifacts uses: actions/download-artifact@v3 with: - name: ${{ matrix.artifact }} + path: . - - name: Upload artifact to release + - name: Upload artifacts to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ github.token }} - file: ${{ steps.download.outputs.download-path }} - asset_name: ${{ matrix.artifact }} + file_glob: true + file: "**/*" tag: ${{ needs.release.outputs.next }}-dev From a960d5808b8b896a374e81ec0d0fbe6ba05d2855 Mon Sep 17 00:00:00 2001 From: Xminent Date: Mon, 11 Dec 2023 11:52:37 -0600 Subject: [PATCH 21/24] ci: :construction_worker: add main branch to ci; compare dev/prod tags when bumping versions --- .github/workflows/ci.yml | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 096b8f9..1cf8eed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: push: branches: + - main - dev paths: - "**ci.yml" @@ -11,6 +12,7 @@ on: - "**CMakeLists.txt" pull_request: branches: + - main - dev paths: - "**ci.yml" @@ -18,6 +20,10 @@ on: - "**.cpp" - "**CMakeLists.txt" +env: + PRERELEASE: ${{ github.ref == 'refs/heads/dev' && 'true' || 'false' }} + TAG_SUFFIX: ${{ github.ref == 'refs/heads/dev' && '-dev' || '' }} + jobs: build: strategy: @@ -101,21 +107,34 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Get Latest Tag + id: latest-tag + run: | + if [[ ${{ github.ref }} == 'refs/heads/dev' ]]; then + latest_tag=$(git tag -l | grep "\-dev" | sort -V | tail -n 1) + else + latest_tag=$(git tag -l | grep -v "\-dev" | sort -V | tail -n 1) + fi + + echo "::set-output name=tag::$latest_tag" + shell: bash + - name: Get Next Version id: semver uses: ietf-tools/semver-action@v1 with: token: ${{ github.token }} - branch: dev + branch: ${{ github.ref == 'refs/heads/dev' && 'dev' || 'main' }} + fromTag: ${{ steps.latest-tag.outputs.tag }} - name: Create Draft Release uses: ncipollo/release-action@v1.12.0 with: - prerelease: true + prerelease: ${{ env.PRERELEASE }} draft: false commit: ${{ github.sha }} - tag: ${{ steps.semver.outputs.next }}-dev - name: ${{ steps.semver.outputs.next }}-dev + tag: ${{ steps.semver.outputs.next }}${{ env.TAG_SUFFIX }} + name: ${{ steps.semver.outputs.next }}${{ env.TAG_SUFFIX }} body: "*pending*" token: ${{ github.token }} @@ -124,19 +143,19 @@ jobs: uses: requarks/changelog-action@v1 with: token: ${{ github.token }} - tag: ${{ steps.semver.outputs.next }}-dev + tag: ${{ steps.semver.outputs.next }}${{ env.TAG_SUFFIX }} writeToFile: false - name: Create Release uses: ncipollo/release-action@v1.12.0 with: - prerelease: true + prerelease: ${{ env.PRERELEASE }} allowUpdates: true draft: false makeLatest: true commit: ${{ github.sha }} - tag: ${{ steps.semver.outputs.next }}-dev - name: ${{ steps.semver.outputs.next }}-dev + tag: ${{ steps.semver.outputs.next }}${{ env.TAG_SUFFIX }} + name: ${{ steps.semver.outputs.next }}${{ env.TAG_SUFFIX }} body: ${{ steps.changelog.outputs.changes }} token: ${{ github.token }} @@ -160,4 +179,4 @@ jobs: repo_token: ${{ github.token }} file_glob: true file: "**/*" - tag: ${{ needs.release.outputs.next }}-dev + tag: ${{ needs.release.outputs.next }}${{ env.TAG_SUFFIX }} From 460ba0c3dc209e672164540fb8a0ae0d21f18849 Mon Sep 17 00:00:00 2001 From: Xminent Date: Mon, 11 Dec 2023 12:48:24 -0600 Subject: [PATCH 22/24] ci: :construction_worker: add change detection for job conditional execution; fix get-latest-tag script --- .github/workflows/ci.yml | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cf8eed..33596a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,32 @@ env: TAG_SUFFIX: ${{ github.ref == 'refs/heads/dev' && '-dev' || '' }} jobs: + changes: + runs-on: ubuntu-22.04 + outputs: + build: ${{ steps.filter.outputs.src }} + ci: ${{ steps.filter.outputs.ci }} + + steps: + - name: Checkout + if: github.event_name == 'push' + uses: actions/checkout@v3 + + - uses: dorny/paths-filter@v2 + id: filter + with: + base: ${{ github.event.pull_request.base.sha || github.sha }} + filters: | + src: + - '**/*.cpp' + - '**/*.hpp' + - '**/CMakeLists.txt' + ci: + - '.github/workflows/ci.yml' build: + needs: changes strategy: - fail-fast: true + fail-fast: false matrix: config: - { @@ -51,6 +74,7 @@ jobs: name: build-${{ matrix.config.os }}-${{ matrix.config.arch }} runs-on: ${{ matrix.config.os }} + if: needs.changes.outputs.build == 'true' steps: - name: Checkout @@ -110,10 +134,14 @@ jobs: - name: Get Latest Tag id: latest-tag run: | - if [[ ${{ github.ref }} == 'refs/heads/dev' ]]; then - latest_tag=$(git tag -l | grep "\-dev" | sort -V | tail -n 1) + if [[ "${{ github.ref }}" == 'refs/heads/dev' ]]; then + latest_tag=$(git tag -l | grep "\-dev" | sort -V | tail -n 1 || true) else - latest_tag=$(git tag -l | grep -v "\-dev" | sort -V | tail -n 1) + latest_tag=$(git tag -l | grep -v "\-dev" | sort -V | tail -n 1 || true) + fi + + if [[ -z $latest_tag ]]; then + latest_tag="" fi echo "::set-output name=tag::$latest_tag" From 729e9ac8ac0a0f7659a5a12e77d2aa9b291a1741 Mon Sep 17 00:00:00 2001 From: Xminent Date: Mon, 11 Dec 2023 12:53:21 -0600 Subject: [PATCH 23/24] refactor: :necktie: exit early if DISCORD_TOKEN is not provided --- src/main.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 502a628..4bed1b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,15 @@ +#include #include int main() { - auto saber = saber::Saber{std::getenv("DISCORD_TOKEN")}; + const auto* token = std::getenv("DISCORD_TOKEN"); + + if (token == nullptr) { + std::cerr << "Missing DISCORD_TOKEN environment variable\n"; + return 1; + } + + auto saber = saber::Saber{token}; saber.run(); } From 56f7187573499bd99c73b1e88099ca686ab71953 Mon Sep 17 00:00:00 2001 From: Xminent Date: Mon, 11 Dec 2023 13:10:54 -0600 Subject: [PATCH 24/24] ci: :green_heart: change base path --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33596a1..1032ab6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,7 @@ on: - "**CMakeLists.txt" env: + BRANCH_NAME: ${{ github.ref == 'refs/heads/dev' && 'dev' || 'main' }} PRERELEASE: ${{ github.ref == 'refs/heads/dev' && 'true' || 'false' }} TAG_SUFFIX: ${{ github.ref == 'refs/heads/dev' && '-dev' || '' }} @@ -39,7 +40,7 @@ jobs: - uses: dorny/paths-filter@v2 id: filter with: - base: ${{ github.event.pull_request.base.sha || github.sha }} + base: ${{ env.BRANCH_NAME }} filters: | src: - '**/*.cpp' @@ -152,7 +153,7 @@ jobs: uses: ietf-tools/semver-action@v1 with: token: ${{ github.token }} - branch: ${{ github.ref == 'refs/heads/dev' && 'dev' || 'main' }} + branch: ${{ env.BRANCH_NAME }} fromTag: ${{ steps.latest-tag.outputs.tag }} - name: Create Draft Release