diff --git a/src/Channel.cpp b/src/Channel.cpp index 777b81e..444dfa5 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -51,6 +51,13 @@ Channel::Channel(ChannelId_t pawn_id, json const &data, GuildId_t guild_id) : } } +Channel::Channel(ChannelId_t pawn_id, Snowflake_t channel_id, Type type) : + m_PawnId(pawn_id), + m_Id(channel_id), + m_Type(type) +{ +} + void Channel::Update(json const &data) { utils::TryGetJsonValue(data, m_Name, "name"), @@ -395,7 +402,7 @@ ChannelId_t ChannelManager::AddChannel(json const &data, GuildId_t guild_id/* = return INVALID_CHANNEL_ID; } - Channel_t const &channel = FindChannelById(sfid); + Channel_t const& channel = FindChannelById(sfid); if (channel) return channel->GetPawnId(); // channel already exists @@ -414,6 +421,35 @@ ChannelId_t ChannelManager::AddChannel(json const &data, GuildId_t guild_id/* = return id; } +ChannelId_t ChannelManager::AddDMChannel(json const& data) +{ + Snowflake_t sfid; + if (!utils::TryGetJsonValue(data, sfid, "channel_id")) + { + Logger::Get()->Log(LogLevel::ERROR, + "invalid JSON: expected \"channel_id\" in \"{}\"", data.dump()); + return INVALID_CHANNEL_ID; + } + + Channel_t const& channel = FindChannelById(sfid); + if (channel) + return channel->GetPawnId(); // channel already exists + + ChannelId_t id = 1; + while (m_Channels.find(id) != m_Channels.end()) + ++id; + + if (!m_Channels.emplace(id, Channel_t(new Channel(id, sfid, Channel::Type::DM))).second) + { + Logger::Get()->Log(LogLevel::ERROR, + "can't create channel: duplicate key '{}'", id); + return INVALID_CHANNEL_ID; + } + + Logger::Get()->Log(LogLevel::INFO, "successfully added channel with id '{}'", id); + return id; +} + void ChannelManager::DeleteChannel(json const &data) { Snowflake_t sfid; diff --git a/src/Channel.hpp b/src/Channel.hpp index c2c301b..a3d1900 100644 --- a/src/Channel.hpp +++ b/src/Channel.hpp @@ -32,6 +32,7 @@ class Channel public: Channel(ChannelId_t pawn_id, json const &data, GuildId_t guild_id); + Channel(ChannelId_t pawn_id, Snowflake_t channel_id, Type type); ~Channel() = default; private: @@ -129,6 +130,8 @@ class ChannelManager : public Singleton } ChannelId_t AddChannel(json const &data, GuildId_t guild_id = 0); + ChannelId_t AddDMChannel(json const & data); + void DeleteChannel(json const &data); Channel_t const &FindChannel(ChannelId_t id); diff --git a/src/Http.cpp b/src/Http.cpp index cea934c..33310f0 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -5,7 +5,6 @@ #include #include -#include Http::Http(std::string token) : @@ -135,12 +134,7 @@ void Http::NetworkThreadFunc() it_r = response.find("X-RateLimit-Reset"); if (it_r != response.end()) { - auto date_str = response.find(boost::beast::http::field::date)->value().to_string(); - std::istringstream date_ss{ date_str }; - date::sys_seconds date_utc; - date_ss >> date::parse("%a, %d %b %Y %T %Z", date_utc); // RFC2616 HTTP header date format - - std::chrono::seconds timepoint_now = date_utc.time_since_epoch(); + std::chrono::seconds timepoint_now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); Logger::Get()->Log(LogLevel::DEBUG, "rate-limiting path {} until {} (current time: {})", limited_url, it_r->value().to_string(), diff --git a/src/Message.cpp b/src/Message.cpp index b4f6024..aefbf6d 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -11,7 +11,7 @@ Message::Message(MessageId_t pawn_id, json const &data) : m_PawnId(pawn_id) { - std::string author_id, channel_id; + std::string author_id, channel_id, guild_id; _valid = utils::TryGetJsonValue(data, m_Id, "id") && utils::TryGetJsonValue(data, author_id, "author", "id") && @@ -28,7 +28,15 @@ Message::Message(MessageId_t pawn_id, json const &data) : m_PawnId(pawn_id) } Channel_t const &channel = ChannelManager::Get()->FindChannelById(channel_id); - m_Channel = channel ? channel->GetPawnId() : INVALID_CHANNEL_ID; + if (!channel && !utils::TryGetJsonValue(data, guild_id, "guild_id")) + { + ChannelId_t cid = ChannelManager::Get()->AddDMChannel(data); + m_Channel = ChannelManager::Get()->FindChannel(cid)->GetPawnId(); + } + else + { + m_Channel = channel ? channel->GetPawnId() : INVALID_CHANNEL_ID; + } User_t const &user = UserManager::Get()->FindUserById(author_id); m_Author = user ? user->GetPawnId() : INVALID_USER_ID; diff --git a/src/Role.cpp b/src/Role.cpp index aec155c..f87b932 100644 --- a/src/Role.cpp +++ b/src/Role.cpp @@ -18,14 +18,16 @@ Role::Role(RoleId_t pawn_id, json const &data) : void Role::Update(json const &data) { + std::string permissions; _valid = utils::TryGetJsonValue(data, m_Name, "name") && utils::TryGetJsonValue(data, m_Color, "color") && utils::TryGetJsonValue(data, m_Hoist, "hoist") && utils::TryGetJsonValue(data, m_Position, "position") && - utils::TryGetJsonValue(data, m_Permissions, "permissions") && + utils::TryGetJsonValue(data, permissions, "permissions") && utils::TryGetJsonValue(data, m_Mentionable, "mentionable"); + m_Permissions = std::stoull(permissions); if (!_valid) { Logger::Get()->Log(LogLevel::ERROR, diff --git a/src/User.cpp b/src/User.cpp index 0b64d36..88c327a 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -19,7 +19,7 @@ User::User(UserId_t pawn_id, json const &data) : Update(data); } -void User::Update(json const &data) +void User::Update(json const &data, bool in_dispatch) { _valid = utils::TryGetJsonValue(data, m_Username, "username") && @@ -35,11 +35,12 @@ void User::Update(json const &data) utils::TryGetJsonValue(data, m_IsBot, "bot"); utils::TryGetJsonValue(data, m_IsVerified, "verified"); - PawnDispatcher::Get()->Dispatch([this]() + // Seems to crash here for some users if we have a pawn dispatch in a pawn dispatch + if (in_dispatch) { pawn_cb::Error error; pawn_cb::Callback::CallFirst(error, "DCC_OnUserUpdate", GetPawnId()); - }); + } } diff --git a/src/User.hpp b/src/User.hpp index 9634517..fb44e60 100644 --- a/src/User.hpp +++ b/src/User.hpp @@ -68,7 +68,7 @@ class User return IsValid(); } - void Update(json const &data); + void Update(json const &data, bool in_dispatch = false); }; diff --git a/src/WebSocket.cpp b/src/WebSocket.cpp index 3f9b302..6f63e16 100644 --- a/src/WebSocket.cpp +++ b/src/WebSocket.cpp @@ -1,8 +1,10 @@ #include "WebSocket.hpp" #include "Logger.hpp" +#include "sdk.hpp" #include +extern logprintf_t logprintf; WebSocket::WebSocket() : _ioContext(), @@ -133,7 +135,7 @@ void WebSocket::OnSslHandshake(beast::error_code ec) _websocket->async_handshake( _gatewayUrl + ":443", - "/?encoding=json&v=6", + "/?encoding=json&v=8", beast::bind_front_handler( &WebSocket::OnHandshake, this)); @@ -258,7 +260,16 @@ void WebSocket::OnRead(beast::error_code ec, "Discord terminated websocket connection; reason: {} ({})", _websocket->reason().reason.c_str(), _websocket->reason().code); - reconnect = true; + + if (_websocket->reason().code == 4014) + { + logprintf(" >> plugin.dc-connector: bot could not connect due to intent permissions. Modify your discord bot settings and enable every intent."); + reconnect = false; + } + else + { + reconnect = true; + } break; case asio::error::operation_aborted: // connection was closed, do nothing @@ -400,10 +411,12 @@ void WebSocket::OnWrite(beast::error_code ec, Logger::Get()->Log(LogLevel::ERROR, "Can't write to Discord websocket gateway: {} ({})", ec.message(), ec.value()); + // we don't handle reconnects here, as the read handler already does this } } +#define ALL_INTENTS 32767 void WebSocket::Identify() { Logger::Get()->Log(LogLevel::DEBUG, "WebSocket::Identify"); @@ -420,6 +433,7 @@ void WebSocket::Identify() { "d",{ { "token", _apiToken }, { "compress", false }, + { "intents", ALL_INTENTS }, { "large_threshold", LARGE_THRESHOLD_NUMBER }, { "properties",{ { "$os", os_name },