summaryrefslogtreecommitdiff
path: root/src/discord
diff options
context:
space:
mode:
authorouwou <26526779+ouwou@users.noreply.github.com>2022-08-12 18:35:58 -0400
committerouwou <26526779+ouwou@users.noreply.github.com>2022-08-12 18:35:58 -0400
commit6a5ecb4d9584f7276cf1d592c95811a66943f61c (patch)
tree2c09268db37461ce9e1882789183c508bb36dd4a /src/discord
parentdc28eae95a46c3079fcc76b3425ffa37844dd37d (diff)
parentf60cea2216dd9677cb9105364cdaa778a0c187db (diff)
downloadabaddon-portaudio-6a5ecb4d9584f7276cf1d592c95811a66943f61c.tar.gz
abaddon-portaudio-6a5ecb4d9584f7276cf1d592c95811a66943f61c.zip
Merge branch 'attachments'
Diffstat (limited to 'src/discord')
-rw-r--r--src/discord/chatsubmitparams.hpp24
-rw-r--r--src/discord/discord.cpp98
-rw-r--r--src/discord/discord.hpp14
-rw-r--r--src/discord/guild.hpp9
-rw-r--r--src/discord/httpclient.cpp19
-rw-r--r--src/discord/httpclient.hpp3
-rw-r--r--src/discord/store.cpp1
-rw-r--r--src/discord/user.cpp4
-rw-r--r--src/discord/user.hpp1
9 files changed, 151 insertions, 22 deletions
diff --git a/src/discord/chatsubmitparams.hpp b/src/discord/chatsubmitparams.hpp
new file mode 100644
index 0000000..6199634
--- /dev/null
+++ b/src/discord/chatsubmitparams.hpp
@@ -0,0 +1,24 @@
+#pragma once
+#include <vector>
+#include <string>
+#include <glibmm/ustring.h>
+#include <giomm/file.h>
+#include "discord/snowflake.hpp"
+
+struct ChatSubmitParams {
+ enum AttachmentType {
+ PastedImage,
+ ExtantFile,
+ };
+
+ struct Attachment {
+ Glib::RefPtr<Gio::File> File;
+ AttachmentType Type;
+ std::string Filename;
+ };
+
+ Snowflake ChannelID;
+ Snowflake InReplyToID;
+ Glib::ustring Message;
+ std::vector<Attachment> Attachments;
+};
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index 4b6f6dd..48f08d6 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -319,6 +319,10 @@ bool DiscordClient::HasGuildPermission(Snowflake user_id, Snowflake guild_id, Pe
return (base & perm) == perm;
}
+bool DiscordClient::HasSelfChannelPermission(Snowflake channel_id, Permission perm) const {
+ return HasChannelPermission(m_user_data.ID, channel_id, perm);
+}
+
bool DiscordClient::HasAnyChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const {
const auto channel = m_store.GetChannel(channel_id);
if (!channel.has_value() || !channel->GuildID.has_value()) return false;
@@ -410,7 +414,7 @@ bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowfla
return actor_highest->Position > target_highest->Position;
}
-void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::response_type &response) {
+void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::response_type &response, const sigc::slot<void(DiscordError)> &callback) {
if (!CheckCode(response)) {
if (response.status_code == http::TooManyRequests) {
try { // not sure if this body is guaranteed
@@ -422,54 +426,108 @@ void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::re
} else {
m_signal_message_send_fail.emit(nonce, 0);
}
+
+ // todo actually callback with correct error code (not necessary rn)
+ callback(DiscordError::GENERIC);
+ } else {
+ callback(DiscordError::NONE);
}
}
-void DiscordClient::SendChatMessage(const std::string &content, Snowflake channel) {
- // @([^@#]{1,32})#(\\d{4})
+void DiscordClient::SendChatMessageNoAttachments(const ChatSubmitParams &params, const sigc::slot<void(DiscordError)> &callback) {
const auto nonce = std::to_string(Snowflake::FromNow());
+
CreateMessageObject obj;
- obj.Content = content;
+ obj.Content = params.Message;
obj.Nonce = nonce;
- m_http.MakePOST("/channels/" + std::to_string(channel) + "/messages", nlohmann::json(obj).dump(), sigc::bind<0>(sigc::mem_fun(*this, &DiscordClient::ChatMessageCallback), nonce));
- // dummy data so the content can be shown while waiting for MESSAGE_CREATE
+ if (params.InReplyToID.IsValid())
+ obj.MessageReference.emplace().MessageID = params.InReplyToID;
+
+ m_http.MakePOST("/channels/" + std::to_string(params.ChannelID) + "/messages",
+ nlohmann::json(obj).dump(),
+ [this, nonce, callback](const http::response_type &r) {
+ ChatMessageCallback(nonce, r, callback);
+ });
+
+ // dummy preview data
Message tmp;
- tmp.Content = content;
+ tmp.Content = params.Message;
tmp.ID = nonce;
- tmp.ChannelID = channel;
+ tmp.ChannelID = params.ChannelID;
tmp.Author = GetUserData();
tmp.IsTTS = false;
tmp.DoesMentionEveryone = false;
tmp.Type = MessageType::DEFAULT;
tmp.IsPinned = false;
tmp.Timestamp = "2000-01-01T00:00:00.000000+00:00";
- tmp.Nonce = obj.Nonce;
+ tmp.Nonce = nonce;
tmp.IsPending = true;
+
m_store.SetMessage(tmp.ID, tmp);
- m_signal_message_sent.emit(tmp);
+ m_signal_message_create.emit(tmp);
}
-void DiscordClient::SendChatMessage(const std::string &content, Snowflake channel, Snowflake referenced_message) {
+void DiscordClient::SendChatMessageAttachments(const ChatSubmitParams &params, const sigc::slot<void(DiscordError)> &callback) {
const auto nonce = std::to_string(Snowflake::FromNow());
+
CreateMessageObject obj;
- obj.Content = content;
+ obj.Content = params.Message;
obj.Nonce = nonce;
- obj.MessageReference.emplace().MessageID = referenced_message;
- m_http.MakePOST("/channels/" + std::to_string(channel) + "/messages", nlohmann::json(obj).dump(), sigc::bind<0>(sigc::mem_fun(*this, &DiscordClient::ChatMessageCallback), nonce));
+ if (params.InReplyToID.IsValid())
+ obj.MessageReference.emplace().MessageID = params.InReplyToID;
+
+ auto req = m_http.CreateRequest(http::REQUEST_POST, "/channels/" + std::to_string(params.ChannelID) + "/messages");
+ m_progress_cb_timer.start();
+ req.set_progress_callback([this, nonce](curl_off_t ultotal, curl_off_t ulnow) {
+ if (m_progress_cb_timer.elapsed() < 0.0417) return; // try to prevent it from blocking ui
+ m_progress_cb_timer.start();
+ m_generic_mutex.lock();
+ m_generic_queue.push([this, nonce, ultotal, ulnow] {
+ m_signal_message_progress.emit(
+ nonce,
+ static_cast<float>(ulnow) / static_cast<float>(ultotal));
+ });
+ m_generic_mutex.unlock();
+ m_generic_dispatch.emit();
+ });
+ req.make_form();
+ req.add_field("payload_json", nlohmann::json(obj).dump().c_str(), CURL_ZERO_TERMINATED);
+ for (size_t i = 0; i < params.Attachments.size(); i++) {
+ const auto field_name = "files[" + std::to_string(i) + "]";
+ req.add_file(field_name, params.Attachments.at(i).File, params.Attachments.at(i).Filename);
+ }
+ m_http.Execute(std::move(req), [this, params, nonce, callback](const http::response_type &res) {
+ for (const auto &attachment : params.Attachments) {
+ if (attachment.Type == ChatSubmitParams::AttachmentType::PastedImage) {
+ attachment.File->remove();
+ }
+ }
+ ChatMessageCallback(nonce, res, callback);
+ });
+
+ // dummy preview data
Message tmp;
- tmp.Content = content;
+ tmp.Content = params.Message;
tmp.ID = nonce;
- tmp.ChannelID = channel;
+ tmp.ChannelID = params.ChannelID;
tmp.Author = GetUserData();
tmp.IsTTS = false;
tmp.DoesMentionEveryone = false;
tmp.Type = MessageType::DEFAULT;
tmp.IsPinned = false;
tmp.Timestamp = "2000-01-01T00:00:00.000000+00:00";
- tmp.Nonce = obj.Nonce;
+ tmp.Nonce = nonce;
tmp.IsPending = true;
+
m_store.SetMessage(tmp.ID, tmp);
- m_signal_message_sent.emit(tmp);
+ m_signal_message_create.emit(tmp);
+}
+
+void DiscordClient::SendChatMessage(const ChatSubmitParams &params, const sigc::slot<void(DiscordError)> &callback) {
+ if (params.Attachments.empty())
+ SendChatMessageNoAttachments(params, callback);
+ else
+ SendChatMessageAttachments(params, callback);
}
void DiscordClient::DeleteMessage(Snowflake channel_id, Snowflake id) {
@@ -2577,6 +2635,10 @@ DiscordClient::type_signal_connected DiscordClient::signal_connected() {
return m_signal_connected;
}
+DiscordClient::type_signal_message_progress DiscordClient::signal_message_progress() {
+ return m_signal_message_progress;
+}
+
DiscordClient::type_signal_role_update DiscordClient::signal_role_update() {
return m_signal_role_update;
}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 75b681a..b1b623b 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -3,6 +3,7 @@
#include "httpclient.hpp"
#include "objects.hpp"
#include "store.hpp"
+#include "chatsubmitparams.hpp"
#include <sigc++/sigc++.h>
#include <nlohmann/json.hpp>
#include <thread>
@@ -96,16 +97,18 @@ public:
bool IsThreadJoined(Snowflake thread_id) const;
bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const;
+ bool HasSelfChannelPermission(Snowflake channel_id, Permission perm) const;
bool HasAnyChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const;
bool HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const;
Permission ComputePermissions(Snowflake member_id, Snowflake guild_id) const;
Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const;
bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name)
- void ChatMessageCallback(const std::string &nonce, const http::response_type &response);
+ void ChatMessageCallback(const std::string &nonce, const http::response_type &response, const sigc::slot<void(DiscordError code)> &callback);
+ void SendChatMessageNoAttachments(const ChatSubmitParams &params, const sigc::slot<void(DiscordError code)> &callback);
+ void SendChatMessageAttachments(const ChatSubmitParams &params, const sigc::slot<void(DiscordError code)> &callback);
- void SendChatMessage(const std::string &content, Snowflake channel);
- void SendChatMessage(const std::string &content, Snowflake channel, Snowflake referenced_message);
+ void SendChatMessage(const ChatSubmitParams &params, const sigc::slot<void(DiscordError code)> &callback);
void DeleteMessage(Snowflake channel_id, Snowflake id);
void EditMessage(Snowflake channel_id, Snowflake id, std::string content);
void SendLazyLoad(Snowflake id);
@@ -346,6 +349,8 @@ private:
Glib::Dispatcher m_generic_dispatch;
std::queue<std::function<void()>> m_generic_queue;
+ Glib::Timer m_progress_cb_timer;
+
std::set<Snowflake> m_channels_pinned_requested;
std::set<Snowflake> m_channels_lazy_loaded;
@@ -405,6 +410,7 @@ public:
typedef sigc::signal<void, std::string /* nonce */, float /* retry_after */> type_signal_message_send_fail; // retry after param will be 0 if it failed for a reason that isnt slowmode
typedef sigc::signal<void, bool, GatewayCloseCode> type_signal_disconnected; // bool true if reconnecting
typedef sigc::signal<void> type_signal_connected;
+ typedef sigc::signal<void, std::string, float> type_signal_message_progress;
type_signal_gateway_ready signal_gateway_ready();
type_signal_message_create signal_message_create();
@@ -458,6 +464,7 @@ public:
type_signal_message_send_fail signal_message_send_fail();
type_signal_disconnected signal_disconnected();
type_signal_connected signal_connected();
+ type_signal_message_progress signal_message_progress();
protected:
type_signal_gateway_ready m_signal_gateway_ready;
@@ -512,4 +519,5 @@ protected:
type_signal_message_send_fail m_signal_message_send_fail;
type_signal_disconnected m_signal_disconnected;
type_signal_connected m_signal_connected;
+ type_signal_message_progress m_signal_message_progress;
};
diff --git a/src/discord/guild.hpp b/src/discord/guild.hpp
index 152e250..0428928 100644
--- a/src/discord/guild.hpp
+++ b/src/discord/guild.hpp
@@ -16,6 +16,13 @@ enum class GuildApplicationStatus {
UNKNOWN,
};
+enum class GuildPremiumTier {
+ NONE = 0,
+ TIER_1 = 1,
+ TIER_2 = 2,
+ TIER_3 = 3,
+};
+
struct GuildApplicationData {
Snowflake UserID;
Snowflake GuildID;
@@ -73,7 +80,7 @@ struct GuildData {
std::optional<std::string> VanityURL; // null
std::optional<std::string> Description; // null
std::optional<std::string> BannerHash; // null
- std::optional<int> PremiumTier;
+ std::optional<GuildPremiumTier> PremiumTier;
std::optional<int> PremiumSubscriptionCount;
std::optional<std::string> PreferredLocale;
std::optional<Snowflake> PublicUpdatesChannelID; // null
diff --git a/src/discord/httpclient.cpp b/src/discord/httpclient.cpp
index cd699f0..d13246d 100644
--- a/src/discord/httpclient.cpp
+++ b/src/discord/httpclient.cpp
@@ -124,6 +124,25 @@ void HTTPClient::MakeGET(const std::string &path, const std::function<void(http:
}));
}
+http::request HTTPClient::CreateRequest(http::EMethod method, std::string path) {
+ http::request req(method, m_api_base + path);
+ req.set_header("Authorization", m_authorization);
+ req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
+#ifdef USE_LOCAL_PROXY
+ req.set_proxy("http://127.0.0.1:8888");
+ req.set_verify_ssl(false);
+#endif
+ return req;
+}
+
+void HTTPClient::Execute(http::request &&req, const std::function<void(http::response_type r)> &cb) {
+ printf("%s %s\n", req.get_method(), req.get_url().c_str());
+ m_futures.push_back(std::async(std::launch::async, [this, cb, req = std::move(req)]() mutable {
+ auto res = req.execute();
+ OnResponse(res, cb);
+ }));
+}
+
void HTTPClient::CleanupFutures() {
for (auto it = m_futures.begin(); it != m_futures.end();) {
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
diff --git a/src/discord/httpclient.hpp b/src/discord/httpclient.hpp
index 841ce11..83b1f5a 100644
--- a/src/discord/httpclient.hpp
+++ b/src/discord/httpclient.hpp
@@ -25,6 +25,9 @@ public:
void MakePOST(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb);
void MakePUT(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb);
+ [[nodiscard]] http::request CreateRequest(http::EMethod method, std::string path);
+ void Execute(http::request &&req, const std::function<void(http::response_type r)> &cb);
+
private:
void AddHeaders(http::request &r);
diff --git a/src/discord/store.cpp b/src/discord/store.cpp
index f08f0c8..892f4aa 100644
--- a/src/discord/store.cpp
+++ b/src/discord/store.cpp
@@ -746,6 +746,7 @@ std::optional<GuildData> Store::GetGuild(Snowflake id) const {
s->Get(2, r.Icon);
s->Get(5, r.OwnerID);
s->Get(20, r.IsUnavailable);
+ s->Get(27, r.PremiumTier);
s->Reset();
diff --git a/src/discord/user.cpp b/src/discord/user.cpp
index 4393992..0ab2af5 100644
--- a/src/discord/user.cpp
+++ b/src/discord/user.cpp
@@ -1,6 +1,10 @@
#include "user.hpp"
#include "abaddon.hpp"
+bool UserData::IsABot() const noexcept {
+ return IsBot.has_value() && *IsBot;
+}
+
bool UserData::IsDeleted() const {
return Discriminator == "0000";
}
diff --git a/src/discord/user.hpp b/src/discord/user.hpp
index 083f5c4..1b9d517 100644
--- a/src/discord/user.hpp
+++ b/src/discord/user.hpp
@@ -60,6 +60,7 @@ struct UserData {
friend void to_json(nlohmann::json &j, const UserData &m);
void update_from_json(const nlohmann::json &j);
+ [[nodiscard]] bool IsABot() const noexcept;
[[nodiscard]] bool IsDeleted() const;
[[nodiscard]] bool HasAvatar() const;
[[nodiscard]] bool HasAnimatedAvatar() const noexcept;