progress 2

This commit is contained in:
ouwou 2020-11-23 20:34:09 -05:00
parent deb482a8db
commit 0b8c83eaa1
19 changed files with 700 additions and 271 deletions

View File

@ -16,7 +16,8 @@
Abaddon::Abaddon()
: m_settings("abaddon.ini")
, m_emojis("res/emojis.bin") {
, m_emojis("res/emojis.bin")
, m_discord(m_settings.GetSettingBool("discord", "memory_db", false)) { // stupid but easy
LoadFromSettings();
m_discord.signal_gateway_ready().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReady));
@ -360,7 +361,7 @@ void Abaddon::ActionChatDeleteMessage(Snowflake channel_id, Snowflake id) {
}
void Abaddon::ActionChatEditMessage(Snowflake channel_id, Snowflake id) {
const auto *msg = m_discord.GetMessage(id);
const auto msg = m_discord.GetMessage(id);
EditMessageDialog dlg(*m_main_window);
dlg.SetContent(msg->Content);
auto response = dlg.run();

View File

@ -87,6 +87,8 @@ protected:
void on_user_menu_open_dm();
private:
SettingsManager m_settings;
DiscordClient m_discord;
std::string m_discord_token;
// todo make these map snowflake to attribs
@ -101,6 +103,5 @@ private:
mutable std::mutex m_mutex;
Glib::RefPtr<Gtk::Application> m_gtk_app;
Glib::RefPtr<Gtk::CssProvider> m_css_provider;
SettingsManager m_settings;
std::unique_ptr<MainWindow> m_main_window; // wah wah cant create a gtkstylecontext fuck you
};

View File

