mirror of
https://github.com/uowuo/abaddon.git
synced 2024-11-12 23:21:31 +00:00
merge
This commit is contained in:
commit
43ea62d444
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -5,7 +5,7 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
windows:
|
||||
name: windows-${{ matrix.buildtype }}
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2016
|
||||
strategy:
|
||||
matrix:
|
||||
buildtype: [Debug, RelWithDebInfo]
|
||||
|
@ -189,6 +189,8 @@ For example, memory_db would be set by adding `memory_db = true` under the line
|
||||
* css (string) - path to the main CSS file
|
||||
* animations (true or false, default true) - use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used
|
||||
* owner_crown (true or false, default true) - show a crown next to the owner
|
||||
* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression
|
||||
* api_base (string) - override base url for Discord API
|
||||
|
||||
#### misc
|
||||
* linkcolor (string) - color to use for links in messages
|
||||
|
12
abaddon.cpp
12
abaddon.cpp
@ -69,7 +69,6 @@ Abaddon &Abaddon::Get() {
|
||||
int Abaddon::StartGTK() {
|
||||
m_gtk_app = Gtk::Application::create("com.github.uowuo.abaddon");
|
||||
|
||||
// tmp css stuff
|
||||
m_css_provider = Gtk::CssProvider::create();
|
||||
m_css_provider->signal_parsing_error().connect([this](const Glib::RefPtr<const Gtk::CssSection> §ion, const Glib::Error &error) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "css failed parsing (" + error.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
@ -77,6 +76,13 @@ int Abaddon::StartGTK() {
|
||||
dlg.run();
|
||||
});
|
||||
|
||||
m_css_low_provider = Gtk::CssProvider::create();
|
||||
m_css_low_provider->signal_parsing_error().connect([this](const Glib::RefPtr<const Gtk::CssSection> §ion, const Glib::Error &error) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "low-priority css failed parsing (" + error.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
});
|
||||
|
||||
m_main_window = std::make_unique<MainWindow>();
|
||||
m_main_window->set_title(APP_TITLE);
|
||||
m_main_window->UpdateComponents();
|
||||
@ -631,6 +637,10 @@ void Abaddon::ActionReloadCSS() {
|
||||
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider);
|
||||
m_css_provider->load_from_path(m_settings.GetMainCSS());
|
||||
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider);
|
||||
m_css_low_provider->load_from_path("./css/application-low-priority.css");
|
||||
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||
} catch (Glib::Error &e) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "css failed to load (" + e.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
|
@ -128,5 +128,6 @@ private:
|
||||
mutable std::mutex m_mutex;
|
||||
Glib::RefPtr<Gtk::Application> m_gtk_app;
|
||||
Glib::RefPtr<Gtk::CssProvider> m_css_provider;
|
||||
std::unique_ptr<MainWindow> m_main_window; // wah wah cant create a gtkstylecontext fuck you
|
||||
Glib::RefPtr<Gtk::CssProvider> m_css_low_provider; // registered with a lower priority to allow better customization
|
||||
std::unique_ptr<MainWindow> m_main_window; // wah wah cant create a gtkstylecontext fuck you
|
||||
};
|
||||
|
2
ci/vcpkg
2
ci/vcpkg
@ -1 +1 @@
|
||||
Subproject commit 50ea8c0ab7aca3bb9245bba7fc877ad2f2a4464c
|
||||
Subproject commit a9b27ed5dffbf70b135eddb0c4729f3ca87f106c
|
@ -1086,7 +1086,7 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
|
||||
m_static_avatar = pb->scale_simple(AvatarSize, AvatarSize, Gdk::INTERP_BILINEAR);
|
||||
m_avatar->property_pixbuf() = m_static_avatar;
|
||||
};
|
||||
img.LoadFromURL(author->GetAvatarURL(), sigc::track_obj(cb, *this));
|
||||
img.LoadFromURL(author->GetAvatarURL(data->GuildID), sigc::track_obj(cb, *this));
|
||||
|
||||
if (author->HasAnimatedAvatar()) {
|
||||
auto cb = [this](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
constexpr static const int MaxMemberListRows = 200;
|
||||
|
||||
MemberListUserRow::MemberListUserRow(const GuildData *guild, const UserData &data) {
|
||||
MemberListUserRow::MemberListUserRow(const std::optional<GuildData> &guild, const UserData &data) {
|
||||
ID = data.ID;
|
||||
m_ev = Gtk::manage(new Gtk::EventBox);
|
||||
m_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
|
||||
@ -15,7 +15,7 @@ MemberListUserRow::MemberListUserRow(const GuildData *guild, const UserData &dat
|
||||
m_status_indicator = Gtk::manage(new StatusIndicator(ID));
|
||||
|
||||
static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown();
|
||||
if (crown && guild != nullptr && guild->OwnerID == data.ID) {
|
||||
if (crown && guild.has_value() && guild->OwnerID == data.ID) {
|
||||
try {
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file("./res/crown.png", 12, 12);
|
||||
m_crown = Gtk::manage(new Gtk::Image(pixbuf));
|
||||
@ -26,7 +26,10 @@ MemberListUserRow::MemberListUserRow(const GuildData *guild, const UserData &dat
|
||||
|
||||
m_status_indicator->set_margin_start(3);
|
||||
|
||||
m_avatar->SetURL(data.GetAvatarURL("png"));
|
||||
if (guild.has_value())
|
||||
m_avatar->SetURL(data.GetAvatarURL(guild->ID, "png"));
|
||||
else
|
||||
m_avatar->SetURL(data.GetAvatarURL("png"));
|
||||
|
||||
get_style_context()->add_class("members-row");
|
||||
get_style_context()->add_class("members-row-member");
|
||||
@ -40,7 +43,7 @@ MemberListUserRow::MemberListUserRow(const GuildData *guild, const UserData &dat
|
||||
std::string display = data.Username;
|
||||
if (show_discriminator)
|
||||
display += "#" + data.Discriminator;
|
||||
if (guild != nullptr) {
|
||||
if (guild.has_value()) {
|
||||
if (const auto col_id = data.GetHoistedRole(guild->ID, true); col_id.IsValid()) {
|
||||
auto color = Abaddon::Get().GetDiscordClient().GetRole(col_id)->Color;
|
||||
m_label->set_use_markup(true);
|
||||
@ -114,7 +117,7 @@ void MemberList::UpdateMemberList() {
|
||||
int num_rows = 0;
|
||||
for (const auto &user : chan->GetDMRecipients()) {
|
||||
if (num_rows++ > MaxMemberListRows) break;
|
||||
auto *row = Gtk::manage(new MemberListUserRow(nullptr, user));
|
||||
auto *row = Gtk::manage(new MemberListUserRow(std::nullopt, user));
|
||||
m_id_to_row[user.ID] = row;
|
||||
AttachUserMenuHandler(row, user.ID);
|
||||
m_listbox->add(*row);
|
||||
@ -156,7 +159,7 @@ void MemberList::UpdateMemberList() {
|
||||
const auto guild = *discord.GetGuild(m_guild_id);
|
||||
auto add_user = [this, &user_to_color, &num_rows, guild](const UserData &data) -> bool {
|
||||
if (num_rows++ > MaxMemberListRows) return false;
|
||||
auto *row = Gtk::manage(new MemberListUserRow(&guild, data));
|
||||
auto *row = Gtk::manage(new MemberListUserRow(guild, data));
|
||||
m_id_to_row[data.ID] = row;
|
||||
AttachUserMenuHandler(row, data.ID);
|
||||
m_listbox->add(*row);
|
||||
|
@ -9,7 +9,7 @@ class LazyImage;
|
||||
class StatusIndicator;
|
||||
class MemberListUserRow : public Gtk::ListBoxRow {
|
||||
public:
|
||||
MemberListUserRow(const GuildData *guild, const UserData &data);
|
||||
MemberListUserRow(const std::optional<GuildData> &guild, const UserData &data);
|
||||
|
||||
Snowflake ID;
|
||||
|
||||
|
86
css/application-low-priority.css
Normal file
86
css/application-low-priority.css
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
application wide stuff
|
||||
has to be separate to allow main.css to override certain things
|
||||
*/
|
||||
|
||||
.app-window label:not(:disabled) {
|
||||
color: @text_color;
|
||||
}
|
||||
|
||||
.app-window entry {
|
||||
background: @secondary_color;
|
||||
color: @text_color;
|
||||
border: 1px solid #1c2e40;
|
||||
}
|
||||
|
||||
.app-window button {
|
||||
background: @secondary_color;
|
||||
color: @text_color;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.app-window button:checked {
|
||||
border-top: 0px;
|
||||
border-left: 0px;
|
||||
border-right: 0px;
|
||||
border-bottom: 3px solid #39a2ed;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.app-window button:not(:checked) {
|
||||
border: 3px #0000ff;
|
||||
}
|
||||
|
||||
.app-window.background {
|
||||
background: @background_color;
|
||||
}
|
||||
|
||||
.app-window treeview {
|
||||
color: @text_color;
|
||||
background: @secondary_color;
|
||||
}
|
||||
|
||||
.app-popup list {
|
||||
background: @secondary_color;
|
||||
}
|
||||
|
||||
.app-window paned separator {
|
||||
background: @background_color;
|
||||
}
|
||||
|
||||
.app-window scrollbar {
|
||||
background: @background_color;
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
.app-window menubar, menu {
|
||||
background: @background_color;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.app-window textview text {
|
||||
caret-color: #ababab;
|
||||
}
|
||||
|
||||
.app-window check,
|
||||
.app-window radio {
|
||||
background-clip: padding-box;
|
||||
background: @secondary_color;
|
||||
border-color: #070707;
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
color: #dddddd;
|
||||
}
|
||||
|
||||
.app-window check:checked,
|
||||
.app-window radio:checked {
|
||||
background-clip: border-box;
|
||||
background: #0b4285;
|
||||
border-color: #092444;
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
color: #dddddd;
|
||||
}
|
||||
|
||||
.app-window colorswatch {
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
}
|
109
css/main.css
109
css/main.css
@ -57,14 +57,8 @@
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.message-container-author {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.message-container-extra {
|
||||
color: #78909c;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
|
||||
.message-container-timestamp {
|
||||
@ -142,6 +136,26 @@
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.message-component {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.message-component.primary {
|
||||
background: #5865F2;
|
||||
}
|
||||
|
||||
.message-component.secondary, .message-component.link {
|
||||
background: #4F545C;
|
||||
}
|
||||
|
||||
.message-component.success {
|
||||
background: #43B581;
|
||||
}
|
||||
|
||||
.message-component.danger {
|
||||
background: #F04747;
|
||||
}
|
||||
|
||||
.reaction-box {
|
||||
padding: 2px 5px 2px 5px;
|
||||
margin: 0px 0px 0px 0px;
|
||||
@ -177,62 +191,6 @@
|
||||
color: @text_color;
|
||||
}
|
||||
|
||||
.app-window label:not(:disabled) {
|
||||
color: @text_color;
|
||||
}
|
||||
|
||||
.app-window entry {
|
||||
background: @secondary_color;
|
||||
color: @text_color;
|
||||
border: 1px solid #1c2e40;
|
||||
}
|
||||
|
||||
.app-window button {
|
||||
background: @secondary_color;
|
||||
color: @text_color;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.app-window button:checked {
|
||||
border-top: 0px;
|
||||
border-left: 0px;
|
||||
border-right: 0px;
|
||||
border-bottom: 3px solid #39a2ed;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.app-window button:not(:checked) {
|
||||
border: 3px #0000ff;
|
||||
}
|
||||
|
||||
.app-window.background {
|
||||
background: @background_color;
|
||||
}
|
||||
|
||||
.app-window treeview {
|
||||
color: @text_color;
|
||||
background: @secondary_color;
|
||||
}
|
||||
|
||||
.app-popup list {
|
||||
background: @secondary_color;
|
||||
}
|
||||
|
||||
.app-window paned separator {
|
||||
background: @background_color;
|
||||
}
|
||||
|
||||
.app-window scrollbar {
|
||||
background: @background_color;
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
.app-window menubar, menu {
|
||||
background: @background_color;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.status-indicator.dnd {
|
||||
color: #982929;
|
||||
}
|
||||
@ -304,37 +262,10 @@
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.app-window textview text {
|
||||
caret-color: #ababab;
|
||||
}
|
||||
|
||||
.guild-members-pane-info {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
.app-window check,
|
||||
.app-window radio {
|
||||
background-clip: padding-box;
|
||||
background: @secondary_color;
|
||||
border-color: #070707;
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
color: #dddddd;
|
||||
}
|
||||
|
||||
.app-window check:checked,
|
||||
.app-window radio:checked {
|
||||
background-clip: border-box;
|
||||
background: #0b4285;
|
||||
border-color: #092444;
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
color: #dddddd;
|
||||
}
|
||||
|
||||
.app-window colorswatch {
|
||||
box-shadow: 0 1px rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.drag-hover-top {
|
||||
background: linear-gradient(to bottom, rgba(255, 66, 66, 0.65) 0%, rgba(0, 0, 0, 0) 35%);
|
||||
}
|
||||
|
@ -1,5 +1,12 @@
|
||||
#include "token.hpp"
|
||||
|
||||
std::string trim(const std::string& str) {
|
||||
const auto first = str.find_first_not_of(' ');
|
||||
if (first == std::string::npos) return str;
|
||||
const auto last = str.find_last_not_of(' ');
|
||||
return str.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
TokenDialog::TokenDialog(Gtk::Window &parent)
|
||||
: Gtk::Dialog("Set Token", parent, true)
|
||||
, m_layout(Gtk::ORIENTATION_VERTICAL)
|
||||
@ -11,7 +18,7 @@ TokenDialog::TokenDialog(Gtk::Window &parent)
|
||||
get_style_context()->add_class("app-popup");
|
||||
|
||||
m_ok.signal_clicked().connect([&]() {
|
||||
m_token = m_entry.get_text();
|
||||
m_token = trim(m_entry.get_text());
|
||||
response(Gtk::RESPONSE_OK);
|
||||
});
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
#include "discord.hpp"
|
||||
#include <cassert>
|
||||
#include "../util.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
|
||||
DiscordClient::DiscordClient(bool mem_store)
|
||||
: m_http(DiscordAPI)
|
||||
, m_decompress_buf(InflateChunkSize)
|
||||
: m_decompress_buf(InflateChunkSize)
|
||||
, m_store(mem_store) {
|
||||
m_msg_dispatch.connect(sigc::mem_fun(*this, &DiscordClient::MessageDispatch));
|
||||
auto dispatch_cb = [this]() {
|
||||
@ -24,13 +24,15 @@ DiscordClient::DiscordClient(bool mem_store)
|
||||
}
|
||||
|
||||
void DiscordClient::Start() {
|
||||
m_http.SetBase(GetAPIURL());
|
||||
|
||||
std::memset(&m_zstream, 0, sizeof(m_zstream));
|
||||
inflateInit2(&m_zstream, MAX_WBITS + 32);
|
||||
|
||||
m_last_sequence = -1;
|
||||
m_heartbeat_acked = true;
|
||||
m_client_connected = true;
|
||||
m_websocket.StartConnection(DiscordGateway);
|
||||
m_websocket.StartConnection(GetGatewayURL());
|
||||
}
|
||||
|
||||
void DiscordClient::Stop() {
|
||||
@ -1104,6 +1106,17 @@ void DiscordClient::HandleGatewayHello(const GatewayMessage &msg) {
|
||||
SendIdentify();
|
||||
}
|
||||
|
||||
// perhaps this should be set by the main class
|
||||
std::string DiscordClient::GetAPIURL() {
|
||||
static const auto url = Abaddon::Get().GetSettings().GetAPIBaseURL();
|
||||
return url;
|
||||
}
|
||||
|
||||
std::string DiscordClient::GetGatewayURL() {
|
||||
static const auto url = Abaddon::Get().GetSettings().GetGatewayURL();
|
||||
return url;
|
||||
}
|
||||
|
||||
DiscordError DiscordClient::GetCodeFromResponse(const http::response_type &response) {
|
||||
try {
|
||||
// pull me somewhere else?
|
||||
@ -1576,7 +1589,7 @@ void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) {
|
||||
std::memset(&m_zstream, 0, sizeof(m_zstream));
|
||||
inflateInit2(&m_zstream, MAX_WBITS + 32);
|
||||
|
||||
m_websocket.StartConnection(DiscordGateway);
|
||||
m_websocket.StartConnection(GetGatewayURL());
|
||||
}
|
||||
|
||||
void DiscordClient::HandleGatewayInvalidSession(const GatewayMessage &msg) {
|
||||
@ -1597,7 +1610,7 @@ void DiscordClient::HandleGatewayInvalidSession(const GatewayMessage &msg) {
|
||||
|
||||
m_websocket.Stop(1000);
|
||||
|
||||
m_websocket.StartConnection(DiscordGateway);
|
||||
m_websocket.StartConnection(GetGatewayURL());
|
||||
}
|
||||
|
||||
void DiscordClient::HandleGatewayMessageUpdate(const GatewayMessage &msg) {
|
||||
|
@ -49,10 +49,6 @@ class Abaddon;
|
||||
class DiscordClient {
|
||||
friend class Abaddon;
|
||||
|
||||
public:
|
||||
static const constexpr char *DiscordGateway = "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream";
|
||||
static const constexpr char *DiscordAPI = "https://discord.com/api/v9";
|
||||
|
||||
public:
|
||||
DiscordClient(bool mem_store = false);
|
||||
void Start();
|
||||
@ -191,6 +187,9 @@ private:
|
||||
std::vector<uint8_t> m_decompress_buf;
|
||||
z_stream m_zstream;
|
||||
|
||||
std::string GetAPIURL();
|
||||
std::string GetGatewayURL();
|
||||
|
||||
static DiscordError GetCodeFromResponse(const http::response_type &response);
|
||||
|
||||
void ProcessNewGuild(GuildData &guild);
|
||||
|
@ -1,11 +1,14 @@
|
||||
#include "httpclient.hpp"
|
||||
|
||||
//#define USE_LOCAL_PROXY
|
||||
HTTPClient::HTTPClient(std::string api_base)
|
||||
: m_api_base(api_base) {
|
||||
HTTPClient::HTTPClient() {
|
||||
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
|
||||
}
|
||||
|
||||
void HTTPClient::SetBase(const std::string &url) {
|
||||
m_api_base = url;
|
||||
}
|
||||
|
||||
void HTTPClient::SetUserAgent(std::string agent) {
|
||||
m_agent = agent;
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient(std::string api_base);
|
||||
HTTPClient();
|
||||
|
||||
void SetBase(const std::string &url);
|
||||
|
||||
void SetUserAgent(std::string agent);
|
||||
void SetAuth(std::string auth);
|
||||
|
@ -10,6 +10,7 @@ void from_json(const nlohmann::json &j, GuildMember &m) {
|
||||
JS_D("deaf", m.IsDeafened);
|
||||
JS_D("mute", m.IsMuted);
|
||||
JS_O("user_id", m.UserID);
|
||||
JS_ON("avatar", m.Avatar);
|
||||
}
|
||||
|
||||
std::vector<RoleData> GuildMember::GetSortedRoles() const {
|
||||
@ -33,4 +34,5 @@ void GuildMember::update_from_json(const nlohmann::json &j) {
|
||||
JS_RD("nick", Nickname);
|
||||
JS_RD("joined_at", JoinedAt);
|
||||
JS_RD("premium_since", PremiumSince);
|
||||
JS_RD("avatar", Avatar);
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ struct GuildMember {
|
||||
bool IsMuted;
|
||||
std::optional<Snowflake> UserID; // present in merged_members
|
||||
|
||||
// undocuemtned moment !!!1
|
||||
std::optional<std::string> Avatar;
|
||||
|
||||
std::vector<RoleData> GetSortedRoles() const;
|
||||
|
||||
void update_from_json(const nlohmann::json &j);
|
||||
|
@ -40,10 +40,11 @@ enum class Permission : uint64_t {
|
||||
MANAGE_EMOJIS = (1ULL << 30), // Allows management and editing of emojis
|
||||
USE_SLASH_COMMANDS = (1ULL << 31), // Allows members to use slash commands in text channels
|
||||
REQUEST_TO_SPEAK = (1ULL << 32), // Allows for requesting to speak in stage channels
|
||||
USE_THREADS = (1ULL << 33), // Allows for creating and participating in threads
|
||||
USE_PRIVATE_THREADS = (1ULL << 34), // Allows for creating and participating in private threads
|
||||
MANAGE_THREADS = (1ULL << 34), // Allows for deleting and archiving threads, and viewing all private threads
|
||||
USE_PUBLIC_THREADS = (1ULL << 35), // Allows for creating and participating in threads
|
||||
USE_PRIVATE_THREADS = (1ULL << 36), // Allows for creating and participating in private threads
|
||||
|
||||
ALL = 0x7FFFFFFFFULL,
|
||||
ALL = 0x1FFFFFFFFFULL,
|
||||
};
|
||||
template<>
|
||||
struct Bitwise<Permission> {
|
||||
@ -107,7 +108,7 @@ constexpr const char *GetPermissionString(Permission perm) {
|
||||
case Permission::USE_EXTERNAL_EMOJIS:
|
||||
return "Use External Emojis";
|
||||
case Permission::VIEW_GUILD_INSIGHTS:
|
||||
return "View Guild Insights";
|
||||
return "View Server Insights";
|
||||
case Permission::CONNECT:
|
||||
return "Connect to Voice";
|
||||
case Permission::SPEAK:
|
||||
@ -132,6 +133,12 @@ constexpr const char *GetPermissionString(Permission perm) {
|
||||
return "Manage Emojis";
|
||||
case Permission::USE_SLASH_COMMANDS:
|
||||
return "Use Slash Commands";
|
||||
case Permission::MANAGE_THREADS:
|
||||
return "Manage Threads";
|
||||
case Permission::USE_PUBLIC_THREADS:
|
||||
return "Use Public Threads";
|
||||
case Permission::USE_PRIVATE_THREADS:
|
||||
return "Use Private Threads";
|
||||
default:
|
||||
return "Unknown Permission";
|
||||
}
|
||||
@ -180,7 +187,7 @@ constexpr const char *GetPermissionDescription(Permission perm) {
|
||||
case Permission::USE_EXTERNAL_EMOJIS:
|
||||
return "Allows members to use emoji from other servers, if they're a Discord Nitro member";
|
||||
case Permission::VIEW_GUILD_INSIGHTS:
|
||||
return "";
|
||||
return "Allows members to view Server Insights, which shows data on community growth, engagement, and more.";
|
||||
case Permission::CONNECT:
|
||||
return "Allows members to join voice channels and hear others.";
|
||||
case Permission::SPEAK:
|
||||
@ -205,6 +212,12 @@ constexpr const char *GetPermissionDescription(Permission perm) {
|
||||
return "Allows members to add or remove custom emojis in this server.";
|
||||
case Permission::USE_SLASH_COMMANDS:
|
||||
return "Allows members to use slash commands in text channels.";
|
||||
case Permission::MANAGE_THREADS:
|
||||
return "Allows members to rename, delete, archive/unarchive, and turn on slow mode for threads.";
|
||||
case Permission::USE_PUBLIC_THREADS:
|
||||
return "Allows members to talk in threads. The \"Send Messages\" permission must be enabled for members to start new threads; if it's disabled, they can only respond to existing threads.";
|
||||
case Permission::USE_PRIVATE_THREADS:
|
||||
return "Allows members to create and chat in private threads. The \"Send Messages\" permission must be enabled for members to start new private threads; if it's disabled, they can only respond to private threads they're added to.";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
@ -210,6 +210,7 @@ void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMem
|
||||
Bind(m_set_member_stmt, 6, data.PremiumSince);
|
||||
Bind(m_set_member_stmt, 7, data.IsDeafened);
|
||||
Bind(m_set_member_stmt, 8, data.IsMuted);
|
||||
Bind(m_set_member_stmt, 9, data.Avatar);
|
||||
|
||||
if (!RunInsert(m_set_member_stmt))
|
||||
fprintf(stderr, "member insert failed: %s\n", sqlite3_errstr(m_db_err));
|
||||
@ -520,6 +521,7 @@ std::optional<GuildMember> Store::GetGuildMember(Snowflake guild_id, Snowflake u
|
||||
Get(m_get_member_stmt, 5, ret.PremiumSince);
|
||||
Get(m_get_member_stmt, 6, ret.IsDeafened);
|
||||
Get(m_get_member_stmt, 7, ret.IsMuted);
|
||||
Get(m_get_member_stmt, 8, ret.Avatar);
|
||||
|
||||
Reset(m_get_member_stmt);
|
||||
|
||||
@ -823,6 +825,7 @@ bool Store::CreateTables() {
|
||||
premium_since TEXT,
|
||||
deaf BOOL NOT NULL,
|
||||
mute BOOL NOT NULL,
|
||||
avatar TEXT,
|
||||
PRIMARY KEY(user_id, guild_id)
|
||||
)
|
||||
)";
|
||||
@ -1039,7 +1042,7 @@ bool Store::CreateStatements() {
|
||||
|
||||
constexpr const char *set_member = R"(
|
||||
REPLACE INTO members VALUES (
|
||||
?, ?, ?, ?, ?, ?, ?, ?
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
)
|
||||
)";
|
||||
|
||||
|
@ -13,11 +13,33 @@ bool UserData::HasAnimatedAvatar() const {
|
||||
return Avatar.size() > 0 && Avatar[0] == 'a' && Avatar[1] == '_';
|
||||
}
|
||||
|
||||
std::string UserData::GetAvatarURL(Snowflake guild_id, std::string ext, std::string size) const {
|
||||
const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
|
||||
if (member.has_value() && member->Avatar.has_value())
|
||||
return "https://cdn.discordapp.com/guilds/" +
|
||||
std::to_string(guild_id) + "/users/" + std::to_string(ID) +
|
||||
"/avatars/" + *member->Avatar + "." +
|
||||
ext + "?" + "size=" + size;
|
||||
else
|
||||
return GetAvatarURL(ext, size);
|
||||
}
|
||||
|
||||
std::string UserData::GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext, std::string size) const {
|
||||
if (guild_id.has_value())
|
||||
return GetAvatarURL(*guild_id, ext, size);
|
||||
else
|
||||
return GetAvatarURL(ext, size);
|
||||
}
|
||||
|
||||
std::string UserData::GetAvatarURL(std::string ext, std::string size) const {
|
||||
if (HasAvatar())
|
||||
return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size;
|
||||
else
|
||||
return "https://cdn.discordapp.com/embed/avatars/" + std::to_string(std::stoul(Discriminator) % 5) + ".png"; // size isn't respected by the cdn
|
||||
return GetDefaultAvatarURL();
|
||||
}
|
||||
|
||||
std::string UserData::GetDefaultAvatarURL() const {
|
||||
return "https://cdn.discordapp.com/embed/avatars/" + std::to_string(std::stoul(Discriminator) % 5) + ".png"; // size isn't respected by the cdn
|
||||
}
|
||||
|
||||
Snowflake UserData::GetHoistedRole(Snowflake guild_id, bool with_color) const {
|
||||
@ -58,6 +80,8 @@ void from_json(const nlohmann::json &j, UserData &m) {
|
||||
JS_O("mobile", m.IsMobile);
|
||||
JS_ON("nsfw_allowed", m.IsNSFWAllowed);
|
||||
JS_ON("phone", m.Phone);
|
||||
JS_ON("bio", m.Bio);
|
||||
JS_ON("banner", m.BannerHash);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const UserData &m) {
|
||||
|
@ -52,6 +52,9 @@ struct UserData {
|
||||
std::optional<bool> IsMobile;
|
||||
std::optional<bool> IsNSFWAllowed; // null
|
||||
std::optional<std::string> Phone; // null?
|
||||
// for now (unserialized)
|
||||
std::optional<std::string> BannerHash; // null
|
||||
std::optional<std::string> Bio; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserData &m);
|
||||
friend void to_json(nlohmann::json &j, const UserData &m);
|
||||
@ -60,7 +63,10 @@ struct UserData {
|
||||
bool IsDeleted() const;
|
||||
bool HasAvatar() const;
|
||||
bool HasAnimatedAvatar() const;
|
||||
std::string GetAvatarURL(Snowflake guild_id, std::string ext = "png", std::string size = "32") const;
|
||||
std::string GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext = "png", std::string size = "32") const;
|
||||
std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const;
|
||||
std::string GetDefaultAvatarURL() const;
|
||||
Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const;
|
||||
std::string GetMention() const;
|
||||
std::string GetEscapedName() const;
|
||||
|
12
http.cpp
12
http.cpp
@ -64,12 +64,14 @@ response request::execute() {
|
||||
detail::check_init();
|
||||
|
||||
std::string str;
|
||||
curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method);
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, detail::curl_write_data_callback);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &str);
|
||||
curl_easy_setopt(m_curl, CURLOPT_ERRORBUFFER, m_error_buf);
|
||||
m_error_buf[0] = '\0';
|
||||
if (m_header_list != nullptr)
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_header_list);
|
||||
|
||||
@ -81,7 +83,7 @@ response request::execute() {
|
||||
return response;
|
||||
}
|
||||
|
||||
int response_code = 0;
|
||||
long response_code = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
|
||||
auto response = detail::make_response(m_url, response_code);
|
||||
@ -95,10 +97,10 @@ void request::prepare() {
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, std::string *outstr) {
|
||||
auto new_length = size * nmemb;
|
||||
outstr->append(reinterpret_cast<char *>(ptr), new_length);
|
||||
return new_length;
|
||||
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
|
||||
const size_t n = size * nmemb;
|
||||
static_cast<std::string*>(userdata)->append(static_cast<char*>(ptr), n);
|
||||
return n;
|
||||
}
|
||||
|
||||
response make_response(const std::string &url, int code) {
|
||||
|
2
http.hpp
2
http.hpp
@ -121,7 +121,7 @@ private:
|
||||
using response_type = response;
|
||||
|
||||
namespace detail {
|
||||
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, std::string *outstr);
|
||||
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
|
||||
response make_response(const std::string &url, int code);
|
||||
void check_init();
|
||||
|
@ -85,3 +85,11 @@ bool SettingsManager::GetShowAnimations() const {
|
||||
bool SettingsManager::GetShowOwnerCrown() const {
|
||||
return GetSettingBool("gui", "owner_crown", true);
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetGatewayURL() const {
|
||||
return GetSettingString("discord", "gateway", "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream");
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetAPIBaseURL() const {
|
||||
return GetSettingString("discord", "api_base", "https://discord.com/api/v9");
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ public:
|
||||
std::string GetMainCSS() const;
|
||||
bool GetShowAnimations() const;
|
||||
bool GetShowOwnerCrown() const;
|
||||
std::string GetGatewayURL() const;
|
||||
std::string GetAPIBaseURL() const;
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
|
@ -263,6 +263,7 @@ GuildSettingsRolesPaneInfo::GuildSettingsRolesPaneInfo(Snowflake guild_id)
|
||||
Permission::MANAGE_ROLES,
|
||||
Permission::MANAGE_EMOJIS,
|
||||
Permission::VIEW_AUDIT_LOG,
|
||||
Permission::VIEW_GUILD_INSIGHTS,
|
||||
Permission::MANAGE_WEBHOOKS,
|
||||
Permission::MANAGE_GUILD });
|
||||
|
||||
@ -275,12 +276,15 @@ GuildSettingsRolesPaneInfo::GuildSettingsRolesPaneInfo(Snowflake guild_id)
|
||||
|
||||
add_perms("Text Channels", RIGHT, {
|
||||
Permission::SEND_MESSAGES,
|
||||
Permission::USE_PUBLIC_THREADS,
|
||||
Permission::USE_PRIVATE_THREADS,
|
||||
Permission::EMBED_LINKS,
|
||||
Permission::ATTACH_FILES,
|
||||
Permission::ADD_REACTIONS,
|
||||
Permission::USE_EXTERNAL_EMOJIS,
|
||||
Permission::MENTION_EVERYONE,
|
||||
Permission::MANAGE_MESSAGES,
|
||||
Permission::MANAGE_THREADS,
|
||||
Permission::READ_MESSAGE_HISTORY,
|
||||
Permission::SEND_TTS_MESSAGES,
|
||||
Permission::USE_SLASH_COMMANDS });
|
||||
|
@ -158,6 +158,25 @@ NotesContainer::type_signal_update_note NotesContainer::signal_update_note() {
|
||||
return m_signal_update_note;
|
||||
}
|
||||
|
||||
BioContainer::BioContainer()
|
||||
: Gtk::Box(Gtk::ORIENTATION_VERTICAL) {
|
||||
m_label.set_markup("<b>ABOUT ME</b>");
|
||||
m_label.set_halign(Gtk::ALIGN_START);
|
||||
m_bio.set_halign(Gtk::ALIGN_START);
|
||||
m_bio.set_line_wrap(true);
|
||||
m_bio.set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
|
||||
|
||||
m_label.show();
|
||||
m_bio.show();
|
||||
|
||||
add(m_label);
|
||||
add(m_bio);
|
||||
}
|
||||
|
||||
void BioContainer::SetBio(const std::string &bio) {
|
||||
m_bio.set_text(bio);
|
||||
}
|
||||
|
||||
ProfileUserInfoPane::ProfileUserInfoPane(Snowflake ID)
|
||||
: Gtk::Box(Gtk::ORIENTATION_VERTICAL)
|
||||
, UserID(ID) {
|
||||
@ -194,12 +213,23 @@ ProfileUserInfoPane::ProfileUserInfoPane(Snowflake ID)
|
||||
m_conns.set_halign(Gtk::ALIGN_START);
|
||||
m_conns.set_hexpand(true);
|
||||
|
||||
m_created.show();
|
||||
m_note.show();
|
||||
m_conns.show();
|
||||
|
||||
add(m_created);
|
||||
add(m_bio);
|
||||
add(m_note);
|
||||
add(m_conns);
|
||||
show_all_children();
|
||||
}
|
||||
|
||||
void ProfileUserInfoPane::SetConnections(const std::vector<ConnectionData> &connections) {
|
||||
m_conns.SetConnections(connections);
|
||||
void ProfileUserInfoPane::SetProfile(const UserProfileData &data) {
|
||||
if (data.User.Bio.has_value() && *data.User.Bio != "") {
|
||||
m_bio.SetBio(*data.User.Bio);
|
||||
m_bio.show();
|
||||
} else {
|
||||
m_bio.hide();
|
||||
}
|
||||
|
||||
m_conns.SetConnections(data.ConnectedAccounts);
|
||||
}
|
||||
|
@ -39,16 +39,27 @@ public:
|
||||
type_signal_update_note signal_update_note();
|
||||
};
|
||||
|
||||
class BioContainer : public Gtk::Box {
|
||||
public:
|
||||
BioContainer();
|
||||
void SetBio(const std::string &bio);
|
||||
|
||||
private:
|
||||
Gtk::Label m_label;
|
||||
Gtk::Label m_bio;
|
||||
};
|
||||
|
||||
class ProfileUserInfoPane : public Gtk::Box {
|
||||
public:
|
||||
ProfileUserInfoPane(Snowflake ID);
|
||||
void SetConnections(const std::vector<ConnectionData> &connections);
|
||||
void SetProfile(const UserProfileData &data);
|
||||
|
||||
Snowflake UserID;
|
||||
|
||||
private:
|
||||
Gtk::Label m_created;
|
||||
|
||||
BioContainer m_bio;
|
||||
NotesContainer m_note;
|
||||
ConnectionsContainer m_conns;
|
||||
};
|
||||
|
@ -96,7 +96,7 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
|
||||
}
|
||||
|
||||
void ProfileWindow::OnFetchProfile(const UserProfileData &data) {
|
||||
m_pane_info.SetConnections(data.ConnectedAccounts);
|
||||
m_pane_info.SetProfile(data);
|
||||
m_pane_guilds.SetMutualGuilds(data.MutualGuilds);
|
||||
|
||||
for (auto child : m_badges.get_children())
|
||||
|
Loading…
Reference in New Issue
Block a user