diff options
Diffstat (limited to 'src/discord')
-rw-r--r-- | src/discord/channel.hpp | 16 | ||||
-rw-r--r-- | src/discord/discord.cpp | 72 | ||||
-rw-r--r-- | src/discord/discord.hpp | 27 | ||||
-rw-r--r-- | src/discord/objects.cpp | 1 | ||||
-rw-r--r-- | src/discord/objects.hpp | 5 | ||||
-rw-r--r-- | src/discord/stage.cpp | 12 | ||||
-rw-r--r-- | src/discord/stage.hpp | 32 | ||||
-rw-r--r-- | src/discord/voiceclient.cpp | 1 | ||||
-rw-r--r-- | src/discord/voiceclient.hpp | 23 | ||||
-rw-r--r-- | src/discord/voicestate.cpp | 5 | ||||
-rw-r--r-- | src/discord/voicestate.hpp (renamed from src/discord/voicestateflags.hpp) | 11 |
11 files changed, 177 insertions, 28 deletions
diff --git a/src/discord/channel.hpp b/src/discord/channel.hpp index cac8b4c..ebf67b0 100644 --- a/src/discord/channel.hpp +++ b/src/discord/channel.hpp @@ -27,22 +27,6 @@ enum class ChannelType : int { GUILD_MEDIA = 16, }; -enum class StagePrivacy { - PUBLIC = 1, - GUILD_ONLY = 2, -}; - -constexpr const char *GetStagePrivacyDisplayString(StagePrivacy e) { - switch (e) { - case StagePrivacy::PUBLIC: - return "Public"; - case StagePrivacy::GUILD_ONLY: - return "Guild Only"; - default: - return "Unknown"; - } -} - // should be moved somewhere? struct ThreadMetadataData { diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 385d6b7..40af498 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1290,13 +1290,18 @@ std::optional<uint32_t> DiscordClient::GetSSRCOfUser(Snowflake id) const { return m_voice.GetSSRCOfUser(id); } -std::optional<std::pair<Snowflake, VoiceStateFlags>> DiscordClient::GetVoiceState(Snowflake user_id) const { +std::optional<std::pair<Snowflake, PackedVoiceState>> DiscordClient::GetVoiceState(Snowflake user_id) const { if (const auto it = m_voice_states.find(user_id); it != m_voice_states.end()) { return it->second; } return std::nullopt; } +bool DiscordClient::IsUserSpeaker(Snowflake user_id) const { + const auto state = GetVoiceState(user_id); + return state.has_value() && state->second.IsSpeaker(); +} + DiscordVoiceClient &DiscordClient::GetVoiceClient() { return m_voice; } @@ -1646,6 +1651,15 @@ void DiscordClient::HandleGatewayMessage(std::string str) { case GatewayEvent::GUILD_MEMBERS_CHUNK: { HandleGatewayGuildMembersChunk(m); } break; + case GatewayEvent::STAGE_INSTANCE_CREATE: { + HandleGatewayStageInstanceCreate(m); + } break; + case GatewayEvent::STAGE_INSTANCE_UPDATE: { + HandleGatewayStageInstanceUpdate(m); + } break; + case GatewayEvent::STAGE_INSTANCE_DELETE: { + HandleGatewayStageInstanceDelete(m); + } break; #ifdef WITH_VOICE case GatewayEvent::VOICE_STATE_UPDATE: { HandleGatewayVoiceStateUpdate(m); @@ -2296,6 +2310,29 @@ void DiscordClient::HandleGatewayGuildMembersChunk(const GatewayMessage &msg) { m_store.EndTransaction(); } +void DiscordClient::HandleGatewayStageInstanceCreate(const GatewayMessage &msg) { + StageInstance data = msg.Data; + spdlog::get("discord")->debug("STAGE_INSTANCE_CREATE: {} in {}", data.ID, data.ChannelID); + m_stage_instances[data.ID] = data; + m_channel_to_stage_instance[data.ChannelID] = data.ID; + m_signal_stage_instance_create.emit(data); +} + +void DiscordClient::HandleGatewayStageInstanceUpdate(const GatewayMessage &msg) { + StageInstance data = msg.Data; + spdlog::get("discord")->debug("STAGE_INSTANCE_UPDATE: {} in {}", data.ID, data.ChannelID); + m_stage_instances[data.ID] = data; + m_signal_stage_instance_update.emit(data); +} + +void DiscordClient::HandleGatewayStageInstanceDelete(const GatewayMessage &msg) { + StageInstance data = msg.Data; + spdlog::get("discord")->debug("STAGE_INSTANCE_DELETE: {} in {}", data.ID, data.ChannelID); + m_stage_instances.erase(data.ID); + m_channel_to_stage_instance.erase(data.ChannelID); + m_signal_stage_instance_delete.emit(data); +} + #ifdef WITH_VOICE /* @@ -2389,9 +2426,14 @@ void DiscordClient::CheckVoiceState(const VoiceState &data) { if (data.ChannelID.has_value()) { const auto old_state = GetVoiceState(data.UserID); SetVoiceState(data.UserID, data); - if (old_state.has_value() && old_state->first != *data.ChannelID) { - m_signal_voice_user_disconnect.emit(data.UserID, old_state->first); - m_signal_voice_user_connect.emit(data.UserID, *data.ChannelID); + const auto new_state = GetVoiceState(data.UserID); + if (old_state.has_value()) { + if (old_state->first != *data.ChannelID) { + m_signal_voice_user_disconnect.emit(data.UserID, old_state->first); + m_signal_voice_user_connect.emit(data.UserID, *data.ChannelID); + } else if (old_state->second.IsSpeaker() != new_state.value().second.IsSpeaker()) { + m_signal_voice_speaker_state_changed.emit(*data.ChannelID, data.UserID, new_state->second.IsSpeaker()); + } } else if (!old_state.has_value()) { m_signal_voice_user_connect.emit(data.UserID, *data.ChannelID); } @@ -2928,8 +2970,9 @@ void DiscordClient::SetVoiceState(Snowflake user_id, const VoiceState &state) { if (state.IsDeafened) flags |= VoiceStateFlags::Deaf; if (state.IsSelfStream) flags |= VoiceStateFlags::SelfStream; if (state.IsSelfVideo) flags |= VoiceStateFlags::SelfVideo; + if (state.IsSuppressed) flags |= VoiceStateFlags::Suppressed; - m_voice_states[user_id] = std::make_pair(*state.ChannelID, flags); + m_voice_states[user_id] = std::make_pair(*state.ChannelID, PackedVoiceState { flags, state.RequestToSpeakTimestamp }); m_voice_state_channel_users[*state.ChannelID].insert(user_id); m_signal_voice_state_set.emit(user_id, *state.ChannelID, flags); @@ -3001,6 +3044,9 @@ void DiscordClient::LoadEventMap() { m_event_map["VOICE_STATE_UPDATE"] = GatewayEvent::VOICE_STATE_UPDATE; m_event_map["VOICE_SERVER_UPDATE"] = GatewayEvent::VOICE_SERVER_UPDATE; m_event_map["CALL_CREATE"] = GatewayEvent::CALL_CREATE; + m_event_map["STAGE_INSTANCE_CREATE"] = GatewayEvent::STAGE_INSTANCE_CREATE; + m_event_map["STAGE_INSTANCE_UPDATE"] = GatewayEvent::STAGE_INSTANCE_UPDATE; + m_event_map["STAGE_INSTANCE_DELETE"] = GatewayEvent::STAGE_INSTANCE_DELETE; } DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() { @@ -3179,6 +3225,18 @@ DiscordClient::type_signal_guild_members_chunk DiscordClient::signal_guild_membe return m_signal_guild_members_chunk; } +DiscordClient::type_signal_stage_instance_create DiscordClient::signal_stage_instance_create() { + return m_signal_stage_instance_create; +} + +DiscordClient::type_signal_stage_instance_update DiscordClient::signal_stage_instance_update() { + return m_signal_stage_instance_update; +} + +DiscordClient::type_signal_stage_instance_delete DiscordClient::signal_stage_instance_delete() { + return m_signal_stage_instance_delete; +} + DiscordClient::type_signal_added_to_thread DiscordClient::signal_added_to_thread() { return m_signal_added_to_thread; } @@ -3255,4 +3313,8 @@ DiscordClient::type_signal_voice_channel_changed DiscordClient::signal_voice_cha DiscordClient::type_signal_voice_state_set DiscordClient::signal_voice_state_set() { return m_signal_voice_state_set; } + +DiscordClient::type_signal_voice_speaker_state_changed DiscordClient::signal_voice_speaker_state_changed() { + return m_signal_voice_speaker_state_changed; +} #endif diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 9c2c080..44a7328 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -5,7 +5,7 @@ #include "objects.hpp" #include "store.hpp" #include "voiceclient.hpp" -#include "voicestateflags.hpp" +#include "voicestate.hpp" #include "websocket.hpp" #include <gdkmm/rgba.h> #include <sigc++/sigc++.h> @@ -202,7 +202,8 @@ public: [[nodiscard]] Snowflake GetVoiceChannelID() const noexcept; [[nodiscard]] std::unordered_set<Snowflake> GetUsersInVoiceChannel(Snowflake channel_id); [[nodiscard]] std::optional<uint32_t> GetSSRCOfUser(Snowflake id) const; - [[nodiscard]] std::optional<std::pair<Snowflake, VoiceStateFlags>> GetVoiceState(Snowflake user_id) const; + [[nodiscard]] std::optional<std::pair<Snowflake, PackedVoiceState>> GetVoiceState(Snowflake user_id) const; + [[nodiscard]] bool IsUserSpeaker(Snowflake user_id) const; DiscordVoiceClient &GetVoiceClient(); @@ -294,12 +295,16 @@ private: void HandleGatewayMessageAck(const GatewayMessage &msg); void HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &msg); void HandleGatewayGuildMembersChunk(const GatewayMessage &msg); + void HandleGatewayStageInstanceCreate(const GatewayMessage &msg); + void HandleGatewayStageInstanceUpdate(const GatewayMessage &msg); + void HandleGatewayStageInstanceDelete(const GatewayMessage &msg); void HandleGatewayReadySupplemental(const GatewayMessage &msg); void HandleGatewayReconnect(const GatewayMessage &msg); void HandleGatewayInvalidSession(const GatewayMessage &msg); #ifdef WITH_VOICE - void HandleGatewayVoiceStateUpdate(const GatewayMessage &msg); + void + HandleGatewayVoiceStateUpdate(const GatewayMessage &msg); void HandleGatewayVoiceServerUpdate(const GatewayMessage &msg); void HandleGatewayCallCreate(const GatewayMessage &msg); @@ -341,6 +346,8 @@ private: std::unordered_set<Snowflake> m_muted_channels; std::unordered_map<Snowflake, int> m_unread; std::unordered_set<Snowflake> m_channel_muted_parent; + std::map<Snowflake, StageInstance> m_stage_instances; + std::map<Snowflake, Snowflake> m_channel_to_stage_instance; UserData m_user_data; UserSettings m_user_settings; @@ -374,7 +381,7 @@ private: Snowflake m_voice_channel_id; // todo sql i guess - std::unordered_map<Snowflake, std::pair<Snowflake, VoiceStateFlags>> m_voice_states; + std::unordered_map<Snowflake, std::pair<Snowflake, PackedVoiceState>> m_voice_states; std::unordered_map<Snowflake, std::unordered_set<Snowflake>> m_voice_state_channel_users; void SendVoiceStateUpdate(); @@ -441,6 +448,9 @@ public: typedef sigc::signal<void, ThreadMemberListUpdateData> type_signal_thread_member_list_update; typedef sigc::signal<void, MessageAckData> type_signal_message_ack; typedef sigc::signal<void, GuildMembersChunkData> type_signal_guild_members_chunk; + typedef sigc::signal<void, StageInstance> type_signal_stage_instance_create; + typedef sigc::signal<void, StageInstance> type_signal_stage_instance_update; + typedef sigc::signal<void, StageInstance> type_signal_stage_instance_delete; // not discord dispatch events typedef sigc::signal<void, Snowflake> type_signal_added_to_thread; @@ -470,6 +480,7 @@ public: using type_signal_voice_client_state_update = sigc::signal<void(DiscordVoiceClient::State)>; using type_signal_voice_channel_changed = sigc::signal<void(Snowflake)>; using type_signal_voice_state_set = sigc::signal<void(Snowflake, Snowflake, VoiceStateFlags)>; + using type_signal_voice_speaker_state_changed = sigc::signal<void(Snowflake /* channel_id */, Snowflake /* user_id */, bool /* is_speaker */)>; #endif type_signal_gateway_ready signal_gateway_ready(); @@ -513,6 +524,9 @@ public: type_signal_thread_member_list_update signal_thread_member_list_update(); type_signal_message_ack signal_message_ack(); type_signal_guild_members_chunk signal_guild_members_chunk(); + type_signal_stage_instance_create signal_stage_instance_create(); + type_signal_stage_instance_update signal_stage_instance_update(); + type_signal_stage_instance_delete signal_stage_instance_delete(); type_signal_added_to_thread signal_added_to_thread(); type_signal_removed_from_thread signal_removed_from_thread(); @@ -538,6 +552,7 @@ public: type_signal_voice_client_state_update signal_voice_client_state_update(); type_signal_voice_channel_changed signal_voice_channel_changed(); type_signal_voice_state_set signal_voice_state_set(); + type_signal_voice_speaker_state_changed signal_voice_speaker_state_changed(); #endif protected: @@ -582,6 +597,9 @@ protected: type_signal_thread_member_list_update m_signal_thread_member_list_update; type_signal_message_ack m_signal_message_ack; type_signal_guild_members_chunk m_signal_guild_members_chunk; + type_signal_stage_instance_create m_signal_stage_instance_create; + type_signal_stage_instance_update m_signal_stage_instance_update; + type_signal_stage_instance_delete m_signal_stage_instance_delete; type_signal_removed_from_thread m_signal_removed_from_thread; type_signal_added_to_thread m_signal_added_to_thread; @@ -607,5 +625,6 @@ protected: type_signal_voice_client_state_update m_signal_voice_client_state_update; type_signal_voice_channel_changed m_signal_voice_channel_changed; type_signal_voice_state_set m_signal_voice_state_set; + type_signal_voice_speaker_state_changed m_signal_voice_speaker_state_changed; #endif }; diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp index 804f10d..1c5dd39 100644 --- a/src/discord/objects.cpp +++ b/src/discord/objects.cpp @@ -714,4 +714,5 @@ void from_json(const nlohmann::json &j, VoiceState &m) { JS_D("user_id", m.UserID); JS_ON("member", m.Member); JS_D("session_id", m.SessionID); + JS_ON("request_to_speak_timestamp", m.RequestToSpeakTimestamp); } diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp index dfe99f0..e026311 100644 --- a/src/discord/objects.hpp +++ b/src/discord/objects.hpp @@ -20,6 +20,7 @@ #include "auditlog.hpp" #include "relationship.hpp" #include "errors.hpp" +#include "stage.hpp" // most stuff below should just be objects that get processed and thrown away immediately @@ -110,6 +111,9 @@ enum class GatewayEvent : int { VOICE_STATE_UPDATE, VOICE_SERVER_UPDATE, CALL_CREATE, + STAGE_INSTANCE_CREATE, + STAGE_INSTANCE_UPDATE, + STAGE_INSTANCE_DELETE, }; enum class GatewayCloseCode : uint16_t { @@ -917,6 +921,7 @@ struct VoiceState { std::string SessionID; bool IsSuppressed; Snowflake UserID; + std::optional<std::string> RequestToSpeakTimestamp; friend void from_json(const nlohmann::json &j, VoiceState &m); }; diff --git a/src/discord/stage.cpp b/src/discord/stage.cpp new file mode 100644 index 0000000..428e1f3 --- /dev/null +++ b/src/discord/stage.cpp @@ -0,0 +1,12 @@ +#include "stage.hpp" + +#include "json.hpp" + +void from_json(const nlohmann::json &j, StageInstance &m) { + JS_D("id", m.ID); + JS_D("guild_id", m.GuildID); + JS_D("channel_id", m.ChannelID); + JS_N("topic", m.Topic); + JS_N("privacy_level", m.PrivacyLevel); + JS_N("guild_scheduled_event_id", m.GuildScheduledEventID); +} diff --git a/src/discord/stage.hpp b/src/discord/stage.hpp new file mode 100644 index 0000000..3df4433 --- /dev/null +++ b/src/discord/stage.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include <nlohmann/json.hpp> + +#include "snowflake.hpp" + +enum class StagePrivacy { + PUBLIC = 1, + GUILD_ONLY = 2, +}; + +constexpr const char *GetStagePrivacyDisplayString(StagePrivacy e) { + switch (e) { + case StagePrivacy::PUBLIC: + return "Public"; + case StagePrivacy::GUILD_ONLY: + return "Guild Only"; + default: + return "Unknown"; + } +} + +struct StageInstance { + Snowflake ID; + Snowflake GuildID; + Snowflake ChannelID; + std::string Topic; + StagePrivacy PrivacyLevel; + Snowflake GuildScheduledEventID; + + friend void from_json(const nlohmann::json &j, StageInstance &m); +}; diff --git a/src/discord/voiceclient.cpp b/src/discord/voiceclient.cpp index 212b878..c0c83b2 100644 --- a/src/discord/voiceclient.cpp +++ b/src/discord/voiceclient.cpp @@ -250,6 +250,7 @@ bool DiscordVoiceClient::IsConnecting() const noexcept { } void DiscordVoiceClient::OnGatewayMessage(const std::string &str) { + m_log->trace("IN: {}", str); VoiceGatewayMessage msg = nlohmann::json::parse(str); switch (msg.Opcode) { case VoiceGatewayOp::Hello: diff --git a/src/discord/voiceclient.hpp b/src/discord/voiceclient.hpp index 0112749..aa1014c 100644 --- a/src/discord/voiceclient.hpp +++ b/src/discord/voiceclient.hpp @@ -43,6 +43,23 @@ enum class VoiceGatewayOp : int { Hello = 8, Resumed = 9, ClientDisconnect = 13, + SessionUpdate = 14, + MediaSinkWants = 15, + VoiceBackendVersion = 16, + ChannelOptionsUpdate = 17, + Flags = 18, + SpeedTest = 19, + Platform = 20, + SecureFramesPrepareProtocolTransition = 21, + SecureFramesExecuteTransition = 22, + SecureFramesReadyForTransition = 23, + SecureFramesPrepareEpoch = 24, + MlsExternalSenderPackage = 25, + MlsKeyPackage = 26, + MlsProposals = 27, + MlsCommitWelcome = 28, + MlsPrepareCommitTransition = 29, + MlsWelcome = 30, }; struct VoiceGatewayMessage { @@ -156,11 +173,11 @@ public: private: void ReadThread(); - #ifdef _WIN32 +#ifdef _WIN32 SOCKET m_socket; - #else +#else int m_socket; - #endif +#endif sockaddr_in m_server; std::atomic<bool> m_running = false; diff --git a/src/discord/voicestate.cpp b/src/discord/voicestate.cpp new file mode 100644 index 0000000..05c050d --- /dev/null +++ b/src/discord/voicestate.cpp @@ -0,0 +1,5 @@ +#include "voicestate.hpp" + +bool PackedVoiceState::IsSpeaker() const noexcept { + return ((Flags & VoiceStateFlags::Suppressed) != VoiceStateFlags::Suppressed) && !RequestToSpeakTimestamp.has_value(); +} diff --git a/src/discord/voicestateflags.hpp b/src/discord/voicestate.hpp index 01fb762..cc75b0c 100644 --- a/src/discord/voicestateflags.hpp +++ b/src/discord/voicestate.hpp @@ -1,7 +1,10 @@ #pragma once #include <cstdint> +#include <optional> +#include <string> #include "misc/bitwise.hpp" +// this is packed into a enum cuz it makes implementing tree models easier enum class VoiceStateFlags : uint8_t { Clear = 0, Deaf = 1 << 0, @@ -10,6 +13,14 @@ enum class VoiceStateFlags : uint8_t { SelfMute = 1 << 3, SelfStream = 1 << 4, SelfVideo = 1 << 5, + Suppressed = 1 << 6, +}; + +struct PackedVoiceState { + VoiceStateFlags Flags; + std::optional<std::string> RequestToSpeakTimestamp; + + [[nodiscard]] bool IsSpeaker() const noexcept; }; template<> |