diff --git a/CMakeLists.txt b/CMakeLists.txt index 1754494e..df5e0b41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,9 @@ message(STATUS "CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 3.8) -file(READ ${CMAKE_SOURCE_DIR}/include/aegis/version.hpp version_hpp) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/include/aegis/version.hpp version_hpp) if (NOT version_hpp MATCHES "AEGIS_VERSION_SHORT ([0-9][0-9])([0-9][0-9])([0-9][0-9])") - message(FATAL_ERROR "Cannot get AEGIS_VERSION_SHORT from version.hpp. ${CMAKE_SOURCE_DIR}/include/aegis/version.hpp") + message(FATAL_ERROR "Cannot get AEGIS_VERSION_SHORT from version.hpp. ${CMAKE_CURRENT_SOURCE_DIR}/include/aegis/version.hpp") endif () math(EXPR AEGIS_VERSION_MAJOR ${CMAKE_MATCH_1}) math(EXPR AEGIS_VERSION_MINOR ${CMAKE_MATCH_2}) @@ -26,7 +26,7 @@ endif() project(libaegis VERSION ${AEGIS_VERSION} LANGUAGES CXX) -list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake) +list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -43,9 +43,9 @@ set(REQUIRED_LIBS OpenSSL::SSL ZLIB::ZLIB Spdlog::Spdlog JSON::JSON Asio::Asio d option(BUILD_SHARED_LIBS "Build the shared library" ON) option(BUILD_EXAMPLES "Build example programs" OFF) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) set(CMAKE_BUILD_TYPE Release) set(AEGIS_FILES @@ -150,7 +150,7 @@ if (BUILD_SHARED_LIBS) ) CONFIGURE_FILE( - "${CMAKE_SOURCE_DIR}/cmake/aegis.pc.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/aegis.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/aegis.pc" ) @@ -195,7 +195,7 @@ else () ) CONFIGURE_FILE( - "${CMAKE_SOURCE_DIR}/cmake/aegis_static.pc.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/aegis_static.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/aegis.pc" ) diff --git a/README.md b/README.md index 3826c342..7ec7cbec 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Including the helper header will automatically include all other files. int main() { - aegis::core bot; + aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::trace).token("TOKEN")); bot.set_on_message_create([](auto obj) { if (obj.msg.get_content() == "Hi") @@ -112,3 +112,8 @@ You can change basic configuration options within the `config.json` file. It sho "log-format": "%^%Y-%m-%d %H:%M:%S.%e [%L] [th#%t]%$ : %v" } ``` + +Alternatively you can configure the library by passing in the [create_bot_t()](https://docs.aegisbot.io/structaegis_1_1create__bot__t.html) object to the constructor of the aegis::core object. You can make use of it fluent-style. +```cpp +aegis::core(aegis::create_bot_t().log_level(spdlog::level::trace).token("TOKEN")) +``` \ No newline at end of file diff --git a/aegis.dox b/aegis.dox index 88bbb2ae..00cfbde9 100644 --- a/aegis.dox +++ b/aegis.dox @@ -21,7 +21,7 @@ using json = nlohmann::json; int main(int argc, char * argv[]) { // Create bot object with a minimum log level of trace - aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::info).token("TOKEN")); + aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::trace).token("TOKEN")); AEGIS_TRACE(bot.log, "Bot object created"); @@ -76,7 +76,7 @@ void message_create(aegis::gateway::events::message_create obj) int main(int argc, char * argv[]) { // Create bot object with a minimum log level of trace - aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::info).token("TOKEN")); + aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::trace).token("TOKEN")); AEGIS_TRACE(bot.log, "Bot object created"); @@ -125,7 +125,7 @@ public: int main(int argc, char * argv[]) { // Create bot object with a minimum log level of trace - aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::info).token("TOKEN")); + aegis::core bot(aegis::create_bot_t().log_level(spdlog::level::trace).token("TOKEN")); my_bot my_bot_instance; diff --git a/cmake/FindAsio.cmake b/cmake/FindAsio.cmake index 7ebb3a32..943dcf72 100644 --- a/cmake/FindAsio.cmake +++ b/cmake/FindAsio.cmake @@ -8,7 +8,7 @@ find_path(Asio_INCLUDE_DIR if (Asio_INCLUDE_DIR STREQUAL "Asio_INCLUDE_DIR-NOTFOUND") message(WARNING "Using git-module path for Asio") - set(Asio_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/lib/asio/asio/include/) + set(Asio_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/asio/asio/include/) endif () file(READ ${Asio_INCLUDE_DIR}/asio/version.hpp version_hpp) diff --git a/cmake/FindJSON.cmake b/cmake/FindJSON.cmake index e62f1a66..16711048 100644 --- a/cmake/FindJSON.cmake +++ b/cmake/FindJSON.cmake @@ -8,7 +8,7 @@ find_path(JSON_INCLUDE_DIR if (JSON_INCLUDE_DIR STREQUAL "JSON_INCLUDE_DIR-NOTFOUND") message(WARNING "Using git-module path for JSON") - set(JSON_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/lib/json/include) + set(JSON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/json/include) set(JSON_SUBDIR true) endif () diff --git a/cmake/FindSpdlog.cmake b/cmake/FindSpdlog.cmake index 446aab85..d4d65f63 100644 --- a/cmake/FindSpdlog.cmake +++ b/cmake/FindSpdlog.cmake @@ -8,7 +8,7 @@ find_path(Spdlog_INCLUDE_DIR if (Spdlog_INCLUDE_DIR STREQUAL "Spdlog_INCLUDE_DIR-NOTFOUND") message(WARNING "Using git-module path for Spdlog") - set(Spdlog_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/lib/spdlog/include/) + set(Spdlog_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/spdlog/include/) endif () file(READ ${Spdlog_INCLUDE_DIR}/spdlog/common.h common_h) diff --git a/cmake/FindWebsocketpp.cmake b/cmake/FindWebsocketpp.cmake index e13e18c8..88c38107 100644 --- a/cmake/FindWebsocketpp.cmake +++ b/cmake/FindWebsocketpp.cmake @@ -8,7 +8,7 @@ find_path(Websocketpp_INCLUDE_DIR if (Websocketpp_INCLUDE_DIR STREQUAL "Websocketpp_INCLUDE_DIR-NOTFOUND") message(WARNING "Using git-module path for Websocketpp") - set(Websocketpp_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/lib/websocketpp/) + set(Websocketpp_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/websocketpp/) endif () file(READ ${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp version_hpp) diff --git a/include/aegis/channel.hpp b/include/aegis/channel.hpp index dea18170..d16788a7 100644 --- a/include/aegis/channel.hpp +++ b/include/aegis/channel.hpp @@ -200,7 +200,7 @@ class channel /// Send message to this channel /** * @param content A string of the message to send - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_message(const std::string & content, int64_t nonce = 0); @@ -208,7 +208,7 @@ class channel /** * @see aegis::create_message_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_message(create_message_t obj); @@ -216,7 +216,7 @@ class channel /**\deprecated * @param content A string of the message to send * @param embed A json object of the embed object itself - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_message_embed(const std::string & content, const json & embed, int64_t nonce = 0); @@ -224,14 +224,22 @@ class channel /**\deprecated * @see aegis::create_message_embed_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_message_embed(create_message_t obj); + + /// Get message from this channel + /** + * @param message_id Snowflake of the message to retrieve + * @returns aegis::future + */ + AEGIS_DECL aegis::future get_message(snowflake message_id); + /// Edit a message in this channel /** * @param message_id Snowflake of the message to replace. Must be your own message * @param content A string of the message to set - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future edit_message(snowflake message_id, const std::string & content); @@ -239,7 +247,7 @@ class channel /** * @see aegis::edit_message_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future edit_message(edit_message_t obj); @@ -248,7 +256,7 @@ class channel * @param message_id Snowflake of the message to replace. Must be your own message * @param content A string of the message to set * @param embed A json object of the embed object itself - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future edit_message_embed(snowflake message_id, const std::string & content, const json & embed); @@ -256,28 +264,28 @@ class channel /**\deprecated * @see aegis::edit_message_embed_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future edit_message_embed(edit_message_t obj); /// Delete a message /** * @param message_id Snowflake of the message to delete - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_message(snowflake message_id); /// Delete up to 100 messages at once /** * @param message Vector of up to 100 message int64_t to delete - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future bulk_delete_message(const std::vector & messages); /// Delete up to 100 messages at once /** * @param message Vector of up to 100 message snowflakes to delete - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future bulk_delete_message(const std::vector & messages); @@ -291,7 +299,7 @@ class channel * @param _user_limit Integer of the channel max user limit (VOICE CHANNEL ONLY) * @param _permission_overwrites Vector of permission_overwrite objects for overriding permissions * @param _parent_id Snowflake of category channel belongs in (empty parent puts channel in no category) - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future modify_channel(lib::optional _name = {}, lib::optional _position = {}, lib::optional _topic = {}, @@ -304,7 +312,7 @@ class channel /** * @see aegis::modify_channel_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future modify_channel(modify_channel_t obj) { @@ -315,7 +323,7 @@ class channel /// Delete this channel /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_channel(); @@ -323,7 +331,7 @@ class channel /** * @param message_id Snowflake of message * @param emoji_text Text of emoji being added `name:snowflake` - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_reaction(snowflake message_id, const std::string & emoji_text); @@ -331,7 +339,7 @@ class channel /** * @see aegis::create_reaction_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_reaction(create_reaction_t obj) { @@ -342,7 +350,7 @@ class channel /** * @param message_id Snowflake of message * @param emoji_text Text of emoji being added `name:snowflake` - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_own_reaction(snowflake message_id, const std::string & emoji_text); @@ -350,7 +358,7 @@ class channel /** * @see aegis::delete_own_reaction_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_own_reaction(delete_own_reaction_t obj) { @@ -362,7 +370,7 @@ class channel * @param message_id Snowflake of message * @param emoji_text Text of emoji being added `name:snowflake` * @param member_id Snowflake of member to remove emoji from - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_user_reaction(snowflake message_id, const std::string & emoji_text, snowflake member_id); @@ -370,7 +378,7 @@ class channel /** * @see aegis::delete_user_reaction_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_user_reaction(delete_user_reaction_t obj) { @@ -381,7 +389,7 @@ class channel /** * @param message_id Snowflake of message * @param emoji_text Text of emoji being added `name:snowflake` - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future get_reactions(snowflake message_id, const std::string & emoji_text); @@ -389,7 +397,7 @@ class channel /** * @see aegis::get_reactions_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future get_reactions(get_reactions_t obj) { @@ -399,7 +407,7 @@ class channel /// Delete all reactions by message /** * @param message_id Snowflake of message - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_all_reactions(snowflake message_id); @@ -409,7 +417,7 @@ class channel * @param _allow Int64 allow flags * @param _deny Int64 deny flags * @param _type Type of override (role/user) - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future edit_channel_permissions(snowflake _overwrite_id, int64_t _allow, int64_t _deny, const std::string & _type); @@ -417,7 +425,7 @@ class channel /** * @see aegis::edit_channel_permissions_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future edit_channel_permissions(edit_channel_permissions_t obj) { @@ -426,7 +434,7 @@ class channel /// Get active channel invites /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future get_channel_invites(); @@ -436,7 +444,7 @@ class channel * @param max_uses The max uses this invite code allows * @param temporary Is this invite code temporary * @param unique Is this invite code a unique one-use - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_channel_invite(const lib::optional max_age, const lib::optional max_uses, @@ -447,7 +455,7 @@ class channel /** * @see aegis::create_channel_invite_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_channel_invite(create_channel_invite_t obj) { @@ -457,49 +465,49 @@ class channel /// Delete channel permission override /** * @param overwrite_id Snowflake of the channel permission to delete - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_channel_permission(snowflake overwrite_id); /// Trigger typing indicator in channel (lasts 10 seconds) /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future trigger_typing_indicator(); /// Get pinned messages in channel /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future get_pinned_messages(); /// Add a pinned message in channel /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future add_pinned_channel_message(snowflake message_id); /// Delete a pinned message in channel /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future delete_pinned_channel_message(snowflake message_id); /// Add member to a group direct message /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future group_dm_add_recipient(snowflake user_id); /// Remove member from a group direct message /** - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future group_dm_remove_recipient(snowflake user_id); - /// Get the snowflake of this channel + /// Get the aegis::snowflake of this channel /** - * @returns A snowflake of the channel + * @returns An aegis::snowflake of the channel */ snowflake get_id() const noexcept { diff --git a/include/aegis/core.hpp b/include/aegis/core.hpp index 2c32ddf0..81230930 100644 --- a/include/aegis/core.hpp +++ b/include/aegis/core.hpp @@ -328,6 +328,18 @@ class core */ AEGIS_DECL int64_t get_user_count() const noexcept; + /// Get count of unique channels tracked + /** + * @returns int64_t of channel count + */ + AEGIS_DECL int64_t get_channel_count() const noexcept; + + /// Get count of unique guilds tracked + /** + * @returns int64_t of guild count + */ + AEGIS_DECL int64_t get_guild_count() const noexcept; + /// Obtain a pointer to a user by snowflake /** * @param id Snowflake of user to search for @@ -394,7 +406,7 @@ class core * @param id Snowflake of member to message * @param content string of message to send * @param nonce Unique id to track when message verifies (can be omitted) - * @returns Message object + * @returns aegis::future */ AEGIS_DECL aegis::future create_dm_message(snowflake member_id, const std::string & content, int64_t nonce = 0); @@ -402,7 +414,7 @@ class core /** * @see aegis::create_message_t * @param obj Struct of the contents of the request - * @returns std::future + * @returns aegis::future */ AEGIS_DECL aegis::future create_dm_message(const create_message_t & obj); @@ -440,16 +452,29 @@ class core /** * @see aegis::gateway::objects::activity * @see aegis::gateway::objects::presence - * @param std::string Test of presence message - * @param aegis::gateway::objects::activity::activity_type Enum of the activity type - * @param aegis::gateway::objects::presence::user_status Enum of the status + * @param text Text of presence message + * @param type Enum of the activity type + * @param status Enum of the status */ AEGIS_DECL void update_presence(const std::string& text, gateway::objects::activity::activity_type type = gateway::objects::activity::Game, gateway::objects::presence::user_status status = gateway::objects::presence::Online); + /// Passes through to Websocket++ + /** + * @param duration Time until function should be run in milliseconds + * @param callback Function to run when timer expires + */ + AEGIS_DECL std::shared_ptr set_timer(long duration, std::function callback) + { + return get_shard_mgr().get_websocket().set_timer(duration, std::move(callback)); + } + std::unordered_map> channels; + std::unordered_map> stale_channels; std::unordered_map> guilds; + std::unordered_map> stale_guilds; #if !defined(AEGIS_DISABLE_ALL_CACHE) - std::unordered_map> members; + std::unordered_map> users; + std::unordered_map> stale_users; #endif std::map message_count; @@ -461,7 +486,7 @@ class core std::unique_ptr ws_open_strand; std::shared_ptr log; -#ifdef AEGIS_PROFILING +#if defined(AEGIS_PROFILING) using message_end_t = std::function; using rest_end_t = std::function; using js_end_t = std::function; @@ -472,6 +497,12 @@ class core js_end_t js_end; #endif +#if defined(AEGIS_EVENTS) + using websocket_event_t = std::function; + void set_on_websocket_event(websocket_event_t cb) { websocket_event = cb; } + websocket_event_t websocket_event; +#endif + using typing_start_t = std::function; using message_create_t = std::function; using message_update_t = std::function; @@ -760,6 +791,62 @@ class core return fut; } + /// Get the internal guild mutex + shared_mutex & get_guild_mutex() { return _guild_m; } + + /// Get the internal channel mutex + shared_mutex & get_channel_mutex() { return _channel_m; } + + /// Get the internal user mutex + shared_mutex & get_user_mutex() { return _user_m; } + + /// Get the guild map + /** + * This will return the internal unordered_map of all the guilds currently tracked. Does not + * include stale items. You MUST lock the appropriate mutex BEFORE accessing it to prevent + * potential race conditions and possible crashes. + * + * Example: + * @code{.cpp} + * std::shared_lock l(get_guild_mutex()); + * @endcode + * + * @returns std::unordered_map> + */ + std::unordered_map> & get_guild_map() { return guilds; }; + + /// Get the channel map + /** + * This will return the internal unordered_map of all the channels currently tracked. Does not + * include stale items. You MUST lock the appropriate mutex BEFORE accessing it to prevent + * potential race conditions and possible crashes. + * + * Example: + * @code{.cpp} + * std::shared_lock l(get_channel_mutex()); + * @endcode + * + * @returns std::unordered_map> + */ + std::unordered_map> & get_channel_map() { return channels; }; + +#if !defined(AEGIS_DISABLE_ALL_CACHE) + /// Get the user map + /** + * This will return the internal unordered_map of all the users currently tracked. Does not + * include stale items. You MUST lock the appropriate mutex BEFORE accessing it to prevent + * potential race conditions and possible crashes. + * + * Example: + * @code{.cpp} + * std::shared_lock l(get_user_mutex()); + * @endcode + * + * @returns std::unordered_map> + */ + std::unordered_map> & get_user_map() { return users; }; +#endif + private: AEGIS_DECL void _thread_track(thread_state * t_state); @@ -852,6 +939,7 @@ class core AEGIS_DECL void load_config(); + AEGIS_DECL void remove_guild(snowflake guild_id) noexcept; AEGIS_DECL void remove_channel(snowflake channel_id) noexcept; AEGIS_DECL void remove_member(snowflake member_id) noexcept; @@ -879,7 +967,7 @@ class core mutable shared_mutex _shard_m; mutable shared_mutex _guild_m; mutable shared_mutex _channel_m; - mutable shared_mutex _member_m; + mutable shared_mutex _user_m; bool file_logging = false; bool external_io_context = true; diff --git a/include/aegis/futures.hpp b/include/aegis/futures.hpp index 8fc52f19..cbb58685 100644 --- a/include/aegis/futures.hpp +++ b/include/aegis/futures.hpp @@ -103,13 +103,16 @@ using remove_future_t = typename remove_future::type; namespace detail { +/// call_state template add_future_t call_state(Func&& func, State&& state); +/// call_future template add_future_t call_future(Func&& func, Future&& fut) noexcept; } +/// future_state template struct future_state { @@ -325,6 +328,7 @@ struct future_state } }; +/// future_state template <> struct future_state { @@ -446,6 +450,7 @@ struct future_state void forward_to(promise& pr) noexcept; }; +/// task class task { public: @@ -453,6 +458,7 @@ class task virtual void run() noexcept = 0; }; +/// continuation_base template class continuation_base : public task { @@ -484,6 +490,7 @@ class continuation_base : public task friend class future; }; +/// continuation_base template <> class continuation_base : public task { @@ -509,6 +516,7 @@ class continuation_base : public task friend class future; }; +/// continuation template struct continuation final : continuation_base { @@ -526,6 +534,7 @@ struct continuation final : continuation_base using task_ptr = std::unique_ptr; +/// promise template class promise { @@ -676,6 +685,7 @@ class promise friend struct future_state; }; +/// future template class future { @@ -1140,6 +1150,7 @@ class future friend future make_exception_future(std::exception_ptr ex) noexcept; }; +/// promise::get_future() template inline future promise::get_future() noexcept { @@ -1147,6 +1158,7 @@ inline future promise::get_future() noexcept return future(this); } +/// promise::make_ready() template template::urgent Urgent> inline void promise::make_ready() noexcept @@ -1168,6 +1180,7 @@ inline void promise::make_ready() noexcept } } +/// promise::abandoned() template inline void promise::abandoned() noexcept { @@ -1186,18 +1199,21 @@ inline void promise::abandoned() noexcept std::atomic_thread_fence(std::memory_order_release); } +/// make_ready_future() template inline future make_ready_future(A&&... value) { return { ready_future_marker(), std::forward(value)... }; } +/// make_ready_future() template inline future make_ready_future(T&& value) { return { ready_future_marker(), std::forward(value) }; } +/// make_exception_future() template inline future make_exception_future(std::exception_ptr ex) noexcept { @@ -1207,18 +1223,21 @@ inline future make_exception_future(std::exception_ptr ex) noexcept namespace detail { +/// to_future() template inline add_future_t to_future(T&& value) { return make_ready_future(std::forward(value)); } +/// to_future() template inline future to_future(future&& fut) { return std::move(fut); } +/// call_function() template> inline std::enable_if_t::value, add_future_t> call_function(std::true_type, Func&& func, A&&... args) { @@ -1232,6 +1251,7 @@ inline std::enable_if_t::value, add_future_t> call_function( } } +/// call_function() template> inline std::enable_if_t::value, add_future_t> call_function(std::true_type, Func&& func, A&&... args) { @@ -1246,6 +1266,7 @@ inline std::enable_if_t::value, add_future_t> call_function(s } } +/// call_function() template inline add_future_t call_function(std::false_type, Func&& func, A&&... args) { @@ -1259,24 +1280,28 @@ inline add_future_t call_function(std::false_type, Func&& func, A&&... args) } } +/// call_from_state() template inline add_future_t call_from_state(std::true_type, Func&& func, State&&) { return call_function(std::is_void{}, std::forward(func)); } +/// call_from_state() template inline add_future_t call_from_state(std::false_type, Func&& func, State&& state) { return call_function(std::is_void{}, std::forward(func), std::forward(state).get_value()); } +/// call_state() template inline add_future_t call_state(Func&& func, State&& state) { return call_from_state(std::is_void{}, std::forward(func), std::forward(state)); } +/// call_future template inline add_future_t call_future(Func&& func, Future&& fut) noexcept { @@ -1284,6 +1309,7 @@ inline add_future_t call_future(Func&& func, Future&& fut) noexcept } } +/// future_state::forward_to() inline void future_state::forward_to(promise& pr) noexcept { std::atomic_thread_fence(std::memory_order_acquire); @@ -1300,6 +1326,7 @@ inline void future_state::forward_to(promise& pr) noexcept std::atomic_thread_fence(std::memory_order_release); } +/// make_exception_future() template inline future make_exception_future(aegis::error ec) { diff --git a/include/aegis/gateway/objects/impl/message.cpp b/include/aegis/gateway/objects/impl/message.cpp index 1710fc48..21c5c2b5 100644 --- a/include/aegis/gateway/objects/impl/message.cpp +++ b/include/aegis/gateway/objects/impl/message.cpp @@ -28,7 +28,7 @@ namespace objects AEGIS_DECL aegis::guild & message::get_guild() { if (_guild == nullptr) - _guild = _bot->find_guild(_guild_id); + _guild = _core->find_guild(_guild_id); assert(_guild != nullptr); if (_guild == nullptr) throw exception("message::get_guild() _guild = nullptr", make_error_code(error::guild_not_found)); @@ -38,7 +38,7 @@ AEGIS_DECL aegis::guild & message::get_guild() AEGIS_DECL aegis::channel & message::get_channel() { if (_channel == nullptr) - _channel = _bot->find_channel(_channel_id); + _channel = _core->find_channel(_channel_id); assert(_channel != nullptr); if (_channel == nullptr) throw exception("message::get_channel() _channel == nullptr", make_error_code(error::channel_not_found)); @@ -49,13 +49,13 @@ AEGIS_DECL aegis::channel & message::get_channel() AEGIS_DECL aegis::user & message::get_user() { if (_user == nullptr) - _user = _bot->find_user(_author_id); + _user = _core->find_user(_author_id); if (_user == nullptr) { if (_author_id == 0) throw exception("message::get_member() _member == nullptr && _author_id == 0", make_error_code(error::member_not_found)); - _user = _bot->user_create(_author_id); + _user = _core->user_create(_author_id); _user->_load_data(author); } return *_user; @@ -136,9 +136,9 @@ AEGIS_DECL aegis::future message::delete_all_reactions() AEGIS_DECL void message::populate_self() { if ((_guild == nullptr) && (_guild_id > 0)) - _guild = _bot->find_guild(_guild_id); + _guild = _core->find_guild(_guild_id); if ((_channel == nullptr) && (_channel_id > 0)) - _channel = _bot->find_channel(_channel_id); + _channel = _core->find_channel(_channel_id); if (!_channel) //throw because channel should always exist or else we have no understanding of the channel //TODO: create a dummy channel in this instance then request full info after? @@ -146,19 +146,19 @@ AEGIS_DECL void message::populate_self() //is requested and populated and throw if it can't be requested? throw aegis::exception(error::channel_not_found); if (_guild == nullptr) - _guild = _bot->find_guild(_channel->get_guild_id()); + _guild = _core->find_guild(_channel->get_guild_id()); #if !defined(AEGIS_DISABLE_ALL_CACHE) if (!is_webhook()) { if ((_user == nullptr) && (_author_id > 0)) - _user = _bot->find_user(_author_id); + _user = _core->find_user(_author_id); if (_user == nullptr) { //create user with the info provided? //user created will be very primitive with minimal information //TODO: add user request queue to lib to pull updated user data - _bot->user_create(_author_id)->_load_data(author); - _bot->log->debug("message::populate_self() user not found - created"); + _core->user_create(_author_id)->_load_data(author); + _core->log->debug("message::populate_self() user not found - created"); //throw aegis::exception(error::member_not_found); } } diff --git a/include/aegis/gateway/objects/message.hpp b/include/aegis/gateway/objects/message.hpp index f30dbfba..f6e35422 100644 --- a/include/aegis/gateway/objects/message.hpp +++ b/include/aegis/gateway/objects/message.hpp @@ -59,58 +59,69 @@ class message public: /// Constructor for the message object /** - * @param _shard Pointer to the shard this message is being handled by - * @param _c Pointer of channel object - * @param _g Pointer of guild object * @param content String of the message sent + * @param _channel Pointer of channel object + * @param _guild Pointer of guild object */ - explicit message(const std::string & content, aegis::channel * _c, aegis::guild * _g) noexcept + explicit message(const std::string & content, aegis::channel * _channel, aegis::guild * _guild) noexcept : _content(content) - , _channel(_c) - , _guild(_g) + , _channel(_channel) + , _guild(_guild) { } - message(const std::string & _json, aegis::core * bot) noexcept - : _bot(bot) + /// Constructor for the message object + /** + * @param _json JSON string of the message object + * @param bot Pointer of core object + */ + message(const std::string & _json, aegis::core * _core) noexcept + : _core(_core) { from_json(nlohmann::json::parse(_json), *this); populate_self(); } - message(const nlohmann::json & _json, aegis::core * bot) noexcept - : _bot(bot) + /// Constructor for the message object + /** + * @param _json JSON object of the message object + * @param _core Pointer of core object + */ + message(const nlohmann::json & _json, aegis::core * _core) noexcept + : _core(_core) { from_json(_json, *this); populate_self(); } - message(aegis::core * bot) noexcept - : _bot(bot) + /// Constructor for the message object + /** + * @param _core Pointer of core object + */ + message(aegis::core * _core) noexcept + : _core(_core) { populate_self(); } - /// Constructor for the message object + /// Set the channel of the message object. This is mostly an internal function + /// though is left public for lower level use /** - * @param _shard Pointer to the shard this message is being handled by - * @param channel_id Snowflake of channel this message belongs to - * @param content String of the message sent + * @param _channel Pointer of the channel to assign the message to */ - AEGIS_DECL void set_channel(aegis::channel * _c) + AEGIS_DECL void set_channel(aegis::channel * _channel) { - _channel = _c; + this->_channel = _channel; } - /// Constructor for the message object + /// Set the guild of the message object. This is mostly an internal function + /// though is left public for lower level use /** - * @param _shard Pointer to the shard this message is being handled by - * @param channel_id Snowflake of channel this message belongs to - * @param content String of the message sent + * @param _guild Pointer of the guild to assign the message to */ - AEGIS_DECL void set_guild(aegis::guild * _g) + AEGIS_DECL void set_guild(aegis::guild * _guild) { - _guild = _g; + this->_guild = _guild; } message() = default; @@ -118,21 +129,25 @@ class message message(const message&) = default; message(message && msg) = default; + /// Comparison of message content inline bool operator==(const std::string& rhs) { return _content == rhs; } + /// Comparison of message content inline bool operator!=(const std::string& rhs) { return !(*this == rhs); } + /// Comparison of message content inline bool operator==(const char * rhs) { return _content == rhs; } + /// Comparison of message content inline bool operator!=(const char * rhs) { return !(*this == rhs); @@ -153,95 +168,192 @@ class message objects::message_type type = Default; /**<\todo Needs documentation */ user author; /**< author object for this message */ + /// Whether message is a Direct Message + /** + * @returns bool + */ bool is_dm() const noexcept { return !!_guild_id; } + /// Whether message is from a bot + /** + * @returns bool + */ bool is_bot() const noexcept { return author.is_bot(); } + /// Whether message is from a webhook + /** + * @returns bool + */ bool is_webhook() const noexcept { return !webhook_id.empty(); } + /// Get a reference to the content of the message + /** + * @returns const std::string & + */ const std::string & get_content() const noexcept { return _content; } + /// Set the message's content + /** + * @param content String of content to set + */ void set_content(const std::string & content) noexcept { _content = content; } + /// Get the ID of the message + /** + * @returns aegis::snowflake + */ snowflake get_id() const noexcept { return _message_id; } + /// Get the Channel ID of the message + /** + * @returns aegis::snowflake + */ snowflake get_channel_id() const noexcept { return _channel_id; } + /// Get the Guild ID of the message + /** + * @returns aegis::snowflake + */ snowflake get_guild_id() const noexcept { return _guild_id; } + /// Get the Member ID of the author of the message + /** + * @returns aegis::snowflake + */ snowflake get_author_id() const noexcept { return _author_id; } + /// Whether the message has a valid guild set. The result of this function + /// does not determine whether the object was a DM or not + /// @see is_dm + /** + * @returns bool + */ bool has_guild() const noexcept { return _guild != nullptr || _guild_id != 0; } + /// Whether the message has a valid channel set + /** + * @returns bool + */ bool has_channel() const noexcept { return _channel != nullptr || _channel_id != 0; } #if !defined(AEGIS_DISABLE_ALL_CACHE) + /// Whether the message has a valid member set + /** + * @returns bool + */ bool has_member() const noexcept { return _user != nullptr || _author_id != 0; } #endif + /// Get a reference to the guild object this message was sent in + /** + * @returns aegis::guild + */ AEGIS_DECL aegis::guild & get_guild(); + /// Get a reference to the channel object this message was sent in + /** + * @returns aegis::channel + */ AEGIS_DECL aegis::channel & get_channel(); #if !defined(AEGIS_DISABLE_ALL_CACHE) + /// Get a reference to the user object this message was sent by + /** + * @returns aegis::user + */ AEGIS_DECL aegis::user & get_user(); #endif + /// Delete this message + /** + * @returns aegis::future + */ AEGIS_DECL aegis::future delete_message(); + /// Edit this message + /** + * @param content String to set the new content to + * @returns aegis::future + */ AEGIS_DECL aegis::future edit(const std::string & content); + /// Edit this message + /** + * @see edit_message_t + * @param obj Struct of the edit message request + * @returns aegis::future + */ AEGIS_DECL aegis::future edit(edit_message_t & obj); + /// Add a reaction to this message + /** + * @param content String of the emoji to add. Unicode emoji or `emojiname:emoji_id` + * @returns aegis::future + */ AEGIS_DECL aegis::future create_reaction(const std::string & content); + /// Delete your reaction to this message + /** + * @param content String of the emoji to set. Unicode emoji or `emojiname:emoji_id` + * @returns aegis::future + */ AEGIS_DECL aegis::future delete_own_reaction(const std::string & content); + /// Delete other user reaction to this message (not available for DMs) + /** + * @param content String of the emoji to set. Unicode emoji or `emojiname:emoji_id` + * @param member_id Snowflake of the member to delete the reaction for + * @returns aegis::future + */ AEGIS_DECL aegis::future delete_user_reaction(const std::string & content, const snowflake member_id); + /// Delete all reactions on this message (not available for DMs) + /** + * @returns aegis::future + */ AEGIS_DECL aegis::future delete_all_reactions(); /// Obtain the relevant snowflakes related to this message /** - * Returns { _channel_id, _guild_id, _message_id, _author_id } + * Returns { channel_id, guild_id, message_id, author_id } * Some may be 0 such as guild for a DM or author for a webhook - * @returns std::tuple + * @returns std::tuple */ std::tuple get_related_ids() const noexcept { @@ -261,7 +373,7 @@ class message #if !defined(AEGIS_DISABLE_ALL_CACHE) aegis::user * _user = nullptr;/**< Pointer to the author of this message */ #endif - aegis::core * _bot = nullptr; + aegis::core * _core = nullptr; snowflake _message_id = 0; /**< snowflake of the message */ snowflake _channel_id = 0; /**< snowflake of the channel this message belongs to */ snowflake _guild_id = 0; /**< snowflake of the guild this message belongs to */ diff --git a/include/aegis/guild.hpp b/include/aegis/guild.hpp index 1849ac79..bcf93b9c 100644 --- a/include/aegis/guild.hpp +++ b/include/aegis/guild.hpp @@ -74,7 +74,7 @@ struct create_text_channel_t struct create_voice_channel_t { - create_voice_channel_t & name(snowflake param) { _name = param; return *this; } + create_voice_channel_t & name(const std::string param) { _name = param; return *this; } create_voice_channel_t & bitrate(int32_t param) { _bitrate = param; return *this; } create_voice_channel_t & user_limit(int32_t param) { _user_limit = param; return *this; } create_voice_channel_t & parent_id(int64_t param) { _parent_id = param; return *this; } @@ -204,6 +204,28 @@ class guild return std::move(_name); } + /// Get icon of guild + /** + * @returns String of guild icon + */ + std::string get_icon() const noexcept + { + std::shared_lock l(_m); + std::string _icon = icon; + return std::move(_icon); + } + + /// Get splash of guild + /** + * @returns String of guild splash + */ + std::string get_splash() const noexcept + { + std::shared_lock l(_m); + std::string _splash = splash; + return std::move(_splash); + } + /// Get region of guild /** * @returns String of region guild is in @@ -471,9 +493,9 @@ class guild /** * @param user_id The snowflake of the user to add new role * @param role_id The snowflake of the role to add to member - * @returns aegis::future + * @returns aegis::future */ - AEGIS_DECL aegis::future add_guild_member_role(snowflake user_id, snowflake role_id); + AEGIS_DECL aegis::future add_guild_member_role(snowflake user_id, snowflake role_id); /// Remove a role from guild member /** diff --git a/include/aegis/impl/channel.cpp b/include/aegis/impl/channel.cpp index 1bdeab21..0c539fd1 100644 --- a/include/aegis/impl/channel.cpp +++ b/include/aegis/impl/channel.cpp @@ -145,6 +145,19 @@ AEGIS_DECL aegis::future channel::create_message(cons return _ratelimit.post_task({ _endpoint, rest::Post, obj.dump(-1, ' ', true) }); } +AEGIS_DECL aegis::future channel::get_message(snowflake message_id) +{ +#if !defined(AEGIS_DISABLE_ALL_CACHE) + if (_guild && !perms().can_read_history()) + return aegis::make_exception_future(error::no_permission); +#endif + + std::shared_lock l(_m); + + std::string _endpoint = fmt::format("/channels/{}/messages/{}", channel_id, message_id); + return _ratelimit.post_task({ _endpoint, rest::Get }); +} + AEGIS_DECL aegis::future channel::create_message(create_message_t obj) { return create_message_embed(obj._content, obj._embed, obj._nonce); diff --git a/include/aegis/impl/core.cpp b/include/aegis/impl/core.cpp index fa5a5b7c..94a0aefe 100644 --- a/include/aegis/impl/core.cpp +++ b/include/aegis/impl/core.cpp @@ -225,27 +225,37 @@ AEGIS_DECL int64_t core::get_member_count() const noexcept AEGIS_DECL int64_t core::get_user_count() const noexcept { - return members.size(); + return users.size(); +} + +AEGIS_DECL int64_t core::get_channel_count() const noexcept +{ + return channels.size(); +} + +AEGIS_DECL int64_t core::get_guild_count() const noexcept +{ + return guilds.size(); } AEGIS_DECL user * core::find_user(snowflake id) const noexcept { - std::shared_lock l(_member_m); - auto it = members.find(id); - if (it == members.end()) + std::shared_lock l(_user_m); + auto it = users.find(id); + if (it == users.end()) return nullptr; return it->second.get(); } AEGIS_DECL user * core::user_create(snowflake id) noexcept { - std::unique_lock l(_member_m); - auto it = members.find(id); - if (it == members.end()) + std::unique_lock l(_user_m); + auto it = users.find(id); + if (it == users.end()) { auto g = std::make_unique(id); auto ptr = g.get(); - members.emplace(id, std::move(g)); + users.emplace(id, std::move(g)); return ptr; } return it->second.get(); @@ -374,28 +384,44 @@ AEGIS_DECL guild * core::guild_create(snowflake id, shards::shard * _shard) noex return it->second.get(); } +AEGIS_DECL void core::remove_guild(snowflake guild_id) noexcept +{ + std::unique_lock l(_guild_m); + auto it = guilds.find(guild_id); + if (it == guilds.end()) + { + AEGIS_DEBUG(log, "Unable to remove guild [{}] (does not exist)", guild_id); + return; + } + stale_guilds.emplace(it->first, std::move(it->second)); + guilds.erase(it); +} + AEGIS_DECL void core::remove_channel(snowflake channel_id) noexcept { + std::unique_lock l(_channel_m); auto it = channels.find(channel_id); if (it == channels.end()) { AEGIS_DEBUG(log, "Unable to remove channel [{}] (does not exist)", channel_id); return; } + stale_channels.emplace(it->first, std::move(it->second)); channels.erase(it); } - #if !defined(AEGIS_DISABLE_ALL_CACHE) -AEGIS_DECL void core::remove_member(snowflake member_id) noexcept +AEGIS_DECL void core::remove_member(snowflake user_id) noexcept { - auto it = members.find(member_id); - if (it == members.end()) + std::unique_lock l(_user_m); + auto it = users.find(user_id); + if (it == users.end()) { - AEGIS_DEBUG(log, "Unable to remove member [{}] (does not exist)", member_id); + AEGIS_DEBUG(log, "Unable to remove member [{}] (does not exist)", user_id); return; } - members.erase(it); + stale_users.emplace(it->first, std::move(it->second)); + users.erase(it); } #endif @@ -621,7 +647,7 @@ AEGIS_DECL void core::process_ready(const json & d, shards::shard * _shard) auto m = std::make_unique(user_id); _self = m.get(); - members.emplace(user_id, std::move(m)); + users.emplace(user_id, std::move(m)); _self->_member_id = user_id; _self->_is_bot = true; _self->_name = username; @@ -719,6 +745,11 @@ AEGIS_DECL void core::on_message(websocketpp::connection_hdl hdl, std::string ms { json result = json::parse(msg); +#if defined(AEGIS_EVENTS) + if (websocket_event) + websocket_event(msg, *_shard); +#endif + if (!result.is_null()) { if (!result["s"].is_null()) @@ -1176,7 +1207,7 @@ AEGIS_DECL void core::ws_message_create(const json & result, shards::shard * _sh gateway::events::message_create obj{ *_shard, std::ref(*m), std::ref(*c) }; obj.msg = result["d"]; - obj.msg._bot = this; + obj.msg._core = this; if (i_message_create_dm) i_message_create_dm(obj); @@ -1188,7 +1219,7 @@ AEGIS_DECL void core::ws_message_create(const json & result, shards::shard * _sh gateway::events::message_create obj{ *_shard, std::ref(*m), std::ref(*c)/*, std::make_optional(std::ref(*g))*/ }; obj.msg = result["d"]; - obj.msg._bot = this; + obj.msg._core = this; if (i_message_create) i_message_create(obj); @@ -1294,7 +1325,8 @@ AEGIS_DECL void core::ws_guild_delete(const json & result, shards::shard * _shar //kicked or left //websocket_o.set_timer(5000, [this, id, _shard](const asio::error_code & ec) //{ - guilds.erase(guild_id); + remove_guild(guild_id); + //guilds.erase(guild_id); //}); } } diff --git a/include/aegis/impl/guild.cpp b/include/aegis/impl/guild.cpp index 4196aa5c..cd39bcf6 100644 --- a/include/aegis/impl/guild.cpp +++ b/include/aegis/impl/guild.cpp @@ -78,7 +78,7 @@ AEGIS_DECL void guild::_remove_member(snowflake member_id) noexcept AEGIS_DECL bool guild::member_has_role(snowflake member_id, snowflake role_id) const noexcept { - std::unique_lock l(_m); + std::shared_lock l(_m); auto _member = find_member(member_id); if (_member == nullptr) return false; @@ -627,9 +627,12 @@ AEGIS_DECL aegis::future guild::create_voice_channel( json obj; obj["name"] = name; obj["type"] = 2; - obj["bitrate"] = bitrate; - obj["user_limit"] = user_limit; - obj["parent_id"] = parent_id; + if (bitrate > 0) + obj["bitrate"] = bitrate; + if (user_limit > 0) + obj["user_limit"] = user_limit; + if (parent_id > 0) + obj["parent_id"] = parent_id; obj["permission_overwrites"] = json::array(); for (auto & p_ow : permission_overwrites) { @@ -763,16 +766,16 @@ AEGIS_DECL aegis::future guild::modify_my_nick(const std::stri return _bot->get_ratelimit().post_task({ fmt::format("/guilds/{}/members/@me/nick", guild_id), rest::Patch, obj.dump() }); } -AEGIS_DECL aegis::future guild::add_guild_member_role(snowflake user_id, snowflake role_id) +AEGIS_DECL aegis::future guild::add_guild_member_role(snowflake user_id, snowflake role_id) { #if !defined(AEGIS_DISABLE_ALL_CACHE) if (!perms().can_manage_roles()) - return aegis::make_exception_future(error::no_permission); + return aegis::make_exception_future(error::no_permission); #endif std::shared_lock l(_m); - return _bot->get_ratelimit().post_task({ fmt::format("/guilds/{}/members/{}/roles/{}", guild_id, user_id, role_id), rest::Put }); + return _bot->get_ratelimit().post_task({ fmt::format("/guilds/{}/members/{}/roles/{}", guild_id, user_id, role_id), rest::Put }); } AEGIS_DECL aegis::future guild::remove_guild_member_role(snowflake user_id, snowflake role_id) diff --git a/include/aegis/impl/snowflake.cpp b/include/aegis/impl/snowflake.cpp index 443376d2..38e5441a 100644 --- a/include/aegis/impl/snowflake.cpp +++ b/include/aegis/impl/snowflake.cpp @@ -22,12 +22,12 @@ namespace aegis { -snowflake::snowflake(const aegis::guild & _guild) noexcept : _id(_guild.get_id()) {} -snowflake::snowflake(const aegis::channel & _channel) noexcept : _id(_channel.get_id()) {} -snowflake::snowflake(const aegis::gateway::objects::role & _role) noexcept : _id(_role.role_id) {} -snowflake::snowflake(const aegis::gateway::objects::message & _message) noexcept : _id(_message.get_id()) {} -snowflake::snowflake(const aegis::gateway::objects::emoji & _emoji) noexcept : _id(_emoji.id) {} -snowflake::snowflake(const aegis::gateway::objects::attachment & _attachment) noexcept : _id(_attachment.id) {} +AEGIS_DECL snowflake::snowflake(const aegis::guild & _guild) noexcept : _id(_guild.get_id()) {} +AEGIS_DECL snowflake::snowflake(const aegis::channel & _channel) noexcept : _id(_channel.get_id()) {} +AEGIS_DECL snowflake::snowflake(const aegis::gateway::objects::role & _role) noexcept : _id(_role.role_id) {} +AEGIS_DECL snowflake::snowflake(const aegis::gateway::objects::message & _message) noexcept : _id(_message.get_id()) {} +AEGIS_DECL snowflake::snowflake(const aegis::gateway::objects::emoji & _emoji) noexcept : _id(_emoji.id) {} +AEGIS_DECL snowflake::snowflake(const aegis::gateway::objects::attachment & _attachment) noexcept : _id(_attachment.id) {} /// \cond TEMPLATES AEGIS_DECL void from_json(const nlohmann::json& j, snowflake& s) diff --git a/include/aegis/impl/user.cpp b/include/aegis/impl/user.cpp index b3d32e24..e142a952 100644 --- a/include/aegis/impl/user.cpp +++ b/include/aegis/impl/user.cpp @@ -86,6 +86,8 @@ AEGIS_DECL void user::_load_nolock(guild * _guild, const json & obj, shards::sha if (obj.count("nick") && !obj["nick"].is_null()) g_info->nickname = obj["nick"].get(); + else + g_info->nickname.reset(); } } catch (std::exception & e) @@ -118,11 +120,30 @@ AEGIS_DECL user::guild_info & user::get_guild_info(snowflake guild_id) noexcept return **g; } +AEGIS_DECL user::guild_info & user::get_guild_info_nolock(snowflake guild_id) noexcept +{ + auto g = std::find_if(std::begin(guilds), std::end(guilds), [&guild_id](const std::unique_ptr & gi) + { + if (gi->id == guild_id) + return true; + return false; + }); + if (g == guilds.end()) + { +#if defined(AEGIS_CXX17) + return *guilds.emplace_back(std::make_unique(guild_id)); +#else + return **guilds.insert(guilds.end(), std::make_unique(guild_id)); +#endif + } + return **g; +} + AEGIS_DECL std::string user::get_name(snowflake guild_id) noexcept { - std::shared_lock l(_m); + std::unique_lock l(_m); - const auto & def = get_guild_info(guild_id).nickname; + const auto & def = get_guild_info_nolock(guild_id).nickname; return def.has_value() ? def.value() : ""; } diff --git a/include/aegis/shards/shard.hpp b/include/aegis/shards/shard.hpp index e983eace..fca624b2 100644 --- a/include/aegis/shards/shard.hpp +++ b/include/aegis/shards/shard.hpp @@ -216,9 +216,9 @@ class shard /** * @see aegis::gateway::objects::activity * @see aegis::gateway::objects::presence - * @param std::string Test of presence message - * @param aegis::gateway::objects::activity::activity_type Enum of the activity type - * @param aegis::gateway::objects::presence::user_status Enum of the status + * @param text String of presence message + * @param type aegis::gateway::objects::activity::activity_type enum of the activity type + * @param status aegis::gateway::objects::presence::user_status enum of the status */ AEGIS_DECL void update_presence(const std::string& text, gateway::objects::activity::activity_type type = gateway::objects::activity::Game, gateway::objects::presence::user_status status = gateway::objects::presence::Online); diff --git a/include/aegis/snowflake.hpp b/include/aegis/snowflake.hpp index 95055ad2..43f5c59f 100644 --- a/include/aegis/snowflake.hpp +++ b/include/aegis/snowflake.hpp @@ -26,13 +26,16 @@ class snowflake constexpr snowflake(const snowflake & _snowflake) noexcept : _id(_snowflake._id) {} explicit snowflake(const char * _snowflake) noexcept : _id(std::stoll(std::string(_snowflake))) {} explicit snowflake(const std::string & _snowflake) noexcept : _id(std::stoll(_snowflake)) {} - explicit snowflake(const nlohmann::json & _snowflake) noexcept : _id(std::stoll(_snowflake.get())) {} - snowflake(const aegis::guild & _guild) noexcept; - snowflake(const aegis::channel & _channel) noexcept; - snowflake(const aegis::gateway::objects::role & _role) noexcept; - snowflake(const aegis::gateway::objects::message & _message) noexcept; - snowflake(const aegis::gateway::objects::emoji & _emoji) noexcept; - snowflake(const aegis::gateway::objects::attachment & _attachment) noexcept; +#if defined(AEGIS_CXX17) + explicit snowflake(const std::string_view _snowflake) noexcept : _id(std::stoll(std::string{ _snowflake })) {} +#endif + explicit snowflake(const nlohmann::json & _snowflake) noexcept : _id(std::stoll(_snowflake.get())) {} + AEGIS_DECL snowflake(const aegis::guild & _guild) noexcept; + AEGIS_DECL snowflake(const aegis::channel & _channel) noexcept; + AEGIS_DECL snowflake(const aegis::gateway::objects::role & _role) noexcept; + AEGIS_DECL snowflake(const aegis::gateway::objects::message & _message) noexcept; + AEGIS_DECL snowflake(const aegis::gateway::objects::emoji & _emoji) noexcept; + AEGIS_DECL snowflake(const aegis::gateway::objects::attachment & _attachment) noexcept; constexpr operator int64_t() const noexcept { diff --git a/include/aegis/user.hpp b/include/aegis/user.hpp index ce49b6cf..b7b5ea64 100644 --- a/include/aegis/user.hpp +++ b/include/aegis/user.hpp @@ -121,6 +121,8 @@ class user */ AEGIS_DECL guild_info & get_guild_info(snowflake guild_id) noexcept; + AEGIS_DECL guild_info & get_guild_info_nolock(snowflake guild_id) noexcept; + /// Get the full name (username\#discriminator) of this user /** * @returns string of the full username and discriminator diff --git a/include/aegis/version.hpp b/include/aegis/version.hpp index 58532833..cbce8fc8 100644 --- a/include/aegis/version.hpp +++ b/include/aegis/version.hpp @@ -10,9 +10,9 @@ #pragma once #if !defined(AEGIS_VERSION_LONG) -#define AEGIS_VERSION_LONG 0x00020300 -#define AEGIS_VERSION_SHORT 020300 -#define AEGIS_VERSION_TEXT "aegis.cpp 2.3.0 2019/9/3" +#define AEGIS_VERSION_LONG 0x00020400 +#define AEGIS_VERSION_SHORT 020400 +#define AEGIS_VERSION_TEXT "aegis.cpp 2.4.0 2019/10/24" #define AEGIS_VERSION_MAJOR ((AEGIS_VERSION_LONG & 0x00ff0000) >> 16) #define AEGIS_VERSION_MINOR ((AEGIS_VERSION_LONG & 0x0000ff00) >> 8) diff --git a/src/example.cpp b/src/example.cpp index 1bc69455..25d36f1b 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -106,16 +106,16 @@ void example::MessageCreate(message_create obj) const json example::make_info_obj(aegis::core & bot, aegis::shards::shard * _shard) { - int64_t guild_count = bot.guilds.size(); - int64_t member_count_unique = bot.members.size(); - int64_t channel_count = bot.channels.size(); + int64_t guild_count = bot.get_guild_count(); + int64_t user_count_unique = bot.get_user_count(); + int64_t channel_count = bot.get_channel_count(); int64_t eventsseen = 0; for (auto & e : bot.message_count) eventsseen += e.second; - std::string members = fmt::format("{}", member_count_unique); + std::string members = fmt::format("{}", user_count_unique); std::string channels = fmt::format("{}", channel_count); std::string guilds = fmt::format("{}", guild_count); std::string events = fmt::format("{}", eventsseen);