summaryrefslogtreecommitdiff
path: root/src/discord
diff options
context:
space:
mode:
authorouwou <26526779+ouwou@users.noreply.github.com>2022-03-26 02:58:59 -0400
committerouwou <26526779+ouwou@users.noreply.github.com>2022-03-26 02:58:59 -0400
commita0b3c9f8a4f8d2c39258d4c142f8604423576d91 (patch)
tree841155a65b439b61a5c58c1f64878152c20d4f51 /src/discord
parent481685b3bbb2b0270870dec5de87e60fc2d84d15 (diff)
parenta2a45757e917aa97e71cf0b84a01dc843759a5f6 (diff)
downloadabaddon-portaudio-a0b3c9f8a4f8d2c39258d4c142f8604423576d91.tar.gz
abaddon-portaudio-a0b3c9f8a4f8d2c39258d4c142f8604423576d91.zip
Merge branch 'master' into msys
Diffstat (limited to 'src/discord')
-rw-r--r--src/discord/channel.cpp10
-rw-r--r--src/discord/channel.hpp12
-rw-r--r--src/discord/discord.cpp65
-rw-r--r--src/discord/discord.hpp29
-rw-r--r--src/discord/objects.cpp19
-rw-r--r--src/discord/objects.hpp55
-rw-r--r--src/discord/role.cpp8
-rw-r--r--src/discord/role.hpp3
-rw-r--r--src/discord/store.cpp45
-rw-r--r--src/discord/store.hpp3
-rw-r--r--src/discord/user.cpp29
-rw-r--r--src/discord/user.hpp4
12 files changed, 246 insertions, 36 deletions
diff --git a/src/discord/channel.cpp b/src/discord/channel.cpp
index 2f5c3c1..9d47076 100644
--- a/src/discord/channel.cpp
+++ b/src/discord/channel.cpp
@@ -13,6 +13,8 @@ void from_json(const nlohmann::json &j, ThreadMemberObject &m) {
JS_O("user_id", m.UserID);
JS_D("join_timestamp", m.JoinTimestamp);
JS_D("flags", m.Flags);
+ JS_O("muted", m.IsMuted);
+ JS_ON("mute_config", m.MuteConfig);
}
void from_json(const nlohmann::json &j, ChannelData &m) {
@@ -82,6 +84,14 @@ bool ChannelData::IsCategory() const noexcept {
return Type == ChannelType::GUILD_CATEGORY;
}
+bool ChannelData::HasIcon() const noexcept {
+ return Icon.has_value();
+}
+
+std::string ChannelData::GetIconURL() const {
+ return "https://cdn.discordapp.com/channel-icons/" + std::to_string(ID) + "/" + *Icon + ".png";
+}
+
std::vector<Snowflake> ChannelData::GetChildIDs() const {
return Abaddon::Get().GetDiscordClient().GetChildChannelIDs(ID);
}
diff --git a/src/discord/channel.hpp b/src/discord/channel.hpp
index 195a09a..89e43a0 100644
--- a/src/discord/channel.hpp
+++ b/src/discord/channel.hpp
@@ -49,9 +49,19 @@ struct ThreadMetadataData {
friend void from_json(const nlohmann::json &j, ThreadMetadataData &m);
};
+struct MuteConfigData {
+ std::optional<std::string> EndTime; // nullopt is encoded as null
+ int SelectedTimeWindow;
+
+ friend void from_json(const nlohmann::json &j, MuteConfigData &m);
+ friend void to_json(nlohmann::json &j, const MuteConfigData &m);
+};
+
struct ThreadMemberObject {
std::optional<Snowflake> ThreadID;
std::optional<Snowflake> UserID;
+ std::optional<bool> IsMuted;
+ std::optional<MuteConfigData> MuteConfig;
std::string JoinTimestamp;
int Flags;
@@ -89,6 +99,8 @@ struct ChannelData {
bool IsThread() const noexcept;
bool IsJoinedThread() const;
bool IsCategory() const noexcept;
+ bool HasIcon() const noexcept;
+ std::string GetIconURL() const;
std::vector<Snowflake> GetChildIDs() const;
std::optional<PermissionOverwrite> GetOverwrite(Snowflake id) const;
std::vector<UserData> GetDMRecipients() const;
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index 5b3cdb5..c11210d 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -983,6 +983,24 @@ void DiscordClient::UnmuteGuild(Snowflake id, sigc::slot<void(DiscordError code)
});
}
+void DiscordClient::MuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
+ m_http.MakePATCH("/channels/" + std::to_string(id) + "/thread-members/@me/settings", R"({"muted":true})", [this, callback](const http::response_type &response) {
+ if (CheckCode(response))
+ callback(DiscordError::NONE);
+ else
+ callback(GetCodeFromResponse(response));
+ });
+}
+
+void DiscordClient::UnmuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
+ m_http.MakePATCH("/channels/" + std::to_string(id) + "/thread-members/@me/settings", R"({"muted":false})", [this, callback](const http::response_type &response) {
+ if (CheckCode(response))
+ callback(DiscordError::NONE);
+ else
+ callback(GetCodeFromResponse(response));
+ });
+}
+
void DiscordClient::FetchPinned(Snowflake id, sigc::slot<void(std::vector<Message>, DiscordError code)> callback) {
// return from db if we know the pins have already been requested
if (m_channels_pinned_requested.find(id) != m_channels_pinned_requested.end()) {
@@ -1206,7 +1224,7 @@ int DiscordClient::GetUnreadDMsCount() const {
const auto channels = GetPrivateChannels();
int count = 0;
for (const auto channel_id : channels)
- if (GetUnreadStateForChannel(channel_id) > -1) count++;
+ if (!IsChannelMuted(channel_id) && GetUnreadStateForChannel(channel_id) > -1) count++;
return count;
}
@@ -1310,7 +1328,7 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
case GatewayOp::InvalidSession: {
HandleGatewayInvalidSession(m);
} break;
- case GatewayOp::Event: {
+ case GatewayOp::Dispatch: {
auto iter = m_event_map.find(m.Type);
if (iter == m_event_map.end()) {
printf("Unknown event %s\n", m.Type.c_str());
@@ -1446,6 +1464,9 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
case GatewayEvent::USER_GUILD_SETTINGS_UPDATE: {
HandleGatewayUserGuildSettingsUpdate(m);
} break;
+ case GatewayEvent::GUILD_MEMBERS_CHUNK: {
+ HandleGatewayGuildMembersChunk(m);
+ } break;
}
} break;
default:
@@ -1921,9 +1942,24 @@ void DiscordClient::HandleGatewayThreadMembersUpdate(const GatewayMessage &msg)
void DiscordClient::HandleGatewayThreadMemberUpdate(const GatewayMessage &msg) {
ThreadMemberUpdateData data = msg.Data;
+ if (!data.Member.ThreadID.has_value()) return;
+
m_joined_threads.insert(*data.Member.ThreadID);
if (*data.Member.UserID == GetUserData().ID)
m_signal_added_to_thread.emit(*data.Member.ThreadID);
+
+ if (data.Member.IsMuted.has_value()) {
+ const bool was_muted = IsChannelMuted(*data.Member.ThreadID);
+ const bool now_muted = *data.Member.IsMuted;
+
+ if (was_muted && !now_muted) {
+ m_muted_channels.erase(*data.Member.ThreadID);
+ m_signal_channel_unmuted.emit(*data.Member.ThreadID);
+ } else if (!was_muted && now_muted) {
+ m_muted_channels.insert(*data.Member.ThreadID);
+ m_signal_channel_muted.emit(*data.Member.ThreadID);
+ }
+ }
}
void DiscordClient::HandleGatewayThreadUpdate(const GatewayMessage &msg) {
@@ -2016,6 +2052,14 @@ void DiscordClient::HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &m
}
}
+void DiscordClient::HandleGatewayGuildMembersChunk(const GatewayMessage &msg) {
+ GuildMembersChunkData data = msg.Data;
+ m_store.BeginTransaction();
+ for (const auto &member : data.Members)
+ m_store.SetGuildMember(data.GuildID, member.User->ID, member);
+ m_store.EndTransaction();
+}
+
void DiscordClient::HandleGatewayReadySupplemental(const GatewayMessage &msg) {
ReadySupplementalData data = msg.Data;
for (const auto &p : data.MergedPresences.Friends) {
@@ -2343,10 +2387,14 @@ void DiscordClient::StoreMessageData(Message &msg) {
// here the absence of an entry in m_unread indicates a read channel and the value is only the mention count since the message doesnt matter
// no entry.id cannot be a guild even though sometimes it looks like it
void DiscordClient::HandleReadyReadState(const ReadyEventData &data) {
- for (const auto &guild : data.Guilds)
+ for (const auto &guild : data.Guilds) {
for (const auto &channel : *guild.Channels)
if (channel.LastMessageID.has_value())
m_last_message_id[channel.ID] = *channel.LastMessageID;
+ for (const auto &thread : *guild.Threads)
+ if (thread.LastMessageID.has_value())
+ m_last_message_id[thread.ID] = *thread.LastMessageID;
+ }
for (const auto &channel : data.PrivateChannels)
if (channel.LastMessageID.has_value())
m_last_message_id[channel.ID] = *channel.LastMessageID;
@@ -2385,10 +2433,14 @@ void DiscordClient::HandleReadyGuildSettings(const ReadyEventData &data) {
// i dont like this implementation for muted categories but its rather simple and doesnt use a horriiible amount of ram
std::unordered_map<Snowflake, std::vector<Snowflake>> category_children;
- for (const auto &guild : data.Guilds)
+ for (const auto &guild : data.Guilds) {
for (const auto &channel : *guild.Channels)
if (channel.ParentID.has_value() && !channel.IsThread())
category_children[*channel.ParentID].push_back(channel.ID);
+ for (const auto &thread : *guild.Threads)
+ if (thread.ThreadMember.has_value() && thread.ThreadMember->IsMuted.has_value() && *thread.ThreadMember->IsMuted)
+ m_muted_channels.insert(thread.ID);
+ }
const auto now = Snowflake::FromNow();
for (const auto &entry : data.GuildSettings.Entries) {
@@ -2470,6 +2522,7 @@ void DiscordClient::LoadEventMap() {
m_event_map["THREAD_MEMBER_LIST_UPDATE"] = GatewayEvent::THREAD_MEMBER_LIST_UPDATE;
m_event_map["MESSAGE_ACK"] = GatewayEvent::MESSAGE_ACK;
m_event_map["USER_GUILD_SETTINGS_UPDATE"] = GatewayEvent::USER_GUILD_SETTINGS_UPDATE;
+ m_event_map["GUILD_MEMBERS_CHUNK"] = GatewayEvent::GUILD_MEMBERS_CHUNK;
}
DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() {
@@ -2636,6 +2689,10 @@ DiscordClient::type_signal_message_ack DiscordClient::signal_message_ack() {
return m_signal_message_ack;
}
+DiscordClient::type_signal_guild_members_chunk DiscordClient::signal_guild_members_chunk() {
+ return m_signal_guild_members_chunk;
+}
+
DiscordClient::type_signal_added_to_thread DiscordClient::signal_added_to_thread() {
return m_signal_added_to_thread;
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 1a6aa14..6add18f 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -84,6 +84,17 @@ public:
void GetArchivedPrivateThreads(Snowflake channel_id, sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> callback);
std::vector<Snowflake> GetChildChannelIDs(Snowflake parent_id) const;
+ // get ids of given list of members for who we do not have the member data
+ template<typename Iter>
+ std::unordered_set<Snowflake> FilterUnknownMembersFrom(Snowflake guild_id, Iter begin, Iter end) {
+ std::unordered_set<Snowflake> ret;
+ const auto known = m_store.GetMembersInGuild(guild_id);
+ for (auto iter = begin; iter != end; iter++)
+ if (known.find(*iter) == known.end())
+ ret.insert(*iter);
+ return ret;
+ }
+
bool IsThreadJoined(Snowflake thread_id) const;
bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const;
@@ -146,10 +157,24 @@ public:
void MarkAllAsRead(sigc::slot<void(DiscordError code)> callback);
void MuteGuild(Snowflake id, sigc::slot<void(DiscordError code)> callback);
void UnmuteGuild(Snowflake id, sigc::slot<void(DiscordError code)> callback);
+ void MuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback);
+ void UnmuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback);
bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const;
bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const;
+ // send op 8 to get member data for unknown members
+ template<typename Iter>
+ void RequestMembers(Snowflake guild_id, Iter begin, Iter end) {
+ if (std::distance(begin, end) == 0) return;
+
+ RequestGuildMembersMessage obj;
+ obj.GuildID = guild_id;
+ obj.Presences = false;
+ obj.UserIDs = { begin, end };
+ m_websocket.Send(obj);
+ }
+
// real client doesn't seem to use the single role endpoints so neither do we
template<typename Iter>
auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, sigc::slot<void(DiscordError code)> callback) {
@@ -260,6 +285,7 @@ private:
void HandleGatewayThreadMemberListUpdate(const GatewayMessage &msg);
void HandleGatewayMessageAck(const GatewayMessage &msg);
void HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &msg);
+ void HandleGatewayGuildMembersChunk(const GatewayMessage &msg);
void HandleGatewayReadySupplemental(const GatewayMessage &msg);
void HandleGatewayReconnect(const GatewayMessage &msg);
void HandleGatewayInvalidSession(const GatewayMessage &msg);
@@ -370,6 +396,7 @@ public:
typedef sigc::signal<void, ThreadUpdateData> type_signal_thread_update;
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;
// not discord dispatch events
typedef sigc::signal<void, Snowflake> type_signal_added_to_thread;
@@ -425,6 +452,7 @@ public:
type_signal_thread_update signal_thread_update();
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_added_to_thread signal_added_to_thread();
type_signal_removed_from_thread signal_removed_from_thread();
@@ -477,6 +505,7 @@ protected:
type_signal_thread_update m_signal_thread_update;
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_removed_from_thread m_signal_removed_from_thread;
type_signal_added_to_thread m_signal_added_to_thread;
diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp
index 8ca8c0f..3c9f770 100644
--- a/src/discord/objects.cpp
+++ b/src/discord/objects.cpp
@@ -77,7 +77,7 @@ void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage &m) {
}
void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m) {
- j["op"] = GatewayOp::LazyLoadRequest;
+ j["op"] = GatewayOp::GuildSubscriptions;
j["d"] = nlohmann::json::object();
j["d"]["guild_id"] = m.GuildID;
if (m.Channels.has_value()) {
@@ -98,7 +98,7 @@ void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m) {
}
void to_json(nlohmann::json &j, const UpdateStatusMessage &m) {
- j["op"] = GatewayOp::UpdateStatus;
+ j["op"] = GatewayOp::PresenceUpdate;
j["d"] = nlohmann::json::object();
j["d"]["since"] = m.Since;
j["d"]["activities"] = m.Activities;
@@ -119,6 +119,14 @@ void to_json(nlohmann::json &j, const UpdateStatusMessage &m) {
}
}
+void to_json(nlohmann::json &j, const RequestGuildMembersMessage &m) {
+ j["op"] = GatewayOp::RequestGuildMembers;
+ j["d"] = nlohmann::json::object();
+ j["d"]["guild_id"] = m.GuildID;
+ j["d"]["presences"] = m.Presences;
+ j["d"]["user_ids"] = m.UserIDs;
+}
+
void from_json(const nlohmann::json &j, ReadStateEntry &m) {
JS_ON("mention_count", m.MentionCount);
JS_ON("last_message_id", m.LastMessageID);
@@ -154,7 +162,7 @@ void to_json(nlohmann::json &j, const UserGuildSettingsChannelOverride &m) {
void from_json(const nlohmann::json &j, MuteConfigData &m) {
JS_ON("end_time", m.EndTime);
- JS_D("selected_time_window", m.SelectedTimeWindow);
+ JS_ON("selected_time_window", m.SelectedTimeWindow);
}
void to_json(nlohmann::json &j, const MuteConfigData &m) {
@@ -626,3 +634,8 @@ void to_json(nlohmann::json &j, const AckBulkData &m) {
void from_json(const nlohmann::json &j, UserGuildSettingsUpdateData &m) {
m.Settings = j;
}
+
+void from_json(const nlohmann::json &j, GuildMembersChunkData &m) {
+ JS_D("members", m.Members);
+ JS_D("guild_id", m.GuildID);
+}
diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp
index c72361b..27542cc 100644
--- a/src/discord/objects.hpp
+++ b/src/discord/objects.hpp
@@ -24,16 +24,35 @@
// most stuff below should just be objects that get processed and thrown away immediately
enum class GatewayOp : int {
- Event = 0,
+ Dispatch = 0,
Heartbeat = 1,
Identify = 2,
- UpdateStatus = 3,
+ PresenceUpdate = 3,
+ VoiceStateUpdate = 4,
+ VoiceServerPing = 5,
Resume = 6,
Reconnect = 7,
+ RequestGuildMembers = 8,
InvalidSession = 9,
Hello = 10,
HeartbeatAck = 11,
- LazyLoadRequest = 14,
+ // 12 unused
+ CallConnect = 13,
+ GuildSubscriptions = 14,
+ LobbyConnect = 15,
+ LobbyDisconnect = 16,
+ LobbyVoiceStatesUpdate = 17,
+ StreamCreate = 18,
+ StreamDelete = 19,
+ StreamWatch = 20,
+ StreamPing = 21,
+ StreamSetPaused = 22,
+ // 23 unused
+ RequestGuildApplicationCommands = 24,
+ EmbeddedActivityLaunch = 25,
+ EmbeddedActivityClose = 26,
+ EmbeddedActivityUpdate = 27,
+ RequestForumUnreads = 28,
};
enum class GatewayEvent : int {
@@ -80,6 +99,7 @@ enum class GatewayEvent : int {
THREAD_MEMBER_LIST_UPDATE,
MESSAGE_ACK,
USER_GUILD_SETTINGS_UPDATE,
+ GUILD_MEMBERS_CHUNK,
};
enum class GatewayCloseCode : uint16_t {
@@ -226,6 +246,14 @@ struct UpdateStatusMessage {
friend void to_json(nlohmann::json &j, const UpdateStatusMessage &m);
};
+struct RequestGuildMembersMessage {
+ Snowflake GuildID;
+ bool Presences;
+ std::vector<Snowflake> UserIDs;
+
+ friend void to_json(nlohmann::json &j, const RequestGuildMembersMessage &m);
+};
+
struct ReadStateEntry {
int MentionCount;
Snowflake LastMessageID;
@@ -244,14 +272,6 @@ struct ReadStateData {
friend void from_json(const nlohmann::json &j, ReadStateData &m);
};
-struct MuteConfigData {
- std::optional<std::string> EndTime; // nullopt is encoded as null
- int SelectedTimeWindow;
-
- friend void from_json(const nlohmann::json &j, MuteConfigData &m);
- friend void to_json(nlohmann::json &j, const MuteConfigData &m);
-};
-
struct UserGuildSettingsChannelOverride {
bool Muted;
MuteConfigData MuteConfig;
@@ -830,3 +850,16 @@ struct UserGuildSettingsUpdateData {
friend void from_json(const nlohmann::json &j, UserGuildSettingsUpdateData &m);
};
+
+struct GuildMembersChunkData {
+ /*
+ not needed so not deserialized
+ int ChunkCount;
+ int ChunkIndex;
+ std::vector<?> NotFound;
+ */
+ Snowflake GuildID;
+ std::vector<GuildMember> Members;
+
+ friend void from_json(const nlohmann::json &j, GuildMembersChunkData &m);
+};
diff --git a/src/discord/role.cpp b/src/discord/role.cpp
index 07a912e..8a9ed50 100644
--- a/src/discord/role.cpp
+++ b/src/discord/role.cpp
@@ -12,3 +12,11 @@ void from_json(const nlohmann::json &j, RoleData &m) {
JS_D("managed", m.IsManaged);
JS_D("mentionable", m.IsMentionable);
}
+
+bool RoleData::HasColor() const noexcept {
+ return Color != 0;
+}
+
+Glib::ustring RoleData::GetEscapedName() const {
+ return Glib::Markup::escape_text(Name);
+}
diff --git a/src/discord/role.hpp b/src/discord/role.hpp
index f638b65..a526f4e 100644
--- a/src/discord/role.hpp
+++ b/src/discord/role.hpp
@@ -16,5 +16,8 @@ struct RoleData {
bool IsManaged;
bool IsMentionable;
+ bool HasColor() const noexcept;
+ Glib::ustring GetEscapedName() const;
+
friend void from_json(const nlohmann::json &j, RoleData &m);
};
diff --git a/src/discord/store.cpp b/src/discord/store.cpp
index 6a3d636..ede6364 100644
--- a/src/discord/store.cpp
+++ b/src/discord/store.cpp
@@ -13,18 +13,6 @@ Store::Store(bool mem_store)
return;
}
- m_db.Execute(R"(
- PRAGMA writable_schema = 1;
- DELETE FROM sqlite_master WHERE TYPE IN ("view", "table", "index", "trigger");
- PRAGMA writable_schema = 0;
- VACUUM;
- PRAGMA integrity_check;
- )");
- if (!m_db.OK()) {
- fprintf(stderr, "failed to clear database: %s\n", m_db.ErrStr());
- return;
- }
-
if (m_db.Execute("PRAGMA journal_mode = WAL") != SQLITE_OK) {
fprintf(stderr, "enabling write-ahead-log failed: %s\n", m_db.ErrStr());
return;
@@ -588,6 +576,23 @@ std::vector<Snowflake> Store::GetChannelIDsWithParentID(Snowflake channel_id) co
return ret;
}
+std::unordered_set<Snowflake> Store::GetMembersInGuild(Snowflake guild_id) const {
+ auto &s = m_stmt_get_guild_member_ids;
+
+ s->Bind(1, guild_id);
+
+ std::unordered_set<Snowflake> ret;
+ while (s->FetchOne()) {
+ Snowflake x;
+ s->Get(0, x);
+ ret.insert(x);
+ }
+
+ s->Reset();
+
+ return ret;
+}
+
void Store::AddReaction(const MessageReactionAddObject &data, bool byself) {
auto &s = m_stmt_add_reaction;
@@ -646,6 +651,7 @@ std::optional<ChannelData> Store::GetChannel(Snowflake id) const {
s->Get(6, r.IsNSFW);
s->Get(7, r.LastMessageID);
s->Get(10, r.RateLimitPerUser);
+ s->Get(11, r.Icon);
s->Get(12, r.OwnerID);
s->Get(14, r.ParentID);
if (!s->IsNull(16)) {
@@ -2145,10 +2151,25 @@ bool Store::CreateStatements() {
return false;
}
+ m_stmt_get_guild_member_ids = std::make_unique<Statement>(m_db, R"(
+ SELECT user_id FROM members WHERE guild_id = ?
+ )");
+ if (!m_stmt_get_guild_member_ids->OK()) {
+ fprintf(stderr, "failed to prepare get guild member ids statement: %s\n", m_db.ErrStr());
+ return false;
+ }
+
return true;
}
Store::Database::Database(const char *path) {
+ if (path != ":memory:"s) {
+ std::error_code ec;
+ if (std::filesystem::exists(path, ec) && !std::filesystem::remove(path, ec)) {
+ fprintf(stderr, "the database could not be removed. the database may be corrupted as a result\n");
+ }
+ }
+
m_err = sqlite3_open(path, &m_db);
}
diff --git a/src/discord/store.hpp b/src/discord/store.hpp
index 4320807..a1e5f81 100644
--- a/src/discord/store.hpp
+++ b/src/discord/store.hpp
@@ -44,6 +44,8 @@ public:
std::vector<Message> GetPinnedMessages(Snowflake channel_id) const;
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const; // public
std::vector<Snowflake> GetChannelIDsWithParentID(Snowflake channel_id) const;
+ std::unordered_set<Snowflake> GetMembersInGuild(Snowflake guild_id) const;
+ // ^ not the same as GetUsersInGuild since users in a guild may include users who do not have retrieved member data
void AddReaction(const MessageReactionAddObject &data, bool byself);
void RemoveReaction(const MessageReactionRemoveObject &data, bool byself);
@@ -302,5 +304,6 @@ private:
STMT(sub_reaction);
STMT(get_reactions);
STMT(get_chan_ids_parent);
+ STMT(get_guild_member_ids);
#undef STMT
};
diff --git a/src/discord/user.cpp b/src/discord/user.cpp
index fae212d..c2e6069 100644
--- a/src/discord/user.cpp
+++ b/src/discord/user.cpp
@@ -6,22 +6,41 @@ bool UserData::IsDeleted() const {
}
bool UserData::HasAvatar() const {
- return Avatar.size() > 0;
+ return !Avatar.empty();
}
-bool UserData::HasAnimatedAvatar() const {
- return Avatar.size() > 0 && Avatar[0] == 'a' && Avatar[1] == '_';
+bool UserData::HasAnimatedAvatar() const noexcept {
+ return !Avatar.empty() && Avatar[0] == 'a' && Avatar[1] == '_';
+}
+
+bool UserData::HasAnimatedAvatar(Snowflake guild_id) const {
+ const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
+ if (member.has_value() && member->Avatar.has_value() && member->Avatar.value()[0] == 'a' && member->Avatar.value()[1] == '_')
+ return true;
+ else if (member.has_value() && !member->Avatar.has_value())
+ return HasAnimatedAvatar();
+ return false;
+}
+
+bool UserData::HasAnimatedAvatar(const std::optional<Snowflake> &guild_id) const {
+ if (guild_id.has_value())
+ return HasAnimatedAvatar(*guild_id);
+ else
+ return HasAnimatedAvatar();
}
std::string UserData::GetAvatarURL(Snowflake guild_id, std::string ext, std::string size) const {
const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
- if (member.has_value() && member->Avatar.has_value())
+ if (member.has_value() && member->Avatar.has_value()) {
+ if (ext == "gif" && !(member->Avatar.value()[0] == 'a' && member->Avatar.value()[1] == '_'))
+ return GetAvatarURL(ext, size);
return "https://cdn.discordapp.com/guilds/" +
std::to_string(guild_id) + "/users/" + std::to_string(ID) +
"/avatars/" + *member->Avatar + "." +
ext + "?" + "size=" + size;
- else
+ } else {
return GetAvatarURL(ext, size);
+ }
}
std::string UserData::GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext, std::string size) const {
diff --git a/src/discord/user.hpp b/src/discord/user.hpp
index d4711fa..c058ea1 100644
--- a/src/discord/user.hpp
+++ b/src/discord/user.hpp
@@ -62,7 +62,9 @@ struct UserData {
bool IsDeleted() const;
bool HasAvatar() const;
- bool HasAnimatedAvatar() const;
+ bool HasAnimatedAvatar() const noexcept;
+ bool HasAnimatedAvatar(Snowflake guild_id) const;
+ bool HasAnimatedAvatar(const std::optional<Snowflake> &guild_id) const;
std::string GetAvatarURL(Snowflake guild_id, std::string ext = "png", std::string size = "32") const;
std::string GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext = "png", std::string size = "32") const;
std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const;