summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--abaddon.cpp20
-rw-r--r--abaddon.hpp4
-rw-r--r--components/chatmessage.cpp108
-rw-r--r--components/chatmessage.hpp10
-rw-r--r--components/chatwindow.cpp22
-rw-r--r--components/chatwindow.hpp7
-rw-r--r--css/main.css16
-rw-r--r--discord/discord.cpp118
-rw-r--r--discord/discord.hpp10
-rw-r--r--discord/emoji.cpp17
-rw-r--r--discord/emoji.hpp1
-rw-r--r--discord/message.cpp14
-rw-r--r--discord/message.hpp12
-rw-r--r--discord/objects.cpp17
-rw-r--r--discord/objects.hpp23
-rw-r--r--discord/store.cpp410
-rw-r--r--discord/user.cpp7
-rw-r--r--discord/user.hpp1
-rw-r--r--windows/mainwindow.cpp8
-rw-r--r--windows/mainwindow.hpp2
20 files changed, 624 insertions, 203 deletions
diff --git a/abaddon.cpp b/abaddon.cpp
index a640e78..d905b05 100644
--- a/abaddon.cpp
+++ b/abaddon.cpp
@@ -35,6 +35,8 @@ Abaddon::Abaddon()
m_discord.signal_channel_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnChannelUpdate));
m_discord.signal_channel_create().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnChannelCreate));
m_discord.signal_guild_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildUpdate));
+ m_discord.signal_reaction_add().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReactionAdd));
+ m_discord.signal_reaction_remove().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReactionRemove));
}
Abaddon::~Abaddon() {
@@ -98,6 +100,8 @@ int Abaddon::StartGTK() {
m_main_window->GetChatWindow()->signal_action_chat_load_history().connect(sigc::mem_fun(*this, &Abaddon::ActionChatLoadHistory));
m_main_window->GetChatWindow()->signal_action_channel_click().connect(sigc::mem_fun(*this, &Abaddon::ActionChannelOpened));
m_main_window->GetChatWindow()->signal_action_insert_mention().connect(sigc::mem_fun(*this, &Abaddon::ActionInsertMention));
+ m_main_window->GetChatWindow()->signal_action_reaction_add().connect(sigc::mem_fun(*this, &Abaddon::ActionReactionAdd));
+ m_main_window->GetChatWindow()->signal_action_reaction_remove().connect(sigc::mem_fun(*this, &Abaddon::ActionReactionRemove));
ActionReloadCSS();
@@ -203,6 +207,14 @@ void Abaddon::DiscordOnGuildUpdate(Snowflake guild_id) {
m_main_window->UpdateChannelsUpdateGuild(guild_id);
}
+void Abaddon::DiscordOnReactionAdd(Snowflake message_id, const Glib::ustring &param) {
+ m_main_window->UpdateChatReactionAdd(message_id, param);
+}
+
+void Abaddon::DiscordOnReactionRemove(Snowflake message_id, const Glib::ustring &param) {
+ m_main_window->UpdateChatReactionAdd(message_id, param);
+}
+
const SettingsManager &Abaddon::GetSettings() const {
return m_settings;
}
@@ -423,6 +435,14 @@ void Abaddon::ActionSetStatus() {
m_discord.UpdateStatus(status, false, activity);
}
+void Abaddon::ActionReactionAdd(Snowflake id, const Glib::ustring &param) {
+ m_discord.AddReaction(id, param);
+}
+
+void Abaddon::ActionReactionRemove(Snowflake id, const Glib::ustring &param) {
+ m_discord.RemoveReaction(id, param);
+}
+
void Abaddon::ActionReloadCSS() {
try {
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider);
diff --git a/abaddon.hpp b/abaddon.hpp
index 74b198b..d2439ae 100644
--- a/abaddon.hpp
+++ b/abaddon.hpp
@@ -43,6 +43,8 @@ public:
void ActionKickMember(Snowflake user_id, Snowflake guild_id);
void ActionBanMember(Snowflake user_id, Snowflake guild_id);
void ActionSetStatus();
+ void ActionReactionAdd(Snowflake id, const Glib::ustring &param);
+ void ActionReactionRemove(Snowflake id, const Glib::ustring &param);
void ActionReloadCSS();
@@ -65,6 +67,8 @@ public:
void DiscordOnChannelUpdate(Snowflake channel_id);
void DiscordOnChannelCreate(Snowflake channel_id);
void DiscordOnGuildUpdate(Snowflake guild_id);
+ void DiscordOnReactionAdd(Snowflake message_id, const Glib::ustring &param);
+ void DiscordOnReactionRemove(Snowflake message_id, const Glib::ustring &param);
const SettingsManager &GetSettings() const;
diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp
index bae26c4..2579482 100644
--- a/components/chatmessage.cpp
+++ b/components/chatmessage.cpp
@@ -76,6 +76,11 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(Snowflake id) {
}
}
+ if (data->Reactions.has_value() && data->Reactions->size() > 0) {
+ container->m_reactions_component = container->CreateReactionsComponent(&*data);
+ container->m_main->add(*container->m_reactions_component);
+ }
+
container->UpdateAttributes();
return container;
@@ -125,6 +130,18 @@ void ChatMessageItemContainer::UpdateImage(std::string url, Glib::RefPtr<Gdk::Pi
}
}
+void ChatMessageItemContainer::UpdateReactions() {
+ if (m_reactions_component != nullptr)
+ delete m_reactions_component;
+
+ const auto data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
+ if (data->Reactions.has_value() && data->Reactions->size() > 0) {
+ m_reactions_component = CreateReactionsComponent(&*data);
+ m_reactions_component->show_all();
+ m_main->add(*m_reactions_component);
+ }
+}
+
void ChatMessageItemContainer::UpdateAttributes() {
const auto data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (!data.has_value()) return;
@@ -395,6 +412,89 @@ Gtk::Widget *ChatMessageItemContainer::CreateStickerComponent(const Sticker &dat
return box;
}
+Gtk::Widget *ChatMessageItemContainer::CreateReactionsComponent(const Message *data) {
+ auto *flow = Gtk::manage(new Gtk::FlowBox);
+ flow->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
+ flow->set_min_children_per_line(5);
+ flow->set_max_children_per_line(20);
+ flow->set_halign(Gtk::ALIGN_START);
+ flow->set_hexpand(false);
+ flow->set_column_spacing(2);
+ flow->set_selection_mode(Gtk::SELECTION_NONE);
+
+ auto &imgr = Abaddon::Get().GetImageManager();
+ auto &emojis = Abaddon::Get().GetEmojis();
+ const auto &placeholder = imgr.GetPlaceholder(16);
+
+ for (const auto &reaction : *data->Reactions) {
+ auto *ev = Gtk::manage(new Gtk::EventBox);
+ auto *box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
+ box->get_style_context()->add_class("reaction-box");
+ ev->add(*box);
+ flow->add(*ev);
+
+ bool is_stock = !reaction.Emoji.ID.IsValid();
+
+ bool has_reacted = reaction.HasReactedWith;
+ if (has_reacted)
+ box->get_style_context()->add_class("reacted");
+
+ ev->signal_button_press_event().connect([this, has_reacted, is_stock, reaction](GdkEventButton *event) -> bool {
+ if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
+ Glib::ustring param; // escaped in client
+ if (is_stock)
+ param = reaction.Emoji.Name;
+ else
+ param = std::to_string(reaction.Emoji.ID);
+ if (has_reacted)
+ m_signal_action_reaction_remove.emit(param);
+ else
+ m_signal_action_reaction_add.emit(param);
+ return true;
+ }
+ return false;
+ });
+
+ ev->signal_realize().connect([ev]() {
+ auto window = ev->get_window();
+ auto display = window->get_display();
+ auto cursor = Gdk::Cursor::create(display, "pointer");
+ window->set_cursor(cursor);
+ });
+
+ // image
+ if (is_stock) { // unicode/stock
+ const auto &pb = emojis.GetPixBuf(reaction.Emoji.Name);
+ auto *img = Gtk::manage(new Gtk::Image(pb->scale_simple(16, 16, Gdk::INTERP_BILINEAR)));
+ img->set_can_focus(false);
+ box->add(*img);
+ } else { // custom
+ const auto &pb = imgr.GetFromURLIfCached(reaction.Emoji.GetURL());
+ Gtk::Image *img;
+ if (pb) {
+ img = Gtk::manage(new Gtk::Image(pb->scale_simple(16, 16, Gdk::INTERP_BILINEAR)));
+ } else {
+ img = Gtk::manage(new Gtk::Image(placeholder));
+ // can track_obj PLEASE work ???
+ imgr.LoadFromURL(reaction.Emoji.GetURL(), sigc::bind<0>(sigc::mem_fun(*this, &ChatMessageItemContainer::ReactionUpdateImage), img));
+ }
+ img->set_can_focus(false);
+ box->add(*img);
+ }
+
+ auto *lbl = Gtk::manage(new Gtk::Label(std::to_string(reaction.Count)));
+ lbl->set_margin_left(5);
+ lbl->get_style_context()->add_class("reaction-count");
+ box->add(*lbl);
+ }
+
+ return flow;
+}
+
+void ChatMessageItemContainer::ReactionUpdateImage(Gtk::Image *img, const Glib::RefPtr<Gdk::Pixbuf> &pb) {
+ img->property_pixbuf() = pb->scale_simple(16, 16, Gdk::INTERP_BILINEAR);
+}
+
void ChatMessageItemContainer::HandleImage(const AttachmentData &data, Gtk::Image *img, std::string url) {
m_img_loadmap[url] = std::make_pair(img, data);
// ask the chatwindow to call UpdateImage because dealing with lifetimes sucks
@@ -715,6 +815,14 @@ ChatMessageItemContainer::type_signal_channel_click ChatMessageItemContainer::si
return m_signal_action_channel_click;
}
+ChatMessageItemContainer::type_signal_action_reaction_add ChatMessageItemContainer::signal_action_reaction_add() {
+ return m_signal_action_reaction_add;
+}
+
+ChatMessageItemContainer::type_signal_action_reaction_remove ChatMessageItemContainer::signal_action_reaction_remove() {
+ return m_signal_action_reaction_remove;
+}
+
ChatMessageItemContainer::type_signal_image_load ChatMessageItemContainer::signal_image_load() {
return m_signal_image_load;
}
diff --git a/components/chatmessage.hpp b/components/chatmessage.hpp
index 0f76f21..cbc774b 100644
--- a/components/chatmessage.hpp
+++ b/components/chatmessage.hpp
@@ -14,6 +14,7 @@ public:
void UpdateAttributes();
void UpdateContent();
void UpdateImage(std::string url, Glib::RefPtr<Gdk::Pixbuf> buf);
+ void UpdateReactions();
protected:
bool EmitImageLoad(std::string url);
@@ -25,6 +26,8 @@ protected:
Gtk::Widget *CreateImageComponent(const AttachmentData &data);
Gtk::Widget *CreateAttachmentComponent(const AttachmentData &data); // non-image attachments
Gtk::Widget *CreateStickerComponent(const Sticker &data);
+ Gtk::Widget *CreateReactionsComponent(const Message *data);
+ void ReactionUpdateImage(Gtk::Image *img, const Glib::RefPtr<Gdk::Pixbuf> &pb);
void HandleImage(const AttachmentData &data, Gtk::Image *img, std::string url);
void OnEmbedImageLoad(const Glib::RefPtr<Gdk::Pixbuf> &pixbuf);
@@ -75,6 +78,7 @@ protected:
Gtk::TextView *m_text_component = nullptr;
Gtk::Widget *m_embed_component = nullptr;
+ Gtk::Widget *m_reactions_component = nullptr;
public:
typedef sigc::signal<void, std::string> type_signal_image_load;
@@ -82,10 +86,14 @@ public:
typedef sigc::signal<void> type_signal_action_delete;
typedef sigc::signal<void> type_signal_action_edit;
typedef sigc::signal<void, Snowflake> type_signal_channel_click;
+ typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_add;
+ typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_remove;
type_signal_action_delete signal_action_delete();
type_signal_action_edit signal_action_edit();
type_signal_channel_click signal_action_channel_click();
+ type_signal_action_reaction_add signal_action_reaction_add();
+ type_signal_action_reaction_remove signal_action_reaction_remove();
type_signal_image_load signal_image_load();
@@ -93,6 +101,8 @@ private:
type_signal_action_delete m_signal_action_delete;
type_signal_action_edit m_signal_action_edit;
type_signal_channel_click m_signal_action_channel_click;
+ type_signal_action_reaction_add m_signal_action_reaction_add;
+ type_signal_action_reaction_remove m_signal_action_reaction_remove;
type_signal_image_load m_signal_image_load;
};
diff --git a/components/chatwindow.cpp b/components/chatwindow.cpp
index ea8da69..25492f8 100644
--- a/components/chatwindow.cpp
+++ b/components/chatwindow.cpp
@@ -128,6 +128,14 @@ Snowflake ChatWindow::GetOldestListedMessage() {
return m;
}
+void ChatWindow::UpdateReactions(Snowflake id) {
+ auto it = m_id_to_widget.find(id);
+ if (it == m_id_to_widget.end()) return;
+ auto *widget = dynamic_cast<ChatMessageItemContainer *>(it->second);
+ if (widget == nullptr) return;
+ widget->UpdateReactions();
+}
+
Snowflake ChatWindow::GetActiveChannel() const {
return m_active_channel;
}
@@ -206,6 +214,12 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
content->signal_action_edit().connect([this, id] {
m_signal_action_message_edit.emit(m_active_channel, id);
});
+ content->signal_action_reaction_add().connect([this, id](const Glib::ustring &param) {
+ m_signal_action_reaction_add.emit(id, param);
+ });
+ content->signal_action_reaction_remove().connect([this, id](const Glib::ustring &param) {
+ m_signal_action_reaction_remove.emit(id, param);
+ });
content->signal_image_load().connect([this, id](std::string url) {
auto &mgr = Abaddon::Get().GetImageManager();
mgr.LoadFromURL(url, [this, id, url](Glib::RefPtr<Gdk::Pixbuf> buf) {
@@ -339,3 +353,11 @@ ChatWindow::type_signal_action_insert_mention ChatWindow::signal_action_insert_m
ChatWindow::type_signal_action_open_user_menu ChatWindow::signal_action_open_user_menu() {
return m_signal_action_open_user_menu;
}
+
+ChatWindow::type_signal_action_reaction_add ChatWindow::signal_action_reaction_add() {
+ return m_signal_action_reaction_add;
+}
+
+ChatWindow::type_signal_action_reaction_remove ChatWindow::signal_action_reaction_remove() {
+ return m_signal_action_reaction_remove;
+}
diff --git a/components/chatwindow.hpp b/components/chatwindow.hpp
index 4b1a251..823e988 100644
--- a/components/chatwindow.hpp
+++ b/components/chatwindow.hpp
@@ -23,6 +23,7 @@ public:
void AddNewHistory(const std::vector<Snowflake> &id); // prepend messages
void InsertChatInput(std::string text);
Snowflake GetOldestListedMessage(); // oldest message that is currently in the ListBox
+ void UpdateReactions(Snowflake id);
protected:
ChatMessageItemContainer *CreateMessageComponent(Snowflake id); // to be inserted into header's content box
@@ -72,6 +73,8 @@ public:
typedef sigc::signal<void, Snowflake> type_signal_action_channel_click;
typedef sigc::signal<void, Snowflake> type_signal_action_insert_mention;
typedef sigc::signal<void, const GdkEvent *, Snowflake, Snowflake> type_signal_action_open_user_menu;
+ typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_action_reaction_add;
+ typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_action_reaction_remove;
type_signal_action_message_delete signal_action_message_delete();
type_signal_action_message_edit signal_action_message_edit();
@@ -80,6 +83,8 @@ public:
type_signal_action_channel_click signal_action_channel_click();
type_signal_action_insert_mention signal_action_insert_mention();
type_signal_action_open_user_menu signal_action_open_user_menu();
+ type_signal_action_reaction_add signal_action_reaction_add();
+ type_signal_action_reaction_remove signal_action_reaction_remove();
private:
type_signal_action_message_delete m_signal_action_message_delete;
@@ -89,4 +94,6 @@ private:
type_signal_action_channel_click m_signal_action_channel_click;
type_signal_action_insert_mention m_signal_action_insert_mention;
type_signal_action_open_user_menu m_signal_action_open_user_menu;
+ type_signal_action_reaction_add m_signal_action_reaction_add;
+ type_signal_action_reaction_remove m_signal_action_reaction_remove;
};
diff --git a/css/main.css b/css/main.css
index 2672f8c..7dbed4f 100644
--- a/css/main.css
+++ b/css/main.css
@@ -105,6 +105,22 @@
margin: 5px;
}
+.reaction-box {
+ padding: 2px 5px 2px 5px;
+ margin: 0px 0px 0px 0px;
+ background-color: rgba(0.4, 0.4, 0.4, 0.4);
+ border-radius: 5px;
+ border: 1px solid transparent;
+}
+
+.reaction-box.reacted {
+ border: 1px solid white;
+}
+
+.reaction-count {
+ color: #cfd8dc;
+}
+
paned separator {
background:#37474f;
}
diff --git a/discord/discord.cpp b/discord/discord.cpp
index 0746250..2151d86 100644
--- a/discord/discord.cpp
+++ b/discord/discord.cpp
@@ -411,6 +411,34 @@ std::optional<Snowflake> DiscordClient::FindDM(Snowflake user_id) {
return std::nullopt;
}
+void DiscordClient::AddReaction(Snowflake id, Glib::ustring param) {
+ if (!param.is_ascii()) // means unicode param
+ param = Glib::uri_escape_string(param, "", false);
+ else {
+ const auto &tmp = m_store.GetEmoji(param);
+ if (tmp.has_value())
+ param = tmp->Name + ":" + std::to_string(tmp->ID);
+ else
+ return;
+ }
+ Snowflake channel_id = m_store.GetMessage(id)->ChannelID;
+ m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id) + "/reactions/" + param + "/@me", "", [](auto) {});
+}
+
+void DiscordClient::RemoveReaction(Snowflake id, Glib::ustring param) {
+ if (!param.is_ascii()) // means unicode param
+ param = Glib::uri_escape_string(param, "", false);
+ else {
+ const auto &tmp = m_store.GetEmoji(param);
+ if (tmp.has_value())
+ param = tmp->Name + ":" + std::to_string(tmp->ID);
+ else
+ return;
+ }
+ Snowflake channel_id = m_store.GetMessage(id)->ChannelID;
+ m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id) + "/reactions/" + param + "/@me", [](auto) {});
+}
+
void DiscordClient::UpdateToken(std::string token) {
if (!IsStarted()) {
m_token = token;
@@ -552,6 +580,12 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
case GatewayEvent::GUILD_ROLE_DELETE: {
HandleGatewayGuildRoleDelete(m);
} break;
+ case GatewayEvent::MESSAGE_REACTION_ADD: {
+ HandleGatewayMessageReactionAdd(m);
+ } break;
+ case GatewayEvent::MESSAGE_REACTION_REMOVE: {
+ HandleGatewayMessageReactionRemove(m);
+ } break;
}
} break;
default:
@@ -735,6 +769,80 @@ void DiscordClient::HandleGatewayGuildRoleDelete(const GatewayMessage &msg) {
m_signal_role_delete.emit(data.RoleID);
}
+void DiscordClient::HandleGatewayMessageReactionAdd(const GatewayMessage &msg) {
+ MessageReactionAddObject data = msg.Data;
+ auto to = m_store.GetMessage(data.MessageID);
+ 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_signal_reaction_add.emit(data.MessageID, std::to_string(data.Emoji.ID));
+}
+
+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_signal_reaction_remove.emit(data.MessageID, std::to_string(data.Emoji.ID));
+}
+
void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) {
m_signal_disconnected.emit(true);
inflateEnd(&m_zstream);
@@ -928,6 +1036,8 @@ void DiscordClient::LoadEventMap() {
m_event_map["GUILD_ROLE_UPDATE"] = GatewayEvent::GUILD_ROLE_UPDATE;
m_event_map["GUILD_ROLE_CREATE"] = GatewayEvent::GUILD_ROLE_CREATE;
m_event_map["GUILD_ROLE_DELETE"] = GatewayEvent::GUILD_ROLE_DELETE;
+ m_event_map["MESSAGE_REACTION_ADD"] = GatewayEvent::MESSAGE_REACTION_ADD;
+ m_event_map["MESSAGE_REACTION_REMOVE"] = GatewayEvent::MESSAGE_REACTION_REMOVE;
}
DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() {
@@ -993,3 +1103,11 @@ DiscordClient::type_signal_role_create DiscordClient::signal_role_create() {
DiscordClient::type_signal_role_delete DiscordClient::signal_role_delete() {
return m_signal_role_delete;
}
+
+DiscordClient::type_signal_reaction_add DiscordClient::signal_reaction_add() {
+ return m_signal_reaction_add;
+}
+
+DiscordClient::type_signal_reaction_remove DiscordClient::signal_reaction_remove() {
+ return m_signal_reaction_remove;
+}
diff --git a/discord/discord.hpp b/discord/discord.hpp
index c2b4bb0..767cf7a 100644
--- a/discord/discord.hpp
+++ b/discord/discord.hpp
@@ -108,6 +108,8 @@ public:
void UpdateStatus(const std::string &status, bool is_afk, const Activity &obj);
void CreateDM(Snowflake user_id);
std::optional<Snowflake> FindDM(Snowflake user_id); // wont find group dms
+ void AddReaction(Snowflake id, Glib::ustring param);
+ void RemoveReaction(Snowflake id, Glib::ustring param);
void UpdateToken(std::string token);
void SetUserAgent(std::string agent);
@@ -140,6 +142,8 @@ private:
void HandleGatewayGuildRoleUpdate(const GatewayMessage &msg);
void HandleGatewayGuildRoleCreate(const GatewayMessage &msg);
void HandleGatewayGuildRoleDelete(const GatewayMessage &msg);
+ void HandleGatewayMessageReactionAdd(const GatewayMessage &msg);
+ void HandleGatewayMessageReactionRemove(const GatewayMessage &msg);
void HandleGatewayReconnect(const GatewayMessage &msg);
void HeartbeatThread();
void SendIdentify();
@@ -202,6 +206,8 @@ public:
typedef sigc::signal<void, Snowflake> type_signal_role_update;
typedef sigc::signal<void, Snowflake> type_signal_role_create;
typedef sigc::signal<void, Snowflake> type_signal_role_delete;
+ typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_reaction_add;
+ typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_reaction_remove;
typedef sigc::signal<void, bool> type_signal_disconnected; // bool true if reconnecting
typedef sigc::signal<void> type_signal_connected;
@@ -219,6 +225,8 @@ public:
type_signal_role_update signal_role_update();
type_signal_role_create signal_role_create();
type_signal_role_delete signal_role_delete();
+ type_signal_reaction_add signal_reaction_add();
+ type_signal_reaction_remove signal_reaction_remove();
type_signal_disconnected signal_disconnected();
type_signal_connected signal_connected();
@@ -237,6 +245,8 @@ protected:
type_signal_role_update m_signal_role_update;
type_signal_role_create m_signal_role_create;
type_signal_role_delete m_signal_role_delete;
+ type_signal_reaction_add m_signal_reaction_add;
+ type_signal_reaction_remove m_signal_reaction_remove;
type_signal_disconnected m_signal_disconnected;
type_signal_connected m_signal_connected;
};
diff --git a/discord/emoji.cpp b/discord/emoji.cpp
index 1804945..2525b57 100644
--- a/discord/emoji.cpp
+++ b/discord/emoji.cpp
@@ -11,6 +11,23 @@ void from_json(const nlohmann::json &j, Emoji &m) {
JS_O("available", m.IsAvailable);
}
+void to_json(nlohmann::json &j, const Emoji &m) {
+ if (m.ID.IsValid())
+ j["id"] = m.ID;
+ else
+ j["id"] = nullptr;
+ if (m.Name != "")
+ j["name"] = m.Name;
+ else
+ j["name"] = nullptr;
+ JS_IF("roles", m.Roles);
+ JS_IF("user", m.Creator);
+ JS_IF("require_colons", m.NeedsColons);
+ JS_IF("managed", m.IsManaged);
+ JS_IF("animated", m.IsAnimated);
+ JS_IF("available", m.IsAvailable);
+}
+
std::string Emoji::GetURL() const {
return "https://cdn.discordapp.com/emojis/" + std::to_string(ID) + ".png";
}
diff --git a/discord/emoji.hpp b/discord/emoji.hpp
index 18e69d3..71eb83a 100644
--- a/discord/emoji.hpp
+++ b/discord/emoji.hpp
@@ -16,6 +16,7 @@ struct Emoji {
std::optional<bool> IsAvailable;
friend void from_json(const nlohmann::json &j, Emoji &m);
+ friend void to_json(nlohmann::json &j, const Emoji &m);
std::string GetURL() const;
static std::string URLFromID(std::string emoji_id);
diff --git a/discord/message.cpp b/discord/message.cpp
index d0f2082..c0e91c1 100644
--- a/discord/message.cpp
+++ b/discord/message.cpp
@@ -152,6 +152,18 @@ void to_json(nlohmann::json &j, const MessageReferenceData &m) {
JS_IF("guild_id", m.GuildID);
}
+void from_json(const nlohmann::json &j, ReactionData &m) {
+ JS_D("count", m.Count);
+ JS_D("me", m.HasReactedWith);
+ JS_D("emoji", m.Emoji);
+}
+
+void to_json(nlohmann::json &j, const ReactionData &m) {
+ j["count"] = m.Count;
+ j["me"] = m.HasReactedWith;
+ j["emoji"] = m.Emoji;
+}
+
void from_json(const nlohmann::json &j, Message &m) {
JS_D("id", m.ID);
JS_D("channel_id", m.ChannelID);
@@ -170,7 +182,7 @@ void from_json(const nlohmann::json &j, Message &m) {
// JS_O("mention_channels", m.MentionChannels);
JS_D("attachments", m.Attachments);
JS_D("embeds", m.Embeds);
- // JS_O("reactions", m.Reactions);
+ JS_O("reactions", m.Reactions);
JS_O("nonce", m.Nonce);
JS_D("pinned", m.IsPinned);
JS_O("webhook_id", m.WebhookID);
diff --git a/discord/message.hpp b/discord/message.hpp
index 99c58ab..4de9235 100644
--- a/discord/message.hpp
+++ b/discord/message.hpp
@@ -3,6 +3,7 @@
#include "json.hpp"
#include "user.hpp"
#include "sticker.hpp"
+#include "emoji.hpp"
#include <string>
#include <vector>
@@ -140,6 +141,15 @@ struct MessageReferenceData {
friend void to_json(nlohmann::json &j, const MessageReferenceData &m);
};
+struct ReactionData {
+ int Count;
+ bool HasReactedWith;
+ Emoji Emoji;
+
+ friend void from_json(const nlohmann::json &j, ReactionData &m);
+ friend void to_json(nlohmann::json &j, const ReactionData &m);
+};
+
struct Message {
Snowflake ID;
Snowflake ChannelID;
@@ -156,7 +166,7 @@ struct Message {
// std::optional<std::vector<ChannelMentionData>> MentionChannels;
std::vector<AttachmentData> Attachments;
std::vector<EmbedData> Embeds;
- // std::optional<std::vector<ReactionData>> Reactions;
+ std::optional<std::vector<ReactionData>> Reactions;
std::optional<std::string> Nonce;
bool IsPinned;
std::optional<Snowflake> WebhookID;
diff --git a/discord/objects.cpp b/discord/objects.cpp
index 5fad61e..35cb856 100644
--- a/discord/objects.cpp
+++ b/discord/objects.cpp
@@ -216,3 +216,20 @@ void from_json(const nlohmann::json &j, GuildRoleDeleteObject &m) {
JS_D("guild_id", m.GuildID);
JS_D("role_id", m.RoleID);
}
+
+void from_json(const nlohmann::json &j, MessageReactionAddObject &m) {
+ JS_D("user_id", m.UserID);
+ JS_D("channel_id", m.ChannelID);
+ JS_D("message_id", m.MessageID);
+ JS_O("guild_id", m.GuildID);
+ JS_O("member", m.Member);
+ JS_D("emoji", m.Emoji);
+}
+
+void from_json(const nlohmann::json &j, MessageReactionRemoveObject &m) {
+ JS_D("user_id", m.UserID);
+ JS_D("channel_id", m.ChannelID);
+ JS_D("message_id", m.MessageID);
+ JS_O("guild_id", m.GuildID);
+ JS_D("emoji", m.Emoji);
+}
diff --git a/discord/objects.hpp b/discord/objects.hpp
index 2213ba5..a3877a7 100644
--- a/discord/objects.hpp
+++ b/discord/objects.hpp
@@ -49,6 +49,8 @@ enum class GatewayEvent : int {
GUILD_ROLE_UPDATE,
GUILD_ROLE_CREATE,
GUILD_ROLE_DELETE,
+ MESSAGE_REACTION_ADD,
+ MESSAGE_REACTION_REMOVE,
};
struct GatewayMessage {
@@ -298,3 +300,24 @@ struct GuildRoleDeleteObject {
friend void from_json(const nlohmann::json &j, GuildRoleDeleteObject &m);
};
+
+struct MessageReactionAddObject {
+ Snowflake UserID;
+ Snowflake ChannelID;
+ Snowflake MessageID;
+ std::optional<Snowflake> GuildID;
+ std::optional<GuildMember> Member;
+ Emoji Emoji;
+
+ friend void from_json(const nlohmann::json &j, MessageReactionAddObject &m);
+};
+
+struct MessageReactionRemoveObject {
+ Snowflake UserID;
+ Snowflake ChannelID;
+ Snowflake MessageID;
+ std::optional<Snowflake> GuildID;
+ Emoji Emoji;
+
+ friend void from_json(const nlohmann::json &j, MessageReactionRemoveObject &m);
+};
diff --git a/discord/store.cpp b/discord/store.cpp
index 8c5b40e..bf6318a 100644
--- a/discord/store.cpp
+++ b/discord/store.cpp
@@ -219,9 +219,13 @@ void Store::SetMessage(Snowflake id, const Message &message) {
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 (message.Reactions.has_value()) {
+ std::string tmp = nlohmann::json(*message.Reactions).dump();
+ Bind(m_set_msg_stmt, 19, tmp);
+ } else
+ Bind(m_set_msg_stmt, 19, nullptr);
+ Bind(m_set_msg_stmt, 20, message.IsDeleted());
+ Bind(m_set_msg_stmt, 21, message.IsEdited());
if (!RunInsert(m_set_msg_stmt))
fprintf(stderr, "message insert failed: %s\n", sqlite3_errstr(m_db_err));
@@ -468,10 +472,13 @@ std::optional<Message> Store::GetMessage(Snowflake id) const {
Get(m_get_msg_stmt, 17, tmps);
if (tmps != "")
ret.Stickers = nlohmann::json::parse(tmps).get<std::vector<Sticker>>();
+ Get(m_get_msg_stmt, 18, tmps);
+ if (tmps != "")
+ ret.Reactions = nlohmann::json::parse(tmps).get<std::vector<ReactionData>>();
bool tmpb = false;
- Get(m_get_msg_stmt, 18, tmpb);
- if (tmpb) ret.SetDeleted();
Get(m_get_msg_stmt, 19, tmpb);
+ if (tmpb) ret.SetDeleted();
+ Get(m_get_msg_stmt, 20, tmpb);
if (tmpb) ret.SetEdited();
Reset(m_get_msg_stmt);
@@ -589,164 +596,165 @@ void Store::EndTransaction() {
bool Store::CreateTables() {
constexpr const char *create_users = R"(
-CREATE TABLE IF NOT EXISTS users (
-id INTEGER PRIMARY KEY,
-username TEXT NOT NULL,
-discriminator TEXT NOT NULL,
-avatar TEXT,
-bot BOOL,
-system BOOL,
-mfa BOOL,
-locale TEXT,
-verified BOOl,
-email TEXT,
-flags INTEGER,
-premium INTEGER,
-pubflags INTEGER
-)
-)";
+ CREATE TABLE IF NOT EXISTS users (
+ id INTEGER PRIMARY KEY,
+ username TEXT NOT NULL,
+ discriminator TEXT NOT NULL,
+ avatar TEXT,
+ bot BOOL,
+ system BOOL,
+ mfa BOOL,
+ locale TEXT,
+ verified BOOl,
+ email TEXT,
+ 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
-)
-)";
+ 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
-)
-)";
+ 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 */
+ reactions 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
-)
-)";
+ 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
-)
-)";
+ 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
+ )
+ )";
constexpr const char *create_members = R"(
-CREATE TABLE IF NOT EXISTS members (
-user_id INTEGER PRIMARY KEY,
-guild_id INTEGER NOT NULL,
-nickname TEXT,
-roles TEXT NOT NULL, /* json */
-joined_at TEXT NOT NULL,
-premium_since TEXT,
-deaf BOOL NOT NULL,
-mute BOOL NOT NULL
-)
-)";
-
- constexpr char *create_guilds = R"(
-CREATE TABLE IF NOT EXISTS guilds (
-id INTEGER PRIMARY KEY,
-name TEXT NOT NULL,
-icon TEXT NOT NULL,
-splash TEXT,
-owner BOOL,
-owner_id INTEGER NOT NULL,
-permissions INTEGER, /* new */
-voice_region TEXT,
-afk_id INTEGER,
-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,
-widget_channel INTEGER,
-system_flags INTEGER NOT NULL,
-rules_channel INTEGER,
-joined_at TEXT,
-large BOOL,
-unavailable BOOL,
-member_count INTEGER,
-channels TEXT NOT NULL, /* json */
-max_presences INTEGER,
-max_members INTEGER,
-vanity TEXT,
-description TEXT,
-banner_hash TEXT,
-premium_tier INTEGER NOT NULL,
-premium_count INTEGER,
-locale TEXT NOT NULL,
-public_updates_id INTEGER,
-max_video_users INTEGER,
-approx_members INTEGER,
-approx_presences INTEGER,
-lazy BOOL
-)
-)";
-
- constexpr char *create_channels = R"(
-CREATE TABLE IF NOT EXISTS channels (
-id INTEGER PRIMARY KEY,
-type INTEGER NOT NULL,
-guild_id INTEGER,
-position INTEGER,
-overwrites TEXT, /* json */
-name TEXT,
-topic TEXT,
-is_nsfw BOOL,
-last_message_id INTEGER,
-bitrate INTEGER,
-user_limit INTEGER,
-rate_limit INTEGER,
-recipients TEXT, /* json */
-icon TEXT,
-owner_id INTEGER,
-application_id INTEGER,
-parent_id INTEGER,
-last_pin_timestamp TEXT
-)
-)";
+ CREATE TABLE IF NOT EXISTS members (
+ user_id INTEGER PRIMARY KEY,
+ guild_id INTEGER NOT NULL,
+ nickname TEXT,
+ roles TEXT NOT NULL, /* json */
+ joined_at TEXT NOT NULL,
+ premium_since TEXT,
+ deaf BOOL NOT NULL,
+ mute BOOL NOT NULL
+ )
+ )";
+
+ constexpr const char *create_guilds = R"(
+ CREATE TABLE IF NOT EXISTS guilds (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ icon TEXT NOT NULL,
+ splash TEXT,
+ owner BOOL,
+ owner_id INTEGER NOT NULL,
+ permissions INTEGER, /* new */
+ voice_region TEXT,
+ afk_id INTEGER,
+ 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,
+ widget_channel INTEGER,
+ system_flags INTEGER NOT NULL,
+ rules_channel INTEGER,
+ joined_at TEXT,
+ large BOOL,
+ unavailable BOOL,
+ member_count INTEGER,
+ channels TEXT NOT NULL, /* json */
+ max_presences INTEGER,
+ max_members INTEGER,
+ vanity TEXT,
+ description TEXT,
+ banner_hash TEXT,
+ premium_tier INTEGER NOT NULL,
+ premium_count INTEGER,
+ locale TEXT NOT NULL,
+ public_updates_id INTEGER,
+ max_video_users INTEGER,
+ approx_members INTEGER,
+ approx_presences INTEGER,
+ lazy BOOL
+ )
+ )";
+
+ constexpr const char *create_channels = R"(
+ CREATE TABLE IF NOT EXISTS channels (
+ id INTEGER PRIMARY KEY,
+ type INTEGER NOT NULL,
+ guild_id INTEGER,
+ position INTEGER,
+ overwrites TEXT, /* json */
+ name TEXT,
+ topic TEXT,
+ is_nsfw BOOL,
+ last_message_id INTEGER,
+ bitrate INTEGER,
+ user_limit INTEGER,
+ rate_limit INTEGER,
+ recipients TEXT, /* json */
+ icon TEXT,
+ owner_id INTEGER,
+ application_id INTEGER,
+ parent_id INTEGER,
+ last_pin_timestamp TEXT
+ )
+ )";
m_db_err = sqlite3_exec(m_db, create_users, nullptr, nullptr, nullptr);
if (m_db_err != SQLITE_OK) {
@@ -801,84 +809,84 @@ last_pin_timestamp TEXT
bool Store::CreateStatements() {
constexpr const char *set_user = R"(
-REPLACE INTO users VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO users VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_user = R"(
-SELECT * FROM users WHERE id = ?
-)";
+ SELECT * FROM users WHERE id = ?
+ )";
constexpr const char *set_perm = R"(
-REPLACE INTO permissions VALUES (
- ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO permissions VALUES (
+ ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_perm = R"(
-SELECT * FROM permissions WHERE id = ? AND channel_id = ?
-)";
+ SELECT * FROM permissions WHERE id = ? AND channel_id = ?
+ )";
constexpr const char *set_msg = R"(
-REPLACE INTO messages VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO messages VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_msg = R"(
-SELECT * FROM messages WHERE id = ?
-)";
+ SELECT * FROM messages WHERE id = ?
+ )";
constexpr const char *set_role = R"(
-REPLACE INTO roles VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO roles VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_role = R"(
-SELECT * FROM roles WHERE id = ?
-)";
+ SELECT * FROM roles WHERE id = ?
+ )";
constexpr const char *set_emoji = R"(
-REPLACE INTO emojis VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO emojis VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_emoji = R"(
-SELECT * FROM emojis WHERE id = ?
-)";
+ SELECT * FROM emojis WHERE id = ?
+ )";
constexpr const char *set_member = R"(
-REPLACE INTO members VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO members VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_member = R"(
-SELECT * FROM members WHERE user_id = ? AND guild_id = ?
-)";
+ SELECT * FROM members WHERE user_id = ? AND guild_id = ?
+ )";
constexpr const char *set_guild = R"(
-REPLACE INTO guilds VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO guilds VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_guild = R"(
-SELECT * FROM guilds WHERE id = ?
-)";
+ SELECT * FROM guilds WHERE id = ?
+ )";
constexpr const char *set_chan = R"(
-REPLACE INTO channels VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
-)
-)";
+ REPLACE INTO channels VALUES (
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
+ )
+ )";
constexpr const char *get_chan = R"(
-SELECT * FROM channels WHERE id = ?
-)";
+ SELECT * 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) {
diff --git a/discord/user.cpp b/discord/user.cpp
index 78d309b..56f0350 100644
--- a/discord/user.cpp
+++ b/discord/user.cpp
@@ -33,6 +33,13 @@ void from_json(const nlohmann::json &j, User &m) {
JS_ON("phone", m.Phone);
}
+void to_json(nlohmann::json &j, const User &m) {
+ j["id"] = m.ID;
+ j["username"] = m.Username;
+ j["avatar"] = m.Avatar;
+ // rest of stuff as needed im too lazy and its probably not necessary
+}
+
void User::update_from_json(const nlohmann::json &j, User &m) {
JS_RD("username", m.Username);
JS_RD("discriminator", m.Discriminator);
diff --git a/discord/user.hpp b/discord/user.hpp
index a34bd81..04817de 100644
--- a/discord/user.hpp
+++ b/discord/user.hpp
@@ -25,6 +25,7 @@ struct User {
std::string Phone; // null?
friend void from_json(const nlohmann::json &j, User &m);
+ friend void to_json(nlohmann::json &j, const User &m);
static void update_from_json(const nlohmann::json &j, User &m);
bool HasAvatar() const;
diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp
index 0ff95e6..570690a 100644
--- a/windows/mainwindow.cpp
+++ b/windows/mainwindow.cpp
@@ -211,6 +211,14 @@ Snowflake MainWindow::GetChatOldestListedMessage() {
return m_chat.GetOldestListedMessage();
}
+void MainWindow::UpdateChatReactionAdd(Snowflake id, const Glib::ustring &param) {
+ m_chat.UpdateReactions(id);
+}
+
+void MainWindow::UpdateChatReactionRemove(Snowflake id, const Glib::ustring &param) {
+ m_chat.UpdateReactions(id);
+}
+
ChannelList *MainWindow::GetChannelList() {
return &m_channel_list;
}
diff --git a/windows/mainwindow.hpp b/windows/mainwindow.hpp
index aef8ba4..a9ebae4 100644
--- a/windows/mainwindow.hpp
+++ b/windows/mainwindow.hpp
@@ -26,6 +26,8 @@ public:
void UpdateChatPrependHistory(const std::vector<Snowflake> &msgs);
void InsertChatInput(std::string text);
Snowflake GetChatOldestListedMessage();
+ void UpdateChatReactionAdd(Snowflake id, const Glib::ustring &param);
+ void UpdateChatReactionRemove(Snowflake id, const Glib::ustring &param);
ChannelList *GetChannelList();
ChatWindow *GetChatWindow();