summaryrefslogtreecommitdiff
path: root/src/discord
diff options
context:
space:
mode:
Diffstat (limited to 'src/discord')
-rw-r--r--src/discord/channel.hpp16
-rw-r--r--src/discord/discord.cpp72
-rw-r--r--src/discord/discord.hpp27
-rw-r--r--src/discord/objects.cpp1
-rw-r--r--src/discord/objects.hpp5
-rw-r--r--src/discord/stage.cpp12
-rw-r--r--src/discord/stage.hpp32
-rw-r--r--src/discord/voiceclient.cpp1
-rw-r--r--src/discord/voiceclient.hpp23
-rw-r--r--src/discord/voicestate.cpp5
-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<>