@ -215,7 +215,7 @@ ChannelList::ChannelList() {
// maybe will regret doing it this way
auto &discord = Abaddon::Get().GetDiscordClient();
discord.signal_message_create().connect(sigc::track_obj([this, &discord](Snowflake message_id) {
const auto *message = discord.GetMessage(message_id);
const auto message = discord.GetMessage(message_id);
const auto *channel = discord.GetChannel(message->ChannelID);
if (channel == nullptr) return;
if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM)

View File

@ -33,22 +33,22 @@ ChatMessageItemContainer::ChatMessageItemContainer() {
}
ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(Snowflake id) {
const auto *data = Abaddon::Get().GetDiscordClient().GetMessage(id);
if (data == nullptr) return nullptr;
const auto data = Abaddon::Get().GetDiscordClient().GetMessage(id);
if (!data.has_value()) return nullptr;
auto *container = Gtk::manage(new ChatMessageItemContainer);
container->ID = data->ID;
container->ChannelID = data->ChannelID;
if (data->Content.size() > 0 || data->Type != MessageType::DEFAULT) {
container->m_text_component = container->CreateTextComponent(data);
container->m_text_component = container->CreateTextComponent(&*data);
container->AttachGuildMenuHandler(container->m_text_component);
container->m_main->add(*container->m_text_component);
}
// there should only ever be 1 embed (i think?)
if (data->Embeds.size() == 1) {
container->m_embed_component = container->CreateEmbedComponent(data);
container->m_embed_component = container->CreateEmbedComponent(&*data);
container->AttachGuildMenuHandler(container->m_embed_component);
container->m_main->add(*container->m_embed_component);
}
@ -83,7 +83,7 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(Snowflake id) {
// this doesnt rly make sense
void ChatMessageItemContainer::UpdateContent() {
const auto *data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
const auto data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (m_text_component != nullptr)
UpdateTextComponent(m_text_component);
@ -93,7 +93,7 @@ void ChatMessageItemContainer::UpdateContent() {
}
if (data->Embeds.size() == 1) {
m_embed_component = CreateEmbedComponent(data);
m_embed_component = CreateEmbedComponent(&*data);
if (m_embed_imgurl.size() > 0) {
m_signal_image_load.emit(m_embed_imgurl);
}
@ -115,15 +115,19 @@ void ChatMessageItemContainer::UpdateImage(std::string url, Glib::RefPtr<Gdk::Pi
auto it = m_img_loadmap.find(url);
if (it != m_img_loadmap.end()) {
int w, h;
GetImageDimensions(it->second.second.Width, it->second.second.Height, w, h);
it->second.first->property_pixbuf() = buf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
const auto inw = it->second.second.Width;
const auto inh = it->second.second.Height;
if (inw.has_value() && inh.has_value()) {
int w, h;
GetImageDimensions(*inw, *inh, w, h);
it->second.first->property_pixbuf() = buf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
}
}
}
void ChatMessageItemContainer::UpdateAttributes() {
const auto *data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (data == nullptr) return;
const auto data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (!data.has_value()) return;
const bool deleted = data->IsDeleted();
const bool edited = data->IsEdited();
@ -176,9 +180,8 @@ Gtk::TextView *ChatMessageItemContainer::CreateTextComponent(const Message *data
}
void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
const auto *data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (data == nullptr)
return;
const auto data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (!data.has_value()) return;
auto b = tv->get_buffer();
b->set_text("");
@ -208,21 +211,21 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const Message *data)
Gtk::Box *main = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
const auto &embed = data->Embeds[0];
if (embed.Author.Name.length() > 0) {
if (embed.Author.has_value() && embed.Author->Name.has_value()) {
auto *author_lbl = Gtk::manage(new Gtk::Label);
author_lbl->set_halign(Gtk::ALIGN_START);
author_lbl->set_line_wrap(true);
author_lbl->set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
author_lbl->set_hexpand(false);
author_lbl->set_text(embed.Author.Name);
author_lbl->set_text(*embed.Author->Name);
author_lbl->get_style_context()->add_class("embed-author");
main->pack_start(*author_lbl);
}
if (embed.Title.length() > 0) {
if (embed.Title.has_value()) {
auto *title_label = Gtk::manage(new Gtk::Label);
title_label->set_use_markup(true);
title_label->set_markup("<b>" + Glib::Markup::escape_text(embed.Title) + "</b>");
title_label->set_markup("<b>" + Glib::Markup::escape_text(*embed.Title) + "</b>");
title_label->set_halign(Gtk::ALIGN_CENTER);
title_label->set_hexpand(false);
title_label->get_style_context()->add_class("embed-title");
@ -233,9 +236,9 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const Message *data)
main->pack_start(*title_label);
}
if (embed.Description.length() > 0) {
if (embed.Description.has_value()) {
auto *desc_label = Gtk::manage(new Gtk::Label);
desc_label->set_text(embed.Description);
desc_label->set_text(*embed.Description);
desc_label->set_line_wrap(true);
desc_label->set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
desc_label->set_max_width_chars(50);
@ -246,7 +249,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const Message *data)
}
// todo: handle inline fields
if (embed.Fields.size() > 0) {
if (embed.Fields.has_value() && embed.Fields->size() > 0) {
auto *flow = Gtk::manage(new Gtk::FlowBox);
flow->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
flow->set_min_children_per_line(3);
@ -257,7 +260,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const Message *data)
flow->set_selection_mode(Gtk::SELECTION_NONE);
main->pack_start(*flow);
for (const auto &field : embed.Fields) {
for (const auto &field : *embed.Fields) {
auto *field_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
auto *field_lbl = Gtk::manage(new Gtk::Label);
auto *field_val = Gtk::manage(new Gtk::Label);
@ -287,42 +290,44 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const Message *data)
}
}
bool is_img = embed.Image.URL.size() > 0;
bool is_thumb = embed.Thumbnail.URL.size() > 0;
if (is_img || is_thumb) {
auto *img = Gtk::manage(new Gtk::Image);
img->set_halign(Gtk::ALIGN_CENTER);
int w, h;
if (is_img)
GetImageDimensions(embed.Image.Width, embed.Image.Height, w, h, 200, 150);
else
GetImageDimensions(embed.Thumbnail.Width, embed.Thumbnail.Height, w, h, 200, 150);
img->set_size_request(w, h);
main->pack_start(*img);
m_embed_img = img;
if (is_img)
m_embed_imgurl = embed.Image.ProxyURL;
else
m_embed_imgurl = embed.Thumbnail.ProxyURL;
Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &ChatMessageItemContainer::EmitImageLoad), m_embed_imgurl));
if (embed.Image.has_value()) {
bool is_img = embed.Image->URL.has_value();
bool is_thumb = embed.Thumbnail.has_value();
if (is_img || is_thumb) {
auto *img = Gtk::manage(new Gtk::Image);
img->set_halign(Gtk::ALIGN_CENTER);
int w = 0, h = 0;
if (is_img)
GetImageDimensions(*embed.Image->Width, *embed.Image->Height, w, h, 200, 150);
else
GetImageDimensions(*embed.Thumbnail->Width, *embed.Thumbnail->Height, w, h, 200, 150);
img->set_size_request(w, h);
main->pack_start(*img);
m_embed_img = img;
if (is_img)
m_embed_imgurl = *embed.Image->ProxyURL;
else
m_embed_imgurl = *embed.Thumbnail->ProxyURL;
Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &ChatMessageItemContainer::EmitImageLoad), m_embed_imgurl));
}
}
if (embed.Footer.Text.length() > 0) {
if (embed.Footer.has_value()) {
auto *footer_lbl = Gtk::manage(new Gtk::Label);
footer_lbl->set_halign(Gtk::ALIGN_START);
footer_lbl->set_line_wrap(true);
footer_lbl->set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
footer_lbl->set_hexpand(false);
footer_lbl->set_text(embed.Footer.Text);
footer_lbl->set_text(embed.Footer->Text);
footer_lbl->get_style_context()->add_class("embed-footer");
main->pack_start(*footer_lbl);
}
auto style = main->get_style_context();
if (embed.Color != -1) {
if (embed.Color.has_value()) {
auto provider = Gtk::CssProvider::create(); // this seems wrong
std::string css = ".embed { border-left: 2px solid #" + IntToCSSColor(embed.Color) + "; }";
std::string css = ".embed { border-left: 2px solid #" + IntToCSSColor(*embed.Color) + "; }";
provider->load_from_data(css);
style->add_provider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
@ -343,7 +348,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const Message *data)
Gtk::Widget *ChatMessageItemContainer::CreateImageComponent(const AttachmentData &data) {
int w, h;
GetImageDimensions(data.Width, data.Height, w, h);
GetImageDimensions(*data.Width, *data.Height, w, h);
Gtk::EventBox *ev = Gtk::manage(new Gtk::EventBox);
Gtk::Image *widget = Gtk::manage(new Gtk::Image);
@ -429,8 +434,8 @@ void ChatMessageItemContainer::HandleUserMentions(Gtk::TextView *tv) {
replacement = "<b>@" + Glib::Markup::escape_text(user->Username) + "#" + user->Discriminator + "</b>";
else {
const auto role_id = user->GetHoistedRole(channel->GuildID, true);
const auto *role = discord.GetRole(role_id);
if (role == nullptr)
const auto role = discord.GetRole(role_id);
if (!role.has_value())
replacement = "<b>@" + Glib::Markup::escape_text(user->Username) + "#" + user->Discriminator + "</b>";
else
replacement = "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">@" + Glib::Markup::escape_text(user->Username) + "#" + user->Discriminator + "</span></b>";
@ -658,7 +663,7 @@ bool ChatMessageItemContainer::OnLinkClick(GdkEventButton *ev) {
void ChatMessageItemContainer::ShowMenu(GdkEvent *event) {
const auto &client = Abaddon::Get().GetDiscordClient();
const auto *data = client.GetMessage(ID);
const auto data = client.GetMessage(ID);
if (data->IsDeleted()) {
m_menu_delete_message->set_sensitive(false);
m_menu_edit_message->set_sensitive(false);
@ -685,8 +690,8 @@ void ChatMessageItemContainer::on_menu_edit_message() {
}
void ChatMessageItemContainer::on_menu_copy_content() {
const auto *msg = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (msg != nullptr)
const auto msg = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (msg.has_value())
Gtk::Clipboard::get()->set_text(msg->Content);
}
@ -731,13 +736,18 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
m_timestamp = Gtk::manage(new Gtk::Label);
m_avatar_ev = Gtk::manage(new Gtk::EventBox);
const auto author = Abaddon::Get().GetDiscordClient().GetUser(UserID);
auto &img = Abaddon::Get().GetImageManager();
auto buf = img.GetFromURLIfCached(data->Author.GetAvatarURL());
Glib::RefPtr<Gdk::Pixbuf> buf;
if (author.has_value())
buf = img.GetFromURLIfCached(author->GetAvatarURL());
if (buf)
m_avatar = Gtk::manage(new Gtk::Image(buf));
else {
m_avatar = Gtk::manage(new Gtk::Image(img.GetPlaceholder(32)));
img.LoadFromURL(data->Author.GetAvatarURL(), sigc::mem_fun(*this, &ChatMessageHeader::OnAvatarLoad));
if (author.has_value())
img.LoadFromURL(author->GetAvatarURL(), sigc::mem_fun(*this, &ChatMessageHeader::OnAvatarLoad));
}
get_style_context()->add_class("message-container");
@ -816,10 +826,10 @@ void ChatMessageHeader::UpdateNameColor() {
const auto role_id = discord.GetMemberHoistedRole(guild_id, UserID, true);
const auto user = discord.GetUser(UserID);
if (!user.has_value()) return;
const auto *role = discord.GetRole(role_id);
const auto role = discord.GetRole(role_id);
std::string md;
if (role != nullptr)
if (role.has_value())
md = "<span weight='bold' color='#" + IntToCSSColor(role->Color) + "'>" + Glib::Markup::escape_text(user->Username) + "</span>";
else
md = "<span weight='bold' color='#eeeeee'>" + Glib::Markup::escape_text(user->Username) + "</span>";

View File

@ -157,8 +157,8 @@ ChatMessageItemContainer *ChatWindow::CreateMessageComponent(Snowflake id) {
void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
const auto &client = Abaddon::Get().GetDiscordClient();
if (!client.IsStarted()) return; // e.g. load channel and then dc
const auto *data = client.GetMessage(id);
if (data == nullptr) return;
const auto data = client.GetMessage(id);
if (!data.has_value()) return;
ChatMessageHeader *last_row = nullptr;
bool should_attach = false;
@ -182,7 +182,7 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
const auto user = client.GetUser(user_id);
if (!user.has_value()) return;
header = Gtk::manage(new ChatMessageHeader(data));
header = Gtk::manage(new ChatMessageHeader(&*data));
header->signal_action_insert_mention().connect([this, user_id]() {
m_signal_action_insert_mention.emit(user_id);
});

View File

@ -111,7 +111,7 @@ void MemberList::UpdateMemberListInternal() {
}
// process all the shit first so its in proper order
std::map<int, const Role *> pos_to_role;
std::map<int, Role> pos_to_role;
std::map<int, std::vector<User>> pos_to_users;
std::unordered_map<Snowflake, int> user_to_color;
std::vector<Snowflake> roleless_users;
@ -128,19 +128,15 @@ void MemberList::UpdateMemberListInternal() {
auto pos_role = discord.GetRole(pos_role_id);
auto col_role = discord.GetRole(col_role_id);
if (pos_role == nullptr) {
if (!pos_role.has_value()) {
roleless_users.push_back(id);
continue;
};
pos_to_role[pos_role->Position] = pos_role;
pos_to_role[pos_role->Position] = *pos_role;
pos_to_users[pos_role->Position].push_back(std::move(*user));
if (col_role != nullptr) {
if (ColorDistance(col_role->Color, 0xFFFFFF) < 15)
user_to_color[id] = 0x000000;
else
user_to_color[id] = col_role->Color;
}
if (col_role.has_value())
user_to_color[id] = col_role->Color;
}
auto add_user = [this, &user_to_color](const User *data) {
@ -191,9 +187,9 @@ void MemberList::UpdateMemberListInternal() {
for (auto it = pos_to_role.crbegin(); it != pos_to_role.crend(); it++) {
auto pos = it->first;
auto role = it->second;
const auto &role = it->second;
add_role(role->Name);
add_role(role.Name);
if (pos_to_users.find(pos) == pos_to_users.end()) continue;

View File

@ -42,6 +42,6 @@ void Channel::update_from_json(const nlohmann::json &j) {
JS_RD("last_pin_timestamp", LastPinTimestamp);
}
const PermissionOverwrite *Channel::GetOverwrite(Snowflake id) const {
std::optional<PermissionOverwrite> Channel::GetOverwrite(Snowflake id) const {
return Abaddon::Get().GetDiscordClient().GetPermissionOverwrite(ID, id);
}

View File

@ -39,5 +39,5 @@ struct Channel {
friend void from_json(const nlohmann::json &j, Channel &m);
void update_from_json(const nlohmann::json &j);
const PermissionOverwrite *GetOverwrite(Snowflake id) const;
std::optional<PermissionOverwrite> GetOverwrite(Snowflake id) const;
};

View File

@ -2,9 +2,10 @@
#include <cassert>
#include "../util.hpp"
DiscordClient::DiscordClient()
DiscordClient::DiscordClient(bool mem_store)
: m_http(DiscordAPI)
, m_decompress_buf(InflateChunkSize) {
, m_decompress_buf(InflateChunkSize)
, m_store(mem_store) {
m_msg_dispatch.connect(sigc::mem_fun(*this, &DiscordClient::MessageDispatch));
m_websocket.signal_message().connect(sigc::mem_fun(*this, &DiscordClient::HandleGatewayMessageRaw));
@ -164,7 +165,7 @@ void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake
});
}
const Message *DiscordClient::GetMessage(Snowflake id) const {
std::optional<Message> DiscordClient::GetMessage(Snowflake id) const {
return m_store.GetMessage(id);
}
@ -176,7 +177,7 @@ std::optional<User> DiscordClient::GetUser(Snowflake id) const {
return m_store.GetUser(id);
}
const Role *DiscordClient::GetRole(Snowflake id) const {
std::optional<Role> DiscordClient::GetRole(Snowflake id) const {
return m_store.GetRole(id);
}
@ -188,7 +189,7 @@ const GuildMember *DiscordClient::GetMember(Snowflake user_id, Snowflake guild_i
return m_store.GetGuildMemberData(guild_id, user_id);
}
const PermissionOverwrite *DiscordClient::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const {
std::optional<PermissionOverwrite> DiscordClient::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const {
return m_store.GetPermissionOverwrite(channel_id, id);
}
@ -200,22 +201,22 @@ Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user
auto *data = m_store.GetGuildMemberData(guild_id, user_id);
if (data == nullptr) return Snowflake::Invalid;
std::vector<const Role *> roles;
std::vector<Role> roles;
for (const auto &id : data->Roles) {
auto *role = GetRole(id);
if (role != nullptr) {
const auto role = GetRole(id);
if (role.has_value()) {
if (role->IsHoisted || (with_color && role->Color != 0))
roles.push_back(role);
roles.push_back(*role);
}
}
if (roles.size() == 0) return Snowflake::Invalid;
std::sort(roles.begin(), roles.end(), [this](const Role *a, const Role *b) -> bool {
return a->Position > b->Position;
std::sort(roles.begin(), roles.end(), [this](const Role &a, const Role &b) -> bool {
return a.Position > b.Position;
});
return roles[0]->ID;
return roles[0].ID;
}
Snowflake DiscordClient::GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const {
@ -226,9 +227,9 @@ Snowflake DiscordClient::GetMemberHighestRole(Snowflake guild_id, Snowflake user
if (data->Roles.size() == 1) return data->Roles[0];
return *std::max(data->Roles.begin(), data->Roles.end(), [this](const auto &a, const auto &b) -> bool {
const auto *role_a = GetRole(*a);
const auto *role_b = GetRole(*b);
if (role_a == nullptr || role_b == nullptr) return false; // for some reason a Snowflake(0) sneaks into here
const auto role_a = GetRole(*a);
const auto role_b = GetRole(*b);
if (!role_a.has_value() || !role_b.has_value()) return false; // for some reason a Snowflake(0) sneaks into here
return role_a->Position < role_b->Position;
});
}
@ -241,14 +242,6 @@ std::unordered_set<Snowflake> DiscordClient::GetUsersInGuild(Snowflake id) const
return std::unordered_set<Snowflake>();
}
std::unordered_set<Snowflake> DiscordClient::GetRolesInGuild(Snowflake id) const {
std::unordered_set<Snowflake> ret;
const auto &roles = m_store.GetRoles();
for (const auto &[rid, rdata] : roles)
ret.insert(rid);
return ret;
}
std::unordered_set<Snowflake> DiscordClient::GetChannelsInGuild(Snowflake id) const {
auto it = m_guild_to_channels.find(id);
if (it != m_guild_to_channels.end())
@ -278,14 +271,14 @@ Permission DiscordClient::ComputePermissions(Snowflake member_id, Snowflake guil
if (guild->OwnerID == member_id)
return Permission::ALL;
const auto *everyone = GetRole(guild_id);
if (everyone == nullptr)
const auto everyone = GetRole(guild_id);
if (!everyone.has_value())
return Permission::NONE;
Permission perms = everyone->Permissions;
for (const auto role_id : member->Roles) {
const auto *role = GetRole(role_id);
if (role != nullptr)
const auto role = GetRole(role_id);
if (role.has_value())
perms |= role->Permissions;
}
@ -305,8 +298,8 @@ Permission DiscordClient::ComputeOverwrites(Permission base, Snowflake member_id
return Permission::NONE;
Permission perms = base;
const auto *overwrite_everyone = GetPermissionOverwrite(channel_id, channel->GuildID);
if (overwrite_everyone != nullptr) {
const auto overwrite_everyone = GetPermissionOverwrite(channel_id, channel->GuildID);
if (overwrite_everyone.has_value()) {
perms &= ~overwrite_everyone->Deny;
perms |= overwrite_everyone->Allow;
}
@ -314,8 +307,8 @@ Permission DiscordClient::ComputeOverwrites(Permission base, Snowflake member_id
Permission allow = Permission::NONE;
Permission deny = Permission::NONE;
for (const auto role_id : member->Roles) {
const auto *overwrite = GetPermissionOverwrite(channel_id, role_id);
if (overwrite != nullptr) {
const auto overwrite = GetPermissionOverwrite(channel_id, role_id);
if (overwrite.has_value()) {
allow |= overwrite->Allow;
deny |= overwrite->Deny;
}
@ -324,8 +317,8 @@ Permission DiscordClient::ComputeOverwrites(Permission base, Snowflake member_id
perms &= ~deny;
perms |= allow;
const auto *member_overwrite = GetPermissionOverwrite(channel_id, member_id);
if (member_overwrite != nullptr) {
const auto member_overwrite = GetPermissionOverwrite(channel_id, member_id);
if (member_overwrite.has_value()) {
perms &= ~member_overwrite->Deny;
perms |= member_overwrite->Allow;
}
@ -338,10 +331,10 @@ bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowfla
if (guild != nullptr && guild->OwnerID == target) return false;
const auto actor_highest_id = GetMemberHighestRole(guild_id, actor);
const auto target_highest_id = GetMemberHighestRole(guild_id, target);
const auto *actor_highest = GetRole(actor_highest_id);
const auto *target_highest = GetRole(target_highest_id);
if (actor_highest == nullptr) return false;
if (target_highest == nullptr) return true;
const auto actor_highest = GetRole(actor_highest_id);
const auto target_highest = GetRole(target_highest_id);
if (!actor_highest.has_value()) return false;
if (!target_highest.has_value()) return true;
return actor_highest->Position > target_highest->Position;
}
@ -581,6 +574,8 @@ void DiscordClient::ProcessNewGuild(Guild &guild) {
return;
}
m_store.BeginTransaction();
m_store.SetGuild(guild.ID, guild);
for (auto &c : guild.Channels) {
c.GuildID = guild.ID;
@ -596,6 +591,8 @@ void DiscordClient::ProcessNewGuild(Guild &guild) {
for (auto &e : guild.Emojis)
m_store.SetEmoji(e.ID, e);
m_store.EndTransaction();
}
void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) {
@ -629,20 +626,28 @@ void DiscordClient::HandleGatewayMessageCreate(const GatewayMessage &msg) {
void DiscordClient::HandleGatewayMessageDelete(const GatewayMessage &msg) {
MessageDeleteData data = msg.Data;
auto *cur = m_store.GetMessage(data.ID);
if (cur != nullptr)
cur->SetDeleted();
auto cur = m_store.GetMessage(data.ID);
if (!cur.has_value())
return;
cur->SetDeleted();
m_store.SetMessage(data.ID, *cur);
m_signal_message_delete.emit(data.ID, data.ChannelID);
}
void DiscordClient::HandleGatewayMessageDeleteBulk(const GatewayMessage &msg) {
MessageDeleteBulkData data = msg.Data;
m_store.BeginTransaction();
for (const auto &id : data.IDs) {
auto *cur = m_store.GetMessage(id);
if (cur != nullptr)
cur->SetDeleted();
auto cur = m_store.GetMessage(id);
if (!cur.has_value())
return;
cur->SetDeleted();
m_store.SetMessage(id, *cur);
m_signal_message_delete.emit(id, data.ChannelID);
}
m_store.EndTransaction();
}
void DiscordClient::HandleGatewayGuildMemberUpdate(const GatewayMessage &msg) {
@ -681,10 +686,12 @@ void DiscordClient::HandleGatewayChannelUpdate(const GatewayMessage &msg) {
void DiscordClient::HandleGatewayChannelCreate(const GatewayMessage &msg) {
Channel data = msg.Data;
m_store.BeginTransaction();
m_store.SetChannel(data.ID, data);
m_guild_to_channels[data.GuildID].insert(data.ID);
for (const auto &p : data.PermissionOverwrites)
m_store.SetPermissionOverwrite(data.ID, p.ID, p);
m_store.EndTransaction();
m_signal_channel_create.emit(data.ID);
}
@ -717,11 +724,12 @@ void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) {
void DiscordClient::HandleGatewayMessageUpdate(const GatewayMessage &msg) {
Snowflake id = msg.Data.at("id");
auto *current = m_store.GetMessage(id);
if (current == nullptr)
auto current = m_store.GetMessage(id);
if (!current.has_value())
return;
current->from_json_edited(msg.Data);
m_store.SetMessage(id, *current);
m_signal_message_update.emit(id, current->ChannelID);
}

View File

@ -55,7 +55,7 @@ public:
static const constexpr char *GatewayIdentity = "Discord";
public:
DiscordClient();
DiscordClient(bool mem_store = false);
void Start();
void Stop();
bool IsStarted() const;
@ -80,18 +80,17 @@ public:
void FetchInviteData(std::string code, std::function<void(Invite)> cb, std::function<void(bool)> err);
void FetchMessagesInChannel(Snowflake id, std::function<void(const std::vector<Snowflake> &)> cb);
void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function<void(const std::vector<Snowflake> &)> cb);
const Message *GetMessage(Snowflake id) const;
std::optional<Message> GetMessage(Snowflake id) const;
const Channel *GetChannel(Snowflake id) const;
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
std::optional<User> GetUser(Snowflake id) const;
const Role *GetRole(Snowflake id) const;
std::optional<Role> GetRole(Snowflake id) const;
const Guild *GetGuild(Snowflake id) const;
const GuildMember *GetMember(Snowflake user_id, Snowflake guild_id) const;
const PermissionOverwrite *GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
const Emoji *GetEmoji(Snowflake id) const;
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
Snowflake GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
std::unordered_set<Snowflake> GetUsersInGuild(Snowflake id) const;
std::unordered_set<Snowflake> GetRolesInGuild(Snowflake id) const;
std::unordered_set<Snowflake> GetChannelsInGuild(Snowflake id) const;
bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const;

View File

@ -6,14 +6,14 @@
#include "user.hpp"
struct Emoji {
Snowflake ID; // null
std::string Name; // null (in reactions)
std::vector<Snowflake> Roles; // opt
User Creator; // opt
bool NeedsColons = false; // opt
bool IsManaged = false; // opt
bool IsAnimated = false; // opt
bool IsAvailable = false; // opt
Snowflake ID; // null
std::string Name; // null (in reactions)
std::optional<std::vector<Snowflake>> Roles;
std::optional<User> Creator; // only reliable to access ID
std::optional<bool> NeedsColons;
std::optional<bool> IsManaged;
std::optional<bool> IsAnimated;
std::optional<bool> IsAvailable;
friend void from_json(const nlohmann::json &j, Emoji &m);

View File

@ -1,11 +1,24 @@
#include "message.hpp"
void to_json(nlohmann::json &j, const EmbedFooterData &m) {
j["text"] = m.Text;
JS_IF("icon_url", m.IconURL);
JS_IF("proxy_icon_url", m.ProxyIconURL);
}
void from_json(const nlohmann::json &j, EmbedFooterData &m) {
JS_D("text", m.Text);
JS_O("icon_url", m.IconURL);
JS_O("proxy_icon_url", m.ProxyIconURL);
}
void to_json(nlohmann::json &j, const EmbedImageData &m) {
JS_IF("url", m.URL);
JS_IF("proxy_url", m.ProxyURL);
JS_IF("height", m.Height);
JS_IF("width", m.Width);
}
void from_json(const nlohmann::json &j, EmbedImageData &m) {
JS_O("url", m.URL);
JS_O("proxy_url", m.ProxyURL);
@ -13,6 +26,13 @@ void from_json(const nlohmann::json &j, EmbedImageData &m) {
JS_O("width", m.Width);
}
void to_json(nlohmann::json &j, const EmbedThumbnailData &m) {
JS_IF("url", m.URL);
JS_IF("proxy_url", m.ProxyURL);
JS_IF("height", m.Height);
JS_IF("width", m.Width);
}
void from_json(const nlohmann::json &j, EmbedThumbnailData &m) {
JS_O("url", m.URL);
JS_O("proxy_url", m.ProxyURL);
@ -20,17 +40,35 @@ void from_json(const nlohmann::json &j, EmbedThumbnailData &m) {
JS_O("width", m.Width);
}
void to_json(nlohmann::json &j, const EmbedVideoData &m) {
JS_IF("url", m.URL);
JS_IF("height", m.Height);
JS_IF("width", m.Width);
}
void from_json(const nlohmann::json &j, EmbedVideoData &m) {
JS_O("url", m.URL);
JS_O("height", m.Height);
JS_O("width", m.Width);
}
void to_json(nlohmann::json &j, const EmbedProviderData &m) {
JS_IF("name", m.Name);
JS_IF("url", m.URL);
}
void from_json(const nlohmann::json &j, EmbedProviderData &m) {
JS_O("name", m.Name);
JS_ON("url", m.URL);
}
void to_json(nlohmann::json &j, const EmbedAuthorData &m) {
JS_IF("name", m.Name);
JS_IF("url", m.URL);
JS_IF("icon_url", m.IconURL);
JS_IF("proxy_icon_url", m.ProxyIconURL);
}
void from_json(const nlohmann::json &j, EmbedAuthorData &m) {
JS_O("name", m.Name);
JS_O("url", m.URL);
@ -38,12 +76,34 @@ void from_json(const nlohmann::json &j, EmbedAuthorData &m) {
JS_O("proxy_icon_url", m.ProxyIconURL);
}
void to_json(nlohmann::json &j, const EmbedFieldData &m) {
j["name"] = m.Name;
j["value"] = m.Value;
JS_IF("inline", m.Inline);
}
void from_json(const nlohmann::json &j, EmbedFieldData &m) {
JS_D("name", m.Name);
JS_D("value", m.Value);
JS_O("inline", m.Inline);
}
void to_json(nlohmann::json &j, const EmbedData &m) {
JS_IF("title", m.Title);
JS_IF("type", m.Type);
JS_IF("description", m.Description);
JS_IF("url", m.URL);
JS_IF("timestamp", m.Timestamp);
JS_IF("color", m.Color);
JS_IF("footer", m.Footer);
JS_IF("image", m.Image);
JS_IF("thumbnail", m.Thumbnail);
JS_IF("video", m.Video);
JS_IF("provider", m.Provider);
JS_IF("author", m.Author);
JS_IF("fields", m.Fields);
}
void from_json(const nlohmann::json &j, EmbedData &m) {
JS_O("title", m.Title);
JS_O("type", m.Type);
@ -60,6 +120,16 @@ void from_json(const nlohmann::json &j, EmbedData &m) {
JS_O("fields", m.Fields);
}
void to_json(nlohmann::json &j, const AttachmentData &m) {
j["id"] = m.ID;
j["filename"] = m.Filename;
j["size"] = m.Bytes;
j["url"] = m.URL;
j["proxy_url"] = m.ProxyURL;
JS_IF("height", m.Height);
JS_IF("width", m.Width);
}
void from_json(const nlohmann::json &j, AttachmentData &m) {
JS_D("id", m.ID);
JS_D("filename", m.Filename);
@ -107,7 +177,7 @@ void from_json(const nlohmann::json &j, Message &m) {
JS_D("type", m.Type);
// JS_O("activity", m.Activity);
// JS_O("application", m.Application);
// JS_O("message_reference", m.MessageReference);
JS_O("message_reference", m.MessageReference);
JS_O("flags", m.Flags);
JS_O("stickers", m.Stickers);
}

View File

@ -35,89 +35,99 @@ enum class MessageFlags {
};
struct EmbedFooterData {
std::string Text; //
std::string IconURL; // opt
std::string ProxyIconURL; // opt
std::string Text;
std::optional<std::string> IconURL;
std::optional<std::string> ProxyIconURL;
friend void to_json(nlohmann::json &j, const EmbedFooterData &m);
friend void from_json(const nlohmann::json &j, EmbedFooterData &m);
};
struct EmbedImageData {
std::string URL; // opt
std::string ProxyURL; // opt
int Height = 0; // opt
int Width = 0; // opt
std::optional<std::string> URL;
std::optional<std::string> ProxyURL;
std::optional<int> Height;
std::optional<int> Width;
friend void to_json(nlohmann::json &j, const EmbedImageData &m);
friend void from_json(const nlohmann::json &j, EmbedImageData &m);
};
struct EmbedThumbnailData {
std::string URL; // opt
std::string ProxyURL; // opt
int Height = 0; // opt
int Width = 0; // opt
std::optional<std::string> URL;
std::optional<std::string> ProxyURL;
std::optional<int> Height;
std::optional<int> Width;
friend void to_json(nlohmann::json &j, const EmbedThumbnailData &m);
friend void from_json(const nlohmann::json &j, EmbedThumbnailData &m);
};
struct EmbedVideoData {
std::string URL; // opt
int Height = 0; // opt
int Width = 0; // opt
std::optional<std::string> URL;
std::optional<int> Height;
std::optional<int> Width;
friend void to_json(nlohmann::json &j, const EmbedVideoData &m);
friend void from_json(const nlohmann::json &j, EmbedVideoData &m);
};
struct EmbedProviderData {
std::string Name; // opt
std::string URL; // opt, null (docs wrong)
std::optional<std::string> Name;
std::optional<std::string> URL; // null
friend void to_json(nlohmann::json &j, const EmbedProviderData &m);
friend void from_json(const nlohmann::json &j, EmbedProviderData &m);
};
struct EmbedAuthorData {
std::string Name; // opt
std::string URL; // opt
std::string IconURL; // opt
std::string ProxyIconURL; // opt
std::optional<std::string> Name;
std::optional<std::string> URL;
std::optional<std::string> IconURL;
std::optional<std::string> ProxyIconURL;
friend void to_json(nlohmann::json &j, const EmbedAuthorData &m);
friend void from_json(const nlohmann::json &j, EmbedAuthorData &m);
};
struct EmbedFieldData {
std::string Name; //
std::string Value; //
bool Inline = false; // opt
std::string Name;
std::string Value;
std::optional<bool> Inline;
friend void to_json(nlohmann::json &j, const EmbedFieldData &m);
friend void from_json(const nlohmann::json &j, EmbedFieldData &m);
};
struct EmbedData {
std::string Title; // opt
std::string Type; // opt
std::string Description; // opt
std::string URL; // opt
std::string Timestamp; // opt
int Color = -1; // opt
EmbedFooterData Footer; // opt
EmbedImageData Image; // opt
EmbedThumbnailData Thumbnail; // opt
EmbedVideoData Video; // opt
EmbedProviderData Provider; // opt
EmbedAuthorData Author; // opt
std::vector<EmbedFieldData> Fields; // opt
std::optional<std::string> Title;
std::optional<std::string> Type;
std::optional<std::string> Description;
std::optional<std::string> URL;
std::optional<std::string> Timestamp;
std::optional<int> Color;
std::optional<EmbedFooterData> Footer;
std::optional<EmbedImageData> Image;
std::optional<EmbedThumbnailData> Thumbnail;
std::optional<EmbedVideoData> Video;
std::optional<EmbedProviderData> Provider;
std::optional<EmbedAuthorData> Author;
std::optional<std::vector<EmbedFieldData>> Fields;
friend void to_json(nlohmann::json &j, const EmbedData &m);
friend void from_json(const nlohmann::json &j, EmbedData &m);
};
struct AttachmentData {
Snowflake ID; //
std::string Filename; //
int Bytes; //
std::string URL; //
std::string ProxyURL; //
int Height = -1; // opt, null
int Width = -1; // opt, null
Snowflake ID;
std::string Filename;
int Bytes;
std::string URL;
std::string ProxyURL;
std::optional<int> Height; // null
std::optional<int> Width; // null
friend void to_json(nlohmann::json &j, const AttachmentData &m);
friend void from_json(const nlohmann::json &j, AttachmentData &m);
};
@ -141,7 +151,7 @@ struct Message {
std::string EditedTimestamp; // null
bool IsTTS;
bool DoesMentionEveryone;
std::vector<User> Mentions;
std::vector<User> Mentions; // currently discarded in store
// std::vector<Role> MentionRoles;
// std::optional<std::vector<ChannelMentionData>> MentionChannels;
std::vector<AttachmentData> Attachments;

View File

@ -3,7 +3,7 @@
void from_json(const nlohmann::json &j, PermissionOverwrite &m) {
JS_D("id", m.ID);
std::string tmp;
m.ID = j.at("type").get<int>() == 0 ? PermissionOverwrite::ROLE : PermissionOverwrite::MEMBER;
m.Type = j.at("type").get<int>() == 0 ? PermissionOverwrite::ROLE : PermissionOverwrite::MEMBER;
JS_D("allow", tmp);
m.Allow = static_cast<Permission>(std::stoull(tmp));
JS_D("deny", tmp);

View File

@ -46,9 +46,9 @@ struct Bitwise<Permission> {
};
struct PermissionOverwrite {
enum OverwriteType {
ROLE,
MEMBER,
enum OverwriteType : uint8_t {
ROLE = 0,
MEMBER = 1,
};
Snowflake ID;

View File

@ -1,12 +1,23 @@
#include "sticker.hpp"
void to_json(nlohmann::json &j, const Sticker &m) {
j["id"] = m.ID;
j["pack_id"] = m.PackID;
j["name"] = m.Name;
j["description"] = m.Description;
JS_IF("tags", m.Tags);
JS_IF("asset", m.AssetHash);
JS_IF("preview_asset", m.PreviewAssetHash);
j["format_type"] = m.FormatType;
}
void from_json(const nlohmann::json &j, Sticker &m) {
JS_D("id", m.ID);
JS_D("pack_id", m.PackID);
JS_D("name", m.Name);
JS_D("description", m.Description);
JS_O("tags", m.Tags);
JS_D("asset", m.AssetHash);
JS_O("asset", m.AssetHash);
JS_N("preview_asset", m.PreviewAssetHash);
JS_D("format_type", m.FormatType);
}

View File

@ -22,6 +22,7 @@ struct Sticker {
std::optional<std::string> PreviewAssetHash;
StickerFormatType FormatType;
friend void to_json(nlohmann::json &j, const Sticker &m);
friend void from_json(const nlohmann::json &j, Sticker &m);
std::string GetURL() const;

View File

@ -1,10 +1,18 @@
#include "store.hpp"
using namespace std::literals::string_literals;
// hopefully the casting between signed and unsigned int64 doesnt cause issues
Store::Store() {
m_db_path = std::filesystem::temp_directory_path() / "abaddon-store.db";
m_db_err = sqlite3_open(m_db_path.string().c_str(), &m_db);
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));
return;
@ -35,7 +43,8 @@ Store::~Store() {
return;
}
std::filesystem::remove(m_db_path);
if (m_db_path != ":memory:")
std::filesystem::remove(m_db_path);
}
bool Store::IsValid() const {
@ -43,10 +52,6 @@ bool Store::IsValid() const {
}
void Store::SetUser(Snowflake id, const User &user) {
if ((uint64_t)id == 0) {
printf("???: %s\n", user.Username.c_str());
}
Bind(m_set_user_stmt, 1, id);
Bind(m_set_user_stmt, 2, user.Username);
Bind(m_set_user_stmt, 3, user.Discriminator);
@ -64,8 +69,6 @@ void Store::SetUser(Snowflake id, const User &user) {
if (!RunInsert(m_set_user_stmt)) {
fprintf(stderr, "user insert failed: %s\n", sqlite3_errstr(m_db_err));
}
// m_users[id] = user;
}
void Store::SetChannel(Snowflake id, const Channel &channel) {
@ -77,11 +80,65 @@ void Store::SetGuild(Snowflake id, const Guild &guild) {
}
void Store::SetRole(Snowflake id, const Role &role) {
m_roles[id] = 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<uint64_t>(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::SetMessage(Snowflake id, const Message &message) {
m_messages[id] = 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, "[]"s); // mentions
{
std::string tmp;
tmp = nlohmann::json(message.Attachments).dump();
Bind(m_set_msg_stmt, 11, tmp);
}
{
std::string tmp = nlohmann::json(message.Embeds).dump();
Bind(m_set_msg_stmt, 12, tmp);
}
Bind(m_set_msg_stmt, 13, message.IsPinned);
Bind(m_set_msg_stmt, 14, message.WebhookID);
Bind(m_set_msg_stmt, 15, static_cast<uint64_t>(message.Type));
if (message.MessageReference.has_value()) {
std::string tmp = nlohmann::json(*message.MessageReference).dump();
Bind(m_set_msg_stmt, 16, tmp);
} else
Bind(m_set_msg_stmt, 16, nullptr);
if (message.Flags.has_value())
Bind(m_set_msg_stmt, 17, static_cast<uint64_t>(*message.Flags));
else
Bind(m_set_msg_stmt, 17, nullptr);
if (message.Stickers.has_value()) {
std::string tmp = nlohmann::json(*message.Stickers).dump();
Bind(m_set_msg_stmt, 18, tmp);
} else
Bind(m_set_msg_stmt, 18, nullptr);
Bind(m_set_msg_stmt, 19, message.IsDeleted());
Bind(m_set_msg_stmt, 20, message.IsEdited());
if (!RunInsert(m_set_msg_stmt))
fprintf(stderr, "message insert failed: %s\n", sqlite3_errstr(m_db_err));
}
void Store::SetGuildMemberData(Snowflake guild_id, Snowflake user_id, const GuildMember &data) {
@ -89,17 +146,146 @@ void Store::SetGuildMemberData(Snowflake guild_id, Snowflake user_id, const Guil
}
void Store::SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm) {
m_permissions[channel_id][id] = 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<int>(perm.Type));
Bind(m_set_perm_stmt, 4, static_cast<uint64_t>(perm.Allow));
Bind(m_set_perm_stmt, 5, static_cast<uint64_t>(perm.Deny));
if (!RunInsert(m_set_perm_stmt))
fprintf(stderr, "permission insert failed: %s\n", sqlite3_errstr(m_db_err));
}
void Store::SetEmoji(Snowflake id, const Emoji &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);
if (emoji.Creator.has_value())
Bind(m_set_emote_stmt, 4, emoji.Creator->ID);
else
Bind(m_set_emote_stmt, 4, nullptr);
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 (!RunInsert(m_set_emote_stmt))
fprintf(stderr, "emoji insert failed: %s\n", sqlite3_errstr(m_db_err));
m_emojis[id] = emoji;
}
std::optional<Message> 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;
}
Message ret;
ret.ID = id;
Get(m_get_msg_stmt, 1, ret.ChannelID);
Get(m_get_msg_stmt, 2, ret.GuildID);
Get(m_get_msg_stmt, 3, ret.Author.ID); // yike
Get(m_get_msg_stmt, 4, ret.Content);
Get(m_get_msg_stmt, 5, ret.Timestamp);
Get(m_get_msg_stmt, 6, ret.EditedTimestamp);
Get(m_get_msg_stmt, 7, ret.IsTTS);
Get(m_get_msg_stmt, 8, ret.DoesMentionEveryone);
std::string tmps;
Get(m_get_msg_stmt, 9, tmps);
nlohmann::json::parse(tmps).get_to(ret.Mentions);
Get(m_get_msg_stmt, 10, tmps);
nlohmann::json::parse(tmps).get_to(ret.Attachments);
Get(m_get_msg_stmt, 11, tmps);
nlohmann::json::parse(tmps).get_to(ret.Embeds);
Get(m_get_msg_stmt, 12, ret.IsPinned);
Get(m_get_msg_stmt, 13, ret.WebhookID);
uint64_t tmpi;
Get(m_get_msg_stmt, 14, tmpi);
ret.Type = static_cast<MessageType>(tmpi);
Get(m_get_msg_stmt, 15, tmps);
if (tmps != "")
ret.MessageReference = nlohmann::json::parse(tmps).get<MessageReferenceData>();
Get(m_get_msg_stmt, 16, tmpi);
ret.Flags = static_cast<MessageFlags>(tmpi);
Get(m_get_msg_stmt, 17, tmps);
if (tmps != "")
ret.Stickers = nlohmann::json::parse(tmps).get<std::vector<Sticker>>();
bool tmpb = false;
Get(m_get_msg_stmt, 18, tmpb);
if (tmpb) ret.SetDeleted();
Get(m_get_msg_stmt, 19, tmpb);
if (tmpb) ret.SetEdited();
Reset(m_get_msg_stmt);
return std::optional<Message>(std::move(ret));
}
std::optional<PermissionOverwrite> 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;
}
PermissionOverwrite ret;
ret.ID = id;
uint64_t tmp;
Get(m_get_perm_stmt, 2, tmp);
ret.Type = static_cast<PermissionOverwrite::OverwriteType>(tmp);
Get(m_get_perm_stmt, 3, tmp);
ret.Allow = static_cast<Permission>(tmp);
Get(m_get_perm_stmt, 4, tmp);
ret.Deny = static_cast<Permission>(tmp);
Reset(m_get_perm_stmt);
return std::optional<PermissionOverwrite>(std::move(ret));
}
std::optional<Role> 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;
}
Role 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<Permission>(tmp);
Get(m_get_role_stmt, 6, ret.IsManaged);
Get(m_get_role_stmt, 7, ret.IsMentionable);
Reset(m_get_role_stmt);
return std::optional<Role>(std::move(ret));
}
std::optional<User> Store::GetUser(Snowflake id) const {
Bind(m_get_user_stmt, 1, id);
if (!FetchOne(m_get_user_stmt)) {
if (m_db_err != SQLITE_DONE) // not an error, just means user isnt found
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;
@ -153,34 +339,6 @@ const Guild *Store::GetGuild(Snowflake id) const {
return &it->second;
}
Role *Store::GetRole(Snowflake id) {
auto it = m_roles.find(id);
if (it == m_roles.end())
return nullptr;
return &it->second;
}
const Role *Store::GetRole(Snowflake id) const {
auto it = m_roles.find(id);
if (it == m_roles.end())
return nullptr;
return &it->second;
}
Message *Store::GetMessage(Snowflake id) {
auto it = m_messages.find(id);
if (it == m_messages.end())
return nullptr;
return &it->second;
}
const Message *Store::GetMessage(Snowflake id) const {
auto it = m_messages.find(id);
if (it == m_messages.end())
return nullptr;
return &it->second;
}
GuildMember *Store::GetGuildMemberData(Snowflake guild_id, Snowflake user_id) {
auto git = m_members.find(guild_id);
if (git == m_members.end())
@ -191,16 +349,6 @@ GuildMember *Store::GetGuildMemberData(Snowflake guild_id, Snowflake user_id) {
return &mit->second;
}
PermissionOverwrite *Store::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) {
auto cit = m_permissions.find(channel_id);
if (cit == m_permissions.end())
return nullptr;
auto pit = cit->second.find(id);
if (pit == cit->second.end())
return nullptr;
return &pit->second;
}
Emoji *Store::GetEmoji(Snowflake id) {
auto it = m_emojis.find(id);
if (it != m_emojis.end())
@ -218,16 +366,6 @@ const GuildMember *Store::GetGuildMemberData(Snowflake guild_id, Snowflake user_
return &mit->second;
}
const PermissionOverwrite *Store::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const {
auto cit = m_permissions.find(channel_id);
if (cit == m_permissions.end())
return nullptr;
auto pit = cit->second.find(id);
if (pit == cit->second.end())
return nullptr;
return &pit->second;
}
const Emoji *Store::GetEmoji(Snowflake id) const {
auto it = m_emojis.find(id);
if (it != m_emojis.end())
@ -250,20 +388,11 @@ const Store::channels_type &Store::GetChannels() const {
const Store::guilds_type &Store::GetGuilds() const {
return m_guilds;
}
const Store::roles_type &Store::GetRoles() const {
return m_roles;
}
void Store::ClearAll() {
m_channels.clear();
m_emojis.clear();
m_guilds.clear();
m_members.clear();
m_messages.clear();
m_permissions.clear();
m_roles.clear();
m_users.clear();
}
void Store::BeginTransaction() {
@ -291,11 +420,97 @@ flags INTEGER,
premium INTEGER,
pubflags INTEGER
)
)";
constexpr const char *create_permissions = R"(
CREATE TABLE IF NOT EXISTS permissions (
id INTEGER NOT NULL,
channel_id INTEGER NOT NULL,
type INTEGER NOT NULL,
allow INTEGER NOT NULL,
deny INTEGER NOT NULL
)
)";
constexpr const char *create_messages = R"(
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY,
channel_id INTEGER NOT NULL,
guild_id INTEGER,
author_id INTEGER NOT NULL,
content TEXT NOT NULL,
timestamp TEXT NOT NULL,
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,
reference TEXT, /* json */
flags INTEGER,
stickers TEXT, /* json */
/* extra */
deleted BOOL,
edited BOOL
)
)";
constexpr const char *create_roles = R"(
CREATE TABLE IF NOT EXISTS roles (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
color INTEGER NOT NULL,
hoisted BOOL NOT NULL,
position INTEGER NOT NULL,
permissions INTEGER NOT NULL,
managed BOOL NOT NULL,
mentionable BOOL NOT NULL
)
)";
constexpr const char *create_emojis = R"(
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,
animated BOOL,
available BOOL
)
)";
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\n");
fprintf(stderr, "failed to create user table: %s\n", sqlite3_errstr(m_db_err));
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));
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));
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));
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));
return false;
}
@ -311,17 +526,105 @@ REPLACE INTO users VALUES (
constexpr const char *get_user = R"(
SELECT * FROM users WHERE id = ?
)";
constexpr const char *set_perm = R"(
REPLACE INTO permissions VALUES (
?, ?, ?, ?, ?
)
)";
constexpr const char *get_perm = R"(
SELECT * FROM permissions WHERE id = ? AND channel_id = ?
)";
constexpr const char *set_msg = R"(
REPLACE INTO messages VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
)";
constexpr const char *get_msg = R"(
SELECT * FROM messages WHERE id = ?
)";
constexpr const char *set_role = R"(
REPLACE INTO roles VALUES (
?, ?, ?, ?, ?, ?, ?, ?
)
)";
constexpr const char *get_role = R"(
SELECT * FROM roles WHERE id = ?
)";
constexpr const char *set_emoji = R"(
REPLACE INTO emojis VALUES (
?, ?, ?, ?, ?, ?, ?, ?
)
)";
constexpr const char *get_emoji = R"(
SELECT * FROM emojis 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\n");
fprintf(stderr, "failed to prepare set user statement: %s\n", sqlite3_errstr(m_db_err));
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\n");
fprintf(stderr, "failed to prepare get user statement: %s\n", sqlite3_errstr(m_db_err));
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));
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));
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));
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));
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));
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));
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));
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));
return false;
}
@ -331,6 +634,14 @@ SELECT * FROM users WHERE id = ?
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);
}
void Store::Bind(sqlite3_stmt *stmt, int index, int num) const {
@ -348,7 +659,7 @@ void Store::Bind(sqlite3_stmt *stmt, int index, uint64_t num) const {
}
void Store::Bind(sqlite3_stmt *stmt, int index, const std::string &str) const {
m_db_err = sqlite3_bind_text(stmt, index, str.c_str(), -1, nullptr);
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));
}
@ -361,10 +672,16 @@ void Store::Bind(sqlite3_stmt *stmt, int index, bool val) const {
}
}
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));
}
}
bool Store::RunInsert(sqlite3_stmt *stmt) {
m_db_err = sqlite3_step(stmt);
sqlite3_reset(stmt);
sqlite3_clear_bindings(stmt);
Reset(stmt);
return m_db_err == SQLITE_DONE;
}
@ -377,6 +694,10 @@ void Store::Get(sqlite3_stmt *stmt, int index, int &out) const {
out = sqlite3_column_int(stmt, index);
}
void Store::Get(sqlite3_stmt *stmt, int index, uint64_t &out) const {
out = sqlite3_column_int64(stmt, index);
}
void Store::Get(sqlite3_stmt *stmt, int index, std::string &out) const {
const unsigned char *ptr = sqlite3_column_text(stmt, index);
if (ptr == nullptr)

View File

@ -12,7 +12,7 @@
class Store {
public:
Store();
Store(bool mem_store = false);
~Store();
bool IsValid() const;
@ -30,18 +30,15 @@ public:
Channel *GetChannel(Snowflake id);
Guild *GetGuild(Snowflake id);
Role *GetRole(Snowflake id);
Message *GetMessage(Snowflake id);
GuildMember *GetGuildMemberData(Snowflake guild_id, Snowflake user_id);
PermissionOverwrite *GetPermissionOverwrite(Snowflake channel_id, Snowflake id);
Emoji *GetEmoji(Snowflake id);
std::optional<Message> GetMessage(Snowflake id) const;
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
std::optional<Role> GetRole(Snowflake id) const;
std::optional<User> GetUser(Snowflake id) const;
const Channel *GetChannel(Snowflake id) const;
const Guild *GetGuild(Snowflake id) const;
const Role *GetRole(Snowflake id) const;
const Message *GetMessage(Snowflake id) const;
const GuildMember *GetGuildMemberData(Snowflake guild_id, Snowflake user_id) const;
const PermissionOverwrite *GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
const Emoji *GetEmoji(Snowflake id) const;
void ClearGuild(Snowflake id);
@ -58,7 +55,6 @@ public:
const channels_type &GetChannels() const;
const guilds_type &GetGuilds() const;
const roles_type &GetRoles() const;
void ClearAll();
@ -66,13 +62,9 @@ public:
void EndTransaction();
private:
users_type m_users;
channels_type m_channels;
guilds_type m_guilds;
roles_type m_roles;
messages_type m_messages;
members_type m_members;
permission_overwrites_type m_permissions;
emojis_type m_emojis;
bool CreateTables();
@ -85,6 +77,7 @@ private:
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<typename T>
@ -101,6 +94,14 @@ private:
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;
};
template<typename T>