diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/abaddon.cpp | 14 | ||||
-rw-r--r-- | src/audio/ma_impl.cpp | 3 | ||||
-rw-r--r-- | src/components/channels.cpp | 82 | ||||
-rw-r--r-- | src/components/channels.hpp | 1 | ||||
-rw-r--r-- | src/components/chatinput.cpp | 6 | ||||
-rw-r--r-- | src/constants.hpp | 2 | ||||
-rw-r--r-- | src/discord/chatsubmitparams.hpp | 1 | ||||
-rw-r--r-- | src/discord/discord.cpp | 80 | ||||
-rw-r--r-- | src/discord/discord.hpp | 3 | ||||
-rw-r--r-- | src/discord/message.hpp | 26 | ||||
-rw-r--r-- | src/discord/objects.cpp | 6 | ||||
-rw-r--r-- | src/discord/objects.hpp | 43 | ||||
-rw-r--r-- | src/discord/snowflake.cpp | 4 | ||||
-rw-r--r-- | src/discord/snowflake.hpp | 1 | ||||
-rw-r--r-- | src/discord/voiceclient.cpp | 4 | ||||
-rw-r--r-- | src/notifications/notifications.cpp | 2 | ||||
-rw-r--r-- | src/settings.cpp | 2 | ||||
-rw-r--r-- | src/settings.hpp | 3 | ||||
-rw-r--r-- | src/windows/voicesettingswindow.cpp | 12 | ||||
-rw-r--r-- | src/windows/voicesettingswindow.hpp | 6 | ||||
-rw-r--r-- | src/windows/voicewindow.cpp | 5 |
21 files changed, 232 insertions, 74 deletions
diff --git a/src/abaddon.cpp b/src/abaddon.cpp index e22afde..92cc494 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -252,6 +252,12 @@ int Abaddon::StartGTK() { } #endif +#ifdef _WIN32 + if (m_settings.GetSettings().HideConsole) { + ShowWindow(GetConsoleWindow(), SW_HIDE); + } +#endif + // store must be checked before this can be called m_main_window->UpdateComponents(); @@ -914,8 +920,14 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) { } void Abaddon::ActionChatInputSubmit(ChatSubmitParams data) { - if (data.Message.substr(0, 7) == "/shrug " || data.Message == "/shrug") + if (data.Message.substr(0, 7) == "/shrug " || data.Message == "/shrug") { data.Message = data.Message.substr(6) + "\xC2\xAF\x5C\x5F\x28\xE3\x83\x84\x29\x5F\x2F\xC2\xAF"; // this is important + } + + if (data.Message.substr(0, 8) == "@silent " || (data.Message.substr(0, 7) == "@silent" && !data.Attachments.empty())) { + data.Silent = true; + data.Message = data.Message.substr(7); + } if (!m_discord.HasChannelPermission(m_discord.GetUserData().ID, data.ChannelID, Permission::VIEW_CHANNEL)) return; diff --git a/src/audio/ma_impl.cpp b/src/audio/ma_impl.cpp index a83ddaf..531b24f 100644 --- a/src/audio/ma_impl.cpp +++ b/src/audio/ma_impl.cpp @@ -1,4 +1,7 @@ #ifdef WITH_MINIAUDIO #define MINIAUDIO_IMPLEMENTATION + #ifdef __APPLE__ + #define MA_NO_RUNTIME_LINKING + #endif #include <miniaudio.h> #endif diff --git a/src/components/channels.cpp b/src/components/channels.cpp index 15a8348..26f3259 100644 --- a/src/components/channels.cpp +++ b/src/components/channels.cpp @@ -42,7 +42,7 @@ ChannelList::ChannelList() const auto type = row[m_columns.m_type]; // text channels should not be allowed to be collapsed // maybe they should be but it seems a little difficult to handle expansion to permit this - if (type != RenderType::TextChannel) { + if (type != RenderType::TextChannel && type != RenderType::DM) { if (row[m_columns.m_expanded]) { m_view.collapse_row(path); row[m_columns.m_expanded] = false; @@ -527,6 +527,7 @@ void ChannelList::OnThreadListSync(const ThreadListSyncData &data) { #ifdef WITH_VOICE void ChannelList::OnVoiceUserConnect(Snowflake user_id, Snowflake channel_id) { auto parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::VoiceChannel); + if (!parent_iter) parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::DM); if (!parent_iter) return; const auto user = Abaddon::Get().GetDiscordClient().GetUser(user_id); if (!user.has_value()) return; @@ -1015,20 +1016,17 @@ void ChannelList::AddPrivateChannels() { row[m_columns.m_name] = Glib::Markup::escape_text(dm->GetDisplayName()); row[m_columns.m_sort] = static_cast<int64_t>(-(dm->LastMessageID.has_value() ? *dm->LastMessageID : dm_id)); row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize); + row[m_columns.m_expanded] = true; - if (dm->HasIcon()) { - const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - if (iter) - (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(dm->GetIconURL(), sigc::track_obj(cb, *this)); - } else if (top_recipient.has_value()) { - const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - if (iter) - (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this)); +#ifdef WITH_VOICE + for (auto user_id : discord.GetUsersInVoiceChannel(dm_id)) { + if (const auto user = discord.GetUser(user_id); user.has_value()) { + CreateVoiceParticipantRow(*user, row->children()); + } } +#endif + + SetDMChannelIcon(iter, *dm); } } @@ -1036,11 +1034,6 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) { auto header_row = m_model->get_iter(m_dm_header); auto &img = Abaddon::Get().GetImageManager(); - std::optional<UserData> top_recipient; - const auto recipients = dm.GetDMRecipients(); - if (!recipients.empty()) - top_recipient = recipients[0]; - auto iter = m_model->append(header_row->children()); auto row = *iter; row[m_columns.m_type] = RenderType::DM; @@ -1049,12 +1042,63 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) { row[m_columns.m_sort] = static_cast<int64_t>(-(dm.LastMessageID.has_value() ? *dm.LastMessageID : dm.ID)); row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize); - if (top_recipient.has_value()) { + SetDMChannelIcon(iter, dm); +} + +void ChannelList::SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm) { + auto &img = Abaddon::Get().GetImageManager(); + + std::optional<UserData> top_recipient; + const auto recipients = dm.GetDMRecipients(); + if (!recipients.empty()) + top_recipient = recipients[0]; + + if (dm.HasIcon()) { + const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { + if (iter) + (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); + }; + img.LoadFromURL(dm.GetIconURL(), sigc::track_obj(cb, *this)); + } else if (dm.Type == ChannelType::DM && top_recipient.has_value()) { const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { if (iter) (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); }; img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this)); + } else { // GROUP_DM + std::string hash; + switch (dm.ID.GetUnixMilliseconds() % 8) { + case 0: + hash = "ee9275c5a437f7dc7f9430ba95f12ebd"; + break; + case 1: + hash = "9baf45aac2a0ec2e2dab288333acb9d9"; + break; + case 2: + hash = "7ba11ffb1900fa2b088cb31324242047"; + break; + case 3: + hash = "f90fca70610c4898bc57b58bce92f587"; + break; + case 4: + hash = "e2779af34b8d9126b77420e5f09213ce"; + break; + case 5: + hash = "c6851bd0b03f1cca5a8c1e720ea6ea17"; + break; + case 6: + hash = "f7e38ac976a2a696161c923502a8345b"; + break; + case 7: + default: + hash = "3cb840d03313467838d658bbec801fcd"; + break; + } + const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { + if (iter) + (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); + }; + img.LoadFromURL("https://discord.com/assets/" + hash + ".png", sigc::track_obj(cb, *this)); } } diff --git a/src/components/channels.hpp b/src/components/channels.hpp index 7a23b3d..d561737 100644 --- a/src/components/channels.hpp +++ b/src/components/channels.hpp @@ -114,6 +114,7 @@ protected: void AddPrivateChannels(); void UpdateCreateDMChannel(const ChannelData &channel); + void SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm); void OnMessageAck(const MessageAckData &data); diff --git a/src/components/chatinput.cpp b/src/components/chatinput.cpp index d19ac4b..28ed1ea 100644 --- a/src/components/chatinput.cpp +++ b/src/components/chatinput.cpp @@ -41,9 +41,15 @@ bool ChatInputText::ProcessKeyPress(GdkEventKey *event) { return true; } +#ifdef __APPLE__ + if ((event->state & GDK_MOD2_MASK) && event->keyval == GDK_KEY_v) { + return CheckHandleClipboardPaste(); + } +#else if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_v) { return CheckHandleClipboardPaste(); } +#endif if (event->keyval == GDK_KEY_Return) { if (event->state & GDK_SHIFT_MASK) diff --git a/src/constants.hpp b/src/constants.hpp index 256f85e..5ed123c 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -4,7 +4,7 @@ constexpr static uint64_t SnowflakeSplitDifference = 600; constexpr static int MaxMessagesForChatCull = 50; // this has to be 50 (for now) cuz that magic number is used in a couple other places and i dont feel like replacing them constexpr static int AttachmentItemSize = 120; -constexpr static int BaseAttachmentSizeLimit = 8 * 1024 * 1024; +constexpr static int BaseAttachmentSizeLimit = 25 * 1024 * 1024; constexpr static int NitroClassicAttachmentSizeLimit = 50 * 1024 * 1024; constexpr static int NitroAttachmentSizeLimit = 100 * 1024 * 1024; constexpr static int BoostLevel2AttachmentSizeLimit = 50 * 1024 * 1024; diff --git a/src/discord/chatsubmitparams.hpp b/src/discord/chatsubmitparams.hpp index 6199634..24c6f50 100644 --- a/src/discord/chatsubmitparams.hpp +++ b/src/discord/chatsubmitparams.hpp @@ -17,6 +17,7 @@ struct ChatSubmitParams { std::string Filename; }; + bool Silent = false; Snowflake ChannelID; Snowflake InReplyToID; Glib::ustring Message; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 2a25a26..817aca8 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -461,8 +461,13 @@ void DiscordClient::SendChatMessageNoAttachments(const ChatSubmitParams ¶ms, CreateMessageObject obj; obj.Content = params.Message; obj.Nonce = nonce; - if (params.InReplyToID.IsValid()) + if (params.Silent) { + obj.Flags |= MessageFlags::SUPPRESS_NOTIFICATIONS; + } + + if (params.InReplyToID.IsValid()) { obj.MessageReference.emplace().MessageID = params.InReplyToID; + } m_http.MakePOST("/channels/" + std::to_string(params.ChannelID) + "/messages", nlohmann::json(obj).dump(), @@ -494,8 +499,13 @@ void DiscordClient::SendChatMessageAttachments(const ChatSubmitParams ¶ms, c CreateMessageObject obj; obj.Content = params.Message; obj.Nonce = nonce; - if (params.InReplyToID.IsValid()) + if (params.Silent) { + obj.Flags |= MessageFlags::SUPPRESS_NOTIFICATIONS; + } + + if (params.InReplyToID.IsValid()) { obj.MessageReference.emplace().MessageID = params.InReplyToID; + } auto req = m_http.CreateRequest(http::REQUEST_POST, "/channels/" + std::to_string(params.ChannelID) + "/messages"); m_progress_cb_timer.start(); @@ -545,10 +555,11 @@ void DiscordClient::SendChatMessageAttachments(const ChatSubmitParams ¶ms, c } void DiscordClient::SendChatMessage(const ChatSubmitParams ¶ms, const sigc::slot<void(DiscordError)> &callback) { - if (params.Attachments.empty()) + if (params.Attachments.empty()) { SendChatMessageNoAttachments(params, callback); - else + } else { SendChatMessageAttachments(params, callback); + } } void DiscordClient::DeleteMessage(Snowflake channel_id, Snowflake id) { @@ -1579,6 +1590,9 @@ void DiscordClient::HandleGatewayMessage(std::string str) { case GatewayEvent::VOICE_SERVER_UPDATE: { HandleGatewayVoiceServerUpdate(m); } break; + case GatewayEvent::CALL_CREATE: { + HandleGatewayCallCreate(m); + } break; #endif } } break; @@ -2253,8 +2267,39 @@ void DiscordClient::HandleGatewayGuildMembersChunk(const GatewayMessage &msg) { void DiscordClient::HandleGatewayVoiceStateUpdate(const GatewayMessage &msg) { spdlog::get("discord")->trace("VOICE_STATE_UPDATE"); - VoiceState data = msg.Data; + CheckVoiceState(msg.Data); +} +void DiscordClient::HandleGatewayVoiceServerUpdate(const GatewayMessage &msg) { + spdlog::get("discord")->trace("VOICE_SERVER_UPDATE"); + + VoiceServerUpdateData data = msg.Data; + spdlog::get("discord")->debug("Voice server endpoint: {}", data.Endpoint); + spdlog::get("discord")->debug("Voice token: {}", data.Token); + m_voice.SetEndpoint(data.Endpoint); + m_voice.SetToken(data.Token); + if (data.GuildID.has_value()) { + m_voice.SetServerID(*data.GuildID); + } else if (data.ChannelID.has_value()) { + m_voice.SetServerID(*data.ChannelID); + } else { + spdlog::get("discord")->error("No guild or channel ID in voice server?"); + } + m_voice.SetUserID(m_user_data.ID); + m_voice.Start(); +} + +void DiscordClient::HandleGatewayCallCreate(const GatewayMessage &msg) { + CallCreateData data = msg.Data; + + spdlog::get("discord")->debug("CALL_CREATE: {}", data.ChannelID); + + for (const auto &state : data.VoiceStates) { + CheckVoiceState(state); + } +} + +void DiscordClient::CheckVoiceState(const VoiceState &data) { if (data.UserID == m_user_data.ID) { spdlog::get("discord")->debug("Voice session ID: {}", data.SessionID); m_voice.SetSessionID(data.SessionID); @@ -2292,25 +2337,6 @@ void DiscordClient::HandleGatewayVoiceStateUpdate(const GatewayMessage &msg) { } } } - -void DiscordClient::HandleGatewayVoiceServerUpdate(const GatewayMessage &msg) { - spdlog::get("discord")->trace("VOICE_SERVER_UPDATE"); - - VoiceServerUpdateData data = msg.Data; - spdlog::get("discord")->debug("Voice server endpoint: {}", data.Endpoint); - spdlog::get("discord")->debug("Voice token: {}", data.Token); - m_voice.SetEndpoint(data.Endpoint); - m_voice.SetToken(data.Token); - if (data.GuildID.has_value()) { - m_voice.SetServerID(*data.GuildID); - } else if (data.ChannelID.has_value()) { - m_voice.SetServerID(*data.ChannelID); - } else { - spdlog::get("discord")->error("No guild or channel ID in voice server?"); - } - m_voice.SetUserID(m_user_data.ID); - m_voice.Start(); -} #endif void DiscordClient::HandleGatewayReadySupplemental(const GatewayMessage &msg) { @@ -2556,7 +2582,7 @@ void DiscordClient::HeartbeatThread() { void DiscordClient::SendIdentify() { IdentifyMessage msg; msg.Token = m_token; - msg.Capabilities = 509; // no idea what this is + msg.Capabilities = 4605; // bit 12 is necessary for CALL_CREATE... apparently? need to get this in sync with official client msg.Properties.OS = "Windows"; msg.Properties.Browser = "Chrome"; msg.Properties.Device = ""; @@ -2575,9 +2601,6 @@ void DiscordClient::SendIdentify() { msg.Presence.Since = 0; msg.Presence.IsAFK = false; msg.DoesSupportCompression = false; - msg.ClientState.HighestLastMessageID = "0"; - msg.ClientState.ReadStateVersion = 0; - msg.ClientState.UserGuildSettingsVersion = -1; SetSuperPropertiesFromIdentity(msg); const bool b = m_websocket.GetPrintMessages(); m_websocket.SetPrintMessages(false); @@ -2893,6 +2916,7 @@ void DiscordClient::LoadEventMap() { m_event_map["GUILD_MEMBERS_CHUNK"] = GatewayEvent::GUILD_MEMBERS_CHUNK; 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; } DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() { diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 7f7518c..d2435dd 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -291,6 +291,9 @@ private: #ifdef WITH_VOICE void HandleGatewayVoiceStateUpdate(const GatewayMessage &msg); void HandleGatewayVoiceServerUpdate(const GatewayMessage &msg); + void HandleGatewayCallCreate(const GatewayMessage &msg); + + void CheckVoiceState(const VoiceState &data); #endif void HeartbeatThread(); diff --git a/src/discord/message.hpp b/src/discord/message.hpp index df2cb38..5028104 100644 --- a/src/discord/message.hpp +++ b/src/discord/message.hpp @@ -8,6 +8,7 @@ #include "emoji.hpp" #include "member.hpp" #include "interactions.hpp" +#include "misc/bitwise.hpp" enum class MessageType { DEFAULT = 0, // yep @@ -35,14 +36,23 @@ enum class MessageType { enum class MessageFlags { NONE = 0, - CROSSPOSTED = 1 << 0, // this message has been published to subscribed channels (via Channel Following) - IS_CROSSPOST = 1 << 1, // this message originated from a message in another channel (via Channel Following) - SUPPRESS_EMBEDS = 1 << 2, // do not include any embeds when serializing this message - SOURCE_MESSAGE_DELETE = 1 << 3, // the source message for this crosspost has been deleted (via Channel Following) - URGENT = 1 << 4, // this message came from the urgent message system - HAS_THREAD = 1 << 5, // this message has an associated thread, with the same id as the message - EPHEMERAL = 1 << 6, // this message is only visible to the user who invoked the Interaction - LOADING = 1 << 7, // this message is an Interaction Response and the bot is "thinking" + CROSSPOSTED = 1 << 0, // this message has been published to subscribed channels (via Channel Following) + IS_CROSSPOST = 1 << 1, // this message originated from a message in another channel (via Channel Following) + SUPPRESS_EMBEDS = 1 << 2, // do not include any embeds when serializing this message + SOURCE_MESSAGE_DELETE = 1 << 3, // the source message for this crosspost has been deleted (via Channel Following) + URGENT = 1 << 4, // this message came from the urgent message system + HAS_THREAD = 1 << 5, // this message has an associated thread, with the same id as the message + EPHEMERAL = 1 << 6, // this message is only visible to the user who invoked the Interaction + LOADING = 1 << 7, // this message is an Interaction Response and the bot is "thinking" + FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8, // this message failed to mention some roles and add their members to the thread + SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10, // + SUPPRESS_NOTIFICATIONS = 1 << 12, // this message will not trigger push and desktop notifications + IS_VOICE_MESSAGE = 1 << 13, // this message is a voice message +}; + +template<> +struct Bitwise<MessageFlags> { + static const bool enable = true; }; struct EmbedFooterData { diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp index 715ae14..63bd43f 100644 --- a/src/discord/objects.cpp +++ b/src/discord/objects.cpp @@ -308,6 +308,7 @@ void to_json(nlohmann::json &j, const HeartbeatMessage &m) { void to_json(nlohmann::json &j, const CreateMessageObject &m) { j["content"] = m.Content; + j["flags"] = m.Flags; JS_IF("message_reference", m.MessageReference); JS_IF("nonce", m.Nonce); } @@ -687,6 +688,11 @@ void from_json(const nlohmann::json &j, VoiceServerUpdateData &m) { JS_ON("guild_id", m.GuildID); JS_ON("channel_id", m.ChannelID); } + +void from_json(const nlohmann::json &j, CallCreateData &m) { + JS_D("channel_id", m.ChannelID); + JS_D("voice_states", m.VoiceStates); +} #endif void from_json(const nlohmann::json &j, VoiceState &m) { diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp index 4d56417..305ac65 100644 --- a/src/discord/objects.hpp +++ b/src/discord/objects.hpp @@ -102,6 +102,7 @@ enum class GatewayEvent : int { GUILD_MEMBERS_CHUNK, VOICE_STATE_UPDATE, VOICE_SERVER_UPDATE, + CALL_CREATE, }; enum class GatewayCloseCode : uint16_t { @@ -427,6 +428,7 @@ struct HeartbeatMessage : GatewayMessage { struct CreateMessageObject { std::string Content; + MessageFlags Flags = MessageFlags::NONE; std::optional<MessageReferenceData> MessageReference; std::optional<std::string> Nonce; @@ -887,6 +889,23 @@ struct GuildMembersChunkData { friend void from_json(const nlohmann::json &j, GuildMembersChunkData &m); }; +struct VoiceState { + std::optional<Snowflake> ChannelID; + bool IsDeafened; + bool IsMuted; + std::optional<Snowflake> GuildID; + std::optional<GuildMember> Member; + bool IsSelfDeafened; + bool IsSelfMuted; + bool IsSelfVideo; + bool IsSelfStream = false; + std::string SessionID; + bool IsSuppressed; + Snowflake UserID; + + friend void from_json(const nlohmann::json &j, VoiceState &m); +}; + #ifdef WITH_VOICE struct VoiceStateUpdateMessage { std::optional<Snowflake> GuildID; @@ -907,21 +926,15 @@ struct VoiceServerUpdateData { friend void from_json(const nlohmann::json &j, VoiceServerUpdateData &m); }; -#endif -struct VoiceState { - std::optional<Snowflake> ChannelID; - bool IsDeafened; - bool IsMuted; - std::optional<Snowflake> GuildID; - std::optional<GuildMember> Member; - bool IsSelfDeafened; - bool IsSelfMuted; - bool IsSelfVideo; - bool IsSelfStream = false; - std::string SessionID; - bool IsSuppressed; - Snowflake UserID; +struct CallCreateData { + Snowflake ChannelID; + std::vector<VoiceState> VoiceStates; + // Snowflake MessageID; + // std::string Region; + // std::vector<?> Ringing; + // std::vector<?> EmbeddedActivities; - friend void from_json(const nlohmann::json &j, VoiceState &m); + friend void from_json(const nlohmann::json &j, CallCreateData &m); }; +#endif diff --git a/src/discord/snowflake.cpp b/src/discord/snowflake.cpp index 15dacae..680d4da 100644 --- a/src/discord/snowflake.cpp +++ b/src/discord/snowflake.cpp @@ -60,6 +60,10 @@ Glib::ustring Snowflake::GetLocalTimestamp() const { return tmp.data(); } +uint64_t Snowflake::GetUnixMilliseconds() const noexcept { + return (m_num >> 22) + DiscordEpochSeconds * 1000; +} + void from_json(const nlohmann::json &j, Snowflake &s) { if (j.is_string()) { std::string tmp; diff --git a/src/discord/snowflake.hpp b/src/discord/snowflake.hpp index 2ced46b..68cb5ea 100644 --- a/src/discord/snowflake.hpp +++ b/src/discord/snowflake.hpp @@ -16,6 +16,7 @@ struct Snowflake { [[nodiscard]] bool IsValid() const; [[nodiscard]] Glib::ustring GetLocalTimestamp() const; + [[nodiscard]] uint64_t GetUnixMilliseconds() const noexcept; bool operator==(const Snowflake &s) const noexcept { return m_num == s.m_num; diff --git a/src/discord/voiceclient.cpp b/src/discord/voiceclient.cpp index 524d930..e9814b6 100644 --- a/src/discord/voiceclient.cpp +++ b/src/discord/voiceclient.cpp @@ -112,8 +112,8 @@ void UDPSocket::ReadThread() { sockaddr_in from; socklen_t addrlen = sizeof(from); - tv.tv_sec = 0; - tv.tv_usec = 1000000; + tv.tv_sec = 1; + tv.tv_usec = 0; fd_set read_fds; FD_ZERO(&read_fds); diff --git a/src/notifications/notifications.cpp b/src/notifications/notifications.cpp index 3c88c44..e95c1c0 100644 --- a/src/notifications/notifications.cpp +++ b/src/notifications/notifications.cpp @@ -88,6 +88,8 @@ bool CheckGuildMessage(const Message &message) { void Notifications::CheckMessage(const Message &message) { if (!Abaddon::Get().GetSettings().NotificationsEnabled) return; + // ignore if silent message + if (message.Flags.has_value() && ((*message.Flags & MessageFlags::SUPPRESS_NOTIFICATIONS) == MessageFlags::SUPPRESS_NOTIFICATIONS)) return; // ignore if our status is do not disturb if (IsDND()) return; auto &discord = Abaddon::Get().GetDiscordClient(); diff --git a/src/settings.cpp b/src/settings.cpp index 82401f5..0b868da 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -70,6 +70,7 @@ void SettingsManager::ReadSettings() { SMSTR("style", "unreadcolor", UnreadIndicatorColor); SMBOOL("notifications", "enabled", NotificationsEnabled); SMBOOL("notifications", "playsound", NotificationsPlaySound); + SMBOOL("windows", "hideconsole", HideConsole); #ifdef WITH_KEYCHAIN keychain::Error error {}; @@ -153,6 +154,7 @@ void SettingsManager::Close() { SMSTR("style", "unreadcolor", UnreadIndicatorColor); SMBOOL("notifications", "enabled", NotificationsEnabled); SMBOOL("notifications", "playsound", NotificationsPlaySound); + SMBOOL("windows", "hideconsole", HideConsole); #ifdef WITH_KEYCHAIN keychain::Error error {}; diff --git a/src/settings.hpp b/src/settings.hpp index 53f3423..40cb1d3 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -52,6 +52,9 @@ public: bool NotificationsEnabled { true }; #endif bool NotificationsPlaySound { true }; + + // [windows] + bool HideConsole { false }; }; SettingsManager(const std::string &filename); diff --git a/src/windows/voicesettingswindow.cpp b/src/windows/voicesettingswindow.cpp index c009cbf..3749986 100644 --- a/src/windows/voicesettingswindow.cpp +++ b/src/windows/voicesettingswindow.cpp @@ -110,9 +110,17 @@ VoiceSettingsWindow::VoiceSettingsWindow() } }); + m_gain.set_increments(1.0, 5.0); + m_gain.set_range(0.0, 6969696969.0); + m_gain.set_value(Abaddon::Get().GetAudio().GetCaptureGain() * 100.0); + m_gain.signal_value_changed().connect([this]() { + m_signal_gain.emit(m_gain.get_value() / 100.0); + }); + m_main.add(m_encoding_mode); m_main.add(m_signal); m_main.add(m_bitrate); + m_main.add(m_gain); add(m_main); show_all_children(); @@ -122,4 +130,8 @@ VoiceSettingsWindow::VoiceSettingsWindow() }); } +VoiceSettingsWindow::type_signal_gain VoiceSettingsWindow::signal_gain() { + return m_signal_gain; +} + #endif diff --git a/src/windows/voicesettingswindow.hpp b/src/windows/voicesettingswindow.hpp index cf6b477..9b3498e 100644 --- a/src/windows/voicesettingswindow.hpp +++ b/src/windows/voicesettingswindow.hpp @@ -18,8 +18,14 @@ public: Gtk::ComboBoxText m_encoding_mode; Gtk::ComboBoxText m_signal; Gtk::Scale m_bitrate; + Gtk::SpinButton m_gain; private: + using type_signal_gain = sigc::signal<void(double)>; + type_signal_gain m_signal_gain; + +public: + type_signal_gain signal_gain(); }; #endif diff --git a/src/windows/voicewindow.cpp b/src/windows/voicewindow.cpp index 35ba8c7..a4677c6 100644 --- a/src/windows/voicewindow.cpp +++ b/src/windows/voicewindow.cpp @@ -163,6 +163,11 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) m_menu_view_sub.append(m_menu_view_settings); m_menu_view_settings.signal_activate().connect([this]() { auto *window = new VoiceSettingsWindow; + const auto cb = [this](double gain) { + m_capture_gain.set_value(gain * 100.0); + m_signal_gain.emit(gain); + }; + window->signal_gain().connect(sigc::track_obj(cb, *this)); window->show(); }); |