summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/abaddon.cpp28
-rw-r--r--src/components/chatmessage.cpp65
-rw-r--r--src/components/chatmessage.hpp5
-rw-r--r--src/discord/discord.cpp68
-rw-r--r--src/discord/discord.hpp6
-rw-r--r--src/discord/httpclient.cpp21
-rw-r--r--src/discord/httpclient.hpp5
-rw-r--r--src/discord/objects.cpp1
-rw-r--r--src/discord/objects.hpp1
-rw-r--r--src/discord/store.cpp22
-rw-r--r--src/discord/store.hpp1
-rw-r--r--src/http.cpp4
-rw-r--r--src/http.hpp2
13 files changed, 168 insertions, 61 deletions
diff --git a/src/abaddon.cpp b/src/abaddon.cpp
index 563a42c..e296aa4 100644
--- a/src/abaddon.cpp
+++ b/src/abaddon.cpp
@@ -390,6 +390,7 @@ void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_
for (const auto child : m_user_menu_roles_submenu->get_children())
delete child;
+
if (guild.has_value() && user.has_value()) {
const auto roles = user->GetSortedRoles();
m_user_menu_roles->set_visible(!roles.empty());
@@ -412,7 +413,7 @@ void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_
if (me == id) {
m_user_menu_ban->set_visible(false);
m_user_menu_kick->set_visible(false);
- m_user_menu_open_dm->set_visible(false);
+ m_user_menu_open_dm->set_sensitive(false);
} else {
const bool has_kick = m_discord.HasGuildPermission(me, guild_id, Permission::KICK_MEMBERS);
const bool has_ban = m_discord.HasGuildPermission(me, guild_id, Permission::BAN_MEMBERS);
@@ -420,7 +421,7 @@ void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_
m_user_menu_kick->set_visible(has_kick && can_manage);
m_user_menu_ban->set_visible(has_ban && can_manage);
- m_user_menu_open_dm->set_visible(true);
+ m_user_menu_open_dm->set_sensitive(m_discord.FindDM(id).has_value());
}
m_user_menu_remove_recipient->hide();
@@ -468,7 +469,7 @@ void Abaddon::SetupUserMenu() {
m_user_menu_ban = Gtk::manage(new Gtk::MenuItem("Ban"));
m_user_menu_kick = Gtk::manage(new Gtk::MenuItem("Kick"));
m_user_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
- m_user_menu_open_dm = Gtk::manage(new Gtk::MenuItem("Open DM"));
+ m_user_menu_open_dm = Gtk::manage(new Gtk::MenuItem("Go to DM"));
m_user_menu_roles = Gtk::manage(new Gtk::MenuItem("Roles"));
m_user_menu_info = Gtk::manage(new Gtk::MenuItem("View Profile"));
m_user_menu_remove_recipient = Gtk::manage(new Gtk::MenuItem("Remove From Group"));
@@ -579,18 +580,9 @@ void Abaddon::on_user_menu_copy_id() {
void Abaddon::on_user_menu_open_dm() {
const auto existing = m_discord.FindDM(m_shown_user_menu_id);
- if (existing.has_value())
+ if (existing.has_value()) {
ActionChannelOpened(*existing);
- else
- m_discord.CreateDM(m_shown_user_menu_id, [this](DiscordError code, Snowflake channel_id) {
- if (code == DiscordError::NONE) {
- // give the gateway a little window to send CHANNEL_CREATE
- auto cb = [this, channel_id] {
- ActionChannelOpened(channel_id);
- };
- Glib::signal_timeout().connect_once(sigc::track_obj(cb, *this), 200);
- }
- });
+ }
}
void Abaddon::on_user_menu_remove_recipient() {
@@ -656,12 +648,17 @@ void Abaddon::ActionJoinGuildDialog() {
}
void Abaddon::ActionChannelOpened(Snowflake id, bool expand_to) {
- if (!id.IsValid() || id == m_main_window->GetChatActiveChannel()) return;
+ if (!id.IsValid()) {
+ m_discord.SetReferringChannel(Snowflake::Invalid);
+ return;
+ }
+ if (id == m_main_window->GetChatActiveChannel()) return;
m_main_window->GetChatWindow()->SetTopic("");
const auto channel = m_discord.GetChannel(id);
if (!channel.has_value()) {
+ m_discord.SetReferringChannel(Snowflake::Invalid);
m_main_window->UpdateChatActiveChannel(Snowflake::Invalid, false);
m_main_window->UpdateChatWindowContents();
return;
@@ -710,6 +707,7 @@ void Abaddon::ActionChannelOpened(Snowflake id, bool expand_to) {
}
m_main_window->UpdateMenus();
+ m_discord.SetReferringChannel(id);
}
void Abaddon::ActionChatLoadHistory(Snowflake id) {
diff --git a/src/components/chatmessage.cpp b/src/components/chatmessage.cpp
index 863349f..1aca81d 100644
--- a/src/components/chatmessage.cpp
+++ b/src/components/chatmessage.cpp
@@ -32,7 +32,6 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(const Message &d
if (!data.Content.empty() || data.Type != MessageType::DEFAULT) {
container->m_text_component = container->CreateTextComponent(data);
- container->AttachEventHandlers(*container->m_text_component);
container->m_main.add(*container->m_text_component);
}
@@ -101,7 +100,6 @@ void ChatMessageItemContainer::UpdateContent() {
if (!data->Embeds.empty()) {
m_embed_component = CreateEmbedsComponent(data->Embeds);
- AttachEventHandlers(*m_embed_component);
m_main.add(*m_embed_component);
m_embed_component->show_all();
}
@@ -155,9 +153,9 @@ void ChatMessageItemContainer::AddClickHandler(Gtk::Widget *widget, const std::s
widget->signal_button_press_event().connect([url](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
LaunchBrowser(url);
- return false;
+ return true;
}
- return true;
+ return false;
}, false);
// clang-format on
}
@@ -174,6 +172,8 @@ Gtk::TextView *ChatMessageItemContainer::CreateTextComponent(const Message &data
tv->set_halign(Gtk::ALIGN_FILL);
tv->set_hexpand(true);
+ tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnTextViewButtonPress), false);
+
UpdateTextComponent(tv);
return tv;
@@ -281,8 +281,6 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
tag->property_weight() = Pango::WEIGHT_BOLD;
m_channel_tagmap[tag] = *data->MessageReference->ChannelID;
b->insert_with_tag(iter, data->Content, tag);
-
- tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnClickChannel), false);
} else {
b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " started a thread: </span><b>" + Glib::Markup::escape_text(data->Content) + "</b></i>");
}
@@ -297,12 +295,10 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedsComponent(const std::vector<E
if (IsEmbedImageOnly(embed)) {
auto *widget = CreateImageComponent(*embed.Thumbnail->ProxyURL, *embed.Thumbnail->URL, *embed.Thumbnail->Width, *embed.Thumbnail->Height);
widget->show();
- AttachEventHandlers(*widget);
box->add(*widget);
} else {
auto *widget = CreateEmbedComponent(embed);
widget->show();
- AttachEventHandlers(*widget);
box->add(*widget);
}
}
@@ -493,12 +489,22 @@ Gtk::Widget *ChatMessageItemContainer::CreateImageComponent(const std::string &p
Gtk::EventBox *ev = Gtk::manage(new Gtk::EventBox);
Gtk::Image *widget = Gtk::manage(new LazyImage(proxy_url, w, h, false));
ev->add(*widget);
+ ev->set_halign(Gtk::ALIGN_START);
widget->set_halign(Gtk::ALIGN_START);
widget->set_size_request(w, h);
- AttachEventHandlers(*ev);
AddClickHandler(ev, url);
+ const auto on_button_press_event = [this, url](GdkEventButton *e) -> bool {
+ if (e->type == GDK_BUTTON_PRESS && e->button == GDK_BUTTON_SECONDARY) {
+ m_selected_link = url;
+ m_link_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(e));
+ return true;
+ }
+ return false;
+ };
+ ev->signal_button_press_event().connect(on_button_press_event, false);
+
return ev;
}
@@ -510,9 +516,18 @@ Gtk::Widget *ChatMessageItemContainer::CreateAttachmentComponent(const Attachmen
ev->get_style_context()->add_class("message-attachment-box");
ev->add(*btn);
- AttachEventHandlers(*ev);
AddClickHandler(ev, data.URL);
+ const auto on_button_press_event = [this, url = data.URL](GdkEventButton *e) -> bool {
+ if (e->type == GDK_BUTTON_PRESS && e->button == GDK_BUTTON_SECONDARY) {
+ m_selected_link = url;
+ m_link_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(e));
+ return true;
+ }
+ return false;
+ };
+ ev->signal_button_press_event().connect(on_button_press_event, false);
+
return ev;
}
@@ -534,7 +549,6 @@ Gtk::Widget *ChatMessageItemContainer::CreateStickersComponent(const std::vector
box->show();
- AttachEventHandlers(*box);
return box;
}
@@ -956,7 +970,6 @@ void ChatMessageItemContainer::HandleChannelMentions(const Glib::RefPtr<Gtk::Tex
}
void ChatMessageItemContainer::HandleChannelMentions(Gtk::TextView *tv) {
- tv->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnClickChannel), false);
HandleChannelMentions(tv->get_buffer());
}
@@ -990,6 +1003,20 @@ bool ChatMessageItemContainer::OnClickChannel(GdkEventButton *ev) {
return false;
}
+bool ChatMessageItemContainer::OnTextViewButtonPress(GdkEventButton *ev) {
+ // run all button press handlers and propagate if none return true
+ if (OnLinkClick(ev)) return true;
+ if (OnClickChannel(ev)) return true;
+
+ if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_SECONDARY) {
+ // send the event upward skipping TextView's handler because we dont want it
+ gtk_propagate_event(GTK_WIDGET(m_main.gobj()), reinterpret_cast<GdkEvent *>(ev));
+ return true;
+ }
+
+ return false;
+}
+
void ChatMessageItemContainer::on_link_menu_copy() {
Gtk::Clipboard::get()->set_text(m_selected_link);
}
@@ -997,8 +1024,6 @@ void ChatMessageItemContainer::on_link_menu_copy() {
void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) {
const auto rgx = Glib::Regex::create(R"(\bhttps?:\/\/[^\s]+\.[^\s]+\b)");
- tv.signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::OnLinkClick), false);
-
auto buf = tv.get_buffer();
Glib::ustring text = GetText(buf);
@@ -1070,18 +1095,6 @@ ChatMessageItemContainer::type_signal_action_reaction_remove ChatMessageItemCont
return m_signal_action_reaction_remove;
}
-void ChatMessageItemContainer::AttachEventHandlers(Gtk::Widget &widget) {
- const auto on_button_press_event = [this](GdkEventButton *e) -> bool {
- if (e->type == GDK_BUTTON_PRESS && e->button == GDK_BUTTON_SECONDARY) {
- event(reinterpret_cast<GdkEvent *>(e)); // illegal ooooooh
- return true;
- }
-
- return false;
- };
- widget.signal_button_press_event().connect(on_button_press_event, false);
-}
-
ChatMessageHeader::ChatMessageHeader(const Message &data)
: m_main_box(Gtk::ORIENTATION_HORIZONTAL)
, m_content_box(Gtk::ORIENTATION_VERTICAL)
diff --git a/src/components/chatmessage.hpp b/src/components/chatmessage.hpp
index 86c3fea..7851351 100644
--- a/src/components/chatmessage.hpp
+++ b/src/components/chatmessage.hpp
@@ -2,7 +2,7 @@
#include <gtkmm.h>
#include "discord/discord.hpp"
-class ChatMessageItemContainer : public Gtk::Box {
+class ChatMessageItemContainer : public Gtk::EventBox {
public:
Snowflake ID;
Snowflake ChannelID;
@@ -44,6 +44,7 @@ protected:
void HandleChannelMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf);
void HandleChannelMentions(Gtk::TextView *tv);
bool OnClickChannel(GdkEventButton *ev);
+ bool OnTextViewButtonPress(GdkEventButton *ev);
// reused for images and links
Gtk::Menu m_link_menu;
@@ -57,8 +58,6 @@ protected:
std::map<Glib::RefPtr<Gtk::TextTag>, std::string> m_link_tagmap;
std::map<Glib::RefPtr<Gtk::TextTag>, Snowflake> m_channel_tagmap;
- void AttachEventHandlers(Gtk::Widget &widget);
-
Gtk::EventBox *_ev;
Gtk::Box m_main;
Gtk::Label *m_attrib_label = nullptr;
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index d2c8edd..48f08d6 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -30,6 +30,7 @@ void DiscordClient::Start() {
if (m_client_started) return;
m_http.SetBase(GetAPIURL());
+ SetHeaders();
std::memset(&m_zstream, 0, sizeof(m_zstream));
inflateInit2(&m_zstream, MAX_WBITS + 32);
@@ -610,19 +611,6 @@ void DiscordClient::UpdateStatus(PresenceStatus status, bool is_afk, const Activ
m_signal_presence_update.emit(GetUserData(), status);
}
-void DiscordClient::CreateDM(Snowflake user_id, const sigc::slot<void(DiscordError code, Snowflake channel_id)> &callback) {
- CreateDMObject obj;
- obj.Recipients.push_back(user_id);
- m_http.MakePOST("/users/@me/channels", nlohmann::json(obj).dump(), [callback](const http::response &response) {
- if (!CheckCode(response)) {
- callback(DiscordError::NONE, Snowflake::Invalid);
- return;
- }
- auto channel = nlohmann::json::parse(response.text).get<ChannelData>();
- callback(GetCodeFromResponse(response), channel.ID);
- });
-}
-
void DiscordClient::CloseDM(Snowflake channel_id) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id), [](const http::response &response) {
CheckCode(response);
@@ -1181,6 +1169,25 @@ void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateI
});
}
+void DiscordClient::SetReferringChannel(Snowflake id) {
+ if (!id.IsValid()) {
+ m_http.SetPersistentHeader("Referer", "https://discord.com/channels/@me");
+ } else {
+ const auto channel = GetChannel(id);
+ if (channel.has_value()) {
+ if (channel->IsDM()) {
+ m_http.SetPersistentHeader("Referer", "https://discord.com/channels/@me/" + std::to_string(id));
+ } else if (channel->GuildID.has_value()) {
+ m_http.SetPersistentHeader("Referer", "https://discord.com/channels/" + std::to_string(*channel->GuildID) + "/" + std::to_string(id));
+ } else {
+ m_http.SetPersistentHeader("Referer", "https://discord.com/channels/@me");
+ }
+ } else {
+ m_http.SetPersistentHeader("Referer", "https://discord.com/channels/@me");
+ }
+ }
+}
+
void DiscordClient::UpdateToken(const std::string &token) {
if (!IsStarted()) {
m_token = token;
@@ -2294,7 +2301,7 @@ void DiscordClient::HeartbeatThread() {
void DiscordClient::SendIdentify() {
IdentifyMessage msg;
msg.Token = m_token;
- msg.Capabilities = 125; // no idea what this is
+ msg.Capabilities = 509; // no idea what this is
msg.Properties.OS = "Windows";
msg.Properties.Browser = "Chrome";
msg.Properties.Device = "";
@@ -2307,7 +2314,7 @@ void DiscordClient::SendIdentify() {
msg.Properties.ReferrerCurrent = "";
msg.Properties.ReferringDomainCurrent = "";
msg.Properties.ReleaseChannel = "stable";
- msg.Properties.ClientBuildNumber = 105691;
+ msg.Properties.ClientBuildNumber = 141021;
msg.Properties.ClientEventSource = "";
msg.Presence.Status = "online";
msg.Presence.Since = 0;
@@ -2316,6 +2323,7 @@ void DiscordClient::SendIdentify() {
msg.ClientState.HighestLastMessageID = "0";
msg.ClientState.ReadStateVersion = 0;
msg.ClientState.UserGuildSettingsVersion = -1;
+ SetSuperPropertiesFromIdentity(msg);
const bool b = m_websocket.GetPrintMessages();
m_websocket.SetPrintMessages(false);
m_websocket.Send(msg);
@@ -2330,6 +2338,36 @@ void DiscordClient::SendResume() {
m_websocket.Send(msg);
}
+void DiscordClient::SetHeaders() {
+ m_http.SetPersistentHeader("Sec-Fetch-Dest", "empty");
+ m_http.SetPersistentHeader("Sec-Fetch-Mode", "cors");
+ m_http.SetPersistentHeader("Sec-Fetch-Site", "same-origin");
+ m_http.SetPersistentHeader("X-Debug-Options", "bugReporterEnabled");
+ m_http.SetPersistentHeader("Accept-Language", "en-US,en;q=0.9");
+
+ SetReferringChannel(Snowflake::Invalid);
+}
+
+void DiscordClient::SetSuperPropertiesFromIdentity(const IdentifyMessage &identity) {
+ nlohmann::ordered_json j;
+ j["os"] = identity.Properties.OS;
+ j["browser"] = identity.Properties.Browser;
+ j["device"] = identity.Properties.Device;
+ j["system_locale"] = identity.Properties.SystemLocale;
+ j["browser_user_agent"] = identity.Properties.BrowserUserAgent;
+ j["browser_version"] = identity.Properties.BrowserVersion;
+ j["os_version"] = identity.Properties.OSVersion;
+ j["referrer"] = identity.Properties.Referrer;
+ j["referring_domain"] = identity.Properties.ReferringDomain;
+ j["referrer_current"] = identity.Properties.ReferrerCurrent;
+ j["referring_domain_current"] = identity.Properties.ReferringDomainCurrent;
+ j["release_channel"] = identity.Properties.ReleaseChannel;
+ j["client_build_number"] = identity.Properties.ClientBuildNumber;
+ j["client_event_source"] = nullptr; // probably will never be non-null ("") anyways
+ m_http.SetPersistentHeader("X-Super-Properties", Glib::Base64::encode(j.dump()));
+ m_http.SetPersistentHeader("X-Discord-Locale", identity.Properties.SystemLocale);
+}
+
void DiscordClient::HandleSocketOpen() {
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 718cb83..b1b623b 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -119,7 +119,6 @@ public:
void BanUser(Snowflake user_id, Snowflake guild_id); // todo: reason, delete messages
void UpdateStatus(PresenceStatus status, bool is_afk);
void UpdateStatus(PresenceStatus status, bool is_afk, const ActivityData &obj);
- void CreateDM(Snowflake user_id, const sigc::slot<void(DiscordError code, Snowflake channel_id)> &callback);
void CloseDM(Snowflake channel_id);
std::optional<Snowflake> FindDM(Snowflake user_id); // wont find group dms
void AddReaction(Snowflake id, Glib::ustring param);
@@ -205,6 +204,8 @@ public:
void GetVerificationGateInfo(Snowflake guild_id, const sigc::slot<void(std::optional<VerificationGateInfoObject>)> &callback);
void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, const sigc::slot<void(DiscordError code)> &callback);
+ void SetReferringChannel(Snowflake id);
+
void UpdateToken(const std::string &token);
void SetUserAgent(const std::string &agent);
@@ -286,6 +287,9 @@ private:
void SendIdentify();
void SendResume();
+ void SetHeaders();
+ void SetSuperPropertiesFromIdentity(const IdentifyMessage &identity);
+
void HandleSocketOpen();
void HandleSocketClose(uint16_t code);
diff --git a/src/discord/httpclient.cpp b/src/discord/httpclient.cpp
index 6646bf3..d13246d 100644
--- a/src/discord/httpclient.cpp
+++ b/src/discord/httpclient.cpp
@@ -19,11 +19,17 @@ void HTTPClient::SetAuth(std::string auth) {
m_authorization = std::move(auth);
}
+void HTTPClient::SetPersistentHeader(std::string name, std::string value) {
+ m_headers.insert_or_assign(std::move(name), std::move(value));
+}
+
void HTTPClient::MakeDELETE(const std::string &path, const std::function<void(http::response_type r)> &cb) {
printf("DELETE %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
http::request req(http::REQUEST_DELETE, m_api_base + path);
+ AddHeaders(req);
req.set_header("Authorization", m_authorization);
+ req.set_header("Origin", "https://discord.com");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
@@ -40,8 +46,10 @@ void HTTPClient::MakePATCH(const std::string &path, const std::string &payload,
printf("PATCH %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PATCH, m_api_base + path);
+ AddHeaders(req);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
+ req.set_header("Origin", "https://discord.com");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
@@ -59,8 +67,10 @@ void HTTPClient::MakePOST(const std::string &path, const std::string &payload, c
printf("POST %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_POST, m_api_base + path);
+ AddHeaders(req);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
+ req.set_header("Origin", "https://discord.com");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
@@ -78,7 +88,9 @@ void HTTPClient::MakePUT(const std::string &path, const std::string &payload, co
printf("PUT %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PUT, m_api_base + path);
+ AddHeaders(req);
req.set_header("Authorization", m_authorization);
+ req.set_header("Origin", "https://discord.com");
if (!payload.empty())
req.set_header("Content-Type", "application/json");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
@@ -98,8 +110,8 @@ void HTTPClient::MakeGET(const std::string &path, const std::function<void(http:
printf("GET %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
http::request req(http::REQUEST_GET, m_api_base + path);
+ AddHeaders(req);
req.set_header("Authorization", m_authorization);
- req.set_header("Content-Type", "application/json");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
@@ -147,6 +159,13 @@ void HTTPClient::RunCallbacks() {
m_mutex.unlock();
}
+void HTTPClient::AddHeaders(http::request &r) {
+ for (const auto &[name, val] : m_headers) {
+ r.set_header(name, val);
+ }
+ curl_easy_setopt(r.get_curl(), CURLOPT_ACCEPT_ENCODING, "gzip, deflate, br");
+}
+
void HTTPClient::OnResponse(const http::response_type &r, const std::function<void(http::response_type r)> &cb) {
CleanupFutures();
try {
diff --git a/src/discord/httpclient.hpp b/src/discord/httpclient.hpp
index 366d4eb..83b1f5a 100644
--- a/src/discord/httpclient.hpp
+++ b/src/discord/httpclient.hpp
@@ -17,6 +17,8 @@ public:
void SetUserAgent(std::string agent);
void SetAuth(std::string auth);
+ void SetPersistentHeader(std::string name, std::string value);
+
void MakeDELETE(const std::string &path, const std::function<void(http::response_type r)> &cb);
void MakeGET(const std::string &path, const std::function<void(http::response_type r)> &cb);
void MakePATCH(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb);
@@ -27,6 +29,8 @@ public:
void Execute(http::request &&req, const std::function<void(http::response_type r)> &cb);
private:
+ void AddHeaders(http::request &r);
+
void OnResponse(const http::response_type &r, const std::function<void(http::response_type r)> &cb);
void CleanupFutures();
@@ -39,4 +43,5 @@ private:
std::string m_api_base;
std::string m_authorization;
std::string m_agent;
+ std::unordered_map<std::string, std::string> m_headers;
};
diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp
index f796000..e43e05a 100644
--- a/src/discord/objects.cpp
+++ b/src/discord/objects.cpp
@@ -262,6 +262,7 @@ void to_json(nlohmann::json &j, const ClientStateProperties &m) {
j["highest_last_message_id"] = m.HighestLastMessageID;
j["read_state_version"] = m.ReadStateVersion;
j["user_guild_settings_version"] = m.UserGuildSettingsVersion;
+ j["user_settings_version"] = m.UserSettingsVersion;
}
void to_json(nlohmann::json &j, const IdentifyMessage &m) {
diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp
index db5fd76..9db9369 100644
--- a/src/discord/objects.hpp
+++ b/src/discord/objects.hpp
@@ -382,6 +382,7 @@ struct ClientStateProperties {
std::string HighestLastMessageID = "0";
int ReadStateVersion = 0;
int UserGuildSettingsVersion = -1;
+ int UserSettingsVersion = -1;
friend void to_json(nlohmann::json &j, const ClientStateProperties &m);
};
diff --git a/src/discord/store.cpp b/src/discord/store.cpp
index 663d113..892f4aa 100644
--- a/src/discord/store.cpp
+++ b/src/discord/store.cpp
@@ -254,6 +254,14 @@ void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMem
s->Reset();
{
+ auto &s = m_stmt_clr_member_roles;
+ s->Bind(1, user_id);
+ s->Bind(2, guild_id);
+ s->Step();
+ s->Reset();
+ }
+
+ {
auto &s = m_stmt_set_member_roles;
BeginTransaction();
@@ -1882,6 +1890,20 @@ bool Store::CreateStatements() {
return false;
}
+ m_stmt_clr_member_roles = std::make_unique<Statement>(m_db, R"(
+ DELETE FROM member_roles
+ WHERE user = ? AND
+ EXISTS (
+ SELECT 1 FROM roles
+ WHERE member_roles.role = roles.id
+ AND roles.guild = ?
+ )
+ )");
+ if (!m_stmt_clr_member_roles->OK()) {
+ fprintf(stderr, "failed to prepare clear member roles statement: %s\n", m_db.ErrStr());
+ return false;
+ }
+
m_stmt_set_guild_emoji = std::make_unique<Statement>(m_db, R"(
REPLACE INTO guild_emojis VALUES (
?, ?
diff --git a/src/discord/store.hpp b/src/discord/store.hpp
index d863fa6..da97dd5 100644
--- a/src/discord/store.hpp
+++ b/src/discord/store.hpp
@@ -281,6 +281,7 @@ private:
STMT(set_interaction);
STMT(set_member_roles);
STMT(get_member_roles);
+ STMT(clr_member_roles);
STMT(set_guild_emoji);
STMT(get_guild_emojis);
STMT(clr_guild_emoji);
diff --git a/src/http.cpp b/src/http.cpp
index ba3ce3c..0fae39f 100644
--- a/src/http.cpp
+++ b/src/http.cpp
@@ -98,6 +98,10 @@ void request::set_user_agent(const std::string &data) {
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, data.c_str());
}
+CURL *request::get_curl() {
+ return m_curl;
+}
+
void request::make_form() {
m_form = curl_mime_init(m_curl);
}
diff --git a/src/http.hpp b/src/http.hpp
index 90a514a..5bf3c69 100644
--- a/src/http.hpp
+++ b/src/http.hpp
@@ -122,6 +122,8 @@ struct request {
response execute();
+ CURL *get_curl();
+
private:
void prepare();