From d425997c263a089e8c0075a6f89fd792a3652923 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 28 Oct 2021 00:31:35 -0400 Subject: [PATCH 01/23] rewrite store this probably should have been broken up into smaller commits. oh well --- abaddon.cpp | 53 +- components/chatmessage.cpp | 2 + components/chatwindow.cpp | 2 +- components/chatwindow.hpp | 2 +- discord/discord.cpp | 90 +- discord/discord.hpp | 10 +- discord/guild.cpp | 7 +- discord/permissions.hpp | 2 +- discord/store.cpp | 2674 +++++++++++++++--------- discord/store.hpp | 341 ++- windows/guildsettings/auditlogpane.cpp | 2 +- windows/guildsettings/auditlogpane.hpp | 2 +- 12 files changed, 1990 insertions(+), 1197 deletions(-) diff --git a/abaddon.cpp b/abaddon.cpp index 5bf6771..14d65bc 100644 --- a/abaddon.cpp +++ b/abaddon.cpp @@ -83,9 +83,30 @@ int Abaddon::StartGTK() { m_main_window = std::make_unique(); m_main_window->set_title(APP_TITLE); - m_main_window->UpdateComponents(); m_main_window->set_position(Gtk::WIN_POS_CENTER); + if (!m_settings.IsValid()) { + Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + dlg.set_position(Gtk::WIN_POS_CENTER); + dlg.run(); + } + + if (!m_emojis.Load()) { + Gtk::MessageDialog dlg(*m_main_window, "The emoji file couldn't be loaded!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + dlg.set_position(Gtk::WIN_POS_CENTER); + dlg.run(); + } + + if (!m_discord.IsStoreValid()) { + Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + dlg.set_position(Gtk::WIN_POS_CENTER); + dlg.run(); + return 1; + } + + // store must be checked before this can be called + m_main_window->UpdateComponents(); + // crashes for some stupid reason if i put it somewhere else SetupUserMenu(); @@ -117,25 +138,6 @@ int Abaddon::StartGTK() { StopDiscord(); }); - if (!m_settings.IsValid()) { - Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - - if (!m_emojis.Load()) { - Gtk::MessageDialog dlg(*m_main_window, "The emoji file couldn't be loaded!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - - if (!m_discord.IsStoreValid()) { - Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - return 1; - } - m_main_window->show(); return m_gtk_app->run(*m_main_window); } @@ -510,16 +512,9 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) { return; Snowflake before_id = m_main_window->GetChatOldestListedMessage(); - auto knownset = m_discord.GetMessageIDsForChannel(id); - std::vector knownvec(knownset.begin(), knownset.end()); - std::sort(knownvec.begin(), knownvec.end()); - auto latest = std::find_if(knownvec.begin(), knownvec.end(), [&before_id](Snowflake x) -> bool { return x == before_id; }); - int distance = std::distance(knownvec.begin(), latest); + auto msgs = m_discord.GetMessagesBefore(id, before_id); - if (distance >= 50) { - std::vector msgs; - for (auto it = knownvec.begin() + distance - 50; it != knownvec.begin() + distance; it++) - msgs.push_back(*m_discord.GetMessage(*it)); + if (msgs.size() >= 50) { m_main_window->UpdateChatPrependHistory(msgs); return; } diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp index 759443f..af68a9d 100644 --- a/components/chatmessage.cpp +++ b/components/chatmessage.cpp @@ -231,11 +231,13 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) { } } break; case MessageType::RECIPIENT_ADD: { + if (data->Mentions.size() == 0) break; const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID); const auto &added = data->Mentions[0]; b->insert_markup(s, "" + adder->Username + " added " + added.Username + ""); } break; case MessageType::RECIPIENT_REMOVE: { + if (data->Mentions.size() == 0) break; const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID); const auto &added = data->Mentions[0]; if (adder->ID == added.ID) diff --git a/components/chatwindow.cpp b/components/chatwindow.cpp index c2bd688..9b34dfd 100644 --- a/components/chatwindow.cpp +++ b/components/chatwindow.cpp @@ -1,6 +1,6 @@ #include "chatwindow.hpp" #include "chatmessage.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" #include "chatinputindicator.hpp" #include "ratelimitindicator.hpp" #include "chatinput.hpp" diff --git a/components/chatwindow.hpp b/components/chatwindow.hpp index 5ef8bad..de55b0a 100644 --- a/components/chatwindow.hpp +++ b/components/chatwindow.hpp @@ -2,7 +2,7 @@ #include #include #include -#include "../discord/discord.hpp" +#include "discord/discord.hpp" #include "completer.hpp" class ChatMessageHeader; diff --git a/discord/discord.cpp b/discord/discord.cpp index c494145..0309508 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -108,8 +108,8 @@ std::vector DiscordClient::GetMessagesForChannel(Snowflake id, size_t l return m_store.GetLastMessages(id, limit); } -std::vector DiscordClient::GetMessageIDsForChannel(Snowflake id) const { - return m_store.GetChannelMessageIDs(id); +std::vector DiscordClient::GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const { + return m_store.GetMessagesBefore(channel_id, message_id, limit); } void DiscordClient::FetchInvite(std::string code, sigc::slot)> callback) { @@ -1347,7 +1347,7 @@ void DiscordClient::ProcessNewGuild(GuildData &guild) { } for (auto &r : *guild.Roles) - m_store.SetRole(r.ID, r); + m_store.SetRole(guild.ID, r); for (auto &e : *guild.Emojis) m_store.SetEmoji(e.ID, e); @@ -1515,7 +1515,7 @@ void DiscordClient::HandleGatewayGuildUpdate(const GatewayMessage &msg) { void DiscordClient::HandleGatewayGuildRoleUpdate(const GatewayMessage &msg) { GuildRoleUpdateObject data = msg.Data; - m_store.SetRole(data.Role.ID, data.Role); + m_store.SetRole(data.GuildID, data.Role); m_signal_role_update.emit(data.GuildID, data.Role.ID); } @@ -1524,7 +1524,7 @@ void DiscordClient::HandleGatewayGuildRoleCreate(const GatewayMessage &msg) { auto guild = *m_store.GetGuild(data.GuildID); guild.Roles->push_back(data.Role); m_store.BeginTransaction(); - m_store.SetRole(data.Role.ID, data.Role); + m_store.SetRole(guild.ID, data.Role); m_store.SetGuild(guild.ID, guild); m_store.EndTransaction(); m_signal_role_create.emit(data.GuildID, data.Role.ID); @@ -1543,81 +1543,22 @@ void DiscordClient::HandleGatewayGuildRoleDelete(const GatewayMessage &msg) { void DiscordClient::HandleGatewayMessageReactionAdd(const GatewayMessage &msg) { MessageReactionAddObject data = msg.Data; - auto to = m_store.GetMessage(data.MessageID); - if (data.Emoji.ID.IsValid()) { - const auto cur_emoji = m_store.GetEmoji(data.Emoji.ID); - if (!cur_emoji.has_value()) - m_store.SetEmoji(data.Emoji.ID, data.Emoji); - } - if (!to.has_value()) return; - if (!to->Reactions.has_value()) to->Reactions.emplace(); - // add if present - bool stock; - auto it = std::find_if(to->Reactions->begin(), to->Reactions->end(), [&](const ReactionData &x) { - if (data.Emoji.ID.IsValid() && x.Emoji.ID.IsValid()) { - stock = false; - return data.Emoji.ID == x.Emoji.ID; - } else { - stock = true; - return data.Emoji.Name == x.Emoji.Name; - } - }); - if (it != to->Reactions->end()) { - it->Count++; - if (data.UserID == GetUserData().ID) - it->HasReactedWith = true; - m_store.SetMessage(data.MessageID, *to); - if (stock) - m_signal_reaction_add.emit(data.MessageID, data.Emoji.Name); - else - m_signal_reaction_add.emit(data.MessageID, std::to_string(data.Emoji.ID)); - return; - } - - // create new - auto &rdata = to->Reactions->emplace_back(); - rdata.Count = 1; - rdata.Emoji = data.Emoji; - rdata.HasReactedWith = data.UserID == GetUserData().ID; - m_store.SetMessage(data.MessageID, *to); - if (stock) - m_signal_reaction_add.emit(data.MessageID, data.Emoji.Name); - else + m_store.AddReaction(data, data.UserID == GetUserData().ID); + if (data.Emoji.ID.IsValid()) m_signal_reaction_add.emit(data.MessageID, std::to_string(data.Emoji.ID)); + else + m_signal_reaction_add.emit(data.MessageID, data.Emoji.Name); } void DiscordClient::HandleGatewayMessageReactionRemove(const GatewayMessage &msg) { MessageReactionRemoveObject data = msg.Data; - auto to = m_store.GetMessage(data.MessageID); - if (!to.has_value()) return; - if (!to->Reactions.has_value()) return; - bool stock; - auto it = std::find_if(to->Reactions->begin(), to->Reactions->end(), [&](const ReactionData &x) { - if (data.Emoji.ID.IsValid() && x.Emoji.ID.IsValid()) { - stock = false; - return data.Emoji.ID == x.Emoji.ID; - } else { - stock = true; - return data.Emoji.Name == x.Emoji.Name; - } - }); - if (it == to->Reactions->end()) return; - if (it->Count == 1) - to->Reactions->erase(it); - else { - if (data.UserID == GetUserData().ID) - it->HasReactedWith = false; - it->Count--; - } - - m_store.SetMessage(data.MessageID, *to); - - if (stock) - m_signal_reaction_remove.emit(data.MessageID, data.Emoji.Name); - else + m_store.RemoveReaction(data, data.UserID == GetUserData().ID); + if (data.Emoji.ID.IsValid()) m_signal_reaction_remove.emit(data.MessageID, std::to_string(data.Emoji.ID)); + else + m_signal_reaction_remove.emit(data.MessageID, data.Emoji.Name); } // todo: update channel list item and member list @@ -1633,10 +1574,7 @@ void DiscordClient::HandleGatewayChannelRecipientAdd(const GatewayMessage &msg) void DiscordClient::HandleGatewayChannelRecipientRemove(const GatewayMessage &msg) { ChannelRecipientRemove data = msg.Data; - auto cur = m_store.GetChannel(data.ChannelID); - if (!cur.has_value() || !cur->RecipientIDs.has_value()) return; - cur->RecipientIDs->erase(std::remove(cur->RecipientIDs->begin(), cur->RecipientIDs->end(), data.User.ID)); - m_store.SetChannel(cur->ID, *cur); + m_store.ClearRecipient(data.ChannelID, data.User.ID); } void DiscordClient::HandleGatewayTypingStart(const GatewayMessage &msg) { diff --git a/discord/discord.hpp b/discord/discord.hpp index aefe1f6..4b9bc82 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -53,20 +53,12 @@ public: bool IsStarted() const; bool IsStoreValid() const; - using guilds_type = Store::guilds_type; - using channels_type = Store::channels_type; - using messages_type = Store::messages_type; - using users_type = Store::users_type; - using roles_type = Store::roles_type; - using members_type = Store::members_type; - using permission_overwrites_type = Store::permission_overwrites_type; - std::unordered_set GetGuilds() const; const UserData &GetUserData() const; const UserSettings &GetUserSettings() const; std::vector GetUserSortedGuilds() const; std::vector GetMessagesForChannel(Snowflake id, size_t limit = 50) const; - std::vector GetMessageIDsForChannel(Snowflake id) const; + std::vector GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit = 50) const; std::set GetPrivateChannels() const; EPremiumType GetSelfPremiumType() const; diff --git a/discord/guild.cpp b/discord/guild.cpp index 1c79a38..16fc3d3 100644 --- a/discord/guild.cpp +++ b/discord/guild.cpp @@ -191,8 +191,11 @@ std::vector GuildData::GetSortedChannels(Snowflake ignore) const { std::vector GuildData::FetchRoles() const { if (!Roles.has_value()) return {}; std::vector ret; - for (const auto thing : *Roles) - ret.push_back(*Abaddon::Get().GetDiscordClient().GetRole(thing.ID)); + for (const auto thing : *Roles) { + auto r = Abaddon::Get().GetDiscordClient().GetRole(thing.ID); + if (r.has_value()) + ret.push_back(*r); + } std::sort(ret.begin(), ret.end(), [](const RoleData &a, const RoleData &b) -> bool { return a.Position > b.Position; }); diff --git a/discord/permissions.hpp b/discord/permissions.hpp index 5609135..c7090f6 100644 --- a/discord/permissions.hpp +++ b/discord/permissions.hpp @@ -4,7 +4,7 @@ #include "json.hpp" #include "../util.hpp" -constexpr static uint64_t PERMISSION_MAX_BIT = 31; +constexpr static uint64_t PERMISSION_MAX_BIT = 36; enum class Permission : uint64_t { NONE = 0, CREATE_INSTANT_INVITE = (1ULL << 0), // Allows creation of instant invites diff --git a/discord/store.cpp b/discord/store.cpp index bc73f67..b39df42 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -4,56 +4,44 @@ using namespace std::literals::string_literals; // hopefully the casting between signed and unsigned int64 doesnt cause issues -Store::Store(bool mem_store) { - if (mem_store) { - m_db_path = ":memory:"; - m_db_err = sqlite3_open(":memory:", &m_db); - } else { - m_db_path = std::filesystem::temp_directory_path() / "abaddon-store.db"; - m_db_err = sqlite3_open(m_db_path.string().c_str(), &m_db); - } - - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error opening database: %s\n", sqlite3_errstr(m_db_err)); +Store::Store(bool mem_store) + : m_db_path(mem_store ? ":memory:" : std::filesystem::temp_directory_path() / "abaddon-store.db") + , m_db(m_db_path.string().c_str()) { + if (!m_db.OK()) { + fprintf(stderr, "error opening database: %s\n", m_db.ErrStr()); return; } - // clang-format off - m_db_err = sqlite3_exec(m_db, R"( + m_db.Execute(R"( PRAGMA writable_schema = 1; DELETE FROM sqlite_master; PRAGMA writable_schema = 0; VACUUM; PRAGMA integrity_check; - )", nullptr, nullptr, nullptr); - // clang-format on - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to clear database: %s\n", sqlite3_errstr(m_db_err)); + )"); + if (!m_db.OK()) { + fprintf(stderr, "failed to clear database: %s\n", m_db.ErrStr()); return; } - m_db_err = sqlite3_exec(m_db, "PRAGMA journal_mode = WAL", nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "enabling write-ahead-log failed: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute("PRAGMA journal_mode = WAL") != SQLITE_OK) { + fprintf(stderr, "enabling write-ahead-log failed: %s\n", m_db.ErrStr()); return; } - m_db_err = sqlite3_exec(m_db, "PRAGMA synchronous = NORMAL", nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "setting synchronous failed: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute("PRAGMA synchronous = NORMAL") != SQLITE_OK) { + fprintf(stderr, "setting synchronous failed: %s\n", m_db.ErrStr()); return; } - CreateTables(); - CreateStatements(); + m_ok &= CreateTables(); + m_ok &= CreateStatements(); } Store::~Store() { - Cleanup(); - - m_db_err = sqlite3_close(m_db); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error closing database: %s\n", sqlite3_errstr(m_db_err)); + m_db.Close(); + if (!m_db.OK()) { + fprintf(stderr, "error closing database: %s\n", m_db.ErrStr()); return; } @@ -64,783 +52,1057 @@ Store::~Store() { } bool Store::IsValid() const { - return m_db_err == SQLITE_OK; + return m_db.OK() && m_ok; } void Store::SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban) { - Bind(m_set_ban_stmt, 1, guild_id); - Bind(m_set_ban_stmt, 2, user_id); - Bind(m_set_ban_stmt, 3, ban.Reason); + auto &s = m_stmt_set_ban; - if (!RunInsert(m_set_ban_stmt)) - fprintf(stderr, "ban insert failed: %s\n", sqlite3_errstr(m_db_err)); + s->Bind(1, guild_id); + s->Bind(2, user_id); + s->Bind(3, ban.Reason); + + if (!s->Insert()) + fprintf(stderr, "ban insert failed for %llu/%llu: %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); + + s->Reset(); } void Store::SetChannel(Snowflake id, const ChannelData &chan) { - Bind(m_set_chan_stmt, 1, id); - Bind(m_set_chan_stmt, 2, static_cast(chan.Type)); - Bind(m_set_chan_stmt, 3, chan.GuildID); - Bind(m_set_chan_stmt, 4, chan.Position); - Bind(m_set_chan_stmt, 5, nullptr); // unused - Bind(m_set_chan_stmt, 6, chan.Name); - Bind(m_set_chan_stmt, 7, chan.Topic); - Bind(m_set_chan_stmt, 8, chan.IsNSFW); - Bind(m_set_chan_stmt, 9, chan.LastMessageID); - Bind(m_set_chan_stmt, 10, chan.Bitrate); - Bind(m_set_chan_stmt, 11, chan.UserLimit); - Bind(m_set_chan_stmt, 12, chan.RateLimitPerUser); - if (chan.Recipients.has_value()) { - std::vector ids; - for (const auto &u : *chan.Recipients) - ids.push_back(u.ID); - Bind(m_set_chan_stmt, 13, nlohmann::json(ids).dump()); - } else if (chan.RecipientIDs.has_value()) { - Bind(m_set_chan_stmt, 13, nlohmann::json(*chan.RecipientIDs).dump()); - } else { - Bind(m_set_chan_stmt, 13, nullptr); - } - Bind(m_set_chan_stmt, 14, chan.Icon); - Bind(m_set_chan_stmt, 15, chan.OwnerID); - Bind(m_set_chan_stmt, 16, chan.ApplicationID); - Bind(m_set_chan_stmt, 17, chan.ParentID); - Bind(m_set_chan_stmt, 18, chan.LastPinTimestamp); + auto &s = m_stmt_set_chan; + s->Bind(1, id); + s->Bind(2, chan.Type); + s->Bind(3, chan.GuildID); + s->Bind(4, chan.Position); + s->Bind(5, chan.Name); + s->Bind(6, chan.Topic); + s->Bind(7, chan.IsNSFW); + s->Bind(8, chan.LastMessageID); + s->Bind(9, chan.Bitrate); + s->Bind(10, chan.UserLimit); + s->Bind(11, chan.RateLimitPerUser); + s->Bind(12, chan.Icon); + s->Bind(13, chan.OwnerID); + s->Bind(14, chan.ApplicationID); + s->Bind(15, chan.ParentID); + s->Bind(16, chan.LastPinTimestamp); if (chan.ThreadMetadata.has_value()) { - Bind(m_set_chan_stmt, 19, chan.ThreadMetadata->IsArchived); - Bind(m_set_chan_stmt, 20, chan.ThreadMetadata->AutoArchiveDuration); - Bind(m_set_chan_stmt, 21, chan.ThreadMetadata->ArchiveTimestamp); + s->Bind(17, chan.ThreadMetadata->IsArchived); + s->Bind(18, chan.ThreadMetadata->AutoArchiveDuration); + s->Bind(19, chan.ThreadMetadata->ArchiveTimestamp); } else { - Bind(m_set_chan_stmt, 19, nullptr); - Bind(m_set_chan_stmt, 20, nullptr); - Bind(m_set_chan_stmt, 21, nullptr); + s->Bind(17); + s->Bind(18); + s->Bind(19); } - if (!RunInsert(m_set_chan_stmt)) - fprintf(stderr, "channel insert failed: %s\n", sqlite3_errstr(m_db_err)); + if (!s->Insert()) + fprintf(stderr, "channel insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); - m_channels.insert(id); + if (chan.Recipients.has_value()) { + BeginTransaction(); + auto &s = m_stmt_set_recipient; + for (const auto &r : *chan.Recipients) { + s->Bind(1, chan.ID); + s->Bind(2, r.ID); + if (!s->Insert()) + fprintf(stderr, "recipient insert failed for %llu/%llu: %s\n", static_cast(chan.ID), static_cast(r.ID), m_db.ErrStr()); + s->Reset(); + } + EndTransaction(); + } else if (chan.RecipientIDs.has_value()) { + BeginTransaction(); + auto &s = m_stmt_set_recipient; + for (const auto &id : *chan.RecipientIDs) { + s->Bind(1, chan.ID); + s->Bind(2, id); + if (!s->Insert()) + fprintf(stderr, "recipient insert failed for %llu/%llu: %s\n", static_cast(chan.ID), static_cast(id), m_db.ErrStr()); + s->Reset(); + } + EndTransaction(); + } + + s->Reset(); } void Store::SetEmoji(Snowflake id, const EmojiData &emoji) { - Bind(m_set_emote_stmt, 1, id); - Bind(m_set_emote_stmt, 2, emoji.Name); - - if (emoji.Roles.has_value()) - Bind(m_set_emote_stmt, 3, nlohmann::json(*emoji.Roles).dump()); - else - Bind(m_set_emote_stmt, 3, nullptr); + auto &s = m_stmt_set_emoji; + s->Bind(1, id); + s->Bind(2, emoji.Name); if (emoji.Creator.has_value()) - Bind(m_set_emote_stmt, 4, emoji.Creator->ID); + s->Bind(3, emoji.Creator->ID); else - Bind(m_set_emote_stmt, 4, nullptr); + s->Bind(3); + s->Bind(4, emoji.NeedsColons); + s->Bind(5, emoji.IsManaged); + s->Bind(6, emoji.IsAnimated); + s->Bind(7, emoji.IsAvailable); - Bind(m_set_emote_stmt, 5, emoji.NeedsColons); - Bind(m_set_emote_stmt, 6, emoji.IsManaged); - Bind(m_set_emote_stmt, 7, emoji.IsAnimated); - Bind(m_set_emote_stmt, 8, emoji.IsAvailable); + if (emoji.Roles.has_value()) { + BeginTransaction(); - if (!RunInsert(m_set_emote_stmt)) - fprintf(stderr, "emoji insert failed: %s\n", sqlite3_errstr(m_db_err)); + auto &s = m_stmt_set_emoji_role; + + for (const auto &r : *emoji.Roles) { + s->Bind(1, id); + s->Bind(2, r); + if (!s->Insert()) + fprintf(stderr, "emoji role insert failed for %llu/%llu: %s\n", static_cast(id), static_cast(r), m_db.ErrStr()); + s->Reset(); + } + + EndTransaction(); + } + + if (!s->Insert()) + fprintf(stderr, "emoji insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + + s->Reset(); } void Store::SetGuild(Snowflake id, const GuildData &guild) { - Bind(m_set_guild_stmt, 1, id); - Bind(m_set_guild_stmt, 2, guild.Name); - Bind(m_set_guild_stmt, 3, guild.Icon); - Bind(m_set_guild_stmt, 4, guild.Splash); - Bind(m_set_guild_stmt, 5, guild.IsOwner); - Bind(m_set_guild_stmt, 6, guild.OwnerID); - Bind(m_set_guild_stmt, 7, guild.PermissionsNew); - Bind(m_set_guild_stmt, 8, guild.VoiceRegion); - Bind(m_set_guild_stmt, 9, guild.AFKChannelID); - Bind(m_set_guild_stmt, 10, guild.AFKTimeout); - Bind(m_set_guild_stmt, 11, guild.VerificationLevel); - Bind(m_set_guild_stmt, 12, guild.DefaultMessageNotifications); - std::vector snowflakes; - if (guild.Roles.has_value()) { - for (const auto &x : *guild.Roles) snowflakes.push_back(x.ID); - Bind(m_set_guild_stmt, 13, nlohmann::json(snowflakes).dump()); - } else { - Bind(m_set_guild_stmt, 13, "[]"s); - } - snowflakes.clear(); + BeginTransaction(); + auto &s = m_stmt_set_guild; + + s->Bind(1, guild.ID); + s->Bind(2, guild.Name); + s->Bind(3, guild.Icon); + s->Bind(4, guild.Splash); + s->Bind(5, guild.IsOwner); + s->Bind(6, guild.OwnerID); + s->Bind(7, guild.PermissionsNew); + s->Bind(8, guild.VoiceRegion); + s->Bind(9, guild.AFKChannelID); + s->Bind(10, guild.AFKTimeout); + s->Bind(11, guild.VerificationLevel); + s->Bind(12, guild.DefaultMessageNotifications); + s->Bind(13, guild.MFALevel); + s->Bind(14, guild.ApplicationID); + s->Bind(15, guild.IsWidgetEnabled); + s->Bind(16, guild.WidgetChannelID); + s->Bind(17, guild.SystemChannelFlags); + s->Bind(18, guild.RulesChannelID); + s->Bind(19, guild.JoinedAt); + s->Bind(20, guild.IsLarge); + s->Bind(21, guild.IsUnavailable); + s->Bind(22, guild.MemberCount); + s->Bind(23, guild.MaxPresences); + s->Bind(24, guild.MaxMembers); + s->Bind(25, guild.VanityURL); + s->Bind(26, guild.Description); + s->Bind(27, guild.BannerHash); + s->Bind(28, guild.PremiumTier); + s->Bind(29, guild.PremiumSubscriptionCount); + s->Bind(30, guild.PreferredLocale); + s->Bind(31, guild.PublicUpdatesChannelID); + s->Bind(32, guild.MaxVideoChannelUsers); + s->Bind(33, guild.ApproximateMemberCount); + s->Bind(34, guild.ApproximatePresenceCount); + s->Bind(35, guild.IsLazy); + + if (!s->Insert()) + fprintf(stderr, "guild insert failed for %llu: %s\n", static_cast(guild.ID), m_db.ErrStr()); + + s->Reset(); + if (guild.Emojis.has_value()) { - for (const auto &x : *guild.Emojis) snowflakes.push_back(x.ID); - Bind(m_set_guild_stmt, 14, nlohmann::json(snowflakes).dump()); - } else { - Bind(m_set_guild_stmt, 14, "[]"s); + auto &s = m_stmt_set_guild_emoji; + for (const auto &emoji : *guild.Emojis) { + s->Bind(1, guild.ID); + s->Bind(2, emoji.ID); + if (!s->Insert()) + fprintf(stderr, "guild emoji insert failed for %llu/%llu: %s\n", static_cast(guild.ID), static_cast(emoji.ID), m_db.ErrStr()); + s->Reset(); + } } - if (guild.Features.has_value()) - Bind(m_set_guild_stmt, 15, nlohmann::json(*guild.Features).dump()); - else - Bind(m_set_guild_stmt, 15, "[]"s); - Bind(m_set_guild_stmt, 16, guild.MFALevel); - Bind(m_set_guild_stmt, 17, guild.ApplicationID); - Bind(m_set_guild_stmt, 18, guild.IsWidgetEnabled); - Bind(m_set_guild_stmt, 19, guild.WidgetChannelID); - Bind(m_set_guild_stmt, 20, guild.SystemChannelFlags); - Bind(m_set_guild_stmt, 21, guild.RulesChannelID); - Bind(m_set_guild_stmt, 22, guild.JoinedAt); - Bind(m_set_guild_stmt, 23, guild.IsLarge); - Bind(m_set_guild_stmt, 24, guild.IsUnavailable); - Bind(m_set_guild_stmt, 25, guild.MemberCount); - if (guild.Channels.has_value()) { - snowflakes.clear(); - for (const auto &x : *guild.Channels) snowflakes.push_back(x.ID); - Bind(m_set_guild_stmt, 26, nlohmann::json(snowflakes).dump()); - } else - Bind(m_set_guild_stmt, 26, "[]"s); - Bind(m_set_guild_stmt, 27, guild.MaxPresences); - Bind(m_set_guild_stmt, 28, guild.MaxMembers); - Bind(m_set_guild_stmt, 29, guild.VanityURL); - Bind(m_set_guild_stmt, 30, guild.Description); - Bind(m_set_guild_stmt, 31, guild.BannerHash); - Bind(m_set_guild_stmt, 32, guild.PremiumTier); - Bind(m_set_guild_stmt, 33, guild.PremiumSubscriptionCount); - Bind(m_set_guild_stmt, 34, guild.PreferredLocale); - Bind(m_set_guild_stmt, 35, guild.PublicUpdatesChannelID); - Bind(m_set_guild_stmt, 36, guild.MaxVideoChannelUsers); - Bind(m_set_guild_stmt, 37, guild.ApproximateMemberCount); - Bind(m_set_guild_stmt, 38, guild.ApproximatePresenceCount); - Bind(m_set_guild_stmt, 39, guild.IsLazy); + + if (guild.Features.has_value()) { + auto &s = m_stmt_set_guild_feature; + + for (const auto &feature : *guild.Features) { + s->Bind(1, guild.ID); + s->Bind(2, feature); + if (!s->Insert()) + fprintf(stderr, "guild feature insert failed for %llu/%s: %s\n", static_cast(guild.ID), feature.c_str(), m_db.ErrStr()); + s->Reset(); + } + } + if (guild.Threads.has_value()) { - snowflakes.clear(); - for (const auto &x : *guild.Threads) snowflakes.push_back(x.ID); - Bind(m_set_guild_stmt, 40, nlohmann::json(snowflakes).dump()); - } else - Bind(m_set_guild_stmt, 40, "[]"s); + auto &s = m_stmt_set_thread; - if (!RunInsert(m_set_guild_stmt)) - fprintf(stderr, "guild insert failed: %s\n", sqlite3_errstr(m_db_err)); + for (const auto &thread : *guild.Threads) { + s->Bind(1, guild.ID); + s->Bind(2, thread.ID); + if (!s->Insert()) + fprintf(stderr, "guild thread insert failed for %llu/%llu: %s\n", static_cast(guild.ID), static_cast(thread.ID), m_db.ErrStr()); + s->Reset(); + } + } - m_guilds.insert(id); + EndTransaction(); } void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data) { - Bind(m_set_member_stmt, 1, user_id); - Bind(m_set_member_stmt, 2, guild_id); - Bind(m_set_member_stmt, 3, data.Nickname); - Bind(m_set_member_stmt, 4, nlohmann::json(data.Roles).dump()); - Bind(m_set_member_stmt, 5, data.JoinedAt); - Bind(m_set_member_stmt, 6, data.PremiumSince); - Bind(m_set_member_stmt, 7, data.IsDeafened); - Bind(m_set_member_stmt, 8, data.IsMuted); - Bind(m_set_member_stmt, 9, data.Avatar); - Bind(m_set_member_stmt, 10, data.IsPending); + auto &s = m_stmt_set_member; - if (!RunInsert(m_set_member_stmt)) - fprintf(stderr, "member insert failed: %s\n", sqlite3_errstr(m_db_err)); -} + s->Bind(1, user_id); + s->Bind(2, guild_id); + s->Bind(3, data.Nickname); + s->Bind(4, data.JoinedAt); + s->Bind(5, data.PremiumSince); + s->Bind(6, data.IsDeafened); + s->Bind(7, data.IsMuted); + s->Bind(8, data.Avatar); + s->Bind(9, data.IsPending); -void Store::SetMessage(Snowflake id, const Message &message) { - Bind(m_set_msg_stmt, 1, id); - Bind(m_set_msg_stmt, 2, message.ChannelID); - Bind(m_set_msg_stmt, 3, message.GuildID); - Bind(m_set_msg_stmt, 4, message.Author.ID); - Bind(m_set_msg_stmt, 5, message.Content); - Bind(m_set_msg_stmt, 6, message.Timestamp); - Bind(m_set_msg_stmt, 7, message.EditedTimestamp); - Bind(m_set_msg_stmt, 8, message.IsTTS); - Bind(m_set_msg_stmt, 9, message.DoesMentionEveryone); - Bind(m_set_msg_stmt, 10, nlohmann::json(message.Mentions).dump()); - Bind(m_set_msg_stmt, 11, nlohmann::json(message.Attachments).dump()); - Bind(m_set_msg_stmt, 12, nlohmann::json(message.Embeds).dump()); - Bind(m_set_msg_stmt, 13, message.IsPinned); - Bind(m_set_msg_stmt, 14, message.WebhookID); - Bind(m_set_msg_stmt, 15, static_cast(message.Type)); + if (!s->Insert()) + fprintf(stderr, "member insert failed for %llu/%llu: %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); - if (message.Application.has_value()) - Bind(m_set_msg_stmt, 16, nlohmann::json(*message.Application).dump()); - else - Bind(m_set_msg_stmt, 16, nullptr); + s->Reset(); - if (message.MessageReference.has_value()) - Bind(m_set_msg_stmt, 17, nlohmann::json(*message.MessageReference).dump()); - else - Bind(m_set_msg_stmt, 17, nullptr); + { + auto &s = m_stmt_set_member_roles; - if (message.Flags.has_value()) - Bind(m_set_msg_stmt, 18, static_cast(*message.Flags)); - else - Bind(m_set_msg_stmt, 18, nullptr); - - if (message.Stickers.has_value()) - Bind(m_set_msg_stmt, 19, nlohmann::json(*message.Stickers).dump()); - else - Bind(m_set_msg_stmt, 19, nullptr); - - if (message.Reactions.has_value()) { - std::string tmp = nlohmann::json(*message.Reactions).dump(); - Bind(m_set_msg_stmt, 20, tmp); - } else - Bind(m_set_msg_stmt, 20, nullptr); - Bind(m_set_msg_stmt, 21, message.IsDeleted()); - Bind(m_set_msg_stmt, 22, message.IsEdited()); - Bind(m_set_msg_stmt, 23, message.IsPending); - Bind(m_set_msg_stmt, 24, message.Nonce); // sorry - - if (message.StickerItems.has_value()) { - std::string tmp = nlohmann::json(*message.StickerItems).dump(); - Bind(m_set_msg_stmt, 25, tmp); - } else - Bind(m_set_msg_stmt, 25, nullptr); - - if (!RunInsert(m_set_msg_stmt)) - fprintf(stderr, "message insert failed: %s\n", sqlite3_errstr(m_db_err)); - - if (message.Interaction.has_value()) - SetMessageInteractionPair(id, *message.Interaction); -} - -void Store::SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm) { - Bind(m_set_perm_stmt, 1, perm.ID); - Bind(m_set_perm_stmt, 2, channel_id); - Bind(m_set_perm_stmt, 3, static_cast(perm.Type)); - Bind(m_set_perm_stmt, 4, static_cast(perm.Allow)); - Bind(m_set_perm_stmt, 5, static_cast(perm.Deny)); - - if (!RunInsert(m_set_perm_stmt)) - fprintf(stderr, "permission insert failed: %s\n", sqlite3_errstr(m_db_err)); -} - -void Store::SetRole(Snowflake id, const RoleData &role) { - Bind(m_set_role_stmt, 1, id); - Bind(m_set_role_stmt, 2, role.Name); - Bind(m_set_role_stmt, 3, role.Color); - Bind(m_set_role_stmt, 4, role.IsHoisted); - Bind(m_set_role_stmt, 5, role.Position); - Bind(m_set_role_stmt, 6, static_cast(role.Permissions)); - Bind(m_set_role_stmt, 7, role.IsManaged); - Bind(m_set_role_stmt, 8, role.IsMentionable); - - if (!RunInsert(m_set_role_stmt)) - fprintf(stderr, "role insert failed: %s\n", sqlite3_errstr(m_db_err)); -} - -void Store::SetUser(Snowflake id, const UserData &user) { - Bind(m_set_user_stmt, 1, id); - Bind(m_set_user_stmt, 2, user.Username); - Bind(m_set_user_stmt, 3, user.Discriminator); - Bind(m_set_user_stmt, 4, user.Avatar); - Bind(m_set_user_stmt, 5, user.IsBot); - Bind(m_set_user_stmt, 6, user.IsSystem); - Bind(m_set_user_stmt, 7, user.IsMFAEnabled); - Bind(m_set_user_stmt, 8, user.Locale); - Bind(m_set_user_stmt, 9, user.IsVerified); - Bind(m_set_user_stmt, 10, user.Email); - Bind(m_set_user_stmt, 11, user.Flags); - Bind(m_set_user_stmt, 12, user.PremiumType); - Bind(m_set_user_stmt, 13, user.PublicFlags); - - if (!RunInsert(m_set_user_stmt)) { - fprintf(stderr, "user insert failed: %s\n", sqlite3_errstr(m_db_err)); + BeginTransaction(); + for (const auto &role : data.Roles) { + s->Bind(1, user_id); + s->Bind(2, role); + if (!s->Insert()) + fprintf(stderr, "member role insert failed for %llu/%llu/%llu: %s\n", static_cast(user_id), static_cast(guild_id), static_cast(role), m_db.ErrStr()); + s->Reset(); + } + EndTransaction(); } } -Message Store::GetMessageBound(sqlite3_stmt *stmt) const { - Message ret; - Get(stmt, 0, ret.ID); - Get(stmt, 1, ret.ChannelID); - Get(stmt, 2, ret.GuildID); - Get(stmt, 3, ret.Author.ID); // yike - Get(stmt, 4, ret.Content); - Get(stmt, 5, ret.Timestamp); - Get(stmt, 6, ret.EditedTimestamp); - Get(stmt, 7, ret.IsTTS); - Get(stmt, 8, ret.DoesMentionEveryone); - std::string tmps; - Get(stmt, 9, tmps); - nlohmann::json::parse(tmps).get_to(ret.Mentions); - Get(stmt, 10, tmps); - nlohmann::json::parse(tmps).get_to(ret.Attachments); - Get(stmt, 11, tmps); - nlohmann::json::parse(tmps).get_to(ret.Embeds); - Get(stmt, 12, ret.IsPinned); - Get(stmt, 13, ret.WebhookID); - uint64_t tmpi; - Get(stmt, 14, tmpi); - ret.Type = static_cast(tmpi); - - Get(stmt, 15, tmps); - if (tmps != "") - ret.Application = nlohmann::json::parse(tmps).get(); - - Get(stmt, 16, tmps); - if (tmps != "") - ret.MessageReference = nlohmann::json::parse(tmps).get(); - - Get(stmt, 17, tmpi); - ret.Flags = static_cast(tmpi); - - Get(stmt, 18, tmps); - if (tmps != "") - ret.Stickers = nlohmann::json::parse(tmps).get>(); - - Get(stmt, 19, tmps); - if (tmps != "") - ret.Reactions = nlohmann::json::parse(tmps).get>(); - - bool tmpb = false; - Get(stmt, 20, tmpb); - if (tmpb) ret.SetDeleted(); - - Get(stmt, 21, tmpb); - if (tmpb) ret.SetEdited(); - - Get(stmt, 22, ret.IsPending); - Get(stmt, 23, ret.Nonce); - - Get(stmt, 24, tmps); - if (tmps != "") - ret.StickerItems = nlohmann::json::parse(tmps).get>(); - - // interaction data from join - - if (!IsNull(stmt, 25)) { - auto &interaction = ret.Interaction.emplace(); - Get(stmt, 25, interaction.ID); - Get(stmt, 26, interaction.Name); - Get(stmt, 27, interaction.Type); - Get(stmt, 28, interaction.User.ID); - } - - Reset(stmt); - - if (ret.MessageReference.has_value() && ret.MessageReference->MessageID.has_value()) { - auto ref = GetMessage(*ret.MessageReference->MessageID); - if (ref.has_value()) - ret.ReferencedMessage = std::make_unique(std::move(*ref)); - else - ret.ReferencedMessage = nullptr; - } - - return ret; -} - void Store::SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction) { - Bind(m_set_msg_interaction_stmt, 1, message_id); - Bind(m_set_msg_interaction_stmt, 2, interaction.ID); - Bind(m_set_msg_interaction_stmt, 3, interaction.Type); - Bind(m_set_msg_interaction_stmt, 4, interaction.Name); - Bind(m_set_msg_interaction_stmt, 5, interaction.User.ID); + auto &s = m_stmt_set_interaction; - if (!RunInsert(m_set_msg_interaction_stmt)) { - fprintf(stderr, "message interaction insert failed: %s\n", sqlite3_errstr(m_db_err)); + s->Bind(1, message_id); + s->Bind(2, interaction.ID); + s->Bind(3, interaction.Type); + s->Bind(4, interaction.Name); + s->Bind(5, interaction.User.ID); + + if (!s->Insert()) + fprintf(stderr, "message interaction failed for %llu: %s\n", static_cast(message_id), m_db.ErrStr()); + + s->Reset(); +} + +void Store::SetMessage(Snowflake id, const Message &message) { + auto &s = m_stmt_set_msg; + + BeginTransaction(); + + s->Bind(1, id); + s->Bind(2, message.ChannelID); + s->Bind(3, message.GuildID); + s->Bind(4, message.Author.ID); + s->Bind(5, message.Content); + s->Bind(6, message.Timestamp); + s->Bind(7, message.EditedTimestamp); + s->Bind(8, message.IsTTS); + s->Bind(9, message.DoesMentionEveryone); + s->BindAsJSON(10, message.Embeds); + s->Bind(11, message.IsPinned); + s->Bind(12, message.WebhookID); + s->Bind(13, message.Type); + s->BindAsJSON(14, message.Application); + s->Bind(15, message.Flags); + s->BindAsJSON(16, message.Stickers); + s->Bind(17, message.IsDeleted()); + s->Bind(18, message.IsEdited()); + s->Bind(19, message.IsPending); + s->Bind(20, message.Nonce); + s->BindAsJSON(21, message.StickerItems); + + if (!s->Insert()) + fprintf(stderr, "message insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + + s->Reset(); + + if (message.MessageReference.has_value()) { + auto &s = m_stmt_set_msg_ref; + s->Bind(1, message.ID); + s->Bind(2, message.MessageReference->MessageID); + s->Bind(3, message.MessageReference->ChannelID); + s->Bind(4, message.MessageReference->GuildID); + + if (!s->Insert()) + fprintf(stderr, "message ref insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + + s->Reset(); } + + for (const auto &u : message.Mentions) { + auto &s = m_stmt_set_mention; + s->Bind(1, id); + s->Bind(2, u.ID); + if (!s->Insert()) + fprintf(stderr, "message mention insert failed for %llu/%llu: %s\n", static_cast(id), static_cast(u.ID), m_db.ErrStr()); + s->Reset(); + } + + for (const auto &a : message.Attachments) { + auto &s = m_stmt_set_attachment; + s->Bind(1, id); + s->Bind(2, a.ID); + s->Bind(3, a.Filename); + s->Bind(4, a.Bytes); + s->Bind(5, a.URL); + s->Bind(6, a.ProxyURL); + s->Bind(7, a.Height); + s->Bind(8, a.Width); + if (!s->Insert()) + fprintf(stderr, "message attachment insert failed for %llu/%llu: %s\n", static_cast(id), static_cast(a.ID), m_db.ErrStr()); + s->Reset(); + } + + if (message.Reactions.has_value()) { + auto &s = m_stmt_add_reaction; + for (size_t i = 0; i < message.Reactions->size(); i++) { + const auto &reaction = (*message.Reactions)[i]; + s->Bind(1, id); + s->Bind(2, reaction.Emoji.ID); + s->Bind(3, reaction.Emoji.Name); + s->Bind(4, reaction.Count); + s->Bind(5, reaction.HasReactedWith); + s->Bind(6, i); + if (!s->Insert()) + fprintf(stderr, "message reaction insert failed for %llu/%llu/%s: %s\n", static_cast(id), static_cast(reaction.Emoji.ID), reaction.Emoji.Name.c_str(), m_db.ErrStr()); + s->Reset(); + } + } + + if (message.Interaction.has_value()) + SetMessageInteractionPair(id, *message.Interaction); + + EndTransaction(); +} + +void Store::SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm) { + auto &s = m_stmt_set_perm; + + s->Bind(1, perm.ID); + s->Bind(2, channel_id); + s->Bind(3, perm.Type); + s->Bind(4, perm.Allow); + s->Bind(5, perm.Deny); + + if (!s->Insert()) + fprintf(stderr, "permission insert failed for %llu/%llu: %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); + + s->Reset(); +} + +void Store::SetRole(Snowflake guild_id, const RoleData &role) { + auto &s = m_stmt_set_role; + + s->Bind(1, role.ID); + s->Bind(2, guild_id); + s->Bind(3, role.Name); + s->Bind(4, role.Color); + s->Bind(5, role.IsHoisted); + s->Bind(6, role.Position); + s->Bind(7, role.Permissions); + s->Bind(8, role.IsManaged); + s->Bind(9, role.IsMentionable); + + if (!s->Insert()) + fprintf(stderr, "role insert failed for %llu: %s\n", static_cast(role.ID), m_db.ErrStr()); + + s->Reset(); +} + +void Store::SetUser(Snowflake id, const UserData &user) { + auto &s = m_stmt_set_user; + + s->Bind(1, id); + s->Bind(2, user.Username); + s->Bind(3, user.Discriminator); + s->Bind(4, user.Avatar); + s->Bind(5, user.IsBot); + s->Bind(6, user.IsSystem); + s->Bind(7, user.IsMFAEnabled); + s->Bind(8, user.PremiumType); + s->Bind(9, user.PublicFlags); + + if (!s->Insert()) + fprintf(stderr, "user insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + + s->Reset(); } std::optional Store::GetBan(Snowflake guild_id, Snowflake user_id) const { - Bind(m_get_ban_stmt, 1, guild_id); - Bind(m_get_ban_stmt, 2, user_id); - if (!FetchOne(m_get_ban_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching ban: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_ban_stmt); - return std::nullopt; + auto &s = m_stmt_get_ban; + + s->Bind(1, guild_id); + s->Bind(2, user_id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching ban for %llu/%llu: %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); + s->Reset(); + return {}; } - BanData ret; - ret.User.ID = user_id; - Get(m_get_ban_stmt, 2, ret.Reason); + BanData r; + r.User.ID = user_id; + s->Get(2, r.Reason); - Reset(m_get_ban_stmt); - return ret; + s->Reset(); + + return r; } std::vector Store::GetBans(Snowflake guild_id) const { - Bind(m_get_bans_stmt, 1, guild_id); + auto &s = m_stmt_get_bans; std::vector ret; - while (FetchOne(m_get_bans_stmt)) { + s->Bind(1, guild_id); + while (s->FetchOne()) { auto &ban = ret.emplace_back(); - Get(m_get_bans_stmt, 1, ban.User.ID); - Get(m_get_bans_stmt, 2, ban.Reason); + s->Get(1, ban.User.ID); + s->Get(2, ban.Reason); } - Reset(m_get_bans_stmt); + s->Reset(); - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching bans: %s\n", sqlite3_errstr(m_db_err)); return ret; } std::vector Store::GetLastMessages(Snowflake id, size_t num) const { - auto ids = GetChannelMessageIDs(id); - std::vector ret; - for (auto it = ids.cend() - std::min(ids.size(), num); it != ids.cend(); it++) - ret.push_back(*GetMessage(*it)); - return ret; -} - -std::vector Store::GetChannelMessageIDs(Snowflake id) const { - std::vector ret; - Bind(m_get_msg_ids_stmt, 1, id); - - while (FetchOne(m_get_msg_ids_stmt)) { - Snowflake x; - Get(m_get_msg_ids_stmt, 0, x); - ret.push_back(x); + auto &s = m_stmt_get_last_msgs; + std::vector msgs; + s->Bind(1, id); + s->Bind(2, num); + while (s->FetchOne()) { + auto msg = GetMessageBound(s); + if (!s->IsNull(33)) { // referenced message id + msg.MessageReference.emplace(); + s->Get(33, msg.MessageReference->MessageID); + } + msgs.push_back(std::move(msg)); } - Reset(m_get_msg_ids_stmt); + s->Reset(); - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching ids: %s\n", sqlite3_errstr(m_db_err)); - return ret; + for (auto &msg : msgs) { + if (msg.MessageReference.has_value() && msg.MessageReference->MessageID.has_value()) { + auto ref = GetMessage(*msg.MessageReference->MessageID); + if (ref.has_value()) + msg.ReferencedMessage = std::make_shared(std::move(*ref)); + } + } + + return msgs; +} + +std::vector Store::GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const { + std::vector msgs; + + auto &s = m_stmt_get_messages_before; + + s->Bind(1, channel_id); + s->Bind(2, message_id); + s->Bind(3, limit); + + while (s->FetchOne()) { + auto msg = GetMessageBound(s); + if (!s->IsNull(33)) { // referenced message id + msg.MessageReference.emplace(); + s->Get(33, msg.MessageReference->MessageID); + } + msgs.push_back(std::move(msg)); + } + + s->Reset(); + + for (auto &msg : msgs) { + if (msg.MessageReference.has_value() && msg.MessageReference->MessageID.has_value()) { + auto ref = GetMessage(*msg.MessageReference->MessageID); + if (ref.has_value()) + msg.ReferencedMessage = std::make_shared(std::move(*ref)); + } + } + + return msgs; } std::vector Store::GetPinnedMessages(Snowflake channel_id) const { - std::vector ret; + std::vector msgs; - Bind(m_get_pins_stmt, 1, channel_id); - while (FetchOne(m_get_pins_stmt)) { - Snowflake x; - Get(m_get_pins_stmt, 0, x); - auto msg = GetMessage(x); - if (msg.has_value()) - ret.push_back(*msg); + auto &s = m_stmt_get_pins; + + s->Bind(1, channel_id); + + while (s->FetchOne()) { + auto msg = GetMessageBound(s); + if (!s->IsNull(33)) { // referenced message id + msg.MessageReference.emplace(); + s->Get(33, msg.MessageReference->MessageID); + } + msgs.push_back(std::move(msg)); } - Reset(m_get_pins_stmt); + s->Reset(); - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching pins: %s\n", sqlite3_errstr(m_db_err)); - return ret; + for (auto &msg : msgs) { + if (msg.MessageReference.has_value() && msg.MessageReference->MessageID.has_value()) { + auto ref = GetMessage(*msg.MessageReference->MessageID); + if (ref.has_value()) + msg.ReferencedMessage = std::make_shared(std::move(*ref)); + } + } + + return msgs; } std::vector Store::GetActiveThreads(Snowflake channel_id) const { std::vector ret; - Bind(m_get_threads_stmt, 1, channel_id); - while (FetchOne(m_get_threads_stmt)) { + auto &s = m_stmt_get_active_threads; + + s->Bind(1, channel_id); + while (s->FetchOne()) { Snowflake x; - Get(m_get_threads_stmt, 0, x); + s->Get(0, x); auto chan = GetChannel(x); if (chan.has_value()) ret.push_back(*chan); } - Reset(m_get_threads_stmt); + s->Reset(); - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching threads: %s\n", sqlite3_errstr(m_db_err)); return ret; } +void Store::AddReaction(const MessageReactionAddObject &data, bool byself) { + auto &s = m_stmt_add_reaction; + + s->Bind(1, data.MessageID); + s->Bind(2, data.Emoji.ID); + s->Bind(3, data.Emoji.Name); + s->Bind(4, 1); + if (byself) + s->Bind(5, true); + else + s->Bind(5); + s->Bind(6); + + if (!s->Insert()) + fprintf(stderr, "failed to add reaction for %llu: %s\n", static_cast(data.MessageID), m_db.ErrStr()); + + s->Reset(); +} + +void Store::RemoveReaction(const MessageReactionRemoveObject &data, bool byself) { + auto &s = m_stmt_sub_reaction; + + s->Bind(1, data.MessageID); + s->Bind(2, data.Emoji.ID); + s->Bind(3, data.Emoji.Name); + if (byself) + s->Bind(4, false); + else + s->Bind(4); + + if (!s->Insert()) + fprintf(stderr, "failed to remove reaction for %llu: %s\n", static_cast(data.MessageID), m_db.ErrStr()); + + s->Reset(); +} + std::optional Store::GetChannel(Snowflake id) const { - Bind(m_get_chan_stmt, 1, id); - if (!FetchOne(m_get_chan_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching channel: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_chan_stmt); - return std::nullopt; + auto &s = m_stmt_get_chan; + s->Bind(1, id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching channel %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - ChannelData ret; - ret.ID = id; - int tmpi; - Get(m_get_chan_stmt, 1, tmpi); - ret.Type = static_cast(tmpi); - Get(m_get_chan_stmt, 2, ret.GuildID); - Get(m_get_chan_stmt, 3, ret.Position); - ret.PermissionOverwrites = std::nullopt; - Get(m_get_chan_stmt, 5, ret.Name); - Get(m_get_chan_stmt, 6, ret.Topic); - Get(m_get_chan_stmt, 7, ret.IsNSFW); - Get(m_get_chan_stmt, 8, ret.LastMessageID); - Get(m_get_chan_stmt, 9, ret.Bitrate); - Get(m_get_chan_stmt, 10, ret.UserLimit); - Get(m_get_chan_stmt, 11, ret.RateLimitPerUser); - if (!IsNull(m_get_chan_stmt, 12)) { - std::string tmps; - Get(m_get_chan_stmt, 12, tmps); - ret.RecipientIDs = nlohmann::json::parse(tmps).get>(); - } - Get(m_get_chan_stmt, 13, ret.Icon); - Get(m_get_chan_stmt, 14, ret.OwnerID); - Get(m_get_chan_stmt, 15, ret.ApplicationID); - Get(m_get_chan_stmt, 16, ret.ParentID); - Get(m_get_chan_stmt, 17, ret.LastPinTimestamp); - if (!IsNull(m_get_chan_stmt, 18)) { - ret.ThreadMetadata.emplace(); - Get(m_get_chan_stmt, 18, ret.ThreadMetadata->IsArchived); - Get(m_get_chan_stmt, 19, ret.ThreadMetadata->AutoArchiveDuration); - Get(m_get_chan_stmt, 20, ret.ThreadMetadata->ArchiveTimestamp); + ChannelData r; + + // uncomment as necessary + r.ID = id; + s->Get(1, r.Type); + s->Get(2, r.GuildID); + s->Get(3, r.Position); + s->Get(4, r.Name); + s->Get(5, r.Topic); + s->Get(6, r.IsNSFW); + s->Get(7, r.LastMessageID); + s->Get(10, r.RateLimitPerUser); + s->Get(12, r.OwnerID); + s->Get(14, r.ParentID); + if (!s->IsNull(16)) { + r.ThreadMetadata.emplace(); + s->Get(16, r.ThreadMetadata->IsArchived); + s->Get(17, r.ThreadMetadata->AutoArchiveDuration); + s->Get(18, r.ThreadMetadata->ArchiveTimestamp); } - Reset(m_get_chan_stmt); + s->Reset(); - return ret; + { + auto &s = m_stmt_get_recipients; + s->Bind(1, id); + std::vector recipients; + while (s->FetchOne()) { + auto &r = recipients.emplace_back(); + s->Get(0, r); + } + s->Reset(); + if (recipients.size() > 0) + r.RecipientIDs = std::move(recipients); + } + + return r; } std::optional Store::GetEmoji(Snowflake id) const { - Bind(m_get_emote_stmt, 1, id); - if (!FetchOne(m_get_emote_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching emoji: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_emote_stmt); - return std::nullopt; + auto &s = m_stmt_get_emoji; + + s->Bind(1, id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching emoji %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - EmojiData ret; - ret.ID = id; - Get(m_get_emote_stmt, 1, ret.Name); + EmojiData r; - if (!IsNull(m_get_emote_stmt, 2)) { - std::string tmp; - Get(m_get_emote_stmt, 2, tmp); - ret.Roles = nlohmann::json::parse(tmp).get>(); + r.ID = id; + s->Get(1, r.Name); + if (!s->IsNull(2)) { + r.Creator.emplace(); + s->Get(2, r.Creator->ID); + } + s->Get(3, r.NeedsColons); + s->Get(4, r.IsManaged); + s->Get(5, r.IsAnimated); + s->Get(6, r.IsAvailable); + + { + auto &s = m_stmt_get_emoji_roles; + + s->Bind(1, id); + r.Roles.emplace(); + while (s->FetchOne()) { + Snowflake id; + s->Get(0, id); + r.Roles->push_back(id); + } + s->Reset(); } - if (!IsNull(m_get_emote_stmt, 3)) { - ret.Creator = std::optional(UserData()); - Get(m_get_emote_stmt, 3, ret.Creator->ID); - } - Get(m_get_emote_stmt, 4, ret.NeedsColons); - Get(m_get_emote_stmt, 5, ret.IsManaged); - Get(m_get_emote_stmt, 6, ret.IsAnimated); - Get(m_get_emote_stmt, 7, ret.IsAvailable); + s->Reset(); - Reset(m_get_emote_stmt); - - return ret; + return r; } std::optional Store::GetGuild(Snowflake id) const { - Bind(m_get_guild_stmt, 1, id); - if (!FetchOne(m_get_guild_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching guild: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_guild_stmt); - return std::nullopt; + auto &s = m_stmt_get_guild; + s->Bind(1, id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching guild %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - GuildData ret; - ret.ID = id; - Get(m_get_guild_stmt, 1, ret.Name); - Get(m_get_guild_stmt, 2, ret.Icon); - Get(m_get_guild_stmt, 3, ret.Splash); - Get(m_get_guild_stmt, 4, ret.IsOwner); - Get(m_get_guild_stmt, 5, ret.OwnerID); - Get(m_get_guild_stmt, 6, ret.PermissionsNew); - Get(m_get_guild_stmt, 7, ret.VoiceRegion); - Get(m_get_guild_stmt, 8, ret.AFKChannelID); - Get(m_get_guild_stmt, 9, ret.AFKTimeout); - Get(m_get_guild_stmt, 10, ret.VerificationLevel); - Get(m_get_guild_stmt, 11, ret.DefaultMessageNotifications); - std::string tmp; - Get(m_get_guild_stmt, 12, tmp); - ret.Roles.emplace(); - for (const auto &id : nlohmann::json::parse(tmp).get>()) - ret.Roles->emplace_back().ID = id; - Get(m_get_guild_stmt, 13, tmp); - ret.Emojis.emplace(); - for (const auto &id : nlohmann::json::parse(tmp).get>()) - ret.Emojis->emplace_back().ID = id; - Get(m_get_guild_stmt, 14, tmp); - ret.Features = nlohmann::json::parse(tmp).get>(); - Get(m_get_guild_stmt, 15, ret.MFALevel); - Get(m_get_guild_stmt, 16, ret.ApplicationID); - Get(m_get_guild_stmt, 17, ret.IsWidgetEnabled); - Get(m_get_guild_stmt, 18, ret.WidgetChannelID); - Get(m_get_guild_stmt, 19, ret.SystemChannelFlags); - Get(m_get_guild_stmt, 20, ret.RulesChannelID); - Get(m_get_guild_stmt, 21, ret.JoinedAt); - Get(m_get_guild_stmt, 22, ret.IsLarge); - Get(m_get_guild_stmt, 23, ret.IsUnavailable); - Get(m_get_guild_stmt, 24, ret.MemberCount); - Get(m_get_guild_stmt, 25, tmp); - ret.Channels.emplace(); - for (const auto &id : nlohmann::json::parse(tmp).get>()) - ret.Channels->emplace_back().ID = id; - Get(m_get_guild_stmt, 26, ret.MaxPresences); - Get(m_get_guild_stmt, 27, ret.MaxMembers); - Get(m_get_guild_stmt, 28, ret.VanityURL); - Get(m_get_guild_stmt, 29, ret.Description); - Get(m_get_guild_stmt, 30, ret.BannerHash); - Get(m_get_guild_stmt, 31, ret.PremiumTier); - Get(m_get_guild_stmt, 32, ret.PremiumSubscriptionCount); - Get(m_get_guild_stmt, 33, ret.PreferredLocale); - Get(m_get_guild_stmt, 34, ret.PublicUpdatesChannelID); - Get(m_get_guild_stmt, 35, ret.MaxVideoChannelUsers); - Get(m_get_guild_stmt, 36, ret.ApproximateMemberCount); - Get(m_get_guild_stmt, 37, ret.ApproximatePresenceCount); - Get(m_get_guild_stmt, 38, ret.IsLazy); - Get(m_get_guild_stmt, 39, tmp); - ret.Threads.emplace(); - for (const auto &id : nlohmann::json::parse(tmp).get>()) - ret.Threads->emplace_back().ID = id; + // unfetched fields arent used anywhere + GuildData r; + r.ID = id; + s->Get(1, r.Name); + s->Get(2, r.Icon); + s->Get(5, r.OwnerID); + s->Get(20, r.IsUnavailable); - Reset(m_get_guild_stmt); + s->Reset(); - return ret; + { + auto &s = m_stmt_get_guild_emojis; + + s->Bind(1, id); + r.Emojis.emplace(); + while (s->FetchOne()) { + auto &q = r.Emojis->emplace_back(); + s->Get(0, q.ID); + } + s->Reset(); + } + + { + auto &s = m_stmt_get_guild_features; + + s->Bind(1, id); + r.Features.emplace(); + while (s->FetchOne()) { + std::string feature; + s->Get(0, feature); + r.Features->insert(feature); + } + s->Reset(); + } + + { + auto &s = m_stmt_get_guild_chans; + s->Bind(1, id); + r.Channels.emplace(); + while (s->FetchOne()) { + auto &q = r.Channels->emplace_back(); + s->Get(0, q.ID); + } + s->Reset(); + } + + { + auto &s = m_stmt_get_threads; + s->Bind(1, id); + r.Threads.emplace(); + while (s->FetchOne()) { + auto &q = r.Threads->emplace_back(); + s->Get(0, q.ID); + } + s->Reset(); + } + + return r; } std::optional Store::GetGuildMember(Snowflake guild_id, Snowflake user_id) const { - Bind(m_get_member_stmt, 1, user_id); - Bind(m_get_member_stmt, 2, guild_id); - if (!FetchOne(m_get_member_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching member: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_member_stmt); - return std::nullopt; + auto &s = m_stmt_get_member; + + s->Bind(1, user_id); + s->Bind(2, guild_id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching member %llu/%llu: %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); + s->Reset(); + return {}; } - GuildMember ret; - ret.User.emplace().ID = user_id; - Get(m_get_member_stmt, 2, ret.Nickname); - std::string tmp; - Get(m_get_member_stmt, 3, tmp); - ret.Roles = nlohmann::json::parse(tmp).get>(); - Get(m_get_member_stmt, 4, ret.JoinedAt); - Get(m_get_member_stmt, 5, ret.PremiumSince); - Get(m_get_member_stmt, 6, ret.IsDeafened); - Get(m_get_member_stmt, 7, ret.IsMuted); - Get(m_get_member_stmt, 8, ret.Avatar); - Get(m_get_member_stmt, 9, ret.IsPending); + GuildMember r; + r.User.emplace().ID = user_id; + s->Get(2, r.Nickname); + s->Get(3, r.JoinedAt); + s->Get(4, r.PremiumSince); + //s->Get(5, r.IsDeafened); + //s->Get(6, r.IsMuted); + s->Get(7, r.Avatar); + s->Get(8, r.IsPending); - Reset(m_get_member_stmt); + s->Reset(); - return ret; + { + auto &s = m_stmt_get_member_roles; + + s->Bind(1, user_id); + s->Bind(2, guild_id); + + while (s->FetchOne()) { + auto &f = r.Roles.emplace_back(); + s->Get(0, f); + } + + s->Reset(); + } + + return r; } std::optional Store::GetMessage(Snowflake id) const { - Bind(m_get_msg_stmt, 1, id); - if (!FetchOne(m_get_msg_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching message: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_msg_stmt); - return std::nullopt; + auto &s = m_stmt_get_msg; + + s->Bind(1, id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching message %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - auto ret = GetMessageBound(m_get_msg_stmt); + auto top = GetMessageBound(s); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching message %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return top; + } - return std::optional(std::move(ret)); + auto ref = GetMessageBound(s); + top.ReferencedMessage = std::make_shared(std::move(ref)); + + return top; +} + +Message Store::GetMessageBound(std::unique_ptr &s) const { + Message r; + + s->Get(0, r.ID); + s->Get(1, r.ChannelID); + s->Get(2, r.GuildID); + s->Get(3, r.Author.ID); + s->Get(4, r.Content); + s->Get(5, r.Timestamp); + s->Get(6, r.EditedTimestamp); + //s->Get(7, r.IsTTS); + //s->Get(8, r.DoesMentionEveryone); + s->GetJSON(9, r.Embeds); + s->Get(10, r.IsPinned); + s->Get(11, r.WebhookID); + s->Get(12, r.Type); + s->GetJSON(13, r.Application); + s->Get(14, r.Flags); + s->GetJSON(15, r.Stickers); + bool tmpb; + s->Get(16, tmpb); + if (tmpb) r.SetDeleted(); + s->Get(17, tmpb); + if (tmpb) r.SetEdited(); + s->Get(18, r.IsPending); + s->Get(19, r.Nonce); + s->GetJSON(20, r.StickerItems); + + if (!s->IsNull(21)) { + auto &i = r.Interaction.emplace(); + s->Get(21, i.ID); + s->Get(22, i.Name); + s->Get(23, i.Type); + s->Get(24, i.User.ID); + } + + if (!s->IsNull(25)) { + auto &a = r.Attachments.emplace_back(); + s->Get(25, a.ID); + s->Get(26, a.Filename); + s->Get(27, a.Bytes); + s->Get(28, a.URL); + s->Get(29, a.ProxyURL); + s->Get(30, a.Height); + s->Get(31, a.Width); + } + + { + auto &s = m_stmt_get_mentions; + s->Bind(1, r.ID); + while (s->FetchOne()) { + Snowflake id; + s->Get(0, id); + auto user = GetUser(id); + if (user.has_value()) + r.Mentions.push_back(std::move(*user)); + } + s->Reset(); + } + + { + auto &s = m_stmt_get_reactions; + s->Bind(1, r.ID); + std::map tmp; + while (s->FetchOne()) { + size_t idx; + ReactionData q; + s->Get(0, q.Emoji.ID); + s->Get(1, q.Emoji.Name); + s->Get(2, q.Count); + s->Get(3, q.HasReactedWith); + s->Get(4, idx); + tmp[idx] = q; + } + s->Reset(); + + r.Reactions.emplace(); + for (const auto &[idx, reaction] : tmp) + r.Reactions->push_back(reaction); + } + + return r; } std::optional Store::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const { - Bind(m_get_perm_stmt, 1, id); - Bind(m_get_perm_stmt, 2, channel_id); - if (!FetchOne(m_get_perm_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching permission: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_perm_stmt); - return std::nullopt; + auto &s = m_stmt_get_perm; + + s->Bind(1, id); + s->Bind(2, channel_id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "failed while fetching permission %llu/%llu: %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - PermissionOverwrite ret; - ret.ID = id; - uint64_t tmp; - Get(m_get_perm_stmt, 2, tmp); - ret.Type = static_cast(tmp); - Get(m_get_perm_stmt, 3, tmp); - ret.Allow = static_cast(tmp); - Get(m_get_perm_stmt, 4, tmp); - ret.Deny = static_cast(tmp); + PermissionOverwrite r; + r.ID = id; + s->Get(2, r.Type); + s->Get(3, r.Allow); + s->Get(4, r.Deny); - Reset(m_get_perm_stmt); + s->Reset(); - return ret; + return r; } std::optional Store::GetRole(Snowflake id) const { - Bind(m_get_role_stmt, 1, id); - if (!FetchOne(m_get_role_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching role: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_role_stmt); - return std::nullopt; + auto &s = m_stmt_get_role; + + s->Bind(1, id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching role %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - RoleData ret; - ret.ID = id; - Get(m_get_role_stmt, 1, ret.Name); - Get(m_get_role_stmt, 2, ret.Color); - Get(m_get_role_stmt, 3, ret.IsHoisted); - Get(m_get_role_stmt, 4, ret.Position); - uint64_t tmp; - Get(m_get_role_stmt, 5, tmp); - ret.Permissions = static_cast(tmp); - Get(m_get_role_stmt, 6, ret.IsManaged); - Get(m_get_role_stmt, 7, ret.IsMentionable); + RoleData r; - Reset(m_get_role_stmt); + r.ID = id; + //s->Get(1, guild id); + s->Get(2, r.Name); + s->Get(3, r.Color); + s->Get(4, r.IsHoisted); + s->Get(5, r.Position); + s->Get(6, r.Permissions); + s->Get(7, r.IsManaged); + s->Get(8, r.IsMentionable); - return ret; + s->Reset(); + + return r; } std::optional Store::GetUser(Snowflake id) const { - Bind(m_get_user_stmt, 1, id); - if (!FetchOne(m_get_user_stmt)) { - if (m_db_err != SQLITE_DONE) - fprintf(stderr, "error while fetching user info: %s\n", sqlite3_errstr(m_db_err)); - Reset(m_get_user_stmt); - return std::nullopt; + auto &s = m_stmt_get_user; + s->Bind(1, id); + if (!s->FetchOne()) { + if (m_db.Error() != SQLITE_DONE) + fprintf(stderr, "error while fetching user %llu: %s\n", static_cast(id), m_db.ErrStr()); + s->Reset(); + return {}; } - UserData ret; - Get(m_get_user_stmt, 0, ret.ID); - Get(m_get_user_stmt, 1, ret.Username); - Get(m_get_user_stmt, 2, ret.Discriminator); - Get(m_get_user_stmt, 3, ret.Avatar); - Get(m_get_user_stmt, 4, ret.IsBot); - Get(m_get_user_stmt, 5, ret.IsSystem); - Get(m_get_user_stmt, 6, ret.IsMFAEnabled); - Get(m_get_user_stmt, 7, ret.Locale); - Get(m_get_user_stmt, 8, ret.IsVerified); - Get(m_get_user_stmt, 9, ret.Email); - Get(m_get_user_stmt, 10, ret.Flags); - Get(m_get_user_stmt, 11, ret.PremiumType); - Get(m_get_user_stmt, 12, ret.PublicFlags); + UserData r; - Reset(m_get_user_stmt); + r.ID = id; + s->Get(1, r.Username); + s->Get(2, r.Discriminator); + s->Get(3, r.Avatar); + s->Get(4, r.IsBot); + s->Get(5, r.IsSystem); + s->Get(6, r.IsMFAEnabled); + s->Get(7, r.PremiumType); + s->Get(8, r.PublicFlags); - return ret; + s->Reset(); + + return r; } void Store::ClearGuild(Snowflake id) { - m_guilds.erase(id); + auto &s = m_stmt_clr_guild; + + s->Bind(1, id); + s->Step(); + s->Reset(); } void Store::ClearChannel(Snowflake id) { - m_channels.erase(id); - Bind(m_clear_chan_stmt, 1, id); + auto &s = m_stmt_clr_chan; - if ((m_db_err = sqlite3_step(m_clear_chan_stmt)) != SQLITE_DONE) - printf("clearing channel failed: %s\n", sqlite3_errstr(m_db_err)); - - Reset(m_clear_chan_stmt); + s->Bind(1, id); + s->Step(); + s->Reset(); } void Store::ClearBan(Snowflake guild_id, Snowflake user_id) { - Bind(m_clear_ban_stmt, 1, guild_id); - Bind(m_clear_ban_stmt, 2, user_id); + auto &s = m_stmt_clr_ban; - if ((m_db_err = sqlite3_step(m_clear_ban_stmt)) != SQLITE_DONE) - printf("clearing ban failed: %s\n", sqlite3_errstr(m_db_err)); - - Reset(m_clear_ban_stmt); + s->Bind(1, guild_id); + s->Bind(2, user_id); + s->Step(); + s->Reset(); } -const std::unordered_set &Store::GetChannels() const { - return m_channels; +void Store::ClearRecipient(Snowflake channel_id, Snowflake user_id) { + auto &s = m_stmt_clr_recipient; + + s->Bind(1, channel_id); + s->Bind(2, user_id); + s->Step(); + s->Reset(); } -const std::unordered_set &Store::GetGuilds() const { - return m_guilds; +std::unordered_set Store::GetChannels() const { + auto &s = m_stmt_get_chan_ids; + std::unordered_set r; + + while (s->FetchOne()) { + Snowflake id; + s->Get(0, id); + r.insert(id); + } + + s->Reset(); + + return r; } + +std::unordered_set Store::GetGuilds() const { + auto &s = m_stmt_get_guild_ids; + std::unordered_set r; + + while (s->FetchOne()) { + Snowflake id; + s->Get(0, id); + r.insert(id); + } + + s->Reset(); + + return r; +} + void Store::ClearAll() { - m_channels.clear(); - m_guilds.clear(); + if (m_db.Execute(R"( + DELETE FROM attachments; + DELETE FROM bans; + DELETE FROM channels; + DELETE FROM emojis; + DELETE FROM emoji_roles; + DELETE FROM guild_emojis; + DELETE FROM guild_features; + DELETE FROM guilds; + DELETE FROM members; + DELETE FROM member_roles; + DELETE FROM mentions; + DELETE FROM message_interactions; + DELETE FROM message_references; + DELETE FROM messages; + DELETE FROM permissions; + DELETE FROM reactions; + DELETE FROM recipients; + DELETE FROM roles; + DELETE FROM threads; + DELETE FROM users; + )") != SQLITE_OK) { + fprintf(stderr, "failed to clear: %s\n", m_db.ErrStr()); + } } void Store::BeginTransaction() { - m_db_err = sqlite3_exec(m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr); + m_db.StartTransaction(); } void Store::EndTransaction() { - m_db_err = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr); + m_db.EndTransaction(); } bool Store::CreateTables() { @@ -853,10 +1115,6 @@ bool Store::CreateTables() { bot BOOL, system BOOL, mfa BOOL, - locale TEXT, - verified BOOl, - email TEXT, - flags INTEGER, premium INTEGER, pubflags INTEGER ) @@ -884,17 +1142,13 @@ bool Store::CreateTables() { edited_timestamp TEXT, tts BOOL NOT NULL, everyone BOOL NOT NULL, - mentions TEXT NOT NULL, /* json */ - attachments TEXT NOT NULL, /* json */ embeds TEXT NOT NULL, /* json */ pinned BOOL, webhook_id INTEGER, type INTEGER, application TEXT, /* json */ - reference TEXT, /* json */ flags INTEGER, stickers TEXT, /* json */ - reactions TEXT, /* json */ deleted BOOL, /* extra */ edited BOOL, /* extra */ pending BOOL, /* extra */ @@ -906,6 +1160,7 @@ bool Store::CreateTables() { const char *create_roles = R"( CREATE TABLE IF NOT EXISTS roles ( id INTEGER PRIMARY KEY, + guild INTEGER NOT NULL, name TEXT NOT NULL, color INTEGER NOT NULL, hoisted BOOL NOT NULL, @@ -920,7 +1175,6 @@ bool Store::CreateTables() { CREATE TABLE IF NOT EXISTS emojis ( id INTEGER PRIMARY KEY, /*though nullable, only custom emojis (with non-null ids) are stored*/ name TEXT NOT NULL, /*same as id*/ - roles TEXT, /* json */ creator_id INTEGER, colons BOOL, managed BOOL, @@ -934,7 +1188,6 @@ bool Store::CreateTables() { user_id INTEGER NOT NULL, guild_id INTEGER NOT NULL, nickname TEXT, - roles TEXT NOT NULL, /* json */ joined_at TEXT NOT NULL, premium_since TEXT, deaf BOOL NOT NULL, @@ -959,9 +1212,6 @@ bool Store::CreateTables() { afk_timeout INTEGER NOT NULL, verification INTEGER NOT NULL, notifications INTEGER NOT NULL, - roles TEXT NOT NULL, /* json */ - emojis TEXT NOT NULL, /* json */ - features TEXT NOT NULL, /* json */ mfa INTEGER NOT NULL, application INTEGER, widget BOOL, @@ -972,7 +1222,6 @@ bool Store::CreateTables() { large BOOL, unavailable BOOL, member_count INTEGER, - channels TEXT NOT NULL, /* json */ max_presences INTEGER, max_members INTEGER, vanity TEXT, @@ -985,8 +1234,7 @@ bool Store::CreateTables() { max_video_users INTEGER, approx_members INTEGER, approx_presences INTEGER, - lazy BOOL, - threads TEXT NOT NULL /* json */ + lazy BOOL ) )"; @@ -996,7 +1244,6 @@ bool Store::CreateTables() { type INTEGER NOT NULL, guild_id INTEGER, position INTEGER, - overwrites TEXT, /* json */ name TEXT, topic TEXT, is_nsfw BOOL, @@ -1004,7 +1251,6 @@ bool Store::CreateTables() { bitrate INTEGER, user_limit INTEGER, rate_limit INTEGER, - recipients TEXT, /* json */ icon TEXT, owner_id INTEGER, application_id INTEGER, @@ -1036,63 +1282,205 @@ bool Store::CreateTables() { ) )"; - m_db_err = sqlite3_exec(m_db, create_users, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create user table: %s\n", sqlite3_errstr(m_db_err)); + const char *create_references = R"( + CREATE TABLE IF NOT EXISTS message_references ( + id INTEGER NOT NULL, + message INTEGER, + channel INTEGER, + guild INTEGER, + PRIMARY KEY(id) + ) + )"; + + const char *create_member_roles = R"( + CREATE TABLE IF NOT EXISTS member_roles ( + user INTEGER NOT NULL, + role INTEGER NOT NULL, + PRIMARY KEY(user, role) + ) + )"; + + const char *create_guild_emojis = R"( + CREATE TABLE IF NOT EXISTS guild_emojis ( + guild INTEGER NOT NULL, + emoji INTEGER NOT NULL, + PRIMARY KEY(guild, emoji) + ) + )"; + + const char *create_guild_features = R"( + CREATE TABLE IF NOT EXISTS guild_features ( + guild INTEGER NOT NULL, + feature CHAR(63) NOT NULL, + PRIMARY KEY(guild, feature) + ) + )"; + + const char *create_threads = R"( + CREATE TABLE IF NOT EXISTS threads ( + guild INTEGER NOT NULL, + id INTEGER NOT NULL, + PRIMARY KEY(guild, id) + ) + )"; + + const char *create_emoji_roles = R"( + CREATE TABLE IF NOT EXISTS emoji_roles ( + emoji INTEGER NOT NULL, + role INTEGER NOT NULL, + PRIMARY KEY(emoji, role) + ) + )"; + + const char *create_mentions = R"( + CREATE TABLE IF NOT EXISTS mentions ( + message INTEGER NOT NULL, + user INTEGER NOT NULL, + PRIMARY KEY(message, user) + ) + )"; + + const char *create_attachments = R"( + CREATE TABLE IF NOT EXISTS attachments ( + message INTEGER NOT NULL, + id INTEGER NOT NULL, + filename TEXT NOT NULL, + size INTEGER NOT NULL, + url TEXT NOT NULL, + proxy TEXT NOT NULL, + height INTEGER, + width INTEGER, + PRIMARY KEY(message, id) + ) + )"; + + const char *create_recipients = R"( + CREATE TABLE IF NOT EXISTS recipients ( + channel INTEGER NOT NULL, + user INTEGER NOT NULL, + PRIMARY KEY(channel, user) + ) + )"; + + const char *create_reactions = R"( + CREATE TABLE IF NOT EXISTS reactions ( + message INTEGER NOT NULL, + emoji_id INTEGER, + name TEXT NOT NULL, + count INTEGER NOT NULL, + me BOOL NOT NULL, + idx INTEGER NOT NULL, + PRIMARY KEY(message, emoji_id, name) + ) + )"; + + if (m_db.Execute(create_users) != SQLITE_OK) { + fprintf(stderr, "failed to create user table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_permissions, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create permissions table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_permissions) != SQLITE_OK) { + fprintf(stderr, "failed to create permissions table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_messages, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create messages table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_messages) != SQLITE_OK) { + fprintf(stderr, "failed to create messages table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_roles, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create roles table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_roles) != SQLITE_OK) { + fprintf(stderr, "failed to create roles table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_emojis, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "faile to create emojis table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_emojis) != SQLITE_OK) { + fprintf(stderr, "failed to create emojis table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_members, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create members table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_members) != SQLITE_OK) { + fprintf(stderr, "failed to create members table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_guilds, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create guilds table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_guilds) != SQLITE_OK) { + fprintf(stderr, "failed to create guilds table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_channels, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create channels table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_channels) != SQLITE_OK) { + fprintf(stderr, "failed to create channels table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_bans, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create bans table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_bans) != SQLITE_OK) { + fprintf(stderr, "failed to create bans table: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_exec(m_db, create_interactions, nullptr, nullptr, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to create message interactions table: %s\n", sqlite3_errstr(m_db_err)); + if (m_db.Execute(create_interactions) != SQLITE_OK) { + fprintf(stderr, "failed to create interactions table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_references) != SQLITE_OK) { + fprintf(stderr, "failed to create references table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_member_roles) != SQLITE_OK) { + fprintf(stderr, "failed to create member roles table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_guild_emojis) != SQLITE_OK) { + fprintf(stderr, "failed to create guild emojis table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_guild_features) != SQLITE_OK) { + fprintf(stderr, "failed to create guild features table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_threads) != SQLITE_OK) { + fprintf(stderr, "failed to create threads table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_emoji_roles) != SQLITE_OK) { + fprintf(stderr, "failed to create emoji roles table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_mentions) != SQLITE_OK) { + fprintf(stderr, "failed to create mentions table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_attachments) != SQLITE_OK) { + fprintf(stderr, "failed to create attachments table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_recipients) != SQLITE_OK) { + fprintf(stderr, "failed to create recipients table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(create_reactions) != SQLITE_OK) { + fprintf(stderr, "failed to create reactions table: %s\n", m_db.ErrStr()); + return false; + } + + if (m_db.Execute(R"( + CREATE TRIGGER remove_zero_reactions AFTER UPDATE ON reactions WHEN new.count = 0 + BEGIN + DELETE FROM reactions WHERE message = new.message AND emoji_id = new.emoji_id AND name = new.name; + END + )") != SQLITE_OK) { + fprintf(stderr, "failed to create reactions trigger: %s\n", m_db.ErrStr()); return false; } @@ -1100,408 +1488,758 @@ bool Store::CreateTables() { } bool Store::CreateStatements() { - const char *set_user = R"( - REPLACE INTO users VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + m_stmt_set_guild = std::make_unique(m_db, R"( + REPLACE INTO guilds VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_guild->OK()) { + fprintf(stderr, "failed to prepare set guild statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_user = R"( - SELECT * FROM users WHERE id = ? - )"; + m_stmt_get_guild = std::make_unique(m_db, R"( + SELECT * FROM guilds WHERE id = ? + )"); + if (!m_stmt_get_guild->OK()) { + fprintf(stderr, "failed to prepare get guild statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_perm = R"( - REPLACE INTO permissions VALUES ( - ?, ?, ?, ?, ? + m_stmt_get_guild_ids = std::make_unique(m_db, R"( + SELECT id FROM guilds + )"); + if (!m_stmt_get_guild_ids->OK()) { + fprintf(stderr, "failed to prepare get guild ids statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_clr_guild = std::make_unique(m_db, R"( + DELETE FROM guilds WHERE id = ? + )"); + if (!m_stmt_clr_guild->OK()) { + fprintf(stderr, "failed to prepare clear guild statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_chan = std::make_unique(m_db, R"( + REPLACE INTO channels VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_chan->OK()) { + fprintf(stderr, "failed to prepare set channel statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_perm = R"( - SELECT * FROM permissions WHERE id = ? AND channel_id = ? - )"; + m_stmt_get_chan = std::make_unique(m_db, R"( + SELECT * FROM channels WHERE id = ? + )"); + if (!m_stmt_get_chan->OK()) { + fprintf(stderr, "failed to prepare get channel statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_msg = R"( + m_stmt_get_chan_ids = std::make_unique(m_db, R"( + SELECT id FROM channels + )"); + if (!m_stmt_get_chan_ids->OK()) { + fprintf(stderr, "failed to prepare get channel ids statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_clr_chan = std::make_unique(m_db, R"( + DELETE FROM channels WHERE id = ? + )"); + if (!m_stmt_clr_chan->OK()) { + fprintf(stderr, "failed to prepare clear channel statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_msg = std::make_unique(m_db, R"( REPLACE INTO messages VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_msg->OK()) { + fprintf(stderr, "failed to prepare set message statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_msg = R"( + // wew + m_stmt_get_msg = std::make_unique(m_db, R"( SELECT messages.*, - message_interactions.interaction_id as interaction_id, - message_interactions.name as interaction_name, - message_interactions.type as interaction_type, - message_interactions.user_id as interaction_user_id + message_interactions.interaction_id, + message_interactions.name, + message_interactions.type, + message_interactions.user_id, + attachments.id, + attachments.filename, + attachments.size, + attachments.url, + attachments.proxy, + attachments.height, + attachments.width FROM messages LEFT OUTER JOIN message_interactions ON messages.id = message_interactions.message_id - WHERE id = ? - )"; + LEFT OUTER JOIN + attachments + ON messages.id = attachments.message + WHERE messages.id = ? + UNION ALL + SELECT messages.*, + message_interactions.interaction_id, + message_interactions.name, + message_interactions.type, + message_interactions.user_id, + attachments.id, + attachments.filename, + attachments.size, + attachments.url, + attachments.proxy, + attachments.height, + attachments.width + FROM messages + LEFT OUTER JOIN + message_interactions + ON messages.id = message_interactions.message_id + LEFT OUTER JOIN + attachments + ON messages.id = attachments.message + WHERE messages.id = (SELECT message FROM message_references WHERE id = ?) + ORDER BY messages.id DESC + )"); + if (!m_stmt_get_msg->OK()) { + fprintf(stderr, "failed to prepare get message statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_role = R"( - REPLACE INTO roles VALUES ( - ?, ?, ?, ?, ?, ?, ?, ? + m_stmt_set_msg_ref = std::make_unique(m_db, R"( + REPLACE INTO message_references VALUES ( + ?, ?, ?, ? + ); + )"); + if (!m_stmt_set_msg_ref->OK()) { + fprintf(stderr, "failed to prepare set message reference statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_get_last_msgs = std::make_unique(m_db, R"( + SELECT * FROM ( + SELECT messages.*, + message_interactions.interaction_id, + message_interactions.name, + message_interactions.type, + message_interactions.user_id, + attachments.id, + attachments.filename, + attachments.size, + attachments.url, + attachments.proxy, + attachments.height, + attachments.width, + message_references.message + FROM messages + LEFT OUTER JOIN + message_interactions + ON messages.id = message_interactions.message_id + LEFT OUTER JOIN + attachments + ON messages.id = attachments.message + LEFT OUTER JOIN + message_references + ON messages.id = message_references.id + WHERE channel_id = ? AND pending = 0 ORDER BY id DESC LIMIT ? + ) ORDER BY id ASC + )"); + if (!m_stmt_get_last_msgs->OK()) { + fprintf(stderr, "failed to prepare get last messages statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_user = std::make_unique(m_db, R"( + REPLACE INTO users VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_user->OK()) { + fprintf(stderr, "failed to prepare set user statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_role = R"( - SELECT * FROM roles WHERE id = ? - )"; + m_stmt_get_user = std::make_unique(m_db, R"( + SELECT * FROM users WHERE id = ? + )"); + if (!m_stmt_get_user->OK()) { + fprintf(stderr, "failed to prepare get user statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_emoji = R"( - REPLACE INTO emojis VALUES ( - ?, ?, ?, ?, ?, ?, ?, ? - ) - )"; - - const char *get_emoji = R"( - SELECT * FROM emojis WHERE id = ? - )"; - - const char *set_member = R"( + m_stmt_set_member = std::make_unique(m_db, R"( REPLACE INTO members VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_member->OK()) { + fprintf(stderr, "failed to prepare set member statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_member = R"( + m_stmt_get_member = std::make_unique(m_db, R"( SELECT * FROM members WHERE user_id = ? AND guild_id = ? - )"; + )"); + if (!m_stmt_get_member->OK()) { + fprintf(stderr, "failed to prepare get member statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_guild = R"( - REPLACE INTO guilds VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + m_stmt_set_role = std::make_unique(m_db, R"( + REPLACE INTO roles VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_role->OK()) { + fprintf(stderr, "failed to prepare set role statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_guild = R"( - SELECT * FROM guilds WHERE id = ? - )"; + m_stmt_get_role = std::make_unique(m_db, R"( + SELECT * FROM roles WHERE id = ? + )"); + if (!m_stmt_get_role->OK()) { + fprintf(stderr, "failed to prepare get role statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_chan = R"( - REPLACE INTO channels VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + m_stmt_set_emoji = std::make_unique(m_db, R"( + REPLACE INTO emojis VALUES ( + ?, ?, ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_emoji->OK()) { + fprintf(stderr, "failed to prepare set emoji statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_chan = R"( - SELECT * FROM channels WHERE id = ? - )"; + m_stmt_get_emoji = std::make_unique(m_db, R"( + SELECT * FROM emojis WHERE id = ? + )"); + if (!m_stmt_get_emoji->OK()) { + fprintf(stderr, "failed to prepare get emoji statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_ban = R"( + m_stmt_set_perm = std::make_unique(m_db, R"( + REPLACE INTO permissions VALUES ( + ?, ?, ?, ?, ? + ) + )"); + if (!m_stmt_set_perm->OK()) { + fprintf(stderr, "failed to prepare set permission statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_get_perm = std::make_unique(m_db, R"( + SELECT * FROM permissions WHERE id = ? AND channel_id = ? + )"); + if (!m_stmt_get_perm->OK()) { + fprintf(stderr, "failed to prepare get permission statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_ban = std::make_unique(m_db, R"( REPLACE INTO bans VALUES ( ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_ban->OK()) { + fprintf(stderr, "failed to prepare set ban statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_ban = R"( + m_stmt_get_ban = std::make_unique(m_db, R"( SELECT * FROM bans WHERE guild_id = ? AND user_id = ? - )"; + )"); + if (!m_stmt_get_ban->OK()) { + fprintf(stderr, "failed to prepare get ban statement: %s\n", m_db.ErrStr()); + return false; + } - const char *clear_ban = R"( - DELETE FROM bans WHERE guild_id = ? AND user_id = ? - )"; - - const char *get_bans = R"( + m_stmt_get_bans = std::make_unique(m_db, R"( SELECT * FROM bans WHERE guild_id = ? - )"; + )"); + if (!m_stmt_get_bans->OK()) { + fprintf(stderr, "failed to prepare get bans statement: %s\n", m_db.ErrStr()); + return false; + } - const char *set_interaction = R"( + m_stmt_clr_ban = std::make_unique(m_db, R"( + DELETE FROM bans WHERE guild_id = ? AND user_id = ? + )"); + if (!m_stmt_clr_ban->OK()) { + fprintf(stderr, "failed to prepare clear ban statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_interaction = std::make_unique(m_db, R"( REPLACE INTO message_interactions VALUES ( ?, ?, ?, ?, ? ) - )"; + )"); + if (!m_stmt_set_interaction->OK()) { + fprintf(stderr, "failed to prepare set interaction statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_last_msgs = R"( - SELECT * FROM ( - SELECT * FROM messages - WHERE channel_id = ? - ORDER BY id DESC - LIMIT ? - ) T1 ORDER BY id ASC - )"; + m_stmt_set_member_roles = std::make_unique(m_db, R"( + REPLACE INTO member_roles VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_member_roles->OK()) { + fprintf(stderr, "faile to prepare set member roles statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_msg_ids = R"( - SELECT id FROM messages WHERE channel_id = ? AND pending = 0 ORDER BY id ASC - )"; + m_stmt_get_member_roles = std::make_unique(m_db, R"( + SELECT id FROM roles, member_roles + WHERE roles.id = member_roles.role + AND member_roles.user = ? + AND roles.guild = ? + )"); + if (!m_stmt_get_member_roles->OK()) { + fprintf(stderr, "failed to prepare get member role statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_pins = R"( - SELECT id FROM messages WHERE channel_id = ? AND pinned = 1 ORDER BY id ASC - )"; + m_stmt_set_guild_emoji = std::make_unique(m_db, R"( + REPLACE INTO guild_emojis VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_guild_emoji->OK()) { + fprintf(stderr, "failed to prepare set guild emoji statement: %s\n", m_db.ErrStr()); + return false; + } - const char *get_threads = R"( + m_stmt_get_guild_emojis = std::make_unique(m_db, R"( + SELECT emoji FROM guild_emojis WHERE guild = ? + )"); + if (!m_stmt_get_guild_emojis->OK()) { + fprintf(stderr, "failed to prepare get guild emojis statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_clr_guild_emoji = std::make_unique(m_db, R"( + DELETE FROM guild_emojis WHERE guild = ? AND emoji = ? + )"); + if (!m_stmt_clr_guild_emoji->OK()) { + fprintf(stderr, "failed to prepare clear guild emoji statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_guild_feature = std::make_unique(m_db, R"( + REPLACE INTO guild_features VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_guild_feature->OK()) { + fprintf(stderr, "failed to prepare set guild feature statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_get_guild_features = std::make_unique(m_db, R"( + SELECT feature FROM guild_features WHERE guild = ? + )"); + if (!m_stmt_get_guild_features->OK()) { + fprintf(stderr, "failed to prepare get guild features statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_get_guild_chans = std::make_unique(m_db, R"( + SELECT id FROM channels WHERE guild_id = ? + )"); + if (!m_stmt_get_guild_chans->OK()) { + fprintf(stderr, "failed to prepare get guild channels statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_set_thread = std::make_unique(m_db, R"( + REPLACE INTO threads VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_thread->OK()) { + fprintf(stderr, "failed to prepare set thread statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_get_threads = std::make_unique(m_db, R"( + SELECT id FROM threads WHERE guild = ? + )"); + if (!m_stmt_get_threads->OK()) { + fprintf(stderr, "failed to prepare get threads statement: %s\n", m_db.ErrStr()); + return false; + } + + m_stmt_get_active_threads = std::make_unique(m_db, R"( SELECT id FROM channels WHERE parent_id = ? AND (type = 10 OR type = 11 OR type = 12) AND archived = FALSE - )"; - - const char *clear_chan = R"( - DELETE FROM channels WHERE id = ? - )"; - - m_db_err = sqlite3_prepare_v2(m_db, set_user, -1, &m_set_user_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set user statement: %s\n", sqlite3_errstr(m_db_err)); + )"); + if (!m_stmt_get_active_threads->OK()) { + fprintf(stderr, "faile to prepare get active threads statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_user, -1, &m_get_user_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get user statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_messages_before = std::make_unique(m_db, R"( + SELECT * FROM ( + SELECT messages.*, + message_interactions.interaction_id, + message_interactions.name, + message_interactions.type, + message_interactions.user_id, + attachments.id, + attachments.filename, + attachments.size, + attachments.url, + attachments.proxy, + attachments.height, + attachments.width, + message_references.message + FROM messages + LEFT OUTER JOIN + message_interactions + ON messages.id = message_interactions.message_id + LEFT OUTER JOIN + attachments + ON messages.id = attachments.message + LEFT OUTER JOIN + message_references + ON messages.id = message_references.id + WHERE channel_id = ? AND pending = 0 AND messages.id < ? ORDER BY id DESC LIMIT ? + ) ORDER BY id ASC + )"); + if (!m_stmt_get_messages_before->OK()) { + fprintf(stderr, "failed to prepare get messages before statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_perm, -1, &m_set_perm_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set permission statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_pins = std::make_unique(m_db, R"( + SELECT messages.*, + message_interactions.interaction_id, + message_interactions.name, + message_interactions.type, + message_interactions.user_id, + attachments.id, + attachments.filename, + attachments.size, + attachments.url, + attachments.proxy, + attachments.height, + attachments.width, + message_references.message + FROM messages + LEFT OUTER JOIN + message_interactions + ON messages.id = message_interactions.message_id + LEFT OUTER JOIN + attachments + ON messages.id = attachments.message + LEFT OUTER JOIN + message_references + ON messages.id = message_references.id + WHERE channel_id = ? AND pinned = 1 ORDER BY id ASC + )"); + if (!m_stmt_get_pins->OK()) { + fprintf(stderr, "failed to prepare get pins statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_perm, -1, &m_get_perm_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get permission statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_set_emoji_role = std::make_unique(m_db, R"( + REPLACE INTO emoji_roles VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_emoji_role->OK()) { + fprintf(stderr, "failed to prepare set emoji role statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_msg, -1, &m_set_msg_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set message statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_emoji_roles = std::make_unique(m_db, R"( + SELECT role FROM emoji_roles WHERE emoji = ? + )"); + if (!m_stmt_get_emoji_roles->OK()) { + fprintf(stderr, "failed to prepare get emoji role statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_msg, -1, &m_get_msg_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get message statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_set_mention = std::make_unique(m_db, R"( + REPLACE INTO mentions VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_mention->OK()) { + fprintf(stderr, "failed to prepare set mention statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_role, -1, &m_set_role_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set role statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_mentions = std::make_unique(m_db, R"( + SELECT user FROM mentions WHERE message = ? + )"); + if (!m_stmt_get_mentions->OK()) { + fprintf(stderr, "failed to prepare get mentions statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_role, -1, &m_get_role_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get role statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_set_attachment = std::make_unique(m_db, R"( + REPLACE INTO attachments VALUES ( + ?, ?, ?, ?, ?, ?, ?, ? + ) + )"); + if (!m_stmt_set_attachment->OK()) { + fprintf(stderr, "failed to prepare set attachment statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_emoji, -1, &m_set_emote_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set emoji statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_attachments = std::make_unique(m_db, R"( + SELECT * FROM attachments WHERE message = ? + )"); + if (!m_stmt_get_attachments->OK()) { + fprintf(stderr, "failed to prepare get attachments statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_emoji, -1, &m_get_emote_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get emoji statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_set_recipient = std::make_unique(m_db, R"( + REPLACE INTO recipients VALUES ( + ?, ? + ) + )"); + if (!m_stmt_set_recipient->OK()) { + fprintf(stderr, "failed to prepare set recipient statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_member, -1, &m_set_member_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set member statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_recipients = std::make_unique(m_db, R"( + SELECT user FROM recipients WHERE channel = ? + )"); + if (!m_stmt_get_recipients->OK()) { + fprintf(stderr, "failed to prepare get recipients statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_member, -1, &m_get_member_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get member statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_clr_recipient = std::make_unique(m_db, R"( + DELETE FROM recipients WHERE channel = ? AND user = ? + )"); + if (!m_stmt_clr_recipient->OK()) { + fprintf(stderr, "failed to prepare clear recipient statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_guild, -1, &m_set_guild_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set guild statement: %s\n", sqlite3_errstr(m_db_err)); + // probably not the best way to do this lol but i just want one statement i guess + m_stmt_add_reaction = std::make_unique(m_db, R"( + INSERT OR REPLACE INTO reactions VALUES ( + ?1, ?2, ?3, + COALESCE( + (SELECT count FROM reactions WHERE message = ?1 AND emoji_id = ?2 AND name = ?3), + 0 + ) + ?4, + COALESCE( + ?5, + (SELECT me FROM reactions WHERE message = ?1 AND emoji_id = ?2 AND name = ?3), + false + ), + COALESCE( + ?6, + (SELECT idx FROM reactions WHERE message = ?1 AND emoji_id = ?2 AND name = ?3), + (SELECT MAX(idx) + 1 FROM reactions WHERE message = ?1), + 0 + ) + ) + )"); + if (!m_stmt_add_reaction->OK()) { + fprintf(stderr, "failed to prepare add reaction statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, get_guild, -1, &m_get_guild_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get guild statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_sub_reaction = std::make_unique(m_db, R"( + UPDATE reactions + SET count = count - 1, + me = COALESCE(?4, me) + WHERE message = ?1 AND emoji_id = ?2 AND name = ?3 + )"); + if (!m_stmt_sub_reaction->OK()) { + fprintf(stderr, "failed to prepare sub reaction statement: %s\n", m_db.ErrStr()); return false; } - m_db_err = sqlite3_prepare_v2(m_db, set_chan, -1, &m_set_chan_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set channel statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_chan, -1, &m_get_chan_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get channel statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, set_ban, -1, &m_set_ban_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set ban statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_ban, -1, &m_get_ban_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get ban statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, clear_ban, -1, &m_clear_ban_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare clear ban statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_bans, -1, &m_get_bans_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get bans statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, set_interaction, -1, &m_set_msg_interaction_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare set message interaction statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_last_msgs, -1, &m_get_last_msgs_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get last messages statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_msg_ids, -1, &m_get_msg_ids_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get msg ids statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_pins, -1, &m_get_pins_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get pins statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, get_threads, -1, &m_get_threads_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare get threads statement: %s\n", sqlite3_errstr(m_db_err)); - return false; - } - - m_db_err = sqlite3_prepare_v2(m_db, clear_chan, -1, &m_clear_chan_stmt, nullptr); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "failed to prepare clear channel statement: %s\n", sqlite3_errstr(m_db_err)); + m_stmt_get_reactions = std::make_unique(m_db, R"( + SELECT emoji_id, name, count, me, idx FROM reactions WHERE message = ? + )"); + if (!m_stmt_get_reactions->OK()) { + fprintf(stderr, "failed to prepare get reactions statement: %s\n", m_db.ErrStr()); return false; } return true; } -void Store::Cleanup() { - sqlite3_finalize(m_set_user_stmt); - sqlite3_finalize(m_get_user_stmt); - sqlite3_finalize(m_set_perm_stmt); - sqlite3_finalize(m_get_perm_stmt); - sqlite3_finalize(m_set_msg_stmt); - sqlite3_finalize(m_get_msg_stmt); - sqlite3_finalize(m_set_role_stmt); - sqlite3_finalize(m_get_role_stmt); - sqlite3_finalize(m_set_emote_stmt); - sqlite3_finalize(m_get_emote_stmt); - sqlite3_finalize(m_set_member_stmt); - sqlite3_finalize(m_get_member_stmt); - sqlite3_finalize(m_set_guild_stmt); - sqlite3_finalize(m_get_guild_stmt); - sqlite3_finalize(m_set_chan_stmt); - sqlite3_finalize(m_get_chan_stmt); - sqlite3_finalize(m_set_ban_stmt); - sqlite3_finalize(m_get_ban_stmt); - sqlite3_finalize(m_clear_ban_stmt); - sqlite3_finalize(m_get_bans_stmt); - sqlite3_finalize(m_set_msg_interaction_stmt); - sqlite3_finalize(m_get_last_msgs_stmt); - sqlite3_finalize(m_get_msg_ids_stmt); - sqlite3_finalize(m_get_pins_stmt); - sqlite3_finalize(m_get_threads_stmt); - sqlite3_finalize(m_clear_chan_stmt); +Store::Database::Database(const char *path) { + m_err = sqlite3_open(path, &m_db); } -void Store::Bind(sqlite3_stmt *stmt, int index, int num) const { - m_db_err = sqlite3_bind_int(stmt, index, num); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err)); - } +Store::Database::~Database() { + Close(); } -void Store::Bind(sqlite3_stmt *stmt, int index, uint64_t num) const { - m_db_err = sqlite3_bind_int64(stmt, index, num); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err)); - } +int Store::Database::Close() { + m_signal_close.emit(); + m_err = sqlite3_close(m_db); + m_db = nullptr; + return m_err; } -void Store::Bind(sqlite3_stmt *stmt, int index, const std::string &str) const { - m_db_err = sqlite3_bind_blob(stmt, index, str.c_str(), str.length(), SQLITE_TRANSIENT); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err)); - } +int Store::Database::StartTransaction() { + return m_err = Execute("BEGIN TRANSACTION"); } -void Store::Bind(sqlite3_stmt *stmt, int index, bool val) const { - m_db_err = sqlite3_bind_int(stmt, index, val ? 1 : 0); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err)); - } +int Store::Database::EndTransaction() { + return m_err = Execute("COMMIT"); } -void Store::Bind(sqlite3_stmt *stmt, int index, std::nullptr_t) const { - m_db_err = sqlite3_bind_null(stmt, index); - if (m_db_err != SQLITE_OK) { - fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err)); - } +int Store::Database::Execute(const char *command) { + return m_err = sqlite3_exec(m_db, command, nullptr, nullptr, nullptr); } -bool Store::RunInsert(sqlite3_stmt *stmt) { - m_db_err = sqlite3_step(stmt); - Reset(stmt); - return m_db_err == SQLITE_DONE; +int Store::Database::Error() const { + return m_err; } -bool Store::FetchOne(sqlite3_stmt *stmt) const { - m_db_err = sqlite3_step(stmt); - return m_db_err == SQLITE_ROW; +bool Store::Database::OK() const { + return Error() == SQLITE_OK; } -void Store::Get(sqlite3_stmt *stmt, int index, int &out) const { - out = sqlite3_column_int(stmt, index); +const char *Store::Database::ErrStr() const { + const char *errstr = sqlite3_errstr(m_err); + const char *errmsg = sqlite3_errmsg(m_db); + strcpy_s(m_err_scratch, sizeof(m_err_scratch), errstr); + strcat_s(m_err_scratch, sizeof(m_err_scratch), "\n\t"); + strcat_s(m_err_scratch, sizeof(m_err_scratch), errmsg); + return m_err_scratch; } -void Store::Get(sqlite3_stmt *stmt, int index, uint64_t &out) const { - out = sqlite3_column_int64(stmt, index); +int Store::Database::SetError(int err) { + return m_err = err; } -void Store::Get(sqlite3_stmt *stmt, int index, std::string &out) const { - const unsigned char *ptr = sqlite3_column_text(stmt, index); +sqlite3 *Store::Database::obj() { + return m_db; +} + +Store::Database::type_signal_close Store::Database::signal_close() { + return m_signal_close; +} + +Store::Statement::Statement(Database &db, const char *command) + : m_db(&db) { + if (m_db->SetError(sqlite3_prepare_v2(m_db->obj(), command, -1, &m_stmt, nullptr)) != SQLITE_OK) return; + std::string tmp = command; + m_db->signal_close().connect([tmp, this] { + puts(tmp.c_str()); + sqlite3_finalize(m_stmt); + m_stmt = nullptr; + }); +} + +Store::Statement::~Statement() { + sqlite3_finalize(m_stmt); +} + +bool Store::Statement::OK() const { + return m_stmt != nullptr; +} + +int Store::Statement::Bind(int index, int32_t num) { + return Bind(index, static_cast(num)); +} + +int Store::Statement::Bind(int index, uint32_t num) { + return m_db->SetError(sqlite3_bind_int(m_stmt, index, num)); +} + +int Store::Statement::Bind(int index, uint64_t num) { + return m_db->SetError(sqlite3_bind_int64(m_stmt, index, num)); +} + +int Store::Statement::Bind(int index, const char *str, size_t len) { + if (len == -1) len = strlen(str); + return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str, len, SQLITE_TRANSIENT)); +} + +int Store::Statement::Bind(int index, const std::string &str) { + return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT)); +} + +int Store::Statement::Bind(int index, bool val) { + return m_db->SetError(sqlite3_bind_int(m_stmt, index, val ? 1L : 0L)); +} + +int Store::Statement::Bind(int index) { + return m_db->SetError(sqlite3_bind_null(m_stmt, index)); +} + +void Store::Statement::Get(int index, uint8_t &out) const { + out = sqlite3_column_int(m_stmt, index); +} + +void Store::Statement::Get(int index, int32_t &out) const { + out = sqlite3_column_int(m_stmt, index); +} + +void Store::Statement::Get(int index, uint64_t &out) const { + out = static_cast(sqlite3_column_int64(m_stmt, index)); +} + +void Store::Statement::Get(int index, bool &out) const { + out = sqlite3_column_int(m_stmt, index) != 0; +} + +void Store::Statement::Get(int index, Snowflake &out) const { + out = static_cast(sqlite3_column_int64(m_stmt, index)); +} + +void Store::Statement::Get(int index, std::string &out) const { + const unsigned char *ptr = sqlite3_column_text(m_stmt, index); if (ptr == nullptr) out = ""; else out = reinterpret_cast(ptr); } -void Store::Get(sqlite3_stmt *stmt, int index, bool &out) const { - out = sqlite3_column_int(stmt, index) != 0; +bool Store::Statement::IsNull(int index) const { + return sqlite3_column_type(m_stmt, index) == SQLITE_NULL; } -void Store::Get(sqlite3_stmt *stmt, int index, Snowflake &out) const { - const int64_t num = sqlite3_column_int64(stmt, index); - out = static_cast(num); +int Store::Statement::Step() { + return m_db->SetError(sqlite3_step(m_stmt)); } -bool Store::IsNull(sqlite3_stmt *stmt, int index) const { - return sqlite3_column_type(stmt, index) == SQLITE_NULL; +bool Store::Statement::Insert() { + return m_db->SetError(sqlite3_step(m_stmt)) == SQLITE_DONE; } -void Store::Reset(sqlite3_stmt *stmt) const { - sqlite3_reset(stmt); - sqlite3_clear_bindings(stmt); +bool Store::Statement::FetchOne() { + return m_db->SetError(sqlite3_step(m_stmt)) == SQLITE_ROW; +} + +int Store::Statement::Reset() { + if (m_db->SetError(sqlite3_reset(m_stmt)) != SQLITE_OK) + return m_db->Error(); + if (m_db->SetError(sqlite3_clear_bindings(m_stmt)) != SQLITE_OK) + return m_db->Error(); + return m_db->Error(); +} + +sqlite3_stmt *Store::Statement::obj() { + return m_stmt; } diff --git a/discord/store.hpp b/discord/store.hpp index f84d13e..776b701 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -21,15 +21,13 @@ public: void SetUser(Snowflake id, const UserData &user); void SetChannel(Snowflake id, const ChannelData &chan); void SetGuild(Snowflake id, const GuildData &guild); - void SetRole(Snowflake id, const RoleData &role); + void SetRole(Snowflake guild_id, const RoleData &role); void SetMessage(Snowflake id, const Message &message); void SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data); void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm); void SetEmoji(Snowflake id, const EmojiData &emoji); void SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban); - // slap const on everything even tho its not *really* const - std::optional GetChannel(Snowflake id) const; std::optional GetEmoji(Snowflake id) const; std::optional GetGuild(Snowflake id) const; @@ -42,25 +40,20 @@ public: std::vector GetBans(Snowflake guild_id) const; std::vector GetLastMessages(Snowflake id, size_t num) const; - std::vector GetChannelMessageIDs(Snowflake id) const; + std::vector GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const; std::vector GetPinnedMessages(Snowflake channel_id) const; std::vector GetActiveThreads(Snowflake channel_id) const; // public + void AddReaction(const MessageReactionAddObject &data, bool byself); + void RemoveReaction(const MessageReactionRemoveObject &data, bool byself); + void ClearGuild(Snowflake id); void ClearChannel(Snowflake id); void ClearBan(Snowflake guild_id, Snowflake user_id); + void ClearRecipient(Snowflake channel_id, Snowflake user_id); - using users_type = std::unordered_map; - using channels_type = std::unordered_map; - using guilds_type = std::unordered_map; - using roles_type = std::unordered_map; - using messages_type = std::unordered_map; - using members_type = std::unordered_map>; // [guild][user] - using permission_overwrites_type = std::unordered_map>; // [channel][user/role] - using emojis_type = std::unordered_map; - - const std::unordered_set &GetChannels() const; - const std::unordered_set &GetGuilds() const; + std::unordered_set GetChannels() const; + std::unordered_set GetGuilds() const; void ClearAll(); @@ -68,105 +61,237 @@ public: void EndTransaction(); private: - Message GetMessageBound(sqlite3_stmt *stmt) const; + class Statement; + class Database { + public: + Database(const char *path); + ~Database(); + + int Close(); + int StartTransaction(); + int EndTransaction(); + int Execute(const char *command); + int Error() const; + bool OK() const; + const char *ErrStr() const; + int SetError(int err); + sqlite3 *obj(); + + private: + sqlite3 *m_db; + int m_err = SQLITE_OK; + mutable char m_err_scratch[256] { 0 }; + + // stupid shit i dont like to allow closing properly + using type_signal_close = sigc::signal; + type_signal_close m_signal_close; + + public: + type_signal_close signal_close(); + }; + + class Statement { + public: + Statement() = delete; + Statement(const Statement &other) = delete; + Statement(Database &db, const char *command); + ~Statement(); + Statement &operator=(Statement &other) = delete; + + bool OK() const; + + int Bind(int index, int32_t num); + int Bind(int index, uint32_t num); + int Bind(int index, uint64_t num); + int Bind(int index, const char *str, size_t len = -1); + int Bind(int index, const std::string &str); + int Bind(int index, bool val); + int Bind(int index); + + template + int Bind(int index, std::optional opt) { + if (opt.has_value()) + return Bind(index, opt.value()); + else + return Bind(index); + } + + template + int BindIDsAsJSON(int index, Iter start, Iter end) { + std::vector x; + for (Iter it = start; it != end; it++) { + x.push_back((*it).ID); + } + return Bind(index, nlohmann::json(x).dump()); + } + + template + int BindAsJSONArray(int index, const std::optional &obj) { + if (obj.has_value()) + return Bind(index, nlohmann::json(obj.value()).dump()); + else + return Bind(index, std::string("[]")); + } + + template + int BindAsJSON(int index, const std::optional &obj) { + if (obj.has_value()) + return Bind(index, nlohmann::json(obj.value()).dump()); + else + return Bind(index); + } + + template + int BindAsJSON(int index, const T &obj) { + return Bind(index, nlohmann::json(obj).dump()); + } + + template + inline typename std::enable_if::value, int>::type + Bind(int index, T val) { + return Bind(index, static_cast::type>(val)); + } + + void Get(int index, uint8_t &out) const; + void Get(int index, int32_t &out) const; + void Get(int index, uint64_t &out) const; + void Get(int index, bool &out) const; + void Get(int index, Snowflake &out) const; + void Get(int index, std::string &out) const; + + template + void GetJSON(int index, std::optional &out) const { + if (IsNull(index)) + out = std::nullopt; + else { + std::string stuff; + Get(index, stuff); + if (stuff == "") + out = std::nullopt; + else + out = nlohmann::json::parse(stuff).get(); + } + } + + template + void GetJSON(int index, T &out) const { + std::string stuff; + Get(index, stuff); + nlohmann::json::parse(stuff).get_to(out); + } + + template + void Get(int index, std::optional &out) const { + if (IsNull(index)) + out = std::nullopt; + else { + T tmp; + Get(index, tmp); + out = std::optional(std::move(tmp)); + } + } + + template + inline typename std::enable_if::value, void>::type + Get(int index, T &val) const { + typename std::underlying_type::type tmp; + Get(index, tmp); + val = static_cast(tmp); + } + + template + void GetIDOnlyStructs(int index, std::optional> &out) const { + out.emplace(); + std::string str; + Get(index, str); + for (const auto &id : nlohmann::json::parse(str)) + out->emplace_back().ID = id.get(); + } + + template + void GetArray(int index, OutputIt first) const { + std::string str; + Get(index, str); + for (const auto &id : nlohmann::json::parse(str)) + *first++ = id.get(); + } + + bool IsNull(int index) const; + int Step(); + bool Insert(); + bool FetchOne(); + int Reset(); + + sqlite3_stmt *obj(); + + private: + Database *m_db; + sqlite3_stmt *m_stmt; + }; + + Message GetMessageBound(std::unique_ptr &stmt) const; void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction); - std::unordered_set m_channels; - std::unordered_set m_guilds; - bool CreateTables(); bool CreateStatements(); - void Cleanup(); - template - void Bind(sqlite3_stmt *stmt, int index, const std::optional &opt) const; - - template - typename std::enable_if::value, void>::type - Bind(sqlite3_stmt *stmt, int index, T val) const; - - void Bind(sqlite3_stmt *stmt, int index, int num) const; - void Bind(sqlite3_stmt *stmt, int index, uint64_t num) const; - void Bind(sqlite3_stmt *stmt, int index, const std::string &str) const; - void Bind(sqlite3_stmt *stmt, int index, bool val) const; - void Bind(sqlite3_stmt *stmt, int index, std::nullptr_t) const; - bool RunInsert(sqlite3_stmt *stmt); - bool FetchOne(sqlite3_stmt *stmt) const; - - template - void Get(sqlite3_stmt *stmt, int index, std::optional &out) const; - - template - typename std::enable_if::value, void>::type - Get(sqlite3_stmt *stmt, int index, T &out) const; - - void Get(sqlite3_stmt *stmt, int index, int &out) const; - void Get(sqlite3_stmt *stmt, int index, uint64_t &out) const; - void Get(sqlite3_stmt *stmt, int index, std::string &out) const; - void Get(sqlite3_stmt *stmt, int index, bool &out) const; - void Get(sqlite3_stmt *stmt, int index, Snowflake &out) const; - bool IsNull(sqlite3_stmt *stmt, int index) const; - void Reset(sqlite3_stmt *stmt) const; + bool m_ok = true; std::filesystem::path m_db_path; - mutable sqlite3 *m_db; - mutable int m_db_err; - mutable sqlite3_stmt *m_set_user_stmt; - mutable sqlite3_stmt *m_get_user_stmt; - mutable sqlite3_stmt *m_set_perm_stmt; - mutable sqlite3_stmt *m_get_perm_stmt; - mutable sqlite3_stmt *m_set_msg_stmt; - mutable sqlite3_stmt *m_get_msg_stmt; - mutable sqlite3_stmt *m_set_role_stmt; - mutable sqlite3_stmt *m_get_role_stmt; - mutable sqlite3_stmt *m_set_emote_stmt; - mutable sqlite3_stmt *m_get_emote_stmt; - mutable sqlite3_stmt *m_set_member_stmt; - mutable sqlite3_stmt *m_get_member_stmt; - mutable sqlite3_stmt *m_set_guild_stmt; - mutable sqlite3_stmt *m_get_guild_stmt; - mutable sqlite3_stmt *m_set_chan_stmt; - mutable sqlite3_stmt *m_get_chan_stmt; - mutable sqlite3_stmt *m_set_ban_stmt; - mutable sqlite3_stmt *m_get_ban_stmt; - mutable sqlite3_stmt *m_clear_ban_stmt; - mutable sqlite3_stmt *m_get_bans_stmt; - mutable sqlite3_stmt *m_set_msg_interaction_stmt; - mutable sqlite3_stmt *m_get_last_msgs_stmt; - mutable sqlite3_stmt *m_get_msg_ids_stmt; - mutable sqlite3_stmt *m_get_pins_stmt; - mutable sqlite3_stmt *m_get_threads_stmt; - mutable sqlite3_stmt *m_clear_chan_stmt; + Database m_db; +#define STMT(x) mutable std::unique_ptr m_stmt_##x + STMT(set_guild); + STMT(get_guild); + STMT(get_guild_ids); + STMT(clr_guild); + STMT(set_chan); + STMT(get_chan); + STMT(get_chan_ids); + STMT(clr_chan); + STMT(set_msg); + STMT(get_msg); + STMT(set_msg_ref); + STMT(get_last_msgs); + STMT(set_user); + STMT(get_user); + STMT(set_member); + STMT(get_member); + STMT(set_role); + STMT(get_role); + STMT(set_emoji); + STMT(get_emoji); + STMT(set_perm); + STMT(get_perm); + STMT(set_ban); + STMT(get_ban); + STMT(get_bans); + STMT(clr_ban); + STMT(set_interaction); + STMT(set_member_roles); + STMT(get_member_roles); + STMT(set_guild_emoji); + STMT(get_guild_emojis); + STMT(clr_guild_emoji); + STMT(set_guild_feature); + STMT(get_guild_features); + STMT(get_guild_chans); + STMT(set_thread); + STMT(get_threads); + STMT(get_active_threads); + STMT(get_messages_before); + STMT(get_pins); + STMT(set_emoji_role); + STMT(get_emoji_roles); + STMT(set_mention); + STMT(get_mentions); + STMT(set_attachment); + STMT(get_attachments); + STMT(set_recipient); + STMT(get_recipients); + STMT(clr_recipient); + STMT(add_reaction); + STMT(sub_reaction); + STMT(get_reactions); +#undef STMT }; - -template -inline void Store::Bind(sqlite3_stmt *stmt, int index, const std::optional &opt) const { - if (opt.has_value()) - Bind(stmt, index, *opt); - else - sqlite3_bind_null(stmt, index); -} - -template -inline typename std::enable_if::value, void>::type -Store::Bind(sqlite3_stmt *stmt, int index, T val) const { - Bind(stmt, index, static_cast::type>(val)); -} - -template -inline void Store::Get(sqlite3_stmt *stmt, int index, std::optional &out) const { - if (sqlite3_column_type(stmt, index) == SQLITE_NULL) - out = std::nullopt; - else { - T v; - Get(stmt, index, v); - out = std::optional(v); - } -} - -template -inline typename std::enable_if::value, void>::type -Store::Get(sqlite3_stmt *stmt, int index, T &out) const { - out = static_cast(sqlite3_column_int(stmt, index)); -} diff --git a/windows/guildsettings/auditlogpane.cpp b/windows/guildsettings/auditlogpane.cpp index 5a6cb82..08f99da 100644 --- a/windows/guildsettings/auditlogpane.cpp +++ b/windows/guildsettings/auditlogpane.cpp @@ -1,5 +1,5 @@ #include "auditlogpane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" using namespace std::string_literals; diff --git a/windows/guildsettings/auditlogpane.hpp b/windows/guildsettings/auditlogpane.hpp index 89749aa..ac12321 100644 --- a/windows/guildsettings/auditlogpane.hpp +++ b/windows/guildsettings/auditlogpane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/objects.hpp" +#include "discord/objects.hpp" class GuildSettingsAuditLogPane : public Gtk::ScrolledWindow { public: From c98b62cacaf58628419c87d326f36b0e989a85e7 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 28 Oct 2021 00:42:04 -0400 Subject: [PATCH 02/23] fix syntax error --- discord/store.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/store.hpp b/discord/store.hpp index 776b701..280d728 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -149,7 +149,7 @@ private: template inline typename std::enable_if::value, int>::type Bind(int index, T val) { - return Bind(index, static_cast::type>(val)); + return Bind(index, static_cast::type>(val)); } void Get(int index, uint8_t &out) const; From 14d025a34223d2c224eb780401b272366fdd0f2e Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 28 Oct 2021 02:08:29 -0400 Subject: [PATCH 03/23] dont use functions that dont actually exist --- discord/store.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/store.cpp b/discord/store.cpp index b39df42..95a28fe 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -2122,9 +2122,9 @@ bool Store::Database::OK() const { const char *Store::Database::ErrStr() const { const char *errstr = sqlite3_errstr(m_err); const char *errmsg = sqlite3_errmsg(m_db); - strcpy_s(m_err_scratch, sizeof(m_err_scratch), errstr); - strcat_s(m_err_scratch, sizeof(m_err_scratch), "\n\t"); - strcat_s(m_err_scratch, sizeof(m_err_scratch), errmsg); + std::string tmp = errstr + std::string("\n\t") + errmsg; + tmp.copy(m_err_scratch, sizeof(m_err_scratch) - 1); + m_err_scratch[sizeof(m_err_scratch) - 1] = '\0'; return m_err_scratch; } From 9175da2cf0fcb0bcc209bec1b777f2b51375361f Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 28 Oct 2021 02:19:13 -0400 Subject: [PATCH 04/23] take care of some warnings --- discord/discord.cpp | 9 +++--- discord/store.cpp | 68 +++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/discord/discord.cpp b/discord/discord.cpp index 0309508..a3766aa 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -1,5 +1,6 @@ #include "discord.hpp" #include +#include #include "../util.hpp" #include "../abaddon.hpp" @@ -1276,11 +1277,11 @@ void DiscordClient::HandleGatewayMessage(std::string str) { } } break; default: - printf("Unknown opcode %d\n", m.Opcode); + printf("Unknown opcode %d\n", static_cast(m.Opcode)); break; } } catch (std::exception &e) { - fprintf(stderr, "error handling message (opcode %d): %s\n", m.Opcode, e.what()); + fprintf(stderr, "error handling message (opcode %d): %s\n", static_cast(m.Opcode), e.what()); } } @@ -1320,7 +1321,7 @@ DiscordError DiscordClient::GetCodeFromResponse(const http::response_type &respo void DiscordClient::ProcessNewGuild(GuildData &guild) { if (guild.IsUnavailable) { - printf("guild (%lld) unavailable\n", static_cast(guild.ID)); + printf("guild (%" PRIu64 ") unavailable\n", static_cast(guild.ID)); return; } @@ -1913,7 +1914,7 @@ void DiscordClient::HandleGatewayGuildDelete(const GatewayMessage &msg) { bool unavailable = msg.Data.contains("unavilable") && msg.Data.at("unavailable").get(); if (unavailable) - printf("guild %llu became unavailable\n", static_cast(id)); + printf("guild %" PRIu64 " became unavailable\n", static_cast(id)); const auto guild = m_store.GetGuild(id); if (!guild.has_value()) { diff --git a/discord/store.cpp b/discord/store.cpp index 95a28fe..0027104 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -1,4 +1,5 @@ #include "store.hpp" +#include using namespace std::literals::string_literals; @@ -63,7 +64,7 @@ void Store::SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban) { s->Bind(3, ban.Reason); if (!s->Insert()) - fprintf(stderr, "ban insert failed for %llu/%llu: %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); + fprintf(stderr, "ban insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); s->Reset(); } @@ -98,7 +99,7 @@ void Store::SetChannel(Snowflake id, const ChannelData &chan) { } if (!s->Insert()) - fprintf(stderr, "channel insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "channel insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); if (chan.Recipients.has_value()) { BeginTransaction(); @@ -107,7 +108,7 @@ void Store::SetChannel(Snowflake id, const ChannelData &chan) { s->Bind(1, chan.ID); s->Bind(2, r.ID); if (!s->Insert()) - fprintf(stderr, "recipient insert failed for %llu/%llu: %s\n", static_cast(chan.ID), static_cast(r.ID), m_db.ErrStr()); + fprintf(stderr, "recipient insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(chan.ID), static_cast(r.ID), m_db.ErrStr()); s->Reset(); } EndTransaction(); @@ -118,7 +119,7 @@ void Store::SetChannel(Snowflake id, const ChannelData &chan) { s->Bind(1, chan.ID); s->Bind(2, id); if (!s->Insert()) - fprintf(stderr, "recipient insert failed for %llu/%llu: %s\n", static_cast(chan.ID), static_cast(id), m_db.ErrStr()); + fprintf(stderr, "recipient insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(chan.ID), static_cast(id), m_db.ErrStr()); s->Reset(); } EndTransaction(); @@ -150,7 +151,7 @@ void Store::SetEmoji(Snowflake id, const EmojiData &emoji) { s->Bind(1, id); s->Bind(2, r); if (!s->Insert()) - fprintf(stderr, "emoji role insert failed for %llu/%llu: %s\n", static_cast(id), static_cast(r), m_db.ErrStr()); + fprintf(stderr, "emoji role insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(id), static_cast(r), m_db.ErrStr()); s->Reset(); } @@ -158,7 +159,7 @@ void Store::SetEmoji(Snowflake id, const EmojiData &emoji) { } if (!s->Insert()) - fprintf(stderr, "emoji insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "emoji insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); } @@ -204,7 +205,7 @@ void Store::SetGuild(Snowflake id, const GuildData &guild) { s->Bind(35, guild.IsLazy); if (!s->Insert()) - fprintf(stderr, "guild insert failed for %llu: %s\n", static_cast(guild.ID), m_db.ErrStr()); + fprintf(stderr, "guild insert failed for %" PRIu64 ": %s\n", static_cast(guild.ID), m_db.ErrStr()); s->Reset(); @@ -214,7 +215,7 @@ void Store::SetGuild(Snowflake id, const GuildData &guild) { s->Bind(1, guild.ID); s->Bind(2, emoji.ID); if (!s->Insert()) - fprintf(stderr, "guild emoji insert failed for %llu/%llu: %s\n", static_cast(guild.ID), static_cast(emoji.ID), m_db.ErrStr()); + fprintf(stderr, "guild emoji insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild.ID), static_cast(emoji.ID), m_db.ErrStr()); s->Reset(); } } @@ -226,7 +227,7 @@ void Store::SetGuild(Snowflake id, const GuildData &guild) { s->Bind(1, guild.ID); s->Bind(2, feature); if (!s->Insert()) - fprintf(stderr, "guild feature insert failed for %llu/%s: %s\n", static_cast(guild.ID), feature.c_str(), m_db.ErrStr()); + fprintf(stderr, "guild feature insert failed for %" PRIu64 "/%s: %s\n", static_cast(guild.ID), feature.c_str(), m_db.ErrStr()); s->Reset(); } } @@ -238,7 +239,7 @@ void Store::SetGuild(Snowflake id, const GuildData &guild) { s->Bind(1, guild.ID); s->Bind(2, thread.ID); if (!s->Insert()) - fprintf(stderr, "guild thread insert failed for %llu/%llu: %s\n", static_cast(guild.ID), static_cast(thread.ID), m_db.ErrStr()); + fprintf(stderr, "guild thread insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild.ID), static_cast(thread.ID), m_db.ErrStr()); s->Reset(); } } @@ -260,7 +261,7 @@ void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMem s->Bind(9, data.IsPending); if (!s->Insert()) - fprintf(stderr, "member insert failed for %llu/%llu: %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); + fprintf(stderr, "member insert failed for %" PRIu64 "/% " PRIu64 ": %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); s->Reset(); @@ -272,7 +273,8 @@ void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMem s->Bind(1, user_id); s->Bind(2, role); if (!s->Insert()) - fprintf(stderr, "member role insert failed for %llu/%llu/%llu: %s\n", static_cast(user_id), static_cast(guild_id), static_cast(role), m_db.ErrStr()); + fprintf(stderr, "member role insert failed for %" PRIu64 "/%" PRIu64 "/%" PRIu64 ": %s\n", + static_cast(user_id), static_cast(guild_id), static_cast(role), m_db.ErrStr()); s->Reset(); } EndTransaction(); @@ -289,7 +291,7 @@ void Store::SetMessageInteractionPair(Snowflake message_id, const MessageInterac s->Bind(5, interaction.User.ID); if (!s->Insert()) - fprintf(stderr, "message interaction failed for %llu: %s\n", static_cast(message_id), m_db.ErrStr()); + fprintf(stderr, "message interaction failed for %" PRIu64 ": %s\n", static_cast(message_id), m_db.ErrStr()); s->Reset(); } @@ -322,7 +324,7 @@ void Store::SetMessage(Snowflake id, const Message &message) { s->BindAsJSON(21, message.StickerItems); if (!s->Insert()) - fprintf(stderr, "message insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "message insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); @@ -334,7 +336,7 @@ void Store::SetMessage(Snowflake id, const Message &message) { s->Bind(4, message.MessageReference->GuildID); if (!s->Insert()) - fprintf(stderr, "message ref insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "message ref insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); } @@ -344,7 +346,7 @@ void Store::SetMessage(Snowflake id, const Message &message) { s->Bind(1, id); s->Bind(2, u.ID); if (!s->Insert()) - fprintf(stderr, "message mention insert failed for %llu/%llu: %s\n", static_cast(id), static_cast(u.ID), m_db.ErrStr()); + fprintf(stderr, "message mention insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(id), static_cast(u.ID), m_db.ErrStr()); s->Reset(); } @@ -359,7 +361,7 @@ void Store::SetMessage(Snowflake id, const Message &message) { s->Bind(7, a.Height); s->Bind(8, a.Width); if (!s->Insert()) - fprintf(stderr, "message attachment insert failed for %llu/%llu: %s\n", static_cast(id), static_cast(a.ID), m_db.ErrStr()); + fprintf(stderr, "message attachment insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(id), static_cast(a.ID), m_db.ErrStr()); s->Reset(); } @@ -374,7 +376,7 @@ void Store::SetMessage(Snowflake id, const Message &message) { s->Bind(5, reaction.HasReactedWith); s->Bind(6, i); if (!s->Insert()) - fprintf(stderr, "message reaction insert failed for %llu/%llu/%s: %s\n", static_cast(id), static_cast(reaction.Emoji.ID), reaction.Emoji.Name.c_str(), m_db.ErrStr()); + fprintf(stderr, "message reaction insert failed for %" PRIu64 "/%" PRIu64 "/%s: %s\n", static_cast(id), static_cast(reaction.Emoji.ID), reaction.Emoji.Name.c_str(), m_db.ErrStr()); s->Reset(); } } @@ -395,7 +397,7 @@ void Store::SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const Per s->Bind(5, perm.Deny); if (!s->Insert()) - fprintf(stderr, "permission insert failed for %llu/%llu: %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); + fprintf(stderr, "permission insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); s->Reset(); } @@ -414,7 +416,7 @@ void Store::SetRole(Snowflake guild_id, const RoleData &role) { s->Bind(9, role.IsMentionable); if (!s->Insert()) - fprintf(stderr, "role insert failed for %llu: %s\n", static_cast(role.ID), m_db.ErrStr()); + fprintf(stderr, "role insert failed for %" PRIu64 ": %s\n", static_cast(role.ID), m_db.ErrStr()); s->Reset(); } @@ -433,7 +435,7 @@ void Store::SetUser(Snowflake id, const UserData &user) { s->Bind(9, user.PublicFlags); if (!s->Insert()) - fprintf(stderr, "user insert failed for %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "user insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); } @@ -445,7 +447,7 @@ std::optional Store::GetBan(Snowflake guild_id, Snowflake user_id) cons s->Bind(2, user_id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching ban for %llu/%llu: %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); + fprintf(stderr, "error while fetching ban for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); s->Reset(); return {}; } @@ -595,7 +597,7 @@ void Store::AddReaction(const MessageReactionAddObject &data, bool byself) { s->Bind(6); if (!s->Insert()) - fprintf(stderr, "failed to add reaction for %llu: %s\n", static_cast(data.MessageID), m_db.ErrStr()); + fprintf(stderr, "failed to add reaction for %" PRIu64 ": %s\n", static_cast(data.MessageID), m_db.ErrStr()); s->Reset(); } @@ -612,7 +614,7 @@ void Store::RemoveReaction(const MessageReactionRemoveObject &data, bool byself) s->Bind(4); if (!s->Insert()) - fprintf(stderr, "failed to remove reaction for %llu: %s\n", static_cast(data.MessageID), m_db.ErrStr()); + fprintf(stderr, "failed to remove reaction for %" PRIu64 ": %s\n", static_cast(data.MessageID), m_db.ErrStr()); s->Reset(); } @@ -622,7 +624,7 @@ std::optional Store::GetChannel(Snowflake id) const { s->Bind(1, id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching channel %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching channel %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } @@ -672,7 +674,7 @@ std::optional Store::GetEmoji(Snowflake id) const { s->Bind(1, id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching emoji %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching emoji %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } @@ -713,7 +715,7 @@ std::optional Store::GetGuild(Snowflake id) const { s->Bind(1, id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching guild %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching guild %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } @@ -785,7 +787,7 @@ std::optional Store::GetGuildMember(Snowflake guild_id, Snowflake u s->Bind(2, guild_id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching member %llu/%llu: %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); + fprintf(stderr, "error while fetching member %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); s->Reset(); return {}; } @@ -825,7 +827,7 @@ std::optional Store::GetMessage(Snowflake id) const { s->Bind(1, id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching message %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching message %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } @@ -833,7 +835,7 @@ std::optional Store::GetMessage(Snowflake id) const { auto top = GetMessageBound(s); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching message %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching message %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return top; } @@ -935,7 +937,7 @@ std::optional Store::GetPermissionOverwrite(Snowflake chann s->Bind(2, channel_id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "failed while fetching permission %llu/%llu: %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); + fprintf(stderr, "failed while fetching permission %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } @@ -957,7 +959,7 @@ std::optional Store::GetRole(Snowflake id) const { s->Bind(1, id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching role %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching role %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } @@ -984,7 +986,7 @@ std::optional Store::GetUser(Snowflake id) const { s->Bind(1, id); if (!s->FetchOne()) { if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching user %llu: %s\n", static_cast(id), m_db.ErrStr()); + fprintf(stderr, "error while fetching user %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); s->Reset(); return {}; } From 2c2686946d3a9364399fb9c24733ed31383cd94f Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 28 Oct 2021 02:33:13 -0400 Subject: [PATCH 05/23] try to fix weird ambiguous int stuff --- discord/store.cpp | 6 +++--- discord/store.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/discord/store.cpp b/discord/store.cpp index 0027104..e7715b4 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -2169,7 +2169,7 @@ int Store::Statement::Bind(int index, uint32_t num) { return m_db->SetError(sqlite3_bind_int(m_stmt, index, num)); } -int Store::Statement::Bind(int index, uint64_t num) { +int Store::Statement::Bind(int index, size_t num) { return m_db->SetError(sqlite3_bind_int64(m_stmt, index, num)); } @@ -2198,8 +2198,8 @@ void Store::Statement::Get(int index, int32_t &out) const { out = sqlite3_column_int(m_stmt, index); } -void Store::Statement::Get(int index, uint64_t &out) const { - out = static_cast(sqlite3_column_int64(m_stmt, index)); +void Store::Statement::Get(int index, size_t &out) const { + out = static_cast(sqlite3_column_int64(m_stmt, index)); } void Store::Statement::Get(int index, bool &out) const { diff --git a/discord/store.hpp b/discord/store.hpp index 280d728..7bb1af1 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -102,7 +102,7 @@ private: int Bind(int index, int32_t num); int Bind(int index, uint32_t num); - int Bind(int index, uint64_t num); + int Bind(int index, size_t num); int Bind(int index, const char *str, size_t len = -1); int Bind(int index, const std::string &str); int Bind(int index, bool val); @@ -154,7 +154,7 @@ private: void Get(int index, uint8_t &out) const; void Get(int index, int32_t &out) const; - void Get(int index, uint64_t &out) const; + void Get(int index, size_t &out) const; void Get(int index, bool &out) const; void Get(int index, Snowflake &out) const; void Get(int index, std::string &out) const; From d950460e149d78962508f221c5e81717798cb228 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 28 Oct 2021 04:19:05 -0400 Subject: [PATCH 06/23] try to fix some more compilation errors/warnings --- discord/store.cpp | 8 ++++++-- discord/store.hpp | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/discord/store.cpp b/discord/store.cpp index e7715b4..159e3dd 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -261,7 +261,7 @@ void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMem s->Bind(9, data.IsPending); if (!s->Insert()) - fprintf(stderr, "member insert failed for %" PRIu64 "/% " PRIu64 ": %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); + fprintf(stderr, "member insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); s->Reset(); @@ -661,7 +661,7 @@ std::optional Store::GetChannel(Snowflake id) const { s->Get(0, r); } s->Reset(); - if (recipients.size() > 0) + if (!recipients.empty()) r.RecipientIDs = std::move(recipients); } @@ -2173,6 +2173,10 @@ int Store::Statement::Bind(int index, size_t num) { return m_db->SetError(sqlite3_bind_int64(m_stmt, index, num)); } +int Store::Statement::Bind(int index, Snowflake id) { + return m_db->SetError(sqlite3_bind_int64(m_stmt, index, static_cast(id))); +} + int Store::Statement::Bind(int index, const char *str, size_t len) { if (len == -1) len = strlen(str); return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str, len, SQLITE_TRANSIENT)); diff --git a/discord/store.hpp b/discord/store.hpp index 7bb1af1..b30d4f4 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -103,6 +103,7 @@ private: int Bind(int index, int32_t num); int Bind(int index, uint32_t num); int Bind(int index, size_t num); + int Bind(int index, Snowflake id); int Bind(int index, const char *str, size_t len = -1); int Bind(int index, const std::string &str); int Bind(int index, bool val); From 12c105623cf236a632d29f41b56c3f1e8df27416 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 29 Oct 2021 01:35:16 -0400 Subject: [PATCH 07/23] templatize some stuff --- discord/store.cpp | 34 +--------------------------------- discord/store.hpp | 36 ++++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 49 deletions(-) diff --git a/discord/store.cpp b/discord/store.cpp index 159e3dd..c313f4e 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -2161,20 +2161,8 @@ bool Store::Statement::OK() const { return m_stmt != nullptr; } -int Store::Statement::Bind(int index, int32_t num) { - return Bind(index, static_cast(num)); -} - -int Store::Statement::Bind(int index, uint32_t num) { - return m_db->SetError(sqlite3_bind_int(m_stmt, index, num)); -} - -int Store::Statement::Bind(int index, size_t num) { - return m_db->SetError(sqlite3_bind_int64(m_stmt, index, num)); -} - int Store::Statement::Bind(int index, Snowflake id) { - return m_db->SetError(sqlite3_bind_int64(m_stmt, index, static_cast(id))); + return Bind(index, static_cast(id)); } int Store::Statement::Bind(int index, const char *str, size_t len) { @@ -2186,30 +2174,10 @@ int Store::Statement::Bind(int index, const std::string &str) { return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT)); } -int Store::Statement::Bind(int index, bool val) { - return m_db->SetError(sqlite3_bind_int(m_stmt, index, val ? 1L : 0L)); -} - int Store::Statement::Bind(int index) { return m_db->SetError(sqlite3_bind_null(m_stmt, index)); } -void Store::Statement::Get(int index, uint8_t &out) const { - out = sqlite3_column_int(m_stmt, index); -} - -void Store::Statement::Get(int index, int32_t &out) const { - out = sqlite3_column_int(m_stmt, index); -} - -void Store::Statement::Get(int index, size_t &out) const { - out = static_cast(sqlite3_column_int64(m_stmt, index)); -} - -void Store::Statement::Get(int index, bool &out) const { - out = sqlite3_column_int(m_stmt, index) != 0; -} - void Store::Statement::Get(int index, Snowflake &out) const { out = static_cast(sqlite3_column_int64(m_stmt, index)); } diff --git a/discord/store.hpp b/discord/store.hpp index b30d4f4..791c790 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -100,13 +100,9 @@ private: bool OK() const; - int Bind(int index, int32_t num); - int Bind(int index, uint32_t num); - int Bind(int index, size_t num); int Bind(int index, Snowflake id); int Bind(int index, const char *str, size_t len = -1); int Bind(int index, const std::string &str); - int Bind(int index, bool val); int Bind(int index); template @@ -134,14 +130,6 @@ private: return Bind(index, std::string("[]")); } - template - int BindAsJSON(int index, const std::optional &obj) { - if (obj.has_value()) - return Bind(index, nlohmann::json(obj.value()).dump()); - else - return Bind(index); - } - template int BindAsJSON(int index, const T &obj) { return Bind(index, nlohmann::json(obj).dump()); @@ -153,10 +141,26 @@ private: return Bind(index, static_cast::type>(val)); } - void Get(int index, uint8_t &out) const; - void Get(int index, int32_t &out) const; - void Get(int index, size_t &out) const; - void Get(int index, bool &out) const; + template + typename std::enable_if::value, int>::type + Bind(int index, T val) { + return m_db->SetError(sqlite3_bind_int64(m_stmt, val, static_cast(val))); + } + + template + int BindAsJSON(int index, const std::optional &obj) { + if (obj.has_value()) + return Bind(index, nlohmann::json(obj.value()).dump()); + else + return Bind(index); + } + + template + typename std::enable_if::value>::type + Get(int index, T &out) const { + out = static_cast(sqlite3_column_int64(m_stmt, index)); + } + void Get(int index, Snowflake &out) const; void Get(int index, std::string &out) const; From 1078d94b73654054bac2458ad8fe0567abbe4dc0 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 30 Oct 2021 02:46:50 -0400 Subject: [PATCH 08/23] fix big mistake that made it not run --- discord/store.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/store.hpp b/discord/store.hpp index 791c790..6a9f316 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -144,7 +144,7 @@ private: template typename std::enable_if::value, int>::type Bind(int index, T val) { - return m_db->SetError(sqlite3_bind_int64(m_stmt, val, static_cast(val))); + return m_db->SetError(sqlite3_bind_int64(m_stmt, index, val)); } template From a51b813f7039c94a34ae45552cd7137e455a00e8 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 30 Oct 2021 02:47:14 -0400 Subject: [PATCH 09/23] some snowflake IsValid checks mainly so i could debug something easier --- windows/mainwindow.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp index 77a936f..3c8dcbb 100644 --- a/windows/mainwindow.cpp +++ b/windows/mainwindow.cpp @@ -229,10 +229,12 @@ void MainWindow::UpdateChatReactionRemove(Snowflake id, const Glib::ustring &par void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) { auto &discord = Abaddon::Get().GetDiscordClient(); auto channel_id = GetChatActiveChannel(); - auto channel = discord.GetChannel(channel_id); m_menu_discord_add_recipient.set_visible(false); - if (channel.has_value() && channel->GetDMRecipients().size() + 1 < 10) - m_menu_discord_add_recipient.set_visible(channel->Type == ChannelType::GROUP_DM); + if (channel_id.IsValid()) { + auto channel = discord.GetChannel(channel_id); + if (channel.has_value() && channel->GetDMRecipients().size() + 1 < 10) + m_menu_discord_add_recipient.set_visible(channel->Type == ChannelType::GROUP_DM); + } const bool discord_active = Abaddon::Get().GetDiscordClient().IsStarted(); @@ -247,12 +249,14 @@ void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const void MainWindow::OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) { m_menu_view_friends.set_sensitive(Abaddon::Get().GetDiscordClient().IsStarted()); auto channel_id = GetChatActiveChannel(); - auto channel = Abaddon::Get().GetDiscordClient().GetChannel(channel_id); m_menu_view_pins.set_sensitive(false); m_menu_view_threads.set_sensitive(false); - if (channel.has_value()) { - m_menu_view_threads.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->IsThread()); - m_menu_view_pins.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM || channel->IsThread()); + if (channel_id.IsValid()) { + auto channel = Abaddon::Get().GetDiscordClient().GetChannel(channel_id); + if (channel.has_value()) { + m_menu_view_threads.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->IsThread()); + m_menu_view_pins.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM || channel->IsThread()); + } } } From 2257ff979854867a9aff32aa7d07769bce5edff8 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 6 Nov 2021 01:10:56 -0400 Subject: [PATCH 10/23] fix double close of store throwing --- discord/store.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/store.cpp b/discord/store.cpp index c313f4e..a119869 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -2095,6 +2095,7 @@ Store::Database::~Database() { } int Store::Database::Close() { + if (m_db == nullptr) return m_err; m_signal_close.emit(); m_err = sqlite3_close(m_db); m_db = nullptr; @@ -2147,7 +2148,6 @@ Store::Statement::Statement(Database &db, const char *command) if (m_db->SetError(sqlite3_prepare_v2(m_db->obj(), command, -1, &m_stmt, nullptr)) != SQLITE_OK) return; std::string tmp = command; m_db->signal_close().connect([tmp, this] { - puts(tmp.c_str()); sqlite3_finalize(m_stmt); m_stmt = nullptr; }); From 1da2c57376961d38be9ed3cdea7f78dc9d7a6ae2 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 16 Nov 2021 02:37:12 -0500 Subject: [PATCH 11/23] fix retrieving message references and also add a reset statement i forgot (raii might be good here...) --- components/chatmessage.cpp | 20 +++++++++++----- discord/store.cpp | 49 ++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp index af68a9d..b7d61a8 100644 --- a/components/chatmessage.cpp +++ b/components/chatmessage.cpp @@ -657,6 +657,14 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data) return author->GetEscapedBoldString(); }; + // if the message wasnt fetched from store it might have an un-fetched reference + std::optional> referenced_message = data.ReferencedMessage; + if (data.MessageReference.has_value() && data.MessageReference->MessageID.has_value() && !referenced_message.has_value()) { + auto refd = discord.GetMessage(*data.MessageReference->MessageID); + if (refd.has_value()) + referenced_message = std::make_shared(std::move(*refd)); + } + if (data.Interaction.has_value()) { const auto user = *discord.GetUser(data.Interaction->User.ID); @@ -668,16 +676,16 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data) } else { lbl->set_markup(user.GetEscapedBoldString()); } - } else if (data.ReferencedMessage.has_value()) { - if (data.ReferencedMessage.value().get() == nullptr) { + } else if (referenced_message.has_value()) { + if (referenced_message.value() == nullptr) { lbl->set_markup("deleted message"); } else { - const auto &referenced = *data.ReferencedMessage.value().get(); + const auto &referenced = *referenced_message.value(); Glib::ustring text; - if (referenced.Content == "") { - if (referenced.Attachments.size() > 0) { + if (referenced.Content.empty()) { + if (!referenced.Attachments.empty()) { text = "attachment"; - } else if (referenced.Embeds.size() > 0) { + } else if (!referenced.Embeds.empty()) { text = "embed"; } } else { diff --git a/discord/store.cpp b/discord/store.cpp index a119869..8e88e7b 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -484,10 +484,6 @@ std::vector Store::GetLastMessages(Snowflake id, size_t num) const { s->Bind(2, num); while (s->FetchOne()) { auto msg = GetMessageBound(s); - if (!s->IsNull(33)) { // referenced message id - msg.MessageReference.emplace(); - s->Get(33, msg.MessageReference->MessageID); - } msgs.push_back(std::move(msg)); } @@ -515,10 +511,6 @@ std::vector Store::GetMessagesBefore(Snowflake channel_id, Snowflake me while (s->FetchOne()) { auto msg = GetMessageBound(s); - if (!s->IsNull(33)) { // referenced message id - msg.MessageReference.emplace(); - s->Get(33, msg.MessageReference->MessageID); - } msgs.push_back(std::move(msg)); } @@ -544,10 +536,6 @@ std::vector Store::GetPinnedMessages(Snowflake channel_id) const { while (s->FetchOne()) { auto msg = GetMessageBound(s); - if (!s->IsNull(33)) { // referenced message id - msg.MessageReference.emplace(); - s->Get(33, msg.MessageReference->MessageID); - } msgs.push_back(std::move(msg)); } @@ -843,6 +831,8 @@ std::optional Store::GetMessage(Snowflake id) const { auto ref = GetMessageBound(s); top.ReferencedMessage = std::make_shared(std::move(ref)); + s->Reset(); + return top; } @@ -893,6 +883,13 @@ Message Store::GetMessageBound(std::unique_ptr &s) const { s->Get(31, a.Width); } + if (!s->IsNull(32)) { + auto &q = r.MessageReference.emplace(); + s->Get(32, q.MessageID); + s->Get(33, q.ChannelID); + s->Get(34, q.GuildID); + } + { auto &s = m_stmt_get_mentions; s->Bind(1, r.ID); @@ -1581,7 +1578,10 @@ bool Store::CreateStatements() { attachments.url, attachments.proxy, attachments.height, - attachments.width + attachments.width, + message_references.message, + message_references.channel, + message_references.guild FROM messages LEFT OUTER JOIN message_interactions @@ -1589,7 +1589,10 @@ bool Store::CreateStatements() { LEFT OUTER JOIN attachments ON messages.id = attachments.message - WHERE messages.id = ? + LEFT OUTER JOIN + message_references + ON messages.id = message_references.id + WHERE messages.id = ?1 UNION ALL SELECT messages.*, message_interactions.interaction_id, @@ -1602,7 +1605,10 @@ bool Store::CreateStatements() { attachments.url, attachments.proxy, attachments.height, - attachments.width + attachments.width, + message_references.message, + message_references.channel, + message_references.guild FROM messages LEFT OUTER JOIN message_interactions @@ -1610,7 +1616,10 @@ bool Store::CreateStatements() { LEFT OUTER JOIN attachments ON messages.id = attachments.message - WHERE messages.id = (SELECT message FROM message_references WHERE id = ?) + LEFT OUTER JOIN + message_references + ON messages.id = message_references.id + WHERE messages.id = (SELECT message FROM message_references WHERE id = ?1) ORDER BY messages.id DESC )"); if (!m_stmt_get_msg->OK()) { @@ -1642,7 +1651,9 @@ bool Store::CreateStatements() { attachments.proxy, attachments.height, attachments.width, - message_references.message + message_references.message, + message_references.channel, + message_references.guild FROM messages LEFT OUTER JOIN message_interactions @@ -1940,7 +1951,9 @@ bool Store::CreateStatements() { attachments.proxy, attachments.height, attachments.width, - message_references.message + message_references.message, + message_references.channel, + message_references.guild FROM messages LEFT OUTER JOIN message_interactions From fc76a15c4636df4961af1879ddecb111a9f68260 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 19 Nov 2021 01:08:03 -0500 Subject: [PATCH 12/23] fix sqlite error messages --- discord/store.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/store.cpp b/discord/store.cpp index 8e88e7b..9b615fd 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -2140,7 +2140,7 @@ const char *Store::Database::ErrStr() const { const char *errmsg = sqlite3_errmsg(m_db); std::string tmp = errstr + std::string("\n\t") + errmsg; tmp.copy(m_err_scratch, sizeof(m_err_scratch) - 1); - m_err_scratch[sizeof(m_err_scratch) - 1] = '\0'; + m_err_scratch[std::min(tmp.size(), sizeof(m_err_scratch) - 1)] = '\0'; return m_err_scratch; } From d88079000a79e6bcbe51c5a2868d57b303b5fcb6 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 20 Nov 2021 22:29:03 -0500 Subject: [PATCH 13/23] normalize include paths --- components/channels.cpp | 6 +++--- components/chatinputindicator.cpp | 4 ++-- components/chatinputindicator.hpp | 4 ++-- components/chatmessage.cpp | 4 ++-- components/chatmessage.hpp | 2 +- components/completer.cpp | 4 ++-- components/completer.hpp | 2 +- components/friendslist.cpp | 2 +- components/friendslist.hpp | 2 +- components/lazyimage.cpp | 2 +- components/memberlist.cpp | 4 ++-- components/memberlist.hpp | 2 +- components/ratelimitindicator.cpp | 2 +- components/ratelimitindicator.hpp | 2 +- components/statusindicator.cpp | 2 +- components/statusindicator.hpp | 4 ++-- dialogs/friendpicker.cpp | 2 +- dialogs/friendpicker.hpp | 2 +- dialogs/joinguild.cpp | 2 +- dialogs/setstatus.hpp | 2 +- dialogs/verificationgate.cpp | 2 +- dialogs/verificationgate.hpp | 2 +- discord/activity.hpp | 2 +- discord/channel.cpp | 2 +- discord/discord.cpp | 4 ++-- discord/guild.cpp | 2 +- discord/httpclient.hpp | 2 +- discord/interactions.cpp | 2 +- discord/json.hpp | 2 +- discord/member.cpp | 2 +- discord/permissions.hpp | 2 +- discord/store.hpp | 2 +- discord/user.cpp | 2 +- windows/guildsettings/banspane.cpp | 2 +- windows/guildsettings/banspane.hpp | 4 ++-- windows/guildsettings/emojispane.cpp | 4 ++-- windows/guildsettings/emojispane.hpp | 2 +- windows/guildsettings/infopane.cpp | 2 +- windows/guildsettings/infopane.hpp | 2 +- windows/guildsettings/invitespane.cpp | 2 +- windows/guildsettings/invitespane.hpp | 2 +- windows/guildsettings/memberspane.cpp | 2 +- windows/guildsettings/memberspane.hpp | 6 +++--- windows/guildsettings/rolespane.cpp | 2 +- windows/guildsettings/rolespane.hpp | 4 ++-- windows/guildsettingswindow.cpp | 2 +- windows/guildsettingswindow.hpp | 2 +- windows/mainwindow.cpp | 2 +- windows/mainwindow.hpp | 8 ++++---- windows/pinnedwindow.cpp | 2 +- windows/pinnedwindow.hpp | 8 ++++---- windows/profile/mutualfriendspane.cpp | 2 +- windows/profile/mutualfriendspane.hpp | 2 +- windows/profile/mutualguildspane.cpp | 2 +- windows/profile/mutualguildspane.hpp | 2 +- windows/profile/userinfopane.cpp | 2 +- windows/profile/userinfopane.hpp | 2 +- windows/profilewindow.cpp | 2 +- windows/profilewindow.hpp | 2 +- windows/threadswindow.cpp | 2 +- windows/threadswindow.hpp | 2 +- 61 files changed, 81 insertions(+), 81 deletions(-) diff --git a/components/channels.cpp b/components/channels.cpp index b4769d9..da31de0 100644 --- a/components/channels.cpp +++ b/components/channels.cpp @@ -2,9 +2,9 @@ #include #include #include -#include "../abaddon.hpp" -#include "../imgmanager.hpp" -#include "../util.hpp" +#include "abaddon.hpp" +#include "imgmanager.hpp" +#include "util.hpp" #include "statusindicator.hpp" ChannelList::ChannelList() diff --git a/components/chatinputindicator.cpp b/components/chatinputindicator.cpp index acc2aa6..9b063b2 100644 --- a/components/chatinputindicator.cpp +++ b/components/chatinputindicator.cpp @@ -1,7 +1,7 @@ #include #include "chatinputindicator.hpp" -#include "../abaddon.hpp" -#include "../util.hpp" +#include "abaddon.hpp" +#include "util.hpp" constexpr static const int MaxUsersInIndicator = 4; diff --git a/components/chatinputindicator.hpp b/components/chatinputindicator.hpp index 3b007b3..ec70dfb 100644 --- a/components/chatinputindicator.hpp +++ b/components/chatinputindicator.hpp @@ -1,8 +1,8 @@ #pragma once #include #include -#include "../discord/message.hpp" -#include "../discord/user.hpp" +#include "discord/message.hpp" +#include "discord/user.hpp" class ChatInputIndicator : public Gtk::Box { public: diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp index f7ca6a5..aa4fc2e 100644 --- a/components/chatmessage.cpp +++ b/components/chatmessage.cpp @@ -1,6 +1,6 @@ #include "chatmessage.hpp" -#include "../abaddon.hpp" -#include "../util.hpp" +#include "abaddon.hpp" +#include "util.hpp" #include "lazyimage.hpp" #include diff --git a/components/chatmessage.hpp b/components/chatmessage.hpp index f319449..8b69117 100644 --- a/components/chatmessage.hpp +++ b/components/chatmessage.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/discord.hpp" +#include "discord/discord.hpp" class ChatMessageItemContainer : public Gtk::Box { public: diff --git a/components/completer.cpp b/components/completer.cpp index 258acb8..327ef95 100644 --- a/components/completer.cpp +++ b/components/completer.cpp @@ -1,7 +1,7 @@ #include #include "completer.hpp" -#include "../abaddon.hpp" -#include "../util.hpp" +#include "abaddon.hpp" +#include "util.hpp" constexpr const int CompleterHeight = 150; constexpr const int MaxCompleterEntries = 30; diff --git a/components/completer.hpp b/components/completer.hpp index a669824..6bd8be9 100644 --- a/components/completer.hpp +++ b/components/completer.hpp @@ -2,7 +2,7 @@ #include #include #include "lazyimage.hpp" -#include "../discord/snowflake.hpp" +#include "discord/snowflake.hpp" constexpr static int CompleterImageSize = 24; diff --git a/components/friendslist.cpp b/components/friendslist.cpp index ec78b57..3896f02 100644 --- a/components/friendslist.cpp +++ b/components/friendslist.cpp @@ -1,5 +1,5 @@ #include "friendslist.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" #include "lazyimage.hpp" using namespace std::string_literals; diff --git a/components/friendslist.hpp b/components/friendslist.hpp index 0101db6..460ad32 100644 --- a/components/friendslist.hpp +++ b/components/friendslist.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/objects.hpp" +#include "discord/objects.hpp" class FriendsListAddComponent : public Gtk::Box { public: diff --git a/components/lazyimage.cpp b/components/lazyimage.cpp index 5574c6c..49bbdeb 100644 --- a/components/lazyimage.cpp +++ b/components/lazyimage.cpp @@ -1,5 +1,5 @@ #include "lazyimage.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" LazyImage::LazyImage(int w, int h, bool use_placeholder) : m_width(w) diff --git a/components/memberlist.cpp b/components/memberlist.cpp index ffc210b..0c4d9bc 100644 --- a/components/memberlist.cpp +++ b/components/memberlist.cpp @@ -1,6 +1,6 @@ #include "memberlist.hpp" -#include "../abaddon.hpp" -#include "../util.hpp" +#include "abaddon.hpp" +#include "util.hpp" #include "lazyimage.hpp" #include "statusindicator.hpp" diff --git a/components/memberlist.hpp b/components/memberlist.hpp index 180e76d..60a25bc 100644 --- a/components/memberlist.hpp +++ b/components/memberlist.hpp @@ -3,7 +3,7 @@ #include #include #include -#include "../discord/discord.hpp" +#include "discord/discord.hpp" class LazyImage; class StatusIndicator; diff --git a/components/ratelimitindicator.cpp b/components/ratelimitindicator.cpp index fe187db..ac4ef4b 100644 --- a/components/ratelimitindicator.cpp +++ b/components/ratelimitindicator.cpp @@ -1,5 +1,5 @@ #include "ratelimitindicator.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" #include RateLimitIndicator::RateLimitIndicator() diff --git a/components/ratelimitindicator.hpp b/components/ratelimitindicator.hpp index d6ada43..b4dbb69 100644 --- a/components/ratelimitindicator.hpp +++ b/components/ratelimitindicator.hpp @@ -2,7 +2,7 @@ #include #include #include -#include "../discord/message.hpp" +#include "discord/message.hpp" class RateLimitIndicator : public Gtk::Box { public: diff --git a/components/statusindicator.cpp b/components/statusindicator.cpp index a0616c0..42eb170 100644 --- a/components/statusindicator.cpp +++ b/components/statusindicator.cpp @@ -1,5 +1,5 @@ #include "statusindicator.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" static const constexpr int Diameter = 8; static const auto OnlineColor = Gdk::RGBA("#43B581"); diff --git a/components/statusindicator.hpp b/components/statusindicator.hpp index 9949b0d..b2cf0bd 100644 --- a/components/statusindicator.hpp +++ b/components/statusindicator.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "../discord/snowflake.hpp" -#include "../discord/activity.hpp" +#include "discord/snowflake.hpp" +#include "discord/activity.hpp" class StatusIndicator : public Gtk::Widget { public: diff --git a/dialogs/friendpicker.cpp b/dialogs/friendpicker.cpp index 420fa9b..fc099aa 100644 --- a/dialogs/friendpicker.cpp +++ b/dialogs/friendpicker.cpp @@ -1,5 +1,5 @@ #include "friendpicker.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" FriendPickerDialog::FriendPickerDialog(Gtk::Window &parent) : Gtk::Dialog("Pick a friend", parent, true) diff --git a/dialogs/friendpicker.hpp b/dialogs/friendpicker.hpp index 8621999..81d02a3 100644 --- a/dialogs/friendpicker.hpp +++ b/dialogs/friendpicker.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/snowflake.hpp" +#include "discord/snowflake.hpp" class FriendPickerDialog : public Gtk::Dialog { public: diff --git a/dialogs/joinguild.cpp b/dialogs/joinguild.cpp index 3ff1bdd..14fab53 100644 --- a/dialogs/joinguild.cpp +++ b/dialogs/joinguild.cpp @@ -1,5 +1,5 @@ #include "joinguild.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" #include #include diff --git a/dialogs/setstatus.hpp b/dialogs/setstatus.hpp index 97db906..b06c182 100644 --- a/dialogs/setstatus.hpp +++ b/dialogs/setstatus.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/objects.hpp" +#include "discord/objects.hpp" class SetStatusDialog : public Gtk::Dialog { public: diff --git a/dialogs/verificationgate.cpp b/dialogs/verificationgate.cpp index 69bd2e2..698ddff 100644 --- a/dialogs/verificationgate.cpp +++ b/dialogs/verificationgate.cpp @@ -1,5 +1,5 @@ #include "verificationgate.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" VerificationGateDialog::VerificationGateDialog(Gtk::Window &parent, Snowflake guild_id) : Gtk::Dialog("Verification Required", parent, true) diff --git a/dialogs/verificationgate.hpp b/dialogs/verificationgate.hpp index 175006b..0a0dc08 100644 --- a/dialogs/verificationgate.hpp +++ b/dialogs/verificationgate.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include "../discord/objects.hpp" +#include "discord/objects.hpp" class VerificationGateDialog : public Gtk::Dialog { public: diff --git a/discord/activity.hpp b/discord/activity.hpp index 76ba9cd..6b8e944 100644 --- a/discord/activity.hpp +++ b/discord/activity.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include "../util.hpp" +#include "util.hpp" #include "json.hpp" #include "snowflake.hpp" diff --git a/discord/channel.cpp b/discord/channel.cpp index 60f481b..80b1760 100644 --- a/discord/channel.cpp +++ b/discord/channel.cpp @@ -1,4 +1,4 @@ -#include "../abaddon.hpp" +#include "abaddon.hpp" #include "channel.hpp" void from_json(const nlohmann::json &j, ThreadMetadataData &m) { diff --git a/discord/discord.cpp b/discord/discord.cpp index dedf19c..bed959f 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -1,8 +1,8 @@ #include "discord.hpp" #include #include -#include "../util.hpp" -#include "../abaddon.hpp" +#include "util.hpp" +#include "abaddon.hpp" DiscordClient::DiscordClient(bool mem_store) : m_decompress_buf(InflateChunkSize) diff --git a/discord/guild.cpp b/discord/guild.cpp index f0b70d7..a02b896 100644 --- a/discord/guild.cpp +++ b/discord/guild.cpp @@ -1,5 +1,5 @@ #include "guild.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" void from_json(const nlohmann::json &j, GuildData &m) { JS_D("id", m.ID); diff --git a/discord/httpclient.hpp b/discord/httpclient.hpp index 81723b8..da8be37 100644 --- a/discord/httpclient.hpp +++ b/discord/httpclient.hpp @@ -7,7 +7,7 @@ #include #include #include -#include "../http.hpp" +#include "http.hpp" class HTTPClient { public: diff --git a/discord/interactions.cpp b/discord/interactions.cpp index 0bb09b1..cc439fc 100644 --- a/discord/interactions.cpp +++ b/discord/interactions.cpp @@ -1,6 +1,6 @@ #include "interactions.hpp" #include "json.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" void from_json(const nlohmann::json &j, MessageInteractionData &m) { JS_D("id", m.ID); diff --git a/discord/json.hpp b/discord/json.hpp index de779b9..837080b 100644 --- a/discord/json.hpp +++ b/discord/json.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include "../util.hpp" +#include "util.hpp" namespace detail { // more or less because idk what to name this stuff template diff --git a/discord/member.cpp b/discord/member.cpp index 70a5727..29c4fae 100644 --- a/discord/member.cpp +++ b/discord/member.cpp @@ -1,5 +1,5 @@ #include "member.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" void from_json(const nlohmann::json &j, GuildMember &m) { JS_O("user", m.User); diff --git a/discord/permissions.hpp b/discord/permissions.hpp index c7090f6..56ef742 100644 --- a/discord/permissions.hpp +++ b/discord/permissions.hpp @@ -2,7 +2,7 @@ #include #include "snowflake.hpp" #include "json.hpp" -#include "../util.hpp" +#include "util.hpp" constexpr static uint64_t PERMISSION_MAX_BIT = 36; enum class Permission : uint64_t { diff --git a/discord/store.hpp b/discord/store.hpp index 6a9f316..80e2407 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../util.hpp" +#include "util.hpp" #include "objects.hpp" #include #include diff --git a/discord/user.cpp b/discord/user.cpp index a68346e..fae212d 100644 --- a/discord/user.cpp +++ b/discord/user.cpp @@ -1,5 +1,5 @@ #include "user.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" bool UserData::IsDeleted() const { return Discriminator == "0000"; diff --git a/windows/guildsettings/banspane.cpp b/windows/guildsettings/banspane.cpp index 0fede20..97a70c4 100644 --- a/windows/guildsettings/banspane.cpp +++ b/windows/guildsettings/banspane.cpp @@ -1,5 +1,5 @@ #include "banspane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" // gtk_list_store_set_value: assertion 'column >= 0 && column < priv->n_columns' failed // dont care to figure out why this happens cuz it doesnt seem to break anything diff --git a/windows/guildsettings/banspane.hpp b/windows/guildsettings/banspane.hpp index ead118e..b2420a9 100644 --- a/windows/guildsettings/banspane.hpp +++ b/windows/guildsettings/banspane.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "../../discord/snowflake.hpp" -#include "../../discord/ban.hpp" +#include "discord/snowflake.hpp" +#include "discord/ban.hpp" class GuildSettingsBansPane : public Gtk::Box { public: diff --git a/windows/guildsettings/emojispane.cpp b/windows/guildsettings/emojispane.cpp index e64f60d..1f4bfa9 100644 --- a/windows/guildsettings/emojispane.cpp +++ b/windows/guildsettings/emojispane.cpp @@ -1,6 +1,6 @@ #include "emojispane.hpp" -#include "../../abaddon.hpp" -#include "../../components/cellrendererpixbufanimation.hpp" +#include "abaddon.hpp" +#include "components/cellrendererpixbufanimation.hpp" GuildSettingsEmojisPane::GuildSettingsEmojisPane(Snowflake guild_id) : Gtk::Box(Gtk::ORIENTATION_VERTICAL) diff --git a/windows/guildsettings/emojispane.hpp b/windows/guildsettings/emojispane.hpp index 19203c7..1c0edd1 100644 --- a/windows/guildsettings/emojispane.hpp +++ b/windows/guildsettings/emojispane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/emoji.hpp" +#include "discord/emoji.hpp" class GuildSettingsEmojisPane : public Gtk::Box { public: diff --git a/windows/guildsettings/infopane.cpp b/windows/guildsettings/infopane.cpp index d75def1..b4f75f3 100644 --- a/windows/guildsettings/infopane.cpp +++ b/windows/guildsettings/infopane.cpp @@ -1,5 +1,5 @@ #include "infopane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" #include GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id) diff --git a/windows/guildsettings/infopane.hpp b/windows/guildsettings/infopane.hpp index 829fa3b..8a7e6a2 100644 --- a/windows/guildsettings/infopane.hpp +++ b/windows/guildsettings/infopane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/guild.hpp" +#include "discord/guild.hpp" class GuildSettingsInfoPane : public Gtk::Grid { public: diff --git a/windows/guildsettings/invitespane.cpp b/windows/guildsettings/invitespane.cpp index 20f5258..bec4784 100644 --- a/windows/guildsettings/invitespane.cpp +++ b/windows/guildsettings/invitespane.cpp @@ -1,5 +1,5 @@ #include "invitespane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" GuildSettingsInvitesPane::GuildSettingsInvitesPane(Snowflake id) : GuildID(id) diff --git a/windows/guildsettings/invitespane.hpp b/windows/guildsettings/invitespane.hpp index 577e195..5268d68 100644 --- a/windows/guildsettings/invitespane.hpp +++ b/windows/guildsettings/invitespane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/objects.hpp" +#include "discord/objects.hpp" class GuildSettingsInvitesPane : public Gtk::ScrolledWindow { public: diff --git a/windows/guildsettings/memberspane.cpp b/windows/guildsettings/memberspane.cpp index e5fcce5..36c5c0b 100644 --- a/windows/guildsettings/memberspane.cpp +++ b/windows/guildsettings/memberspane.cpp @@ -1,5 +1,5 @@ #include "memberspane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" GuildSettingsMembersPane::GuildSettingsMembersPane(Snowflake id) : Gtk::Box(Gtk::ORIENTATION_VERTICAL) diff --git a/windows/guildsettings/memberspane.hpp b/windows/guildsettings/memberspane.hpp index 7b221b2..01398da 100644 --- a/windows/guildsettings/memberspane.hpp +++ b/windows/guildsettings/memberspane.hpp @@ -1,9 +1,9 @@ #pragma once #include #include -#include "../../discord/member.hpp" -#include "../../discord/guild.hpp" -#include "../../components/lazyimage.hpp" +#include "discord/member.hpp" +#include "discord/guild.hpp" +#include "components/lazyimage.hpp" class GuildSettingsMembersPaneRolesItem : public Gtk::ListBoxRow { public: diff --git a/windows/guildsettings/rolespane.cpp b/windows/guildsettings/rolespane.cpp index b2f5b35..8d355ee 100644 --- a/windows/guildsettings/rolespane.cpp +++ b/windows/guildsettings/rolespane.cpp @@ -1,5 +1,5 @@ #include "rolespane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" GuildSettingsRolesPane::GuildSettingsRolesPane(Snowflake id) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) diff --git a/windows/guildsettings/rolespane.hpp b/windows/guildsettings/rolespane.hpp index e5e342a..2999f32 100644 --- a/windows/guildsettings/rolespane.hpp +++ b/windows/guildsettings/rolespane.hpp @@ -1,8 +1,8 @@ #pragma once #include #include -#include "../../discord/guild.hpp" -#include "../../components/draglistbox.hpp" +#include "discord/guild.hpp" +#include "components/draglistbox.hpp" class GuildSettingsRolesPaneRolesListItem : public Gtk::ListBoxRow { public: diff --git a/windows/guildsettingswindow.cpp b/windows/guildsettingswindow.cpp index bf0160b..1e3395d 100644 --- a/windows/guildsettingswindow.cpp +++ b/windows/guildsettingswindow.cpp @@ -1,5 +1,5 @@ #include "guildsettingswindow.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" GuildSettingsWindow::GuildSettingsWindow(Snowflake id) : m_main(Gtk::ORIENTATION_VERTICAL) diff --git a/windows/guildsettingswindow.hpp b/windows/guildsettingswindow.hpp index 7447840..b591640 100644 --- a/windows/guildsettingswindow.hpp +++ b/windows/guildsettingswindow.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/snowflake.hpp" +#include "discord/snowflake.hpp" #include "guildsettings/infopane.hpp" #include "guildsettings/banspane.hpp" #include "guildsettings/invitespane.hpp" diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp index 13c9348..659107a 100644 --- a/windows/mainwindow.cpp +++ b/windows/mainwindow.cpp @@ -1,5 +1,5 @@ #include "mainwindow.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" MainWindow::MainWindow() : m_main_box(Gtk::ORIENTATION_VERTICAL) diff --git a/windows/mainwindow.hpp b/windows/mainwindow.hpp index 3b41d16..df1c968 100644 --- a/windows/mainwindow.hpp +++ b/windows/mainwindow.hpp @@ -1,8 +1,8 @@ #pragma once -#include "../components/channels.hpp" -#include "../components/chatwindow.hpp" -#include "../components/memberlist.hpp" -#include "../components/friendslist.hpp" +#include "components/channels.hpp" +#include "components/chatwindow.hpp" +#include "components/memberlist.hpp" +#include "components/friendslist.hpp" #include class MainWindow : public Gtk::Window { diff --git a/windows/pinnedwindow.cpp b/windows/pinnedwindow.cpp index 2e9b6fd..a5484e3 100644 --- a/windows/pinnedwindow.cpp +++ b/windows/pinnedwindow.cpp @@ -1,5 +1,5 @@ #include "pinnedwindow.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" PinnedWindow::PinnedWindow(const ChannelData &data) : ChannelID(data.ID) { diff --git a/windows/pinnedwindow.hpp b/windows/pinnedwindow.hpp index 56461a5..cf2ec3c 100644 --- a/windows/pinnedwindow.hpp +++ b/windows/pinnedwindow.hpp @@ -1,9 +1,9 @@ #pragma once #include -#include "../discord/errors.hpp" -#include "../discord/channel.hpp" -#include "../discord/message.hpp" -#include "../components/chatlist.hpp" +#include "discord/errors.hpp" +#include "discord/channel.hpp" +#include "discord/message.hpp" +#include "components/chatlist.hpp" class PinnedWindow : public Gtk::Window { public: diff --git a/windows/profile/mutualfriendspane.cpp b/windows/profile/mutualfriendspane.cpp index 5e6120a..339fd71 100644 --- a/windows/profile/mutualfriendspane.cpp +++ b/windows/profile/mutualfriendspane.cpp @@ -1,5 +1,5 @@ #include "mutualfriendspane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" MutualFriendItem::MutualFriendItem(const UserData &user) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) { diff --git a/windows/profile/mutualfriendspane.hpp b/windows/profile/mutualfriendspane.hpp index 764dee9..ef41aa6 100644 --- a/windows/profile/mutualfriendspane.hpp +++ b/windows/profile/mutualfriendspane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/objects.hpp" +#include "discord/objects.hpp" class MutualFriendItem : public Gtk::Box { public: diff --git a/windows/profile/mutualguildspane.cpp b/windows/profile/mutualguildspane.cpp index f7e70f0..6bfdc7b 100644 --- a/windows/profile/mutualguildspane.cpp +++ b/windows/profile/mutualguildspane.cpp @@ -1,5 +1,5 @@ #include "mutualguildspane.hpp" -#include "../../abaddon.hpp" +#include "abaddon.hpp" MutualGuildItem::MutualGuildItem(const MutualGuildData &guild) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) diff --git a/windows/profile/mutualguildspane.hpp b/windows/profile/mutualguildspane.hpp index 6afdb07..9bdd97e 100644 --- a/windows/profile/mutualguildspane.hpp +++ b/windows/profile/mutualguildspane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/objects.hpp" +#include "discord/objects.hpp" class MutualGuildItem : public Gtk::Box { public: diff --git a/windows/profile/userinfopane.cpp b/windows/profile/userinfopane.cpp index bfeb4ed..a95a14c 100644 --- a/windows/profile/userinfopane.cpp +++ b/windows/profile/userinfopane.cpp @@ -1,6 +1,6 @@ #include "userinfopane.hpp" #include -#include "../../abaddon.hpp" +#include "abaddon.hpp" ConnectionItem::ConnectionItem(const ConnectionData &conn) : m_box(Gtk::ORIENTATION_HORIZONTAL) diff --git a/windows/profile/userinfopane.hpp b/windows/profile/userinfopane.hpp index b29cb76..90a4d55 100644 --- a/windows/profile/userinfopane.hpp +++ b/windows/profile/userinfopane.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../../discord/objects.hpp" +#include "discord/objects.hpp" class ConnectionItem : public Gtk::EventBox { public: diff --git a/windows/profilewindow.cpp b/windows/profilewindow.cpp index f4bc3c9..4d41f89 100644 --- a/windows/profilewindow.cpp +++ b/windows/profilewindow.cpp @@ -1,5 +1,5 @@ #include "profilewindow.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" ProfileWindow::ProfileWindow(Snowflake user_id) : ID(user_id) diff --git a/windows/profilewindow.hpp b/windows/profilewindow.hpp index 044c128..3d8199b 100644 --- a/windows/profilewindow.hpp +++ b/windows/profilewindow.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/snowflake.hpp" +#include "discord/snowflake.hpp" #include "profile/userinfopane.hpp" #include "profile/mutualguildspane.hpp" #include "profile/mutualfriendspane.hpp" diff --git a/windows/threadswindow.cpp b/windows/threadswindow.cpp index b4cbac7..9071d81 100644 --- a/windows/threadswindow.cpp +++ b/windows/threadswindow.cpp @@ -1,5 +1,5 @@ #include "threadswindow.hpp" -#include "../abaddon.hpp" +#include "abaddon.hpp" ThreadsWindow::ThreadsWindow(const ChannelData &channel) : m_channel_id(channel.ID) diff --git a/windows/threadswindow.hpp b/windows/threadswindow.hpp index 28d9c7f..0e42414 100644 --- a/windows/threadswindow.hpp +++ b/windows/threadswindow.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "../discord/objects.hpp" +#include "discord/objects.hpp" class ActiveThreadsList : public Gtk::ScrolledWindow { public: From a51a54bc5979a2491f152abc47ad54e6b63f27c8 Mon Sep 17 00:00:00 2001 From: Dylam De La Torre Date: Tue, 23 Nov 2021 05:21:56 +0100 Subject: [PATCH 14/23] Restructure source and resource files (#46) importantly, res is now res/res and css is now res/css --- .github/workflows/ci.yml | 14 ++++----- .gitmodules | 12 ++++---- CMakeLists.txt | 28 +++++------------- {css => res/css}/application-low-priority.css | 0 {css => res/css}/bare.css | 0 {css => res/css}/main.css | 0 {fonts => res/fonts}/TwitterColorEmoji.ttf | Bin {fonts => res/fonts}/conf.d/10-autohint.conf | 0 .../fonts}/conf.d/10-hinting-full.conf | 0 .../fonts}/conf.d/10-hinting-medium.conf | 0 .../fonts}/conf.d/10-hinting-none.conf | 0 .../fonts}/conf.d/10-hinting-slight.conf | 0 .../fonts}/conf.d/10-no-sub-pixel.conf | 0 .../fonts}/conf.d/10-scale-bitmap-fonts.conf | 0 .../fonts}/conf.d/10-sub-pixel-bgr.conf | 0 .../fonts}/conf.d/10-sub-pixel-rgb.conf | 0 .../fonts}/conf.d/10-sub-pixel-vbgr.conf | 0 .../fonts}/conf.d/10-sub-pixel-vrgb.conf | 0 {fonts => res/fonts}/conf.d/10-unhinted.conf | 0 .../fonts}/conf.d/11-lcdfilter-default.conf | 0 .../fonts}/conf.d/11-lcdfilter-legacy.conf | 0 .../fonts}/conf.d/11-lcdfilter-light.conf | 0 .../fonts}/conf.d/20-unhint-small-vera.conf | 0 .../fonts}/conf.d/25-unhint-nonlatin.conf | 0 .../fonts}/conf.d/30-metric-aliases.conf | 0 .../fonts}/conf.d/30-urw-aliases.conf | 0 {fonts => res/fonts}/conf.d/40-nonlatin.conf | 0 {fonts => res/fonts}/conf.d/45-latin.conf | 0 {fonts => res/fonts}/conf.d/49-sansserif.conf | 0 {fonts => res/fonts}/conf.d/50-user.conf | 0 {fonts => res/fonts}/conf.d/51-local.conf | 0 .../fonts}/conf.d/55-emoji-prepend.conf | 0 {fonts => res/fonts}/conf.d/60-latin.conf | 0 .../fonts}/conf.d/65-fonts-persian.conf | 0 {fonts => res/fonts}/conf.d/65-khmer.conf | 0 {fonts => res/fonts}/conf.d/65-nonlatin.conf | 0 {fonts => res/fonts}/conf.d/69-unifont.conf | 0 .../fonts}/conf.d/70-yes-bitmaps.conf | 0 {fonts => res/fonts}/conf.d/80-delicious.conf | 0 {fonts => res/fonts}/conf.d/90-synthetic.conf | 0 {fonts => res/fonts}/fonts.template.conf | 0 res/{ => res}/battlenet.png | Bin res/{ => res}/certifiedmoderator.png | Bin res/{ => res}/checkmark.png | Bin res/{ => res}/clock.png | Bin res/{ => res}/crown.png | Bin res/{ => res}/decamarks.png | Bin res/{ => res}/discordbughunter.png | Bin res/{ => res}/discordbughunter2.png | Bin res/{ => res}/discordstaff.png | Bin res/{ => res}/earlysupporter.png | Bin res/{ => res}/earlyverifiedbotdeveloper.png | Bin res/{ => res}/emojis.bin | Bin res/{ => res}/facebook.png | Bin res/{ => res}/github.png | Bin res/{ => res}/guildsubscriber.png | Bin res/{ => res}/hypesquadbalance.png | Bin res/{ => res}/hypesquadbravery.png | Bin res/{ => res}/hypesquadbrilliance.png | Bin res/{ => res}/hypesquadevents.png | Bin res/{ => res}/leagueoflegends.png | Bin res/{ => res}/partneredowner.png | Bin res/{ => res}/premium.png | Bin res/{ => res}/reddit.png | Bin res/{ => res}/skype.png | Bin res/{ => res}/spotify.png | Bin res/{ => res}/steam.png | Bin res/{ => res}/twitch.png | Bin res/{ => res}/twitter.png | Bin res/{ => res}/typing_indicator.gif | Bin res/{ => res}/xbox.png | Bin res/{ => res}/youtube.png | Bin MurmurHash3.cpp => src/MurmurHash3.cpp | 0 MurmurHash3.h => src/MurmurHash3.h | 0 abaddon.cpp => src/abaddon.cpp | 0 abaddon.hpp => src/abaddon.hpp | 0 .../cellrendererpixbufanimation.cpp | 0 .../cellrendererpixbufanimation.hpp | 0 {components => src/components}/channels.cpp | 0 {components => src/components}/channels.hpp | 0 {components => src/components}/chatinput.cpp | 0 {components => src/components}/chatinput.hpp | 0 .../components}/chatinputindicator.cpp | 0 .../components}/chatinputindicator.hpp | 0 {components => src/components}/chatlist.cpp | 0 {components => src/components}/chatlist.hpp | 0 .../components}/chatmessage.cpp | 0 .../components}/chatmessage.hpp | 0 {components => src/components}/chatwindow.cpp | 0 {components => src/components}/chatwindow.hpp | 0 {components => src/components}/completer.cpp | 0 {components => src/components}/completer.hpp | 0 .../components}/draglistbox.cpp | 0 .../components}/draglistbox.hpp | 0 .../components}/friendslist.cpp | 0 .../components}/friendslist.hpp | 0 {components => src/components}/lazyimage.cpp | 0 {components => src/components}/lazyimage.hpp | 0 {components => src/components}/memberlist.cpp | 0 {components => src/components}/memberlist.hpp | 0 .../components}/ratelimitindicator.cpp | 0 .../components}/ratelimitindicator.hpp | 0 .../components}/statusindicator.cpp | 0 .../components}/statusindicator.hpp | 0 config.h.in => src/config.h.in | 0 constants.hpp => src/constants.hpp | 0 {dialogs => src/dialogs}/confirm.cpp | 0 {dialogs => src/dialogs}/confirm.hpp | 0 {dialogs => src/dialogs}/editmessage.cpp | 0 {dialogs => src/dialogs}/editmessage.hpp | 0 {dialogs => src/dialogs}/friendpicker.cpp | 0 {dialogs => src/dialogs}/friendpicker.hpp | 0 {dialogs => src/dialogs}/joinguild.cpp | 0 {dialogs => src/dialogs}/joinguild.hpp | 0 {dialogs => src/dialogs}/setstatus.cpp | 0 {dialogs => src/dialogs}/setstatus.hpp | 0 {dialogs => src/dialogs}/token.cpp | 0 {dialogs => src/dialogs}/token.hpp | 0 {dialogs => src/dialogs}/verificationgate.cpp | 0 {dialogs => src/dialogs}/verificationgate.hpp | 0 {discord => src/discord}/activity.cpp | 0 {discord => src/discord}/activity.hpp | 0 {discord => src/discord}/auditlog.cpp | 0 {discord => src/discord}/auditlog.hpp | 0 {discord => src/discord}/ban.cpp | 0 {discord => src/discord}/ban.hpp | 0 {discord => src/discord}/channel.cpp | 0 {discord => src/discord}/channel.hpp | 0 {discord => src/discord}/discord.cpp | 0 {discord => src/discord}/discord.hpp | 0 {discord => src/discord}/emoji.cpp | 0 {discord => src/discord}/emoji.hpp | 0 {discord => src/discord}/errors.hpp | 0 {discord => src/discord}/guild.cpp | 0 {discord => src/discord}/guild.hpp | 0 {discord => src/discord}/httpclient.cpp | 0 {discord => src/discord}/httpclient.hpp | 0 {discord => src/discord}/interactions.cpp | 0 {discord => src/discord}/interactions.hpp | 0 {discord => src/discord}/invite.cpp | 0 {discord => src/discord}/invite.hpp | 0 {discord => src/discord}/json.hpp | 0 {discord => src/discord}/member.cpp | 0 {discord => src/discord}/member.hpp | 0 {discord => src/discord}/message.cpp | 0 {discord => src/discord}/message.hpp | 0 {discord => src/discord}/objects.cpp | 0 {discord => src/discord}/objects.hpp | 0 {discord => src/discord}/permissions.cpp | 0 {discord => src/discord}/permissions.hpp | 0 {discord => src/discord}/relationship.cpp | 0 {discord => src/discord}/relationship.hpp | 0 {discord => src/discord}/role.cpp | 0 {discord => src/discord}/role.hpp | 0 {discord => src/discord}/snowflake.cpp | 0 {discord => src/discord}/snowflake.hpp | 0 {discord => src/discord}/sticker.cpp | 0 {discord => src/discord}/sticker.hpp | 0 {discord => src/discord}/store.cpp | 0 {discord => src/discord}/store.hpp | 0 {discord => src/discord}/user.cpp | 0 {discord => src/discord}/user.hpp | 0 {discord => src/discord}/usersettings.cpp | 0 {discord => src/discord}/usersettings.hpp | 0 {discord => src/discord}/webhook.cpp | 0 {discord => src/discord}/webhook.hpp | 0 {discord => src/discord}/websocket.cpp | 0 {discord => src/discord}/websocket.hpp | 0 emojis.cpp => src/emojis.cpp | 0 emojis.hpp => src/emojis.hpp | 0 filecache.cpp => src/filecache.cpp | 0 filecache.hpp => src/filecache.hpp | 0 http.cpp => src/http.cpp | 0 http.hpp => src/http.hpp | 0 imgmanager.cpp => src/imgmanager.cpp | 0 imgmanager.hpp => src/imgmanager.hpp | 0 platform.cpp => src/platform.cpp | 0 platform.hpp => src/platform.hpp | 0 settings.cpp => src/settings.cpp | 0 settings.hpp => src/settings.hpp | 0 state.cpp => src/state.cpp | 0 state.hpp => src/state.hpp | 0 util.cpp => src/util.cpp | 0 util.hpp => src/util.hpp | 0 .../windows}/guildsettings/auditlogpane.cpp | 0 .../windows}/guildsettings/auditlogpane.hpp | 0 .../windows}/guildsettings/banspane.cpp | 0 .../windows}/guildsettings/banspane.hpp | 0 .../windows}/guildsettings/emojispane.cpp | 0 .../windows}/guildsettings/emojispane.hpp | 0 .../windows}/guildsettings/infopane.cpp | 0 .../windows}/guildsettings/infopane.hpp | 0 .../windows}/guildsettings/invitespane.cpp | 0 .../windows}/guildsettings/invitespane.hpp | 0 .../windows}/guildsettings/memberspane.cpp | 0 .../windows}/guildsettings/memberspane.hpp | 0 .../windows}/guildsettings/rolespane.cpp | 0 .../windows}/guildsettings/rolespane.hpp | 0 .../windows}/guildsettingswindow.cpp | 0 .../windows}/guildsettingswindow.hpp | 0 {windows => src/windows}/mainwindow.cpp | 0 {windows => src/windows}/mainwindow.hpp | 0 {windows => src/windows}/pinnedwindow.cpp | 0 {windows => src/windows}/pinnedwindow.hpp | 0 .../windows}/profile/mutualfriendspane.cpp | 0 .../windows}/profile/mutualfriendspane.hpp | 0 .../windows}/profile/mutualguildspane.cpp | 0 .../windows}/profile/mutualguildspane.hpp | 0 .../windows}/profile/userinfopane.cpp | 0 .../windows}/profile/userinfopane.hpp | 0 {windows => src/windows}/profilewindow.cpp | 0 {windows => src/windows}/profilewindow.hpp | 0 {windows => src/windows}/threadswindow.cpp | 0 {windows => src/windows}/threadswindow.hpp | 0 .../IXWebSocket => subprojects/ixwebsocket | 0 {thirdparty => subprojects}/simpleini | 0 216 files changed, 21 insertions(+), 33 deletions(-) rename {css => res/css}/application-low-priority.css (100%) rename {css => res/css}/bare.css (100%) rename {css => res/css}/main.css (100%) rename {fonts => res/fonts}/TwitterColorEmoji.ttf (100%) rename {fonts => res/fonts}/conf.d/10-autohint.conf (100%) rename {fonts => res/fonts}/conf.d/10-hinting-full.conf (100%) rename {fonts => res/fonts}/conf.d/10-hinting-medium.conf (100%) rename {fonts => res/fonts}/conf.d/10-hinting-none.conf (100%) rename {fonts => res/fonts}/conf.d/10-hinting-slight.conf (100%) rename {fonts => res/fonts}/conf.d/10-no-sub-pixel.conf (100%) rename {fonts => res/fonts}/conf.d/10-scale-bitmap-fonts.conf (100%) rename {fonts => res/fonts}/conf.d/10-sub-pixel-bgr.conf (100%) rename {fonts => res/fonts}/conf.d/10-sub-pixel-rgb.conf (100%) rename {fonts => res/fonts}/conf.d/10-sub-pixel-vbgr.conf (100%) rename {fonts => res/fonts}/conf.d/10-sub-pixel-vrgb.conf (100%) rename {fonts => res/fonts}/conf.d/10-unhinted.conf (100%) rename {fonts => res/fonts}/conf.d/11-lcdfilter-default.conf (100%) rename {fonts => res/fonts}/conf.d/11-lcdfilter-legacy.conf (100%) rename {fonts => res/fonts}/conf.d/11-lcdfilter-light.conf (100%) rename {fonts => res/fonts}/conf.d/20-unhint-small-vera.conf (100%) rename {fonts => res/fonts}/conf.d/25-unhint-nonlatin.conf (100%) rename {fonts => res/fonts}/conf.d/30-metric-aliases.conf (100%) rename {fonts => res/fonts}/conf.d/30-urw-aliases.conf (100%) rename {fonts => res/fonts}/conf.d/40-nonlatin.conf (100%) rename {fonts => res/fonts}/conf.d/45-latin.conf (100%) rename {fonts => res/fonts}/conf.d/49-sansserif.conf (100%) rename {fonts => res/fonts}/conf.d/50-user.conf (100%) rename {fonts => res/fonts}/conf.d/51-local.conf (100%) rename {fonts => res/fonts}/conf.d/55-emoji-prepend.conf (100%) rename {fonts => res/fonts}/conf.d/60-latin.conf (100%) rename {fonts => res/fonts}/conf.d/65-fonts-persian.conf (100%) rename {fonts => res/fonts}/conf.d/65-khmer.conf (100%) rename {fonts => res/fonts}/conf.d/65-nonlatin.conf (100%) rename {fonts => res/fonts}/conf.d/69-unifont.conf (100%) rename {fonts => res/fonts}/conf.d/70-yes-bitmaps.conf (100%) rename {fonts => res/fonts}/conf.d/80-delicious.conf (100%) rename {fonts => res/fonts}/conf.d/90-synthetic.conf (100%) rename {fonts => res/fonts}/fonts.template.conf (100%) rename res/{ => res}/battlenet.png (100%) rename res/{ => res}/certifiedmoderator.png (100%) rename res/{ => res}/checkmark.png (100%) rename res/{ => res}/clock.png (100%) rename res/{ => res}/crown.png (100%) rename res/{ => res}/decamarks.png (100%) rename res/{ => res}/discordbughunter.png (100%) rename res/{ => res}/discordbughunter2.png (100%) rename res/{ => res}/discordstaff.png (100%) rename res/{ => res}/earlysupporter.png (100%) rename res/{ => res}/earlyverifiedbotdeveloper.png (100%) rename res/{ => res}/emojis.bin (100%) rename res/{ => res}/facebook.png (100%) rename res/{ => res}/github.png (100%) rename res/{ => res}/guildsubscriber.png (100%) rename res/{ => res}/hypesquadbalance.png (100%) rename res/{ => res}/hypesquadbravery.png (100%) rename res/{ => res}/hypesquadbrilliance.png (100%) rename res/{ => res}/hypesquadevents.png (100%) rename res/{ => res}/leagueoflegends.png (100%) rename res/{ => res}/partneredowner.png (100%) rename res/{ => res}/premium.png (100%) rename res/{ => res}/reddit.png (100%) rename res/{ => res}/skype.png (100%) rename res/{ => res}/spotify.png (100%) rename res/{ => res}/steam.png (100%) rename res/{ => res}/twitch.png (100%) rename res/{ => res}/twitter.png (100%) rename res/{ => res}/typing_indicator.gif (100%) rename res/{ => res}/xbox.png (100%) rename res/{ => res}/youtube.png (100%) rename MurmurHash3.cpp => src/MurmurHash3.cpp (100%) rename MurmurHash3.h => src/MurmurHash3.h (100%) rename abaddon.cpp => src/abaddon.cpp (100%) rename abaddon.hpp => src/abaddon.hpp (100%) rename {components => src/components}/cellrendererpixbufanimation.cpp (100%) rename {components => src/components}/cellrendererpixbufanimation.hpp (100%) rename {components => src/components}/channels.cpp (100%) rename {components => src/components}/channels.hpp (100%) rename {components => src/components}/chatinput.cpp (100%) rename {components => src/components}/chatinput.hpp (100%) rename {components => src/components}/chatinputindicator.cpp (100%) rename {components => src/components}/chatinputindicator.hpp (100%) rename {components => src/components}/chatlist.cpp (100%) rename {components => src/components}/chatlist.hpp (100%) rename {components => src/components}/chatmessage.cpp (100%) rename {components => src/components}/chatmessage.hpp (100%) rename {components => src/components}/chatwindow.cpp (100%) rename {components => src/components}/chatwindow.hpp (100%) rename {components => src/components}/completer.cpp (100%) rename {components => src/components}/completer.hpp (100%) rename {components => src/components}/draglistbox.cpp (100%) rename {components => src/components}/draglistbox.hpp (100%) rename {components => src/components}/friendslist.cpp (100%) rename {components => src/components}/friendslist.hpp (100%) rename {components => src/components}/lazyimage.cpp (100%) rename {components => src/components}/lazyimage.hpp (100%) rename {components => src/components}/memberlist.cpp (100%) rename {components => src/components}/memberlist.hpp (100%) rename {components => src/components}/ratelimitindicator.cpp (100%) rename {components => src/components}/ratelimitindicator.hpp (100%) rename {components => src/components}/statusindicator.cpp (100%) rename {components => src/components}/statusindicator.hpp (100%) rename config.h.in => src/config.h.in (100%) rename constants.hpp => src/constants.hpp (100%) rename {dialogs => src/dialogs}/confirm.cpp (100%) rename {dialogs => src/dialogs}/confirm.hpp (100%) rename {dialogs => src/dialogs}/editmessage.cpp (100%) rename {dialogs => src/dialogs}/editmessage.hpp (100%) rename {dialogs => src/dialogs}/friendpicker.cpp (100%) rename {dialogs => src/dialogs}/friendpicker.hpp (100%) rename {dialogs => src/dialogs}/joinguild.cpp (100%) rename {dialogs => src/dialogs}/joinguild.hpp (100%) rename {dialogs => src/dialogs}/setstatus.cpp (100%) rename {dialogs => src/dialogs}/setstatus.hpp (100%) rename {dialogs => src/dialogs}/token.cpp (100%) rename {dialogs => src/dialogs}/token.hpp (100%) rename {dialogs => src/dialogs}/verificationgate.cpp (100%) rename {dialogs => src/dialogs}/verificationgate.hpp (100%) rename {discord => src/discord}/activity.cpp (100%) rename {discord => src/discord}/activity.hpp (100%) rename {discord => src/discord}/auditlog.cpp (100%) rename {discord => src/discord}/auditlog.hpp (100%) rename {discord => src/discord}/ban.cpp (100%) rename {discord => src/discord}/ban.hpp (100%) rename {discord => src/discord}/channel.cpp (100%) rename {discord => src/discord}/channel.hpp (100%) rename {discord => src/discord}/discord.cpp (100%) rename {discord => src/discord}/discord.hpp (100%) rename {discord => src/discord}/emoji.cpp (100%) rename {discord => src/discord}/emoji.hpp (100%) rename {discord => src/discord}/errors.hpp (100%) rename {discord => src/discord}/guild.cpp (100%) rename {discord => src/discord}/guild.hpp (100%) rename {discord => src/discord}/httpclient.cpp (100%) rename {discord => src/discord}/httpclient.hpp (100%) rename {discord => src/discord}/interactions.cpp (100%) rename {discord => src/discord}/interactions.hpp (100%) rename {discord => src/discord}/invite.cpp (100%) rename {discord => src/discord}/invite.hpp (100%) rename {discord => src/discord}/json.hpp (100%) rename {discord => src/discord}/member.cpp (100%) rename {discord => src/discord}/member.hpp (100%) rename {discord => src/discord}/message.cpp (100%) rename {discord => src/discord}/message.hpp (100%) rename {discord => src/discord}/objects.cpp (100%) rename {discord => src/discord}/objects.hpp (100%) rename {discord => src/discord}/permissions.cpp (100%) rename {discord => src/discord}/permissions.hpp (100%) rename {discord => src/discord}/relationship.cpp (100%) rename {discord => src/discord}/relationship.hpp (100%) rename {discord => src/discord}/role.cpp (100%) rename {discord => src/discord}/role.hpp (100%) rename {discord => src/discord}/snowflake.cpp (100%) rename {discord => src/discord}/snowflake.hpp (100%) rename {discord => src/discord}/sticker.cpp (100%) rename {discord => src/discord}/sticker.hpp (100%) rename {discord => src/discord}/store.cpp (100%) rename {discord => src/discord}/store.hpp (100%) rename {discord => src/discord}/user.cpp (100%) rename {discord => src/discord}/user.hpp (100%) rename {discord => src/discord}/usersettings.cpp (100%) rename {discord => src/discord}/usersettings.hpp (100%) rename {discord => src/discord}/webhook.cpp (100%) rename {discord => src/discord}/webhook.hpp (100%) rename {discord => src/discord}/websocket.cpp (100%) rename {discord => src/discord}/websocket.hpp (100%) rename emojis.cpp => src/emojis.cpp (100%) rename emojis.hpp => src/emojis.hpp (100%) rename filecache.cpp => src/filecache.cpp (100%) rename filecache.hpp => src/filecache.hpp (100%) rename http.cpp => src/http.cpp (100%) rename http.hpp => src/http.hpp (100%) rename imgmanager.cpp => src/imgmanager.cpp (100%) rename imgmanager.hpp => src/imgmanager.hpp (100%) rename platform.cpp => src/platform.cpp (100%) rename platform.hpp => src/platform.hpp (100%) rename settings.cpp => src/settings.cpp (100%) rename settings.hpp => src/settings.hpp (100%) rename state.cpp => src/state.cpp (100%) rename state.hpp => src/state.hpp (100%) rename util.cpp => src/util.cpp (100%) rename util.hpp => src/util.hpp (100%) rename {windows => src/windows}/guildsettings/auditlogpane.cpp (100%) rename {windows => src/windows}/guildsettings/auditlogpane.hpp (100%) rename {windows => src/windows}/guildsettings/banspane.cpp (100%) rename {windows => src/windows}/guildsettings/banspane.hpp (100%) rename {windows => src/windows}/guildsettings/emojispane.cpp (100%) rename {windows => src/windows}/guildsettings/emojispane.hpp (100%) rename {windows => src/windows}/guildsettings/infopane.cpp (100%) rename {windows => src/windows}/guildsettings/infopane.hpp (100%) rename {windows => src/windows}/guildsettings/invitespane.cpp (100%) rename {windows => src/windows}/guildsettings/invitespane.hpp (100%) rename {windows => src/windows}/guildsettings/memberspane.cpp (100%) rename {windows => src/windows}/guildsettings/memberspane.hpp (100%) rename {windows => src/windows}/guildsettings/rolespane.cpp (100%) rename {windows => src/windows}/guildsettings/rolespane.hpp (100%) rename {windows => src/windows}/guildsettingswindow.cpp (100%) rename {windows => src/windows}/guildsettingswindow.hpp (100%) rename {windows => src/windows}/mainwindow.cpp (100%) rename {windows => src/windows}/mainwindow.hpp (100%) rename {windows => src/windows}/pinnedwindow.cpp (100%) rename {windows => src/windows}/pinnedwindow.hpp (100%) rename {windows => src/windows}/profile/mutualfriendspane.cpp (100%) rename {windows => src/windows}/profile/mutualfriendspane.hpp (100%) rename {windows => src/windows}/profile/mutualguildspane.cpp (100%) rename {windows => src/windows}/profile/mutualguildspane.hpp (100%) rename {windows => src/windows}/profile/userinfopane.cpp (100%) rename {windows => src/windows}/profile/userinfopane.hpp (100%) rename {windows => src/windows}/profilewindow.cpp (100%) rename {windows => src/windows}/profilewindow.hpp (100%) rename {windows => src/windows}/threadswindow.cpp (100%) rename {windows => src/windows}/threadswindow.hpp (100%) rename thirdparty/IXWebSocket => subprojects/ixwebsocket (100%) rename {thirdparty => subprojects}/simpleini (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7db575b..9137bbf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,9 +41,9 @@ jobs: del /f /s /q "${{ runner.workspace }}\build\.ninja_log" del /f /s /q "${{ runner.workspace }}\build\abaddon.ilk" del /f /s /q "${{ runner.workspace }}\build\CMakeCache.txt" - xcopy /E /I "${{ github.workspace }}\css" "${{ runner.workspace }}\build\css" - xcopy /E /I "${{ github.workspace }}\res" "${{ runner.workspace }}\build\res" - xcopy /E /I "${{ github.workspace }}\fonts" "${{ runner.workspace }}\build\fonts" + xcopy /E /I "${{ github.workspace }}\res\css" "${{ runner.workspace }}\build\css" + xcopy /E /I "${{ github.workspace }}\res\res" "${{ runner.workspace }}\build\res" + xcopy /E /I "${{ github.workspace }}\res\fonts" "${{ runner.workspace }}\build\fonts" mkdir "${{ runner.workspace }}\build\share" xcopy /E /I "${{ github.workspace }}\ci\gtk-for-windows\gtk-nsis-pack\share\glib-2.0" "${{ runner.workspace }}\build\share\glib-2.0" copy "${{ github.workspace }}\ci\vcpkg\installed\x64-windows\tools\glib\gspawn-win64-helper.exe" "${{ runner.workspace }}\build\gspawn-win64-helper.exe" @@ -83,8 +83,8 @@ jobs: run: | mkdir "${{ runner.workspace }}/artifactdir" cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon" - cp -r "${{ github.workspace }}/css" "${{ runner.workspace }}/artifactdir/css" - cp -r "${{ github.workspace }}/res" "${{ runner.workspace }}/artifactdir/res" + cp -r "${{ github.workspace }}/res/css" "${{ runner.workspace }}/artifactdir/css" + cp -r "${{ github.workspace }}/res/res" "${{ runner.workspace }}/artifactdir/res" - name: Upload build uses: actions/upload-artifact@v2 @@ -136,8 +136,8 @@ jobs: run: | mkdir "${{ runner.workspace }}/artifactdir" cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon" - cp -r "${{ github.workspace }}/css" "${{ runner.workspace }}/artifactdir/css" - cp -r "${{ github.workspace }}/res" "${{ runner.workspace }}/artifactdir/res" + cp -r "${{ github.workspace }}/res/css" "${{ runner.workspace }}/artifactdir/css" + cp -r "${{ github.workspace }}/res/res" "${{ runner.workspace }}/artifactdir/res" - name: Upload build uses: actions/upload-artifact@v2 diff --git a/.gitmodules b/.gitmodules index bcbf397..412fb9a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,15 @@ [submodule "vcpkg"] path = ci/vcpkg url = https://github.com/microsoft/vcpkg/ -[submodule "thirdparty/simpleini"] - path = thirdparty/simpleini - url = https://github.com/brofield/simpleini -[submodule "thirdparty/IXWebSocket"] - path = thirdparty/IXWebSocket - url = https://github.com/machinezone/ixwebsocket [submodule "ci/vcpkg"] path = ci/vcpkg url = https://github.com/microsoft/vcpkg [submodule "ci/gtk-for-windows"] path = ci/gtk-for-windows url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer +[submodule "subprojects/simpleini"] + path = subprojects/simpleini + url = https://github.com/brofield/simpleini +[submodule "subprojects/ixwebsocket"] + path = subprojects/ixwebsocket + url = https://github.com/machinezone/ixwebsocket diff --git a/CMakeLists.txt b/CMakeLists.txt index 638be3e..d27980b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(USE_OPEN_SSL TRUE) find_package(IXWebSocket QUIET) if (NOT IXWebSocket_FOUND) message("ixwebsocket was not found and will be included as a submodule") - add_subdirectory(thirdparty/IXWebSocket) + add_subdirectory(subprojects/ixwebsocket) include_directories(IXWEBSOCKET_INCLUDE_DIRS) endif() @@ -26,7 +26,7 @@ add_compile_definitions(SI_NO_CONVERSION) # only CSimpleIniA is used find_package(simpleini QUIET) if (NOT simpleini_FOUND) message("simpleini was not found and will be included as a submodule") - include_directories(thirdparty/simpleini) + include_directories(subprojects/simpleini) endif() if(MINGW OR WIN32) @@ -41,28 +41,16 @@ if(WIN32) link_libraries(${Fontconfig_LIBRARIES}) endif() -configure_file(${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h) +configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/config.h) -file(GLOB ABADDON_SOURCES - "*.h" - "*.hpp" - "*.cpp" - "discord/*.hpp" - "discord/*.cpp" - "components/*.hpp" - "components/*.cpp" - "windows/*.hpp" - "windows/*.cpp" - "windows/guildsettings/*.hpp" - "windows/guildsettings/*.cpp" - "windows/profile/*.hpp" - "windows/profile/*.cpp" - "dialogs/*.hpp" - "dialogs/*.cpp" +file(GLOB_RECURSE ABADDON_SOURCES + "src/*.h" + "src/*.hpp" + "src/*.cpp" ) add_executable(abaddon ${ABADDON_SOURCES}) -target_include_directories(abaddon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(abaddon PUBLIC ${PROJECT_SOURCE_DIR}/src) target_include_directories(abaddon PUBLIC ${PROJECT_BINARY_DIR}) target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS}) diff --git a/css/application-low-priority.css b/res/css/application-low-priority.css similarity index 100% rename from css/application-low-priority.css rename to res/css/application-low-priority.css diff --git a/css/bare.css b/res/css/bare.css similarity index 100% rename from css/bare.css rename to res/css/bare.css diff --git a/css/main.css b/res/css/main.css similarity index 100% rename from css/main.css rename to res/css/main.css diff --git a/fonts/TwitterColorEmoji.ttf b/res/fonts/TwitterColorEmoji.ttf similarity index 100% rename from fonts/TwitterColorEmoji.ttf rename to res/fonts/TwitterColorEmoji.ttf diff --git a/fonts/conf.d/10-autohint.conf b/res/fonts/conf.d/10-autohint.conf similarity index 100% rename from fonts/conf.d/10-autohint.conf rename to res/fonts/conf.d/10-autohint.conf diff --git a/fonts/conf.d/10-hinting-full.conf b/res/fonts/conf.d/10-hinting-full.conf similarity index 100% rename from fonts/conf.d/10-hinting-full.conf rename to res/fonts/conf.d/10-hinting-full.conf diff --git a/fonts/conf.d/10-hinting-medium.conf b/res/fonts/conf.d/10-hinting-medium.conf similarity index 100% rename from fonts/conf.d/10-hinting-medium.conf rename to res/fonts/conf.d/10-hinting-medium.conf diff --git a/fonts/conf.d/10-hinting-none.conf b/res/fonts/conf.d/10-hinting-none.conf similarity index 100% rename from fonts/conf.d/10-hinting-none.conf rename to res/fonts/conf.d/10-hinting-none.conf diff --git a/fonts/conf.d/10-hinting-slight.conf b/res/fonts/conf.d/10-hinting-slight.conf similarity index 100% rename from fonts/conf.d/10-hinting-slight.conf rename to res/fonts/conf.d/10-hinting-slight.conf diff --git a/fonts/conf.d/10-no-sub-pixel.conf b/res/fonts/conf.d/10-no-sub-pixel.conf similarity index 100% rename from fonts/conf.d/10-no-sub-pixel.conf rename to res/fonts/conf.d/10-no-sub-pixel.conf diff --git a/fonts/conf.d/10-scale-bitmap-fonts.conf b/res/fonts/conf.d/10-scale-bitmap-fonts.conf similarity index 100% rename from fonts/conf.d/10-scale-bitmap-fonts.conf rename to res/fonts/conf.d/10-scale-bitmap-fonts.conf diff --git a/fonts/conf.d/10-sub-pixel-bgr.conf b/res/fonts/conf.d/10-sub-pixel-bgr.conf similarity index 100% rename from fonts/conf.d/10-sub-pixel-bgr.conf rename to res/fonts/conf.d/10-sub-pixel-bgr.conf diff --git a/fonts/conf.d/10-sub-pixel-rgb.conf b/res/fonts/conf.d/10-sub-pixel-rgb.conf similarity index 100% rename from fonts/conf.d/10-sub-pixel-rgb.conf rename to res/fonts/conf.d/10-sub-pixel-rgb.conf diff --git a/fonts/conf.d/10-sub-pixel-vbgr.conf b/res/fonts/conf.d/10-sub-pixel-vbgr.conf similarity index 100% rename from fonts/conf.d/10-sub-pixel-vbgr.conf rename to res/fonts/conf.d/10-sub-pixel-vbgr.conf diff --git a/fonts/conf.d/10-sub-pixel-vrgb.conf b/res/fonts/conf.d/10-sub-pixel-vrgb.conf similarity index 100% rename from fonts/conf.d/10-sub-pixel-vrgb.conf rename to res/fonts/conf.d/10-sub-pixel-vrgb.conf diff --git a/fonts/conf.d/10-unhinted.conf b/res/fonts/conf.d/10-unhinted.conf similarity index 100% rename from fonts/conf.d/10-unhinted.conf rename to res/fonts/conf.d/10-unhinted.conf diff --git a/fonts/conf.d/11-lcdfilter-default.conf b/res/fonts/conf.d/11-lcdfilter-default.conf similarity index 100% rename from fonts/conf.d/11-lcdfilter-default.conf rename to res/fonts/conf.d/11-lcdfilter-default.conf diff --git a/fonts/conf.d/11-lcdfilter-legacy.conf b/res/fonts/conf.d/11-lcdfilter-legacy.conf similarity index 100% rename from fonts/conf.d/11-lcdfilter-legacy.conf rename to res/fonts/conf.d/11-lcdfilter-legacy.conf diff --git a/fonts/conf.d/11-lcdfilter-light.conf b/res/fonts/conf.d/11-lcdfilter-light.conf similarity index 100% rename from fonts/conf.d/11-lcdfilter-light.conf rename to res/fonts/conf.d/11-lcdfilter-light.conf diff --git a/fonts/conf.d/20-unhint-small-vera.conf b/res/fonts/conf.d/20-unhint-small-vera.conf similarity index 100% rename from fonts/conf.d/20-unhint-small-vera.conf rename to res/fonts/conf.d/20-unhint-small-vera.conf diff --git a/fonts/conf.d/25-unhint-nonlatin.conf b/res/fonts/conf.d/25-unhint-nonlatin.conf similarity index 100% rename from fonts/conf.d/25-unhint-nonlatin.conf rename to res/fonts/conf.d/25-unhint-nonlatin.conf diff --git a/fonts/conf.d/30-metric-aliases.conf b/res/fonts/conf.d/30-metric-aliases.conf similarity index 100% rename from fonts/conf.d/30-metric-aliases.conf rename to res/fonts/conf.d/30-metric-aliases.conf diff --git a/fonts/conf.d/30-urw-aliases.conf b/res/fonts/conf.d/30-urw-aliases.conf similarity index 100% rename from fonts/conf.d/30-urw-aliases.conf rename to res/fonts/conf.d/30-urw-aliases.conf diff --git a/fonts/conf.d/40-nonlatin.conf b/res/fonts/conf.d/40-nonlatin.conf similarity index 100% rename from fonts/conf.d/40-nonlatin.conf rename to res/fonts/conf.d/40-nonlatin.conf diff --git a/fonts/conf.d/45-latin.conf b/res/fonts/conf.d/45-latin.conf similarity index 100% rename from fonts/conf.d/45-latin.conf rename to res/fonts/conf.d/45-latin.conf diff --git a/fonts/conf.d/49-sansserif.conf b/res/fonts/conf.d/49-sansserif.conf similarity index 100% rename from fonts/conf.d/49-sansserif.conf rename to res/fonts/conf.d/49-sansserif.conf diff --git a/fonts/conf.d/50-user.conf b/res/fonts/conf.d/50-user.conf similarity index 100% rename from fonts/conf.d/50-user.conf rename to res/fonts/conf.d/50-user.conf diff --git a/fonts/conf.d/51-local.conf b/res/fonts/conf.d/51-local.conf similarity index 100% rename from fonts/conf.d/51-local.conf rename to res/fonts/conf.d/51-local.conf diff --git a/fonts/conf.d/55-emoji-prepend.conf b/res/fonts/conf.d/55-emoji-prepend.conf similarity index 100% rename from fonts/conf.d/55-emoji-prepend.conf rename to res/fonts/conf.d/55-emoji-prepend.conf diff --git a/fonts/conf.d/60-latin.conf b/res/fonts/conf.d/60-latin.conf similarity index 100% rename from fonts/conf.d/60-latin.conf rename to res/fonts/conf.d/60-latin.conf diff --git a/fonts/conf.d/65-fonts-persian.conf b/res/fonts/conf.d/65-fonts-persian.conf similarity index 100% rename from fonts/conf.d/65-fonts-persian.conf rename to res/fonts/conf.d/65-fonts-persian.conf diff --git a/fonts/conf.d/65-khmer.conf b/res/fonts/conf.d/65-khmer.conf similarity index 100% rename from fonts/conf.d/65-khmer.conf rename to res/fonts/conf.d/65-khmer.conf diff --git a/fonts/conf.d/65-nonlatin.conf b/res/fonts/conf.d/65-nonlatin.conf similarity index 100% rename from fonts/conf.d/65-nonlatin.conf rename to res/fonts/conf.d/65-nonlatin.conf diff --git a/fonts/conf.d/69-unifont.conf b/res/fonts/conf.d/69-unifont.conf similarity index 100% rename from fonts/conf.d/69-unifont.conf rename to res/fonts/conf.d/69-unifont.conf diff --git a/fonts/conf.d/70-yes-bitmaps.conf b/res/fonts/conf.d/70-yes-bitmaps.conf similarity index 100% rename from fonts/conf.d/70-yes-bitmaps.conf rename to res/fonts/conf.d/70-yes-bitmaps.conf diff --git a/fonts/conf.d/80-delicious.conf b/res/fonts/conf.d/80-delicious.conf similarity index 100% rename from fonts/conf.d/80-delicious.conf rename to res/fonts/conf.d/80-delicious.conf diff --git a/fonts/conf.d/90-synthetic.conf b/res/fonts/conf.d/90-synthetic.conf similarity index 100% rename from fonts/conf.d/90-synthetic.conf rename to res/fonts/conf.d/90-synthetic.conf diff --git a/fonts/fonts.template.conf b/res/fonts/fonts.template.conf similarity index 100% rename from fonts/fonts.template.conf rename to res/fonts/fonts.template.conf diff --git a/res/battlenet.png b/res/res/battlenet.png similarity index 100% rename from res/battlenet.png rename to res/res/battlenet.png diff --git a/res/certifiedmoderator.png b/res/res/certifiedmoderator.png similarity index 100% rename from res/certifiedmoderator.png rename to res/res/certifiedmoderator.png diff --git a/res/checkmark.png b/res/res/checkmark.png similarity index 100% rename from res/checkmark.png rename to res/res/checkmark.png diff --git a/res/clock.png b/res/res/clock.png similarity index 100% rename from res/clock.png rename to res/res/clock.png diff --git a/res/crown.png b/res/res/crown.png similarity index 100% rename from res/crown.png rename to res/res/crown.png diff --git a/res/decamarks.png b/res/res/decamarks.png similarity index 100% rename from res/decamarks.png rename to res/res/decamarks.png diff --git a/res/discordbughunter.png b/res/res/discordbughunter.png similarity index 100% rename from res/discordbughunter.png rename to res/res/discordbughunter.png diff --git a/res/discordbughunter2.png b/res/res/discordbughunter2.png similarity index 100% rename from res/discordbughunter2.png rename to res/res/discordbughunter2.png diff --git a/res/discordstaff.png b/res/res/discordstaff.png similarity index 100% rename from res/discordstaff.png rename to res/res/discordstaff.png diff --git a/res/earlysupporter.png b/res/res/earlysupporter.png similarity index 100% rename from res/earlysupporter.png rename to res/res/earlysupporter.png diff --git a/res/earlyverifiedbotdeveloper.png b/res/res/earlyverifiedbotdeveloper.png similarity index 100% rename from res/earlyverifiedbotdeveloper.png rename to res/res/earlyverifiedbotdeveloper.png diff --git a/res/emojis.bin b/res/res/emojis.bin similarity index 100% rename from res/emojis.bin rename to res/res/emojis.bin diff --git a/res/facebook.png b/res/res/facebook.png similarity index 100% rename from res/facebook.png rename to res/res/facebook.png diff --git a/res/github.png b/res/res/github.png similarity index 100% rename from res/github.png rename to res/res/github.png diff --git a/res/guildsubscriber.png b/res/res/guildsubscriber.png similarity index 100% rename from res/guildsubscriber.png rename to res/res/guildsubscriber.png diff --git a/res/hypesquadbalance.png b/res/res/hypesquadbalance.png similarity index 100% rename from res/hypesquadbalance.png rename to res/res/hypesquadbalance.png diff --git a/res/hypesquadbravery.png b/res/res/hypesquadbravery.png similarity index 100% rename from res/hypesquadbravery.png rename to res/res/hypesquadbravery.png diff --git a/res/hypesquadbrilliance.png b/res/res/hypesquadbrilliance.png similarity index 100% rename from res/hypesquadbrilliance.png rename to res/res/hypesquadbrilliance.png diff --git a/res/hypesquadevents.png b/res/res/hypesquadevents.png similarity index 100% rename from res/hypesquadevents.png rename to res/res/hypesquadevents.png diff --git a/res/leagueoflegends.png b/res/res/leagueoflegends.png similarity index 100% rename from res/leagueoflegends.png rename to res/res/leagueoflegends.png diff --git a/res/partneredowner.png b/res/res/partneredowner.png similarity index 100% rename from res/partneredowner.png rename to res/res/partneredowner.png diff --git a/res/premium.png b/res/res/premium.png similarity index 100% rename from res/premium.png rename to res/res/premium.png diff --git a/res/reddit.png b/res/res/reddit.png similarity index 100% rename from res/reddit.png rename to res/res/reddit.png diff --git a/res/skype.png b/res/res/skype.png similarity index 100% rename from res/skype.png rename to res/res/skype.png diff --git a/res/spotify.png b/res/res/spotify.png similarity index 100% rename from res/spotify.png rename to res/res/spotify.png diff --git a/res/steam.png b/res/res/steam.png similarity index 100% rename from res/steam.png rename to res/res/steam.png diff --git a/res/twitch.png b/res/res/twitch.png similarity index 100% rename from res/twitch.png rename to res/res/twitch.png diff --git a/res/twitter.png b/res/res/twitter.png similarity index 100% rename from res/twitter.png rename to res/res/twitter.png diff --git a/res/typing_indicator.gif b/res/res/typing_indicator.gif similarity index 100% rename from res/typing_indicator.gif rename to res/res/typing_indicator.gif diff --git a/res/xbox.png b/res/res/xbox.png similarity index 100% rename from res/xbox.png rename to res/res/xbox.png diff --git a/res/youtube.png b/res/res/youtube.png similarity index 100% rename from res/youtube.png rename to res/res/youtube.png diff --git a/MurmurHash3.cpp b/src/MurmurHash3.cpp similarity index 100% rename from MurmurHash3.cpp rename to src/MurmurHash3.cpp diff --git a/MurmurHash3.h b/src/MurmurHash3.h similarity index 100% rename from MurmurHash3.h rename to src/MurmurHash3.h diff --git a/abaddon.cpp b/src/abaddon.cpp similarity index 100% rename from abaddon.cpp rename to src/abaddon.cpp diff --git a/abaddon.hpp b/src/abaddon.hpp similarity index 100% rename from abaddon.hpp rename to src/abaddon.hpp diff --git a/components/cellrendererpixbufanimation.cpp b/src/components/cellrendererpixbufanimation.cpp similarity index 100% rename from components/cellrendererpixbufanimation.cpp rename to src/components/cellrendererpixbufanimation.cpp diff --git a/components/cellrendererpixbufanimation.hpp b/src/components/cellrendererpixbufanimation.hpp similarity index 100% rename from components/cellrendererpixbufanimation.hpp rename to src/components/cellrendererpixbufanimation.hpp diff --git a/components/channels.cpp b/src/components/channels.cpp similarity index 100% rename from components/channels.cpp rename to src/components/channels.cpp diff --git a/components/channels.hpp b/src/components/channels.hpp similarity index 100% rename from components/channels.hpp rename to src/components/channels.hpp diff --git a/components/chatinput.cpp b/src/components/chatinput.cpp similarity index 100% rename from components/chatinput.cpp rename to src/components/chatinput.cpp diff --git a/components/chatinput.hpp b/src/components/chatinput.hpp similarity index 100% rename from components/chatinput.hpp rename to src/components/chatinput.hpp diff --git a/components/chatinputindicator.cpp b/src/components/chatinputindicator.cpp similarity index 100% rename from components/chatinputindicator.cpp rename to src/components/chatinputindicator.cpp diff --git a/components/chatinputindicator.hpp b/src/components/chatinputindicator.hpp similarity index 100% rename from components/chatinputindicator.hpp rename to src/components/chatinputindicator.hpp diff --git a/components/chatlist.cpp b/src/components/chatlist.cpp similarity index 100% rename from components/chatlist.cpp rename to src/components/chatlist.cpp diff --git a/components/chatlist.hpp b/src/components/chatlist.hpp similarity index 100% rename from components/chatlist.hpp rename to src/components/chatlist.hpp diff --git a/components/chatmessage.cpp b/src/components/chatmessage.cpp similarity index 100% rename from components/chatmessage.cpp rename to src/components/chatmessage.cpp diff --git a/components/chatmessage.hpp b/src/components/chatmessage.hpp similarity index 100% rename from components/chatmessage.hpp rename to src/components/chatmessage.hpp diff --git a/components/chatwindow.cpp b/src/components/chatwindow.cpp similarity index 100% rename from components/chatwindow.cpp rename to src/components/chatwindow.cpp diff --git a/components/chatwindow.hpp b/src/components/chatwindow.hpp similarity index 100% rename from components/chatwindow.hpp rename to src/components/chatwindow.hpp diff --git a/components/completer.cpp b/src/components/completer.cpp similarity index 100% rename from components/completer.cpp rename to src/components/completer.cpp diff --git a/components/completer.hpp b/src/components/completer.hpp similarity index 100% rename from components/completer.hpp rename to src/components/completer.hpp diff --git a/components/draglistbox.cpp b/src/components/draglistbox.cpp similarity index 100% rename from components/draglistbox.cpp rename to src/components/draglistbox.cpp diff --git a/components/draglistbox.hpp b/src/components/draglistbox.hpp similarity index 100% rename from components/draglistbox.hpp rename to src/components/draglistbox.hpp diff --git a/components/friendslist.cpp b/src/components/friendslist.cpp similarity index 100% rename from components/friendslist.cpp rename to src/components/friendslist.cpp diff --git a/components/friendslist.hpp b/src/components/friendslist.hpp similarity index 100% rename from components/friendslist.hpp rename to src/components/friendslist.hpp diff --git a/components/lazyimage.cpp b/src/components/lazyimage.cpp similarity index 100% rename from components/lazyimage.cpp rename to src/components/lazyimage.cpp diff --git a/components/lazyimage.hpp b/src/components/lazyimage.hpp similarity index 100% rename from components/lazyimage.hpp rename to src/components/lazyimage.hpp diff --git a/components/memberlist.cpp b/src/components/memberlist.cpp similarity index 100% rename from components/memberlist.cpp rename to src/components/memberlist.cpp diff --git a/components/memberlist.hpp b/src/components/memberlist.hpp similarity index 100% rename from components/memberlist.hpp rename to src/components/memberlist.hpp diff --git a/components/ratelimitindicator.cpp b/src/components/ratelimitindicator.cpp similarity index 100% rename from components/ratelimitindicator.cpp rename to src/components/ratelimitindicator.cpp diff --git a/components/ratelimitindicator.hpp b/src/components/ratelimitindicator.hpp similarity index 100% rename from components/ratelimitindicator.hpp rename to src/components/ratelimitindicator.hpp diff --git a/components/statusindicator.cpp b/src/components/statusindicator.cpp similarity index 100% rename from components/statusindicator.cpp rename to src/components/statusindicator.cpp diff --git a/components/statusindicator.hpp b/src/components/statusindicator.hpp similarity index 100% rename from components/statusindicator.hpp rename to src/components/statusindicator.hpp diff --git a/config.h.in b/src/config.h.in similarity index 100% rename from config.h.in rename to src/config.h.in diff --git a/constants.hpp b/src/constants.hpp similarity index 100% rename from constants.hpp rename to src/constants.hpp diff --git a/dialogs/confirm.cpp b/src/dialogs/confirm.cpp similarity index 100% rename from dialogs/confirm.cpp rename to src/dialogs/confirm.cpp diff --git a/dialogs/confirm.hpp b/src/dialogs/confirm.hpp similarity index 100% rename from dialogs/confirm.hpp rename to src/dialogs/confirm.hpp diff --git a/dialogs/editmessage.cpp b/src/dialogs/editmessage.cpp similarity index 100% rename from dialogs/editmessage.cpp rename to src/dialogs/editmessage.cpp diff --git a/dialogs/editmessage.hpp b/src/dialogs/editmessage.hpp similarity index 100% rename from dialogs/editmessage.hpp rename to src/dialogs/editmessage.hpp diff --git a/dialogs/friendpicker.cpp b/src/dialogs/friendpicker.cpp similarity index 100% rename from dialogs/friendpicker.cpp rename to src/dialogs/friendpicker.cpp diff --git a/dialogs/friendpicker.hpp b/src/dialogs/friendpicker.hpp similarity index 100% rename from dialogs/friendpicker.hpp rename to src/dialogs/friendpicker.hpp diff --git a/dialogs/joinguild.cpp b/src/dialogs/joinguild.cpp similarity index 100% rename from dialogs/joinguild.cpp rename to src/dialogs/joinguild.cpp diff --git a/dialogs/joinguild.hpp b/src/dialogs/joinguild.hpp similarity index 100% rename from dialogs/joinguild.hpp rename to src/dialogs/joinguild.hpp diff --git a/dialogs/setstatus.cpp b/src/dialogs/setstatus.cpp similarity index 100% rename from dialogs/setstatus.cpp rename to src/dialogs/setstatus.cpp diff --git a/dialogs/setstatus.hpp b/src/dialogs/setstatus.hpp similarity index 100% rename from dialogs/setstatus.hpp rename to src/dialogs/setstatus.hpp diff --git a/dialogs/token.cpp b/src/dialogs/token.cpp similarity index 100% rename from dialogs/token.cpp rename to src/dialogs/token.cpp diff --git a/dialogs/token.hpp b/src/dialogs/token.hpp similarity index 100% rename from dialogs/token.hpp rename to src/dialogs/token.hpp diff --git a/dialogs/verificationgate.cpp b/src/dialogs/verificationgate.cpp similarity index 100% rename from dialogs/verificationgate.cpp rename to src/dialogs/verificationgate.cpp diff --git a/dialogs/verificationgate.hpp b/src/dialogs/verificationgate.hpp similarity index 100% rename from dialogs/verificationgate.hpp rename to src/dialogs/verificationgate.hpp diff --git a/discord/activity.cpp b/src/discord/activity.cpp similarity index 100% rename from discord/activity.cpp rename to src/discord/activity.cpp diff --git a/discord/activity.hpp b/src/discord/activity.hpp similarity index 100% rename from discord/activity.hpp rename to src/discord/activity.hpp diff --git a/discord/auditlog.cpp b/src/discord/auditlog.cpp similarity index 100% rename from discord/auditlog.cpp rename to src/discord/auditlog.cpp diff --git a/discord/auditlog.hpp b/src/discord/auditlog.hpp similarity index 100% rename from discord/auditlog.hpp rename to src/discord/auditlog.hpp diff --git a/discord/ban.cpp b/src/discord/ban.cpp similarity index 100% rename from discord/ban.cpp rename to src/discord/ban.cpp diff --git a/discord/ban.hpp b/src/discord/ban.hpp similarity index 100% rename from discord/ban.hpp rename to src/discord/ban.hpp diff --git a/discord/channel.cpp b/src/discord/channel.cpp similarity index 100% rename from discord/channel.cpp rename to src/discord/channel.cpp diff --git a/discord/channel.hpp b/src/discord/channel.hpp similarity index 100% rename from discord/channel.hpp rename to src/discord/channel.hpp diff --git a/discord/discord.cpp b/src/discord/discord.cpp similarity index 100% rename from discord/discord.cpp rename to src/discord/discord.cpp diff --git a/discord/discord.hpp b/src/discord/discord.hpp similarity index 100% rename from discord/discord.hpp rename to src/discord/discord.hpp diff --git a/discord/emoji.cpp b/src/discord/emoji.cpp similarity index 100% rename from discord/emoji.cpp rename to src/discord/emoji.cpp diff --git a/discord/emoji.hpp b/src/discord/emoji.hpp similarity index 100% rename from discord/emoji.hpp rename to src/discord/emoji.hpp diff --git a/discord/errors.hpp b/src/discord/errors.hpp similarity index 100% rename from discord/errors.hpp rename to src/discord/errors.hpp diff --git a/discord/guild.cpp b/src/discord/guild.cpp similarity index 100% rename from discord/guild.cpp rename to src/discord/guild.cpp diff --git a/discord/guild.hpp b/src/discord/guild.hpp similarity index 100% rename from discord/guild.hpp rename to src/discord/guild.hpp diff --git a/discord/httpclient.cpp b/src/discord/httpclient.cpp similarity index 100% rename from discord/httpclient.cpp rename to src/discord/httpclient.cpp diff --git a/discord/httpclient.hpp b/src/discord/httpclient.hpp similarity index 100% rename from discord/httpclient.hpp rename to src/discord/httpclient.hpp diff --git a/discord/interactions.cpp b/src/discord/interactions.cpp similarity index 100% rename from discord/interactions.cpp rename to src/discord/interactions.cpp diff --git a/discord/interactions.hpp b/src/discord/interactions.hpp similarity index 100% rename from discord/interactions.hpp rename to src/discord/interactions.hpp diff --git a/discord/invite.cpp b/src/discord/invite.cpp similarity index 100% rename from discord/invite.cpp rename to src/discord/invite.cpp diff --git a/discord/invite.hpp b/src/discord/invite.hpp similarity index 100% rename from discord/invite.hpp rename to src/discord/invite.hpp diff --git a/discord/json.hpp b/src/discord/json.hpp similarity index 100% rename from discord/json.hpp rename to src/discord/json.hpp diff --git a/discord/member.cpp b/src/discord/member.cpp similarity index 100% rename from discord/member.cpp rename to src/discord/member.cpp diff --git a/discord/member.hpp b/src/discord/member.hpp similarity index 100% rename from discord/member.hpp rename to src/discord/member.hpp diff --git a/discord/message.cpp b/src/discord/message.cpp similarity index 100% rename from discord/message.cpp rename to src/discord/message.cpp diff --git a/discord/message.hpp b/src/discord/message.hpp similarity index 100% rename from discord/message.hpp rename to src/discord/message.hpp diff --git a/discord/objects.cpp b/src/discord/objects.cpp similarity index 100% rename from discord/objects.cpp rename to src/discord/objects.cpp diff --git a/discord/objects.hpp b/src/discord/objects.hpp similarity index 100% rename from discord/objects.hpp rename to src/discord/objects.hpp diff --git a/discord/permissions.cpp b/src/discord/permissions.cpp similarity index 100% rename from discord/permissions.cpp rename to src/discord/permissions.cpp diff --git a/discord/permissions.hpp b/src/discord/permissions.hpp similarity index 100% rename from discord/permissions.hpp rename to src/discord/permissions.hpp diff --git a/discord/relationship.cpp b/src/discord/relationship.cpp similarity index 100% rename from discord/relationship.cpp rename to src/discord/relationship.cpp diff --git a/discord/relationship.hpp b/src/discord/relationship.hpp similarity index 100% rename from discord/relationship.hpp rename to src/discord/relationship.hpp diff --git a/discord/role.cpp b/src/discord/role.cpp similarity index 100% rename from discord/role.cpp rename to src/discord/role.cpp diff --git a/discord/role.hpp b/src/discord/role.hpp similarity index 100% rename from discord/role.hpp rename to src/discord/role.hpp diff --git a/discord/snowflake.cpp b/src/discord/snowflake.cpp similarity index 100% rename from discord/snowflake.cpp rename to src/discord/snowflake.cpp diff --git a/discord/snowflake.hpp b/src/discord/snowflake.hpp similarity index 100% rename from discord/snowflake.hpp rename to src/discord/snowflake.hpp diff --git a/discord/sticker.cpp b/src/discord/sticker.cpp similarity index 100% rename from discord/sticker.cpp rename to src/discord/sticker.cpp diff --git a/discord/sticker.hpp b/src/discord/sticker.hpp similarity index 100% rename from discord/sticker.hpp rename to src/discord/sticker.hpp diff --git a/discord/store.cpp b/src/discord/store.cpp similarity index 100% rename from discord/store.cpp rename to src/discord/store.cpp diff --git a/discord/store.hpp b/src/discord/store.hpp similarity index 100% rename from discord/store.hpp rename to src/discord/store.hpp diff --git a/discord/user.cpp b/src/discord/user.cpp similarity index 100% rename from discord/user.cpp rename to src/discord/user.cpp diff --git a/discord/user.hpp b/src/discord/user.hpp similarity index 100% rename from discord/user.hpp rename to src/discord/user.hpp diff --git a/discord/usersettings.cpp b/src/discord/usersettings.cpp similarity index 100% rename from discord/usersettings.cpp rename to src/discord/usersettings.cpp diff --git a/discord/usersettings.hpp b/src/discord/usersettings.hpp similarity index 100% rename from discord/usersettings.hpp rename to src/discord/usersettings.hpp diff --git a/discord/webhook.cpp b/src/discord/webhook.cpp similarity index 100% rename from discord/webhook.cpp rename to src/discord/webhook.cpp diff --git a/discord/webhook.hpp b/src/discord/webhook.hpp similarity index 100% rename from discord/webhook.hpp rename to src/discord/webhook.hpp diff --git a/discord/websocket.cpp b/src/discord/websocket.cpp similarity index 100% rename from discord/websocket.cpp rename to src/discord/websocket.cpp diff --git a/discord/websocket.hpp b/src/discord/websocket.hpp similarity index 100% rename from discord/websocket.hpp rename to src/discord/websocket.hpp diff --git a/emojis.cpp b/src/emojis.cpp similarity index 100% rename from emojis.cpp rename to src/emojis.cpp diff --git a/emojis.hpp b/src/emojis.hpp similarity index 100% rename from emojis.hpp rename to src/emojis.hpp diff --git a/filecache.cpp b/src/filecache.cpp similarity index 100% rename from filecache.cpp rename to src/filecache.cpp diff --git a/filecache.hpp b/src/filecache.hpp similarity index 100% rename from filecache.hpp rename to src/filecache.hpp diff --git a/http.cpp b/src/http.cpp similarity index 100% rename from http.cpp rename to src/http.cpp diff --git a/http.hpp b/src/http.hpp similarity index 100% rename from http.hpp rename to src/http.hpp diff --git a/imgmanager.cpp b/src/imgmanager.cpp similarity index 100% rename from imgmanager.cpp rename to src/imgmanager.cpp diff --git a/imgmanager.hpp b/src/imgmanager.hpp similarity index 100% rename from imgmanager.hpp rename to src/imgmanager.hpp diff --git a/platform.cpp b/src/platform.cpp similarity index 100% rename from platform.cpp rename to src/platform.cpp diff --git a/platform.hpp b/src/platform.hpp similarity index 100% rename from platform.hpp rename to src/platform.hpp diff --git a/settings.cpp b/src/settings.cpp similarity index 100% rename from settings.cpp rename to src/settings.cpp diff --git a/settings.hpp b/src/settings.hpp similarity index 100% rename from settings.hpp rename to src/settings.hpp diff --git a/state.cpp b/src/state.cpp similarity index 100% rename from state.cpp rename to src/state.cpp diff --git a/state.hpp b/src/state.hpp similarity index 100% rename from state.hpp rename to src/state.hpp diff --git a/util.cpp b/src/util.cpp similarity index 100% rename from util.cpp rename to src/util.cpp diff --git a/util.hpp b/src/util.hpp similarity index 100% rename from util.hpp rename to src/util.hpp diff --git a/windows/guildsettings/auditlogpane.cpp b/src/windows/guildsettings/auditlogpane.cpp similarity index 100% rename from windows/guildsettings/auditlogpane.cpp rename to src/windows/guildsettings/auditlogpane.cpp diff --git a/windows/guildsettings/auditlogpane.hpp b/src/windows/guildsettings/auditlogpane.hpp similarity index 100% rename from windows/guildsettings/auditlogpane.hpp rename to src/windows/guildsettings/auditlogpane.hpp diff --git a/windows/guildsettings/banspane.cpp b/src/windows/guildsettings/banspane.cpp similarity index 100% rename from windows/guildsettings/banspane.cpp rename to src/windows/guildsettings/banspane.cpp diff --git a/windows/guildsettings/banspane.hpp b/src/windows/guildsettings/banspane.hpp similarity index 100% rename from windows/guildsettings/banspane.hpp rename to src/windows/guildsettings/banspane.hpp diff --git a/windows/guildsettings/emojispane.cpp b/src/windows/guildsettings/emojispane.cpp similarity index 100% rename from windows/guildsettings/emojispane.cpp rename to src/windows/guildsettings/emojispane.cpp diff --git a/windows/guildsettings/emojispane.hpp b/src/windows/guildsettings/emojispane.hpp similarity index 100% rename from windows/guildsettings/emojispane.hpp rename to src/windows/guildsettings/emojispane.hpp diff --git a/windows/guildsettings/infopane.cpp b/src/windows/guildsettings/infopane.cpp similarity index 100% rename from windows/guildsettings/infopane.cpp rename to src/windows/guildsettings/infopane.cpp diff --git a/windows/guildsettings/infopane.hpp b/src/windows/guildsettings/infopane.hpp similarity index 100% rename from windows/guildsettings/infopane.hpp rename to src/windows/guildsettings/infopane.hpp diff --git a/windows/guildsettings/invitespane.cpp b/src/windows/guildsettings/invitespane.cpp similarity index 100% rename from windows/guildsettings/invitespane.cpp rename to src/windows/guildsettings/invitespane.cpp diff --git a/windows/guildsettings/invitespane.hpp b/src/windows/guildsettings/invitespane.hpp similarity index 100% rename from windows/guildsettings/invitespane.hpp rename to src/windows/guildsettings/invitespane.hpp diff --git a/windows/guildsettings/memberspane.cpp b/src/windows/guildsettings/memberspane.cpp similarity index 100% rename from windows/guildsettings/memberspane.cpp rename to src/windows/guildsettings/memberspane.cpp diff --git a/windows/guildsettings/memberspane.hpp b/src/windows/guildsettings/memberspane.hpp similarity index 100% rename from windows/guildsettings/memberspane.hpp rename to src/windows/guildsettings/memberspane.hpp diff --git a/windows/guildsettings/rolespane.cpp b/src/windows/guildsettings/rolespane.cpp similarity index 100% rename from windows/guildsettings/rolespane.cpp rename to src/windows/guildsettings/rolespane.cpp diff --git a/windows/guildsettings/rolespane.hpp b/src/windows/guildsettings/rolespane.hpp similarity index 100% rename from windows/guildsettings/rolespane.hpp rename to src/windows/guildsettings/rolespane.hpp diff --git a/windows/guildsettingswindow.cpp b/src/windows/guildsettingswindow.cpp similarity index 100% rename from windows/guildsettingswindow.cpp rename to src/windows/guildsettingswindow.cpp diff --git a/windows/guildsettingswindow.hpp b/src/windows/guildsettingswindow.hpp similarity index 100% rename from windows/guildsettingswindow.hpp rename to src/windows/guildsettingswindow.hpp diff --git a/windows/mainwindow.cpp b/src/windows/mainwindow.cpp similarity index 100% rename from windows/mainwindow.cpp rename to src/windows/mainwindow.cpp diff --git a/windows/mainwindow.hpp b/src/windows/mainwindow.hpp similarity index 100% rename from windows/mainwindow.hpp rename to src/windows/mainwindow.hpp diff --git a/windows/pinnedwindow.cpp b/src/windows/pinnedwindow.cpp similarity index 100% rename from windows/pinnedwindow.cpp rename to src/windows/pinnedwindow.cpp diff --git a/windows/pinnedwindow.hpp b/src/windows/pinnedwindow.hpp similarity index 100% rename from windows/pinnedwindow.hpp rename to src/windows/pinnedwindow.hpp diff --git a/windows/profile/mutualfriendspane.cpp b/src/windows/profile/mutualfriendspane.cpp similarity index 100% rename from windows/profile/mutualfriendspane.cpp rename to src/windows/profile/mutualfriendspane.cpp diff --git a/windows/profile/mutualfriendspane.hpp b/src/windows/profile/mutualfriendspane.hpp similarity index 100% rename from windows/profile/mutualfriendspane.hpp rename to src/windows/profile/mutualfriendspane.hpp diff --git a/windows/profile/mutualguildspane.cpp b/src/windows/profile/mutualguildspane.cpp similarity index 100% rename from windows/profile/mutualguildspane.cpp rename to src/windows/profile/mutualguildspane.cpp diff --git a/windows/profile/mutualguildspane.hpp b/src/windows/profile/mutualguildspane.hpp similarity index 100% rename from windows/profile/mutualguildspane.hpp rename to src/windows/profile/mutualguildspane.hpp diff --git a/windows/profile/userinfopane.cpp b/src/windows/profile/userinfopane.cpp similarity index 100% rename from windows/profile/userinfopane.cpp rename to src/windows/profile/userinfopane.cpp diff --git a/windows/profile/userinfopane.hpp b/src/windows/profile/userinfopane.hpp similarity index 100% rename from windows/profile/userinfopane.hpp rename to src/windows/profile/userinfopane.hpp diff --git a/windows/profilewindow.cpp b/src/windows/profilewindow.cpp similarity index 100% rename from windows/profilewindow.cpp rename to src/windows/profilewindow.cpp diff --git a/windows/profilewindow.hpp b/src/windows/profilewindow.hpp similarity index 100% rename from windows/profilewindow.hpp rename to src/windows/profilewindow.hpp diff --git a/windows/threadswindow.cpp b/src/windows/threadswindow.cpp similarity index 100% rename from windows/threadswindow.cpp rename to src/windows/threadswindow.cpp diff --git a/windows/threadswindow.hpp b/src/windows/threadswindow.hpp similarity index 100% rename from windows/threadswindow.hpp rename to src/windows/threadswindow.hpp diff --git a/thirdparty/IXWebSocket b/subprojects/ixwebsocket similarity index 100% rename from thirdparty/IXWebSocket rename to subprojects/ixwebsocket diff --git a/thirdparty/simpleini b/subprojects/simpleini similarity index 100% rename from thirdparty/simpleini rename to subprojects/simpleini From 4326c5e29b279ba8ca58139848aaea4e3c62fb03 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 24 Nov 2021 03:14:41 -0500 Subject: [PATCH 15/23] remove SimpleIni as a dependency use Glib::KeyFile instead which is basically the same file format also read into and save from struct once, cuz its faster and less redundant --- .gitmodules | 3 - CMakeLists.txt | 7 - README.md | 13 +- cmake/Findsimpleini.cmake | 15 -- src/abaddon.cpp | 33 ++-- src/abaddon.hpp | 5 +- src/components/channels.cpp | 14 +- src/components/chatmessage.cpp | 19 +-- src/components/friendslist.cpp | 3 +- src/components/memberlist.cpp | 6 +- src/dialogs/friendpicker.cpp | 2 +- src/discord/discord.cpp | 6 +- src/filecache.cpp | 2 +- src/settings.cpp | 176 +++++++++++----------- src/settings.hpp | 87 +++++------ src/windows/guildsettings/emojispane.cpp | 3 +- src/windows/guildsettings/infopane.cpp | 2 +- src/windows/guildsettings/memberspane.cpp | 5 +- src/windows/profile/mutualfriendspane.cpp | 3 +- src/windows/profile/mutualguildspane.cpp | 3 +- src/windows/profilewindow.cpp | 3 +- subprojects/simpleini | 1 - 22 files changed, 181 insertions(+), 230 deletions(-) delete mode 100644 cmake/Findsimpleini.cmake delete mode 160000 subprojects/simpleini diff --git a/.gitmodules b/.gitmodules index 412fb9a..65a1996 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,6 @@ [submodule "ci/gtk-for-windows"] path = ci/gtk-for-windows url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer -[submodule "subprojects/simpleini"] - path = subprojects/simpleini - url = https://github.com/brofield/simpleini [submodule "subprojects/ixwebsocket"] path = subprojects/ixwebsocket url = https://github.com/machinezone/ixwebsocket diff --git a/CMakeLists.txt b/CMakeLists.txt index d27980b..fa56d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,13 +22,6 @@ if (NOT IXWebSocket_FOUND) include_directories(IXWEBSOCKET_INCLUDE_DIRS) endif() -add_compile_definitions(SI_NO_CONVERSION) # only CSimpleIniA is used -find_package(simpleini QUIET) -if (NOT simpleini_FOUND) - message("simpleini was not found and will be included as a submodule") - include_directories(subprojects/simpleini) -endif() - if(MINGW OR WIN32) link_libraries(ws2_32) endif() diff --git a/README.md b/README.md index 7a890a8..5274c0a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Current features: ### Building manually (recommended if not on Windows): #### Windows: 1. `git clone https://github.com/uowuo/abaddon && cd abaddon` -2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows simpleini:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows` +2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows` 3. `mkdir build && cd build` 4. `cmake -G"Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=c:\path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVCPKG_TARGET_TRIPLET=x64-windows ..` 5. Build with Visual Studio @@ -75,7 +75,6 @@ On Linux, `css` and `res` can also be loaded from `~/.local/share/abaddon` or `/ * [IXWebSocket](https://github.com/machinezone/IXWebSocket) * [libcurl](https://curl.se/) * [zlib](https://zlib.net/) -* [simpleini](https://github.com/brofield/simpleini) * [SQLite3](https://www.sqlite.org/index.html) ### TODO: @@ -178,18 +177,24 @@ Used in profile popup: ### Settings Settings are configured (for now) by editing abaddon.ini +The format is similar to the standard Windows ini format **except**: +* `#` is used to begin comments as opposed to `;` +* Section and key names are case-sensitive + You should edit these while the client is closed even though there's an option to reload while running This listing is organized by section. For example, memory_db would be set by adding `memory_db = true` under the line `[discord]` #### discord +* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression +* api_base (string) - override base url for Discord API * memory_db (true or false, default false) - if true, Discord data will be kept in memory as opposed to on disk * token (string) - Discord token used to login, this can be set from the menu * prefetch (true or false, default false) - if true, new messages will cause the avatar and image attachments to be automatically downloaded #### http * user_agent (string) - sets the user-agent to use in HTTP requests to the Discord API (not including media/images) -* concurrent (int, default 10) - how many images can be concurrently retrieved +* concurrent (int, default 20) - how many images can be concurrently retrieved #### gui * member_list_discriminator (true or false, default true) - show user discriminators in the member list @@ -199,8 +204,6 @@ For example, memory_db would be set by adding `memory_db = true` under the line * animations (true or false, default true) - use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used * animated_guild_hover_only (true or false, default true) - only animate guild icons when the guild is being hovered over * owner_crown (true or false, default true) - show a crown next to the owner -* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression -* api_base (string) - override base url for Discord API #### style * linkcolor (string) - color to use for links in messages diff --git a/cmake/Findsimpleini.cmake b/cmake/Findsimpleini.cmake deleted file mode 100644 index fa6598a..0000000 --- a/cmake/Findsimpleini.cmake +++ /dev/null @@ -1,15 +0,0 @@ -set(simpleini_LIBRARY_NAME simpleini) - -find_path(simpleini_INCLUDE_DIR - NAMES SimpleIni.h - HINTS /usr/include - /usr/local/include - /opt/local/include - PATH_SUFFIXES ${simpleini_LIBRARY_NAME}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(simpleini - REQUIRED_VARS - simpleini_INCLUDE_DIR) - -mark_as_advanced(simpleini_INCLUDE_DIR) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index f0f8574..f6c9ef5 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -23,12 +23,12 @@ Abaddon::Abaddon() : m_settings(Platform::FindConfigFile()) - , m_discord(m_settings.GetUseMemoryDB()) // stupid but easy + , m_discord(GetSettings().UseMemoryDB) // stupid but easy , m_emojis(GetResPath("/emojis.bin")) { LoadFromSettings(); // todo: set user agent for non-client(?) - std::string ua = m_settings.GetUserAgent(); + std::string ua = GetSettings().UserAgent; m_discord.SetUserAgent(ua); m_discord.signal_gateway_ready().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReady)); @@ -43,7 +43,7 @@ Abaddon::Abaddon() m_discord.signal_thread_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnThreadUpdate)); m_discord.signal_message_sent().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageSent)); m_discord.signal_disconnected().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnDisconnect)); - if (m_settings.GetPrefetch()) + if (GetSettings().Prefetch) m_discord.signal_message_create().connect([this](const Message &message) { if (message.Author.HasAvatar()) m_img_mgr.Prefetch(message.Author.GetAvatarURL()); @@ -54,10 +54,6 @@ Abaddon::Abaddon() }); } -Abaddon::~Abaddon() { - m_settings.Close(); -} - Abaddon &Abaddon::Get() { static Abaddon instance; return instance; @@ -85,7 +81,7 @@ int Abaddon::StartGTK() { m_main_window->set_position(Gtk::WIN_POS_CENTER); if (!m_settings.IsValid()) { - Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be opened!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); dlg.set_position(Gtk::WIN_POS_CENTER); dlg.run(); } @@ -133,14 +129,19 @@ int Abaddon::StartGTK() { ActionReloadCSS(); - m_gtk_app->signal_shutdown().connect(sigc::mem_fun(*this, &Abaddon::StopDiscord), false); + m_gtk_app->signal_shutdown().connect(sigc::mem_fun(*this, &Abaddon::OnShutdown), false); m_main_window->show(); return m_gtk_app->run(*m_main_window); } +void Abaddon::OnShutdown() { + StopDiscord(); + m_settings.Close(); +} + void Abaddon::LoadFromSettings() { - std::string token = m_settings.GetDiscordToken(); + std::string token = GetSettings().DiscordToken; if (token.size()) { m_discord_token = token; m_discord.UpdateToken(m_discord_token); @@ -248,8 +249,8 @@ void Abaddon::DiscordOnThreadUpdate(const ThreadUpdateData &data) { } } -const SettingsManager &Abaddon::GetSettings() const { - return m_settings; +SettingsManager::Settings &Abaddon::GetSettings() { + return m_settings.GetSettings(); } Glib::RefPtr Abaddon::GetStyleProvider() { @@ -367,7 +368,7 @@ void Abaddon::SetupUserMenu() { } void Abaddon::SaveState() { - if (!m_settings.GetSaveState()) return; + if (!GetSettings().SaveState) return; AbaddonApplicationState state; state.ActiveChannel = m_main_window->GetChatActiveChannel(); @@ -387,7 +388,7 @@ void Abaddon::SaveState() { } void Abaddon::LoadState() { - if (!m_settings.GetSaveState()) return; + if (!GetSettings().SaveState) return; const auto data = ReadWholeFile(GetStateCachePath("/state.json")); if (data.empty()) return; @@ -491,7 +492,7 @@ void Abaddon::ActionSetToken() { m_discord_token = dlg.GetToken(); m_discord.UpdateToken(m_discord_token); m_main_window->UpdateComponents(); - m_settings.SetSetting("discord", "token", m_discord_token); + GetSettings().DiscordToken = m_discord_token; } } @@ -698,7 +699,7 @@ bool Abaddon::ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window) { void Abaddon::ActionReloadCSS() { try { Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider); - m_css_provider->load_from_path(GetCSSPath("/" + m_settings.GetMainCSS())); + m_css_provider->load_from_path(GetCSSPath("/" + GetSettings().MainCSS)); Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider); diff --git a/src/abaddon.hpp b/src/abaddon.hpp index 0fb4f1f..d9d0bb0 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -14,7 +14,6 @@ class Abaddon { private: Abaddon(); - ~Abaddon(); Abaddon(const Abaddon &) = delete; Abaddon &operator=(const Abaddon &) = delete; Abaddon(Abaddon &&) = delete; @@ -24,6 +23,8 @@ public: static Abaddon &Get(); int StartGTK(); + void OnShutdown(); + void StartDiscord(); void StopDiscord(); @@ -74,7 +75,7 @@ public: void DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code); void DiscordOnThreadUpdate(const ThreadUpdateData &data); - const SettingsManager &GetSettings() const; + SettingsManager::Settings &GetSettings(); Glib::RefPtr GetStyleProvider(); diff --git a/src/components/channels.cpp b/src/components/channels.cpp index da31de0..6d5e1a6 100644 --- a/src/components/channels.cpp +++ b/src/components/channels.cpp @@ -263,11 +263,9 @@ void ChannelList::UpdateGuild(Snowflake id) { const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); if (!iter || !guild.has_value()) return; - static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - (*iter)[m_columns.m_name] = "" + Glib::Markup::escape_text(guild->Name) + ""; (*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); - if (show_animations && guild->HasAnimatedIcon()) { + if (Abaddon::Get().GetSettings().ShowAnimations && guild->HasAnimatedIcon()) { const auto cb = [this, id](const Glib::RefPtr &pb) { auto iter = GetIteratorForGuildFromID(id); if (iter) (*iter)[m_columns.m_icon_anim] = pb; @@ -436,9 +434,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) { guild_row[m_columns.m_name] = "" + Glib::Markup::escape_text(guild.Name) + ""; guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); - static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - - if (show_animations && guild.HasAnimatedIcon()) { + if (Abaddon::Get().GetSettings().ShowAnimations && guild.HasAnimatedIcon()) { const auto cb = [this, id = guild.ID](const Glib::RefPtr &pb) { auto iter = GetIteratorForGuildFromID(id); if (iter) (*iter)[m_columns.m_icon_anim] = pb; @@ -998,7 +994,7 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtrmove_to(x1, y1); cr->line_to(x2, y2); cr->line_to(x3, y3); - static const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().GetChannelsExpanderColor()); + const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor); cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue()); cr->stroke(); @@ -1115,7 +1111,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtroverride_color(Gdk::RGBA(color)); title_label->set_markup("" + Glib::Markup::escape_text(*embed.Title) + ""); } @@ -798,7 +798,6 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) { int mstart, mend; if (!match.fetch_pos(0, mstart, mend)) break; const bool is_animated = match.fetch(0)[1] == 'a'; - const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart); const auto chars_end = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mend); @@ -806,7 +805,7 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) { auto end_it = buf->get_iter_at_offset(chars_end); startpos = mend; - if (is_animated && show_animations) { + if (is_animated && Abaddon::Get().GetSettings().ShowAnimations) { const auto mark_start = buf->create_mark(start_it, false); end_it.backward_char(); const auto mark_end = buf->create_mark(end_it, false); @@ -845,11 +844,8 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) { } void ChatMessageItemContainer::HandleEmojis(Gtk::TextView &tv) { - static const bool stock_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis(); - static const bool custom_emojis = Abaddon::Get().GetSettings().GetShowCustomEmojis(); - - if (stock_emojis) HandleStockEmojis(tv); - if (custom_emojis) HandleCustomEmojis(tv); + if (Abaddon::Get().GetSettings().ShowStockEmojis) HandleStockEmojis(tv); + if (Abaddon::Get().GetSettings().ShowCustomEmojis) HandleCustomEmojis(tv); } void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr buf) { @@ -969,9 +965,6 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) { auto buf = tv.get_buffer(); Glib::ustring text = GetText(buf); - // i'd like to let this be done thru css like .message-link { color: #bitch; } but idk how - static auto link_color = Abaddon::Get().GetSettings().GetLinkColor(); - int startpos = 0; Glib::MatchInfo match; while (rgx->match(text, startpos, match)) { @@ -980,7 +973,7 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) { std::string link = match.fetch(0); auto tag = buf->create_tag(); m_link_tagmap[tag] = link; - tag->property_foreground_rgba() = Gdk::RGBA(link_color); + tag->property_foreground_rgba() = Gdk::RGBA(Abaddon::Get().GetSettings().LinkColor); tag->set_property("underline", 1); // stupid workaround for vcpkg bug (i think) const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart); @@ -1138,7 +1131,7 @@ ChatMessageHeader::ChatMessageHeader(const Message &data) m_content_box_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); m_meta_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); m_avatar_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); - if (Abaddon::Get().GetSettings().GetShowAnimations()) { + if (Abaddon::Get().GetSettings().ShowAnimations) { m_content_box_ev.signal_enter_notify_event().connect(on_enter_cb); m_content_box_ev.signal_leave_notify_event().connect(on_leave_cb); m_meta_ev.signal_enter_notify_event().connect(on_enter_cb); diff --git a/src/components/friendslist.cpp b/src/components/friendslist.cpp index 3896f02..1331d19 100644 --- a/src/components/friendslist.cpp +++ b/src/components/friendslist.cpp @@ -257,8 +257,7 @@ FriendsListFriendRow::FriendsListFriendRow(RelationshipType type, const UserData auto &discord = Abaddon::Get().GetDiscordClient(); discord.signal_presence_update().connect(sigc::mem_fun(*this, &FriendsListFriendRow::OnPresenceUpdate)); - static bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - if (data.HasAnimatedAvatar() && show_animations) { + if (data.HasAnimatedAvatar() && Abaddon::Get().GetSettings().ShowAnimations) { img->SetAnimated(true); img->SetURL(data.GetAvatarURL("gif", "32")); } else { diff --git a/src/components/memberlist.cpp b/src/components/memberlist.cpp index 0c4d9bc..4f21700 100644 --- a/src/components/memberlist.cpp +++ b/src/components/memberlist.cpp @@ -14,8 +14,7 @@ MemberListUserRow::MemberListUserRow(const std::optional &guild, cons m_avatar = Gtk::manage(new LazyImage(16, 16)); m_status_indicator = Gtk::manage(new StatusIndicator(ID)); - static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown(); - if (crown && guild.has_value() && guild->OwnerID == data.ID) { + if (Abaddon::Get().GetSettings().ShowOwnerCrown && guild.has_value() && guild->OwnerID == data.ID) { try { const static auto crown_path = Abaddon::GetResPath("/crown.png"); auto pixbuf = Gdk::Pixbuf::create_from_file(crown_path, 12, 12); @@ -40,9 +39,8 @@ MemberListUserRow::MemberListUserRow(const std::optional &guild, cons m_label->set_single_line_mode(true); m_label->set_ellipsize(Pango::ELLIPSIZE_END); - static bool show_discriminator = Abaddon::Get().GetSettings().GetShowMemberListDiscriminators(); std::string display = data.Username; - if (show_discriminator) + if (Abaddon::Get().GetSettings().ShowMemberListDiscriminators) display += "#" + data.Discriminator; if (guild.has_value()) { if (const auto col_id = data.GetHoistedRole(guild->ID, true); col_id.IsValid()) { diff --git a/src/dialogs/friendpicker.cpp b/src/dialogs/friendpicker.cpp index fc099aa..476e5f6 100644 --- a/src/dialogs/friendpicker.cpp +++ b/src/dialogs/friendpicker.cpp @@ -67,7 +67,7 @@ FriendPickerDialogItem::FriendPickerDialogItem(Snowflake user_id) m_name.set_single_line_mode(true); m_avatar.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(32); - if (user.HasAnimatedAvatar() && Abaddon::Get().GetSettings().GetShowAnimations()) { + if (user.HasAnimatedAvatar() && Abaddon::Get().GetSettings().ShowAnimations) { auto cb = [this](const Glib::RefPtr &pb) { m_avatar.property_pixbuf_animation() = pb; }; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index bed959f..f920099 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1303,13 +1303,11 @@ void DiscordClient::HandleGatewayHello(const GatewayMessage &msg) { // perhaps this should be set by the main class std::string DiscordClient::GetAPIURL() { - static const auto url = Abaddon::Get().GetSettings().GetAPIBaseURL(); - return url; + return Abaddon::Get().GetSettings().APIBaseURL; } std::string DiscordClient::GetGatewayURL() { - static const auto url = Abaddon::Get().GetSettings().GetGatewayURL(); - return url; + return Abaddon::Get().GetSettings().GatewayURL; } DiscordError DiscordClient::GetCodeFromResponse(const http::response_type &response) { diff --git a/src/filecache.cpp b/src/filecache.cpp index a731750..e04fbcb 100644 --- a/src/filecache.cpp +++ b/src/filecache.cpp @@ -142,7 +142,7 @@ void FileCacheWorkerThread::loop() { m_cv.wait(lock); } - static const auto concurrency = static_cast(Abaddon::Get().GetSettings().GetCacheHTTPConcurrency()); + static const auto concurrency = static_cast(Abaddon::Get().GetSettings().CacheHTTPConcurrency); if (m_handles.size() < concurrency) { std::optional entry; m_queue_mutex.lock(); diff --git a/src/settings.cpp b/src/settings.cpp index 0a7dbb7..beef624 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2,7 +2,7 @@ #include #include -SettingsManager::SettingsManager(std::string filename) +SettingsManager::SettingsManager(std::string_view filename) : m_filename(filename) { if (!std::filesystem::exists(filename)) { std::fstream fs; @@ -10,106 +10,106 @@ SettingsManager::SettingsManager(std::string filename) fs.close(); } - auto rc = m_ini.LoadFile(filename.c_str()); - m_ok = rc == SI_OK; + try { + m_ok = m_file.load_from_file(m_filename, Glib::KEY_FILE_KEEP_COMMENTS); + } catch (const Glib::Error &e) { + fprintf(stderr, "error opening settings KeyFile: %s\n", e.what().c_str()); + m_ok = false; + } + + if (m_ok) ReadSettings(); } -void SettingsManager::Reload() { - m_ok = m_ini.LoadFile(m_filename.c_str()) == SI_OK; -} +void SettingsManager::ReadSettings() { +#define SMBOOL(section, key, var) \ + try { \ + m_settings.var = m_file.get_boolean(section, key); \ + } catch (...) {} +#define SMSTR(section, key, var) \ + try { \ + m_settings.var = m_file.get_string(section, key); \ + } catch (...) {} +#define SMINT(section, key, var) \ + try { \ + m_settings.var = m_file.get_integer(section, key); \ + } catch (...) {} -std::string SettingsManager::GetSettingString(const std::string §ion, const std::string &key, std::string fallback) const { - return m_ini.GetValue(section.c_str(), key.c_str(), fallback.c_str()); -} + SMSTR("discord", "api_base", APIBaseURL); + SMSTR("discord", "gateway", GatewayURL); + SMSTR("discord", "token", DiscordToken); + SMBOOL("discord", "memory_db", UseMemoryDB); + SMBOOL("discord", "prefetch", Prefetch); + SMSTR("gui", "css", MainCSS); + SMBOOL("gui", "animated_guild_hover_only", AnimatedGuildHoverOnly); + SMBOOL("gui", "animations", ShowAnimations); + SMBOOL("gui", "custom_emojis", ShowCustomEmojis); + SMBOOL("gui", "member_list_discriminator", ShowMemberListDiscriminators); + SMBOOL("gui", "owner_crown", ShowOwnerCrown); + SMBOOL("gui", "save_state", SaveState); + SMBOOL("gui", "stock_emojis", ShowStockEmojis); + SMINT("http", "concurrent", CacheHTTPConcurrency); + SMSTR("http", "user_agent", UserAgent); + SMSTR("style", "expandercolor", ChannelsExpanderColor); + SMSTR("style", "linkcolor", LinkColor); + SMSTR("style", "nsfwchannelcolor", NSFWChannelColor); -int SettingsManager::GetSettingInt(const std::string §ion, const std::string &key, int fallback) const { - return std::stoul(GetSettingString(section, key, std::to_string(fallback))); -} +#undef SMBOOL +#undef SMSTR +#undef SMINT -bool SettingsManager::GetSettingBool(const std::string §ion, const std::string &key, bool fallback) const { - return GetSettingString(section, key, fallback ? "true" : "false") != "false"; + m_read_settings = m_settings; } bool SettingsManager::IsValid() const { return m_ok; } +SettingsManager::Settings &SettingsManager::GetSettings() { + return m_settings; +} + void SettingsManager::Close() { - m_ini.SaveFile(m_filename.c_str()); -} + if (m_ok) { + // save anything that changed + // (futureproofing since only DiscordToken can actually change) +#define SMSTR(section, key, var) \ + if (m_settings.var != m_read_settings.var) \ + m_file.set_string(section, key, m_settings.var); +#define SMBOOL(section, key, var) \ + if (m_settings.var != m_read_settings.var) \ + m_file.set_boolean(section, key, m_settings.var); +#define SMINT(section, key, var) \ + if (m_settings.var != m_read_settings.var) \ + m_file.set_integer(section, key, m_settings.var); -bool SettingsManager::GetUseMemoryDB() const { - return GetSettingBool("discord", "memory_db", false); -} + SMSTR("discord", "api_base", APIBaseURL); + SMSTR("discord", "gateway", GatewayURL); + SMSTR("discord", "token", DiscordToken); + SMBOOL("discord", "memory_db", UseMemoryDB); + SMBOOL("discord", "prefetch", Prefetch); + SMSTR("gui", "css", MainCSS); + SMBOOL("gui", "animated_guild_hover_only", AnimatedGuildHoverOnly); + SMBOOL("gui", "animations", ShowAnimations); + SMBOOL("gui", "custom_emojis", ShowCustomEmojis); + SMBOOL("gui", "member_list_discriminator", ShowMemberListDiscriminators); + SMBOOL("gui", "owner_crown", ShowOwnerCrown); + SMBOOL("gui", "save_state", SaveState); + SMBOOL("gui", "stock_emojis", ShowStockEmojis); + SMINT("http", "concurrent", CacheHTTPConcurrency); + SMSTR("http", "user_agent", UserAgent); + SMSTR("style", "expandercolor", ChannelsExpanderColor); + SMSTR("style", "linkcolor", LinkColor); + SMSTR("style", "nsfwchannelcolor", NSFWChannelColor); -std::string SettingsManager::GetUserAgent() const { - return GetSettingString("http", "user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"); -} +#undef SMSTR +#undef SMBOOL +#undef SMINT -std::string SettingsManager::GetDiscordToken() const { - return GetSettingString("discord", "token"); -} - -bool SettingsManager::GetShowMemberListDiscriminators() const { - return GetSettingBool("gui", "member_list_discriminator", true); -} - -bool SettingsManager::GetShowStockEmojis() const { -#ifdef _WIN32 - return GetSettingBool("gui", "stock_emojis", false); -#else - return GetSettingBool("gui", "stock_emojis", true); -#endif -} - -bool SettingsManager::GetShowCustomEmojis() const { - return GetSettingBool("gui", "custom_emojis", true); -} - -std::string SettingsManager::GetLinkColor() const { - return GetSettingString("style", "linkcolor", "rgba(40, 200, 180, 255)"); -} - -std::string SettingsManager::GetChannelsExpanderColor() const { - return GetSettingString("style", "expandercolor", "rgba(255, 83, 112, 255)"); -} - -std::string SettingsManager::GetNSFWChannelColor() const { - return GetSettingString("style", "nsfwchannelcolor", "#ed6666"); -} - -int SettingsManager::GetCacheHTTPConcurrency() const { - return GetSettingInt("http", "concurrent", 20); -} - -bool SettingsManager::GetPrefetch() const { - return GetSettingBool("discord", "prefetch", false); -} - -std::string SettingsManager::GetMainCSS() const { - return GetSettingString("gui", "css", "main.css"); -} - -bool SettingsManager::GetShowAnimations() const { - return GetSettingBool("gui", "animations", true); -} - -bool SettingsManager::GetShowOwnerCrown() const { - return GetSettingBool("gui", "owner_crown", true); -} - -std::string SettingsManager::GetGatewayURL() const { - return GetSettingString("discord", "gateway", "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"); -} - -std::string SettingsManager::GetAPIBaseURL() const { - return GetSettingString("discord", "api_base", "https://discord.com/api/v9"); -} - -bool SettingsManager::GetAnimatedGuildHoverOnly() const { - return GetSettingBool("gui", "animated_guild_hover_only", true); -} - -bool SettingsManager::GetSaveState() const { - return GetSettingBool("gui", "save_state", true); + try { + if (!m_file.save_to_file(m_filename)) + fputs("failed to save settings KeyFile", stderr); + } catch (const Glib::Error &e) { + fprintf(stderr, "failed to save settings KeyFile: %s\n", e.what().c_str()); + } + } } diff --git a/src/settings.hpp b/src/settings.hpp index 3fff593..1192861 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -1,62 +1,55 @@ #pragma once #include #include -#include +#include class SettingsManager { public: - SettingsManager(std::string filename); - void Reload(); + struct Settings { + // [discord] + std::string APIBaseURL { "https://discord.com/api/v9" }; + std::string GatewayURL { "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream" }; + std::string DiscordToken; + bool UseMemoryDB { false }; + bool Prefetch { false }; + + // [gui] + std::string MainCSS { "main.css" }; + bool AnimatedGuildHoverOnly { true }; + bool ShowAnimations { true }; + bool ShowCustomEmojis { true }; + bool ShowMemberListDiscriminators { true }; + bool ShowOwnerCrown { true }; + bool SaveState { true }; +#ifdef _WIN32 + bool ShowStockEmojis { false }; +#else + bool ShowStockEmojis { true }; +#endif + + // [http] + int CacheHTTPConcurrency { 20 }; + std::string UserAgent { "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36" }; + + // [style] + // TODO: convert to StyleProperty + std::string LinkColor { "rgba(40, 200, 180, 255)" }; + std::string ChannelsExpanderColor { "rgba(255, 83, 112, 255)" }; + std::string NSFWChannelColor { "#ed6666" }; + }; + + SettingsManager(std::string_view filename); void Close(); - bool GetUseMemoryDB() const; - std::string GetUserAgent() const; - std::string GetDiscordToken() const; - bool GetShowMemberListDiscriminators() const; - bool GetShowStockEmojis() const; - bool GetShowCustomEmojis() const; - int GetCacheHTTPConcurrency() const; - bool GetPrefetch() const; - std::string GetMainCSS() const; - bool GetShowAnimations() const; - bool GetShowOwnerCrown() const; - std::string GetGatewayURL() const; - std::string GetAPIBaseURL() const; - bool GetAnimatedGuildHoverOnly() const; - bool GetSaveState() const; - - // i would like to use Gtk::StyleProperty for this, but it will not work on windows - // #1 it's missing from the project files for the version used by vcpkg - // #2 it's still broken and doesn't function even when added to the solution - // #3 it's a massive pain in the ass to try and bump the version to a functioning version - // because they switch build systems to nmake/meson (took months to get merged in vcpkg) - // #4 c++ build systems sucks - // three options are: use gtk4 with updated vcpkg, try and port it myself, or use msys2 instead of vcpkg - // im leaning towards msys - std::string GetLinkColor() const; - std::string GetChannelsExpanderColor() const; - std::string GetNSFWChannelColor() const; - bool IsValid() const; - - template - void SetSetting(std::string section, std::string key, T value) { - m_ini.SetValue(section.c_str(), key.c_str(), std::to_string(value).c_str()); - m_ini.SaveFile(m_filename.c_str()); - } - - void SetSetting(std::string section, std::string key, std::string value) { - m_ini.SetValue(section.c_str(), key.c_str(), value.c_str()); - m_ini.SaveFile(m_filename.c_str()); - } + Settings &GetSettings(); private: - std::string GetSettingString(const std::string §ion, const std::string &key, std::string fallback = "") const; - int GetSettingInt(const std::string §ion, const std::string &key, int fallback) const; - bool GetSettingBool(const std::string §ion, const std::string &key, bool fallback) const; + void ReadSettings(); -private: bool m_ok; std::string m_filename; - CSimpleIniA m_ini; + Glib::KeyFile m_file; + Settings m_settings; + Settings m_read_settings; }; diff --git a/src/windows/guildsettings/emojispane.cpp b/src/windows/guildsettings/emojispane.cpp index 1f4bfa9..57b697c 100644 --- a/src/windows/guildsettings/emojispane.cpp +++ b/src/windows/guildsettings/emojispane.cpp @@ -130,8 +130,7 @@ void GuildSettingsEmojisPane::AddEmojiRow(const EmojiData &emoji) { else row[m_columns.m_col_available] = "Yes"; - static bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - if (show_animations && emoji.IsAnimated.has_value() && *emoji.IsAnimated) { + if (Abaddon::Get().GetSettings().ShowAnimations && emoji.IsAnimated.has_value() && *emoji.IsAnimated) { const auto cb = [this, id = emoji.ID](const Glib::RefPtr &pb) { for (auto &row : m_model->children()) { if (static_cast(row[m_columns.m_col_id]) == id) { diff --git a/src/windows/guildsettings/infopane.cpp b/src/windows/guildsettings/infopane.cpp index b4f75f3..9ef116f 100644 --- a/src/windows/guildsettings/infopane.cpp +++ b/src/windows/guildsettings/infopane.cpp @@ -81,7 +81,7 @@ GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id) void GuildSettingsInfoPane::FetchGuildIcon(const GuildData &guild) { m_guild_icon.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(32); if (guild.HasIcon()) { - if (Abaddon::Get().GetSettings().GetShowAnimations() && guild.HasAnimatedIcon()) { + if (Abaddon::Get().GetSettings().ShowAnimations && guild.HasAnimatedIcon()) { auto cb = [this](const Glib::RefPtr &pixbuf) { m_guild_icon.property_pixbuf_animation() = pixbuf; }; diff --git a/src/windows/guildsettings/memberspane.cpp b/src/windows/guildsettings/memberspane.cpp index 36c5c0b..9dc76d3 100644 --- a/src/windows/guildsettings/memberspane.cpp +++ b/src/windows/guildsettings/memberspane.cpp @@ -99,7 +99,7 @@ GuildSettingsMembersListItem::GuildSettingsMembersListItem(const GuildData &guil auto &discord = Abaddon::Get().GetDiscordClient(); - if (member.User->HasAnimatedAvatar() && Abaddon::Get().GetSettings().GetShowAnimations()) + if (member.User->HasAnimatedAvatar() && Abaddon::Get().GetSettings().ShowAnimations) m_avatar.SetURL(member.User->GetAvatarURL("gif", "32")); else m_avatar.SetURL(member.User->GetAvatarURL("png", "32")); @@ -113,8 +113,7 @@ GuildSettingsMembersListItem::GuildSettingsMembersListItem(const GuildData &guil discord.signal_guild_member_update().connect(sigc::track_obj(member_update_cb, *this)); UpdateColor(); - static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown(); - if (crown && guild.OwnerID == member.User->ID) { + if (Abaddon::Get().GetSettings().ShowOwnerCrown && guild.OwnerID == member.User->ID) { try { const static auto crown_path = Abaddon::GetResPath("/crown.png"); auto pixbuf = Gdk::Pixbuf::create_from_file(crown_path, 12, 12); diff --git a/src/windows/profile/mutualfriendspane.cpp b/src/windows/profile/mutualfriendspane.cpp index 339fd71..ca36e1d 100644 --- a/src/windows/profile/mutualfriendspane.cpp +++ b/src/windows/profile/mutualfriendspane.cpp @@ -9,10 +9,9 @@ MutualFriendItem::MutualFriendItem(const UserData &user) m_avatar.set_margin_end(10); - const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); auto &img = Abaddon::Get().GetImageManager(); m_avatar.property_pixbuf() = img.GetPlaceholder(24); - if (user.HasAnimatedAvatar() && show_animations) { + if (user.HasAnimatedAvatar() && Abaddon::Get().GetSettings().ShowAnimations) { auto cb = [this](const Glib::RefPtr &pb) { m_avatar.property_pixbuf_animation() = pb; }; diff --git a/src/windows/profile/mutualguildspane.cpp b/src/windows/profile/mutualguildspane.cpp index 6bfdc7b..6c14fc4 100644 --- a/src/windows/profile/mutualguildspane.cpp +++ b/src/windows/profile/mutualguildspane.cpp @@ -13,11 +13,10 @@ MutualGuildItem::MutualGuildItem(const MutualGuildData &guild) // discord will return info (id + nick) for "deleted" guilds from this endpoint. strange ! const auto data = Abaddon::Get().GetDiscordClient().GetGuild(guild.ID); if (data.has_value()) { - const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); auto &img = Abaddon::Get().GetImageManager(); m_icon.property_pixbuf() = img.GetPlaceholder(24); if (data->HasIcon()) { - if (data->HasAnimatedIcon() && show_animations) { + if (data->HasAnimatedIcon() && Abaddon::Get().GetSettings().ShowAnimations) { auto cb = [this](const Glib::RefPtr &pb) { m_icon.property_pixbuf_animation() = pb; }; diff --git a/src/windows/profilewindow.cpp b/src/windows/profilewindow.cpp index 4d41f89..9d93564 100644 --- a/src/windows/profilewindow.cpp +++ b/src/windows/profilewindow.cpp @@ -44,7 +44,6 @@ ProfileWindow::ProfileWindow(Snowflake user_id) return false; }); - static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); auto &img = Abaddon::Get().GetImageManager(); m_avatar.property_pixbuf() = img.GetPlaceholder(64); auto icon_cb = [this](const Glib::RefPtr &pb) { @@ -52,7 +51,7 @@ ProfileWindow::ProfileWindow(Snowflake user_id) }; img.LoadFromURL(user.GetAvatarURL("png", "64"), sigc::track_obj(icon_cb, *this)); - if (show_animations && user.HasAnimatedAvatar()) { + if (Abaddon::Get().GetSettings().ShowAnimations && user.HasAnimatedAvatar()) { auto cb = [this](const Glib::RefPtr &pb) { m_avatar.property_pixbuf_animation() = pb; }; diff --git a/subprojects/simpleini b/subprojects/simpleini deleted file mode 160000 index 67156f6..0000000 --- a/subprojects/simpleini +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 67156f64b3447ce1eb81d6be44d29132fb49b70a From 8f30bb33a3d79a7207809b35d4a39751e4d53123 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 24 Nov 2021 03:29:29 -0500 Subject: [PATCH 16/23] fix build --- src/settings.cpp | 2 +- src/settings.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index beef624..6820ed0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2,7 +2,7 @@ #include #include -SettingsManager::SettingsManager(std::string_view filename) +SettingsManager::SettingsManager(const std::string &filename) : m_filename(filename) { if (!std::filesystem::exists(filename)) { std::fstream fs; diff --git a/src/settings.hpp b/src/settings.hpp index 1192861..ca2303f 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -38,7 +38,7 @@ public: std::string NSFWChannelColor { "#ed6666" }; }; - SettingsManager(std::string_view filename); + SettingsManager(const std::string &filename); void Close(); bool IsValid() const; From 069c22e9cd37158fa50e3fac67d4d6938d173554 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:31:34 -0500 Subject: [PATCH 17/23] add fetching private archived threads --- src/discord/discord.cpp | 13 +++++++++++++ src/discord/discord.hpp | 1 + src/windows/threadswindow.cpp | 5 +++-- src/windows/threadswindow.hpp | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index f920099..a90fc50 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -292,6 +292,19 @@ void DiscordClient::GetArchivedPublicThreads(Snowflake channel_id, sigc::slot callback) { + m_http.MakeGET("/channels/" + std::to_string(channel_id) + "/users/@me/threads/archived/private", [this, callback](const http::response_type &r) { + if (CheckCode(r)) { + const auto data = nlohmann::json::parse(r.text).get(); + for (const auto &thread : data.Threads) + m_store.SetChannel(thread.ID, thread); + callback(DiscordError::NONE, data); + } else { + callback(GetCodeFromResponse(r), {}); + } + }); +} + bool DiscordClient::IsThreadJoined(Snowflake thread_id) const { return std::find(m_joined_threads.begin(), m_joined_threads.end(), thread_id) != m_joined_threads.end(); } diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 4b9bc82..4010977 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -81,6 +81,7 @@ public: std::vector GetUsersInThread(Snowflake id) const; std::vector GetActiveThreads(Snowflake channel_id) const; void GetArchivedPublicThreads(Snowflake channel_id, sigc::slot callback); + void GetArchivedPrivateThreads(Snowflake channel_id, sigc::slot callback); bool IsThreadJoined(Snowflake thread_id) const; bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const; diff --git a/src/windows/threadswindow.cpp b/src/windows/threadswindow.cpp index 9071d81..c18819b 100644 --- a/src/windows/threadswindow.cpp +++ b/src/windows/threadswindow.cpp @@ -129,14 +129,15 @@ ArchivedThreadsList::ArchivedThreadsList(const ChannelData &channel, const Gtk:: return false; }); - Abaddon::Get().GetDiscordClient().GetArchivedPublicThreads(channel.ID, sigc::mem_fun(*this, &ArchivedThreadsList::OnPublicFetched)); + Abaddon::Get().GetDiscordClient().GetArchivedPublicThreads(channel.ID, sigc::mem_fun(*this, &ArchivedThreadsList::OnThreadsFetched)); + Abaddon::Get().GetDiscordClient().GetArchivedPrivateThreads(channel.ID, sigc::mem_fun(*this, &ArchivedThreadsList::OnThreadsFetched)); } void ArchivedThreadsList::InvalidateFilter() { m_list.invalidate_filter(); } -void ArchivedThreadsList::OnPublicFetched(DiscordError code, const ArchivedThreadsResponseData &data) { +void ArchivedThreadsList::OnThreadsFetched(DiscordError code, const ArchivedThreadsResponseData &data) { for (const auto &thread : data.Threads) { auto row = Gtk::manage(new ThreadListRow(thread)); row->show(); diff --git a/src/windows/threadswindow.hpp b/src/windows/threadswindow.hpp index 0e42414..ebcbd13 100644 --- a/src/windows/threadswindow.hpp +++ b/src/windows/threadswindow.hpp @@ -27,7 +27,7 @@ public: private: Gtk::ListBox m_list; - void OnPublicFetched(DiscordError code, const ArchivedThreadsResponseData &data); + void OnThreadsFetched(DiscordError code, const ArchivedThreadsResponseData &data); using type_signal_thread_open = sigc::signal; type_signal_thread_open m_signal_thread_open; From c40208ac660fa0b5dbc6dc10a0702abfb156bbb9 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 24 Nov 2021 22:24:40 -0500 Subject: [PATCH 18/23] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5274c0a..8588abd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Alternative Discord client made in C++ with GTK -[😎Discord Server](https://discord.gg/wkCU3vuzG5) + Current features: * Not Electron From fb3d69c5e796512edd72dc4ebc81fdfb32f7d40d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 24 Nov 2021 22:36:39 -0500 Subject: [PATCH 19/23] bump build number in identify --- src/discord/discord.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index a90fc50..801282c 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -2003,7 +2003,7 @@ void DiscordClient::SendIdentify() { msg.Properties.ReferrerCurrent = ""; msg.Properties.ReferringDomainCurrent = ""; msg.Properties.ReleaseChannel = "stable"; - msg.Properties.ClientBuildNumber = 91734; + msg.Properties.ClientBuildNumber = 105691; msg.Properties.ClientEventSource = ""; msg.Presence.Status = "online"; msg.Presence.Since = 0; From 0da913cd4a76aae1ca41abe5b1a51874c974d3aa Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 25 Nov 2021 02:32:23 -0500 Subject: [PATCH 20/23] remove unnecessary copying left over from debugging --- src/discord/store.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/discord/store.cpp b/src/discord/store.cpp index 9b615fd..63fb5f7 100644 --- a/src/discord/store.cpp +++ b/src/discord/store.cpp @@ -2159,8 +2159,7 @@ Store::Database::type_signal_close Store::Database::signal_close() { Store::Statement::Statement(Database &db, const char *command) : m_db(&db) { if (m_db->SetError(sqlite3_prepare_v2(m_db->obj(), command, -1, &m_stmt, nullptr)) != SQLITE_OK) return; - std::string tmp = command; - m_db->signal_close().connect([tmp, this] { + m_db->signal_close().connect([this] { sqlite3_finalize(m_stmt); m_stmt = nullptr; }); From 8c72d4c18d9c27ce1b5cd20f0cb98e5638c8becf Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 25 Nov 2021 02:57:11 -0500 Subject: [PATCH 21/23] dont print identify message to console mainly since i feel its only a matter of time before someone copy pastes it somewhere and itd be my fault also typedef -> using --- src/discord/discord.cpp | 3 +++ src/discord/websocket.cpp | 11 ++++++++++- src/discord/websocket.hpp | 11 ++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 801282c..83db97b 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -2012,7 +2012,10 @@ void DiscordClient::SendIdentify() { msg.ClientState.HighestLastMessageID = "0"; msg.ClientState.ReadStateVersion = 0; msg.ClientState.UserGuildSettingsVersion = -1; + const bool b = m_websocket.GetPrintMessages(); + m_websocket.SetPrintMessages(false); m_websocket.Send(msg); + m_websocket.SetPrintMessages(b); } void DiscordClient::SendResume() { diff --git a/src/discord/websocket.cpp b/src/discord/websocket.cpp index ff50cd3..c7e43e9 100644 --- a/src/discord/websocket.cpp +++ b/src/discord/websocket.cpp @@ -15,6 +15,14 @@ void Websocket::SetUserAgent(std::string agent) { m_agent = agent; } +bool Websocket::GetPrintMessages() const noexcept { + return m_print_messages; +} + +void Websocket::SetPrintMessages(bool show) noexcept { + m_print_messages = show; +} + void Websocket::Stop() { Stop(ix::WebSocketCloseConstants::kNormalClosureCode); } @@ -29,7 +37,8 @@ bool Websocket::IsOpen() const { } void Websocket::Send(const std::string &str) { - printf("sending %s\n", str.c_str()); + if (m_print_messages) + printf("sending %s\n", str.c_str()); m_websocket.sendText(str); } diff --git a/src/discord/websocket.hpp b/src/discord/websocket.hpp index e6a6489..26cd5d4 100644 --- a/src/discord/websocket.hpp +++ b/src/discord/websocket.hpp @@ -13,6 +13,9 @@ public: void SetUserAgent(std::string agent); + bool GetPrintMessages() const noexcept; + void SetPrintMessages(bool show) noexcept; + void Send(const std::string &str); void Send(const nlohmann::json &j); void Stop(); @@ -26,9 +29,9 @@ private: std::string m_agent; public: - typedef sigc::signal type_signal_open; - typedef sigc::signal type_signal_close; - typedef sigc::signal type_signal_message; + using type_signal_open = sigc::signal; + using type_signal_close = sigc::signal; + using type_signal_message = sigc::signal; type_signal_open signal_open(); type_signal_close signal_close(); @@ -38,4 +41,6 @@ private: type_signal_open m_signal_open; type_signal_close m_signal_close; type_signal_message m_signal_message; + + bool m_print_messages = true; }; From 192b043e7ac60bb06fbb25b2e46ef096b48c16fd Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 28 Nov 2021 22:40:41 -0500 Subject: [PATCH 22/23] fix distortion of non-1:1 emojis --- src/components/chatmessage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/chatmessage.cpp b/src/components/chatmessage.cpp index 9514c8a..ef972bb 100644 --- a/src/components/chatmessage.cpp +++ b/src/components/chatmessage.cpp @@ -834,7 +834,9 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) { buf->delete_mark(mark_start); buf->delete_mark(mark_end); auto it = buf->erase(start_it, end_it); - buf->insert_pixbuf(it, pixbuf->scale_simple(EmojiSize, EmojiSize, Gdk::INTERP_BILINEAR)); + int width, height; + GetImageDimensions(pixbuf->get_width(), pixbuf->get_height(), width, height, EmojiSize, EmojiSize); + buf->insert_pixbuf(it, pixbuf->scale_simple(width, height, Gdk::INTERP_BILINEAR)); }; img.LoadFromURL(EmojiData::URLFromID(match.fetch(2)), sigc::track_obj(cb, tv)); } From e02107feea8214a045e6faa969f00dcbc0d2b072 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 28 Nov 2021 22:42:55 -0500 Subject: [PATCH 23/23] actually retrieve roles for guilds FetchRoles isnt needed anymore cuz full roles are fetched now --- src/discord/discord.cpp | 4 ++- src/discord/guild.cpp | 15 ------------ src/discord/guild.hpp | 3 +-- src/discord/store.cpp | 30 ++++++++++++++++++++--- src/discord/store.hpp | 2 ++ src/windows/guildsettings/memberspane.cpp | 7 +++--- src/windows/guildsettings/rolespane.cpp | 25 ++++++++++--------- 7 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 83db97b..b678de0 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -720,7 +720,9 @@ void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk:: } void DiscordClient::ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot callback) { - const auto roles = GetGuild(guild_id)->FetchRoles(); + const auto guild = GetGuild(guild_id); + if (!guild.has_value() || !guild->Roles.has_value()) return; + const auto &roles = *guild->Roles; if (static_cast(position) > roles.size()) return; // gay and makes you send every role in between new and old position constexpr auto IDX_MAX = ~size_t { 0 }; diff --git a/src/discord/guild.cpp b/src/discord/guild.cpp index a02b896..966bd44 100644 --- a/src/discord/guild.cpp +++ b/src/discord/guild.cpp @@ -188,21 +188,6 @@ std::vector GuildData::GetSortedChannels(Snowflake ignore) const { return ret; } -std::vector GuildData::FetchRoles() const { - if (!Roles.has_value()) return {}; - std::vector ret; - ret.reserve(Roles->size()); - for (const auto thing : *Roles) { - auto r = Abaddon::Get().GetDiscordClient().GetRole(thing.ID); - if (r.has_value()) - ret.push_back(*r); - } - std::sort(ret.begin(), ret.end(), [](const RoleData &a, const RoleData &b) -> bool { - return a.Position > b.Position; - }); - return ret; -} - void from_json(const nlohmann::json &j, GuildApplicationData &m) { JS_D("user_id", m.UserID); JS_D("guild_id", m.GuildID); diff --git a/src/discord/guild.hpp b/src/discord/guild.hpp index 3c3828d..51b5a01 100644 --- a/src/discord/guild.hpp +++ b/src/discord/guild.hpp @@ -50,7 +50,7 @@ struct GuildData { std::optional VerificationLevel; std::optional DefaultMessageNotifications; std::optional ExplicitContentFilter; - std::optional> Roles; // only access id + std::optional> Roles; std::optional> Emojis; // only access id std::optional> Features; std::optional MFALevel; @@ -96,5 +96,4 @@ struct GuildData { bool HasAnimatedIcon() const; std::string GetIconURL(std::string ext = "png", std::string size = "32") const; std::vector GetSortedChannels(Snowflake ignore = Snowflake::Invalid) const; - std::vector FetchRoles() const; // sorted }; diff --git a/src/discord/store.cpp b/src/discord/store.cpp index 63fb5f7..1cb7231 100644 --- a/src/discord/store.cpp +++ b/src/discord/store.cpp @@ -765,6 +765,16 @@ std::optional Store::GetGuild(Snowflake id) const { s->Reset(); } + { + auto &s = m_stmt_get_guild_roles; + s->Bind(1, id); + r.Roles.emplace(); + while (s->FetchOne()) { + r.Roles->push_back(GetRoleBound(s)); + } + s->Reset(); + } + return r; } @@ -961,9 +971,17 @@ std::optional Store::GetRole(Snowflake id) const { return {}; } + auto role = GetRoleBound(s); + + s->Reset(); + + return role; +} + +RoleData Store::GetRoleBound(std::unique_ptr &s) const { RoleData r; - r.ID = id; + s->Get(0, r.ID); //s->Get(1, guild id); s->Get(2, r.Name); s->Get(3, r.Color); @@ -973,8 +991,6 @@ std::optional Store::GetRole(Snowflake id) const { s->Get(7, r.IsManaged); s->Get(8, r.IsMentionable); - s->Reset(); - return r; } @@ -1726,6 +1742,14 @@ bool Store::CreateStatements() { return false; } + m_stmt_get_guild_roles = std::make_unique(m_db, R"( + SELECT * FROM roles WHERE guild = ? + )"); + if (!m_stmt_get_guild_roles->OK()) { + fprintf(stderr, "failed to prepare get guild roles statement: %s\n", m_db.ErrStr()); + return false; + } + m_stmt_set_emoji = std::make_unique(m_db, R"( REPLACE INTO emojis VALUES ( ?, ?, ?, ?, ?, ?, ? diff --git a/src/discord/store.hpp b/src/discord/store.hpp index 80e2407..715f280 100644 --- a/src/discord/store.hpp +++ b/src/discord/store.hpp @@ -235,6 +235,7 @@ private: }; Message GetMessageBound(std::unique_ptr &stmt) const; + RoleData GetRoleBound(std::unique_ptr &stmt) const; void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction); @@ -264,6 +265,7 @@ private: STMT(get_member); STMT(set_role); STMT(get_role); + STMT(get_guild_roles); STMT(set_emoji); STMT(get_emoji); STMT(set_perm); diff --git a/src/windows/guildsettings/memberspane.cpp b/src/windows/guildsettings/memberspane.cpp index 9dc76d3..bda92b4 100644 --- a/src/windows/guildsettings/memberspane.cpp +++ b/src/windows/guildsettings/memberspane.cpp @@ -238,9 +238,10 @@ GuildSettingsMembersPaneRoles::GuildSettingsMembersPaneRoles(Snowflake guild_id) discord.signal_role_delete().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleDelete)); const auto guild = *discord.GetGuild(guild_id); - const auto roles = guild.FetchRoles(); - for (const auto &role : roles) { - CreateRow(can_modify, role, guild.OwnerID == self_id); + if (guild.Roles.has_value()) { + for (const auto &role : *guild.Roles) { + CreateRow(can_modify, role, guild.OwnerID == self_id); + } } m_list.set_sort_func([this](Gtk::ListBoxRow *a, Gtk::ListBoxRow *b) -> int { diff --git a/src/windows/guildsettings/rolespane.cpp b/src/windows/guildsettings/rolespane.cpp index 8d355ee..3567e95 100644 --- a/src/windows/guildsettings/rolespane.cpp +++ b/src/windows/guildsettings/rolespane.cpp @@ -79,19 +79,20 @@ GuildSettingsRolesPaneRoles::GuildSettingsRolesPaneRoles(Snowflake guild_id) discord.signal_role_delete().connect(sigc::mem_fun(*this, &GuildSettingsRolesPaneRoles::OnRoleDelete)); const auto guild = *discord.GetGuild(GuildID); - const auto roles = guild.FetchRoles(); const bool can_modify = discord.HasGuildPermission(discord.GetUserData().ID, GuildID, Permission::MANAGE_ROLES); - for (const auto &role : roles) { - auto *row = Gtk::manage(new GuildSettingsRolesPaneRolesListItem(guild, role)); - row->drag_source_set(g_target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); - row->set_margin_start(5); - row->set_halign(Gtk::ALIGN_FILL); - row->show(); - m_rows[role.ID] = row; - if (can_modify) - m_list.add_draggable(row); - else - m_list.add(*row); + if (guild.Roles.has_value()) { + for (const auto &role : *guild.Roles) { + auto *row = Gtk::manage(new GuildSettingsRolesPaneRolesListItem(guild, role)); + row->drag_source_set(g_target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); + row->set_margin_start(5); + row->set_halign(Gtk::ALIGN_FILL); + row->show(); + m_rows[role.ID] = row; + if (can_modify) + m_list.add_draggable(row); + else + m_list.add(*row); + } } m_list.set_sort_func([this](Gtk::ListBoxRow *rowa_, Gtk::ListBoxRow *rowb_) -> int {