diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2022-08-12 18:35:58 -0400 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2022-08-12 18:35:58 -0400 |
commit | 6a5ecb4d9584f7276cf1d592c95811a66943f61c (patch) | |
tree | 2c09268db37461ce9e1882789183c508bb36dd4a /src/discord | |
parent | dc28eae95a46c3079fcc76b3425ffa37844dd37d (diff) | |
parent | f60cea2216dd9677cb9105364cdaa778a0c187db (diff) | |
download | abaddon-portaudio-6a5ecb4d9584f7276cf1d592c95811a66943f61c.tar.gz abaddon-portaudio-6a5ecb4d9584f7276cf1d592c95811a66943f61c.zip |
Merge branch 'attachments'
Diffstat (limited to 'src/discord')
-rw-r--r-- | src/discord/chatsubmitparams.hpp | 24 | ||||
-rw-r--r-- | src/discord/discord.cpp | 98 | ||||
-rw-r--r-- | src/discord/discord.hpp | 14 | ||||
-rw-r--r-- | src/discord/guild.hpp | 9 | ||||
-rw-r--r-- | src/discord/httpclient.cpp | 19 | ||||
-rw-r--r-- | src/discord/httpclient.hpp | 3 | ||||
-rw-r--r-- | src/discord/store.cpp | 1 | ||||
-rw-r--r-- | src/discord/user.cpp | 4 | ||||
-rw-r--r-- | src/discord/user.hpp | 1 |
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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, const sigc::slot<void(DiscordError code)> &callback); + void SendChatMessageAttachments(const ChatSubmitParams ¶ms, 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 ¶ms, 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; |