forked from OpenGamers/abaddon
add viewing audit log
This commit is contained in:
12
css/main.css
12
css/main.css
@@ -144,16 +144,16 @@
|
||||
}
|
||||
|
||||
.completer {
|
||||
background-color: @secondary_color;
|
||||
padding: 5px;
|
||||
background-color: @secondary_color;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.completer-entry {
|
||||
color: @text_color;
|
||||
color: @text_color;
|
||||
}
|
||||
|
||||
.completer-entry-image {
|
||||
margin-right: 6px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.typing-indicator {
|
||||
@@ -200,8 +200,8 @@
|
||||
background: @secondary_color;
|
||||
}
|
||||
|
||||
.app-window listbox {
|
||||
background: @background_color;
|
||||
.app-popup list {
|
||||
background: @secondary_color;
|
||||
}
|
||||
|
||||
.app-window paned separator {
|
||||
|
||||
34
discord/auditlog.cpp
Normal file
34
discord/auditlog.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "auditlog.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogChange &m) {
|
||||
JS_D("key", m.Key);
|
||||
JS_O("old_value", m.OldValue);
|
||||
JS_O("new_value", m.NewValue);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogOptions &m) {
|
||||
JS_O("delete_member_days", m.DeleteMemberDays);
|
||||
JS_O("members_removed", m.MembersRemoved);
|
||||
JS_O("channel_id", m.ChannelID);
|
||||
JS_O("message_id", m.MessageID);
|
||||
JS_O("count", m.Count);
|
||||
JS_O("id", m.ID);
|
||||
JS_O("type", m.Type);
|
||||
JS_O("role_name", m.RoleName);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogEntry &m) {
|
||||
JS_N("target_id", m.TargetID);
|
||||
JS_O("changes", m.Changes);
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("id", m.ID);
|
||||
JS_D("action_type", m.Type);
|
||||
JS_O("options", m.Options);
|
||||
JS_O("reason", m.Reason);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogData &m) {
|
||||
JS_D("audit_log_entries", m.Entries);
|
||||
JS_D("users", m.Users);
|
||||
JS_D("webhooks", m.Webhooks);
|
||||
}
|
||||
111
discord/auditlog.hpp
Normal file
111
discord/auditlog.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
#include "snowflake.hpp"
|
||||
#include "user.hpp"
|
||||
#include "json.hpp"
|
||||
#include "webhook.hpp"
|
||||
|
||||
enum class AuditLogActionType {
|
||||
GUILD_UPDATE = 1,
|
||||
CHANNEL_CREATE = 10,
|
||||
CHANNEL_UPDATE = 11,
|
||||
CHANNEL_DELETE = 12,
|
||||
CHANNEL_OVERWRITE_CREATE = 13,
|
||||
CHANNEL_OVERWRITE_UPDATE = 14,
|
||||
CHANNEL_OVERWRITE_DELETE = 15,
|
||||
MEMBER_KICK = 20,
|
||||
MEMBER_PRUNE = 21,
|
||||
MEMBER_BAN_ADD = 22,
|
||||
MEMBER_BAN_REMOVE = 23,
|
||||
MEMBER_UPDATE = 24,
|
||||
MEMBER_ROLE_UPDATE = 25,
|
||||
MEMBER_MOVE = 26,
|
||||
MEMBER_DISCONNECT = 27,
|
||||
BOT_ADD = 28,
|
||||
ROLE_CREATE = 30,
|
||||
ROLE_UPDATE = 31,
|
||||
ROLE_DELETE = 32,
|
||||
INVITE_CREATE = 40,
|
||||
INVITE_UPDATE = 41,
|
||||
INVITE_DELETE = 42,
|
||||
WEBHOOK_CREATE = 50,
|
||||
WEBHOOK_UPDATE = 51,
|
||||
WEBHOOK_DELETE = 52,
|
||||
EMOJI_CREATE = 60,
|
||||
EMOJI_UPDATE = 61,
|
||||
EMOJI_DELETE = 62,
|
||||
MESSAGE_DELETE = 72,
|
||||
MESSAGE_BULK_DELETE = 73,
|
||||
MESSAGE_PIN = 74,
|
||||
MESSAGE_UNPIN = 75,
|
||||
INTEGRATION_CREATE = 80,
|
||||
INTEGRATION_UPDATE = 81,
|
||||
INTEGRATION_DELETE = 82,
|
||||
};
|
||||
|
||||
struct AuditLogChange {
|
||||
std::string Key;
|
||||
std::optional<nlohmann::json> OldValue;
|
||||
std::optional<nlohmann::json> NewValue;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogChange &m);
|
||||
};
|
||||
|
||||
struct AuditLogOptions {
|
||||
std::optional<std::string> DeleteMemberDays; // MEMBER_PRUNE
|
||||
std::optional<std::string> MembersRemoved; // MEMBER_PRUNE
|
||||
std::optional<Snowflake> ChannelID; // MEMBER_MOVE, MESSAGE_PIN, MESSAGE_UNPIN, MESSAGE_DELETE
|
||||
std::optional<Snowflake> MessageID; // MESSAGE_PIN, MESSAGE_UNPIN,
|
||||
std::optional<std::string> Count; // MESSAGE_DELETE, MESSAGE_BULK_DELETE, MEMBER_DISCONNECT, MEMBER_MOVE
|
||||
std::optional<Snowflake> ID; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE
|
||||
std::optional<std::string> Type; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE
|
||||
std::optional<std::string> RoleName; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogOptions &m);
|
||||
};
|
||||
|
||||
struct AuditLogEntry {
|
||||
Snowflake ID;
|
||||
std::string TargetID; // null
|
||||
Snowflake UserID;
|
||||
AuditLogActionType Type;
|
||||
std::optional<std::string> Reason;
|
||||
std::optional<std::vector<AuditLogChange>> Changes;
|
||||
std::optional<AuditLogOptions> Options;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogEntry &m);
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> GetOldFromKey(const std::string &key) const;
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> GetNewFromKey(const std::string &key) const;
|
||||
};
|
||||
|
||||
struct AuditLogData {
|
||||
std::vector<AuditLogEntry> Entries;
|
||||
std::vector<UserData> Users;
|
||||
std::vector<WebhookData> Webhooks;
|
||||
// std::vector<IntegrationData> Integrations;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogData &m);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> AuditLogEntry::GetOldFromKey(const std::string &key) const {
|
||||
if (!Changes.has_value()) return std::nullopt;
|
||||
for (const auto &change : *Changes)
|
||||
if (change.Key == key && change.OldValue.has_value())
|
||||
return change.OldValue->get<T>();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> AuditLogEntry::GetNewFromKey(const std::string &key) const {
|
||||
if (!Changes.has_value()) return std::nullopt;
|
||||
for (const auto &change : *Changes)
|
||||
if (change.Key == key && change.NewValue.has_value())
|
||||
return change.NewValue->get<T>();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -553,6 +553,22 @@ void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::v
|
||||
});
|
||||
}
|
||||
|
||||
void DiscordClient::FetchAuditLog(Snowflake guild_id, sigc::slot<void(AuditLogData)> callback) {
|
||||
sigc::signal<void, AuditLogData> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/audit-logs", [this, callback](const http::response &response) {
|
||||
if (!CheckCode(response)) return;
|
||||
auto data = nlohmann::json::parse(response.text).get<AuditLogData>();
|
||||
|
||||
m_store.BeginTransaction();
|
||||
for (const auto &user : data.Users)
|
||||
m_store.SetUser(user.ID, user);
|
||||
m_store.EndTransaction();
|
||||
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
|
||||
void DiscordClient::UpdateToken(std::string token) {
|
||||
if (!IsStarted()) {
|
||||
m_token = token;
|
||||
|
||||
@@ -127,6 +127,8 @@ public:
|
||||
void FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback);
|
||||
void FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback);
|
||||
|
||||
void FetchAuditLog(Snowflake guild_id, sigc::slot<void(AuditLogData)> callback);
|
||||
|
||||
void UpdateToken(std::string token);
|
||||
void SetUserAgent(std::string agent);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "activity.hpp"
|
||||
#include "sticker.hpp"
|
||||
#include "ban.hpp"
|
||||
#include "auditlog.hpp"
|
||||
|
||||
// most stuff below should just be objects that get processed and thrown away immediately
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ std::string UserData::GetMention() const {
|
||||
return "<@" + std::to_string(ID) + ">";
|
||||
}
|
||||
|
||||
std::string UserData::GetEscapedString() const {
|
||||
return Glib::Markup::escape_text(Username) + "#" + Discriminator;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UserData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("username", m.Username);
|
||||
|
||||
@@ -33,4 +33,5 @@ struct UserData {
|
||||
std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const;
|
||||
Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const;
|
||||
std::string GetMention() const;
|
||||
std::string GetEscapedString() const;
|
||||
};
|
||||
|
||||
13
discord/webhook.cpp
Normal file
13
discord/webhook.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "webhook.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, WebhookData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("user", m.User);
|
||||
JS_N("name", m.Name);
|
||||
JS_N("avatar", m.Avatar);
|
||||
JS_O("token", m.Token);
|
||||
JS_N("application_id", m.ApplicationID);
|
||||
}
|
||||
24
discord/webhook.hpp
Normal file
24
discord/webhook.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
#include "user.hpp"
|
||||
|
||||
enum class WebhookType {
|
||||
Incoming = 1,
|
||||
ChannelFollower = 2,
|
||||
};
|
||||
|
||||
struct WebhookData {
|
||||
Snowflake ID;
|
||||
WebhookType Type;
|
||||
std::optional<Snowflake> GuildID;
|
||||
Snowflake ChannelID;
|
||||
std::optional<UserData> User;
|
||||
std::string Name; // null
|
||||
std::string Avatar; // null
|
||||
std::optional<std::string> Token;
|
||||
Snowflake ApplicationID; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, WebhookData &m);
|
||||
};
|
||||
456
windows/guildsettings/auditlogpane.cpp
Normal file
456
windows/guildsettings/auditlogpane.cpp
Normal file
@@ -0,0 +1,456 @@
|
||||
#include "auditlogpane.hpp"
|
||||
#include "../../abaddon.hpp"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
GuildSettingsAuditLogPane::GuildSettingsAuditLogPane(Snowflake id)
|
||||
: GuildID(id) {
|
||||
set_name("guild-audit-log-pane");
|
||||
set_hexpand(true);
|
||||
set_vexpand(true);
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto self_id = discord.GetUserData().ID;
|
||||
|
||||
if (discord.HasGuildPermission(self_id, GuildID, Permission::VIEW_AUDIT_LOG))
|
||||
discord.FetchAuditLog(GuildID, sigc::mem_fun(*this, &GuildSettingsAuditLogPane::OnAuditLogFetch));
|
||||
|
||||
m_list.set_selection_mode(Gtk::SELECTION_NONE);
|
||||
m_list.show();
|
||||
add(m_list);
|
||||
}
|
||||
|
||||
void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
auto guild = *discord.GetGuild(GuildID);
|
||||
for (const auto &entry : data.Entries) {
|
||||
auto expander = Gtk::manage(new Gtk::Expander);
|
||||
auto label = Gtk::manage(new Gtk::Label);
|
||||
label->set_ellipsize(Pango::ELLIPSIZE_END);
|
||||
|
||||
auto user = *discord.GetUser(entry.UserID);
|
||||
|
||||
// spaghetti moment
|
||||
Glib::ustring markup;
|
||||
std::vector<Glib::ustring> extra_markup;
|
||||
switch (entry.Type) {
|
||||
case AuditLogActionType::GUILD_UPDATE: {
|
||||
markup =
|
||||
"<b>" + user.GetEscapedString() + "</b> " +
|
||||
"made changes to <b>" +
|
||||
Glib::Markup::escape_text(guild.Name) +
|
||||
"</b>";
|
||||
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "icon_hash") {
|
||||
extra_markup.push_back("Set the server icon");
|
||||
} else if (change.Key == "name") {
|
||||
auto new_name = change.NewValue;
|
||||
if (new_name.has_value())
|
||||
extra_markup.push_back("Set the server name to <b>" +
|
||||
Glib::Markup::escape_text(new_name->get<std::string>()) +
|
||||
"</b>");
|
||||
else
|
||||
extra_markup.push_back("Set the server name");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::CHANNEL_CREATE: {
|
||||
const auto type = *entry.GetNewFromKey<ChannelType>("type");
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created a " + (type == ChannelType::GUILD_VOICE ? "voice" : "text") +
|
||||
" channel <b>#" +
|
||||
Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "name" && change.NewValue.has_value())
|
||||
extra_markup.push_back("Set the name to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
else if (change.Key == "nsfw" && change.NewValue.has_value())
|
||||
extra_markup.push_back((*change.NewValue ? "Marked" : "Unmarked") +
|
||||
" the channel as NSFW"s);
|
||||
}
|
||||
|
||||
} break;
|
||||
case AuditLogActionType::CHANNEL_UPDATE: {
|
||||
const auto target_channel = discord.GetChannel(entry.TargetID);
|
||||
if (target_channel.has_value()) {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"made changes to <b>#" +
|
||||
Glib::Markup::escape_text(*target_channel->Name) +
|
||||
"</b>";
|
||||
} else {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"made changes to <b><#" +
|
||||
entry.TargetID +
|
||||
"></b>";
|
||||
}
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "name" && change.NewValue.has_value()) {
|
||||
if (change.OldValue.has_value())
|
||||
extra_markup.push_back("Changed the name from <b>" +
|
||||
Glib::Markup::escape_text(change.OldValue->get<std::string>()) +
|
||||
"</b> to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
else
|
||||
extra_markup.push_back("Changed the name to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
} else if (change.Key == "topic") {
|
||||
if (change.NewValue.has_value())
|
||||
extra_markup.push_back("Changed the topic to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
else
|
||||
extra_markup.push_back("Cleared the topic");
|
||||
} else if (change.Key == "nsfw" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back((*change.NewValue ? "Marked" : "Unmarked") + " the channel as NSFW"s);
|
||||
} else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) {
|
||||
const int secs = change.NewValue->get<int>();
|
||||
if (secs == 0)
|
||||
extra_markup.push_back("Disabled slowmode");
|
||||
else
|
||||
extra_markup.push_back("Set slowmode to <b>" +
|
||||
std::to_string(secs) + " seconds</b>");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::CHANNEL_DELETE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"removed <b>#" +
|
||||
Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::CHANNEL_OVERWRITE_CREATE: {
|
||||
const auto channel = discord.GetChannel(entry.TargetID);
|
||||
if (channel.has_value()) {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created channel overrides for <b>#" +
|
||||
Glib::Markup::escape_text(*channel->Name) + "</b>";
|
||||
} else {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created channel overrides for <b><#" +
|
||||
entry.TargetID + "></b>";
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::CHANNEL_OVERWRITE_UPDATE: {
|
||||
const auto channel = discord.GetChannel(entry.TargetID);
|
||||
if (channel.has_value()) {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated channel overrides for <b>#" +
|
||||
Glib::Markup::escape_text(*channel->Name) + "</b>";
|
||||
} else {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated channel overrides for <b><#" +
|
||||
entry.TargetID + "></b>";
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::CHANNEL_OVERWRITE_DELETE: {
|
||||
const auto channel = discord.GetChannel(entry.TargetID);
|
||||
if (channel.has_value()) {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"removed channel overrides for <b>#" +
|
||||
Glib::Markup::escape_text(*channel->Name) + "</b>";
|
||||
} else {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"removed channel overrides for <b><#" +
|
||||
entry.TargetID + "></b>";
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_KICK: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"kicked <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_PRUNE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"pruned <b>" +
|
||||
*entry.Options->MembersRemoved +
|
||||
"</b> members";
|
||||
extra_markup.push_back("For <b>" +
|
||||
*entry.Options->DeleteMemberDays +
|
||||
" days</b> of inactivity");
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_BAN_ADD: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"banned <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_BAN_REMOVE: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"removed the ban for <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_UPDATE: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b>";
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "deaf" && change.NewValue.has_value())
|
||||
extra_markup.push_back(
|
||||
(change.NewValue->get<bool>() ? "<b>Deafened</b>"s : "<b>Undeafened</b>"s) +
|
||||
" them");
|
||||
else if (change.Key == "mute" && change.NewValue.has_value())
|
||||
extra_markup.push_back(
|
||||
(change.NewValue->get<bool>() ? "<b>Muted</b>"s : "<b>Unmuted</b>"s) +
|
||||
" them");
|
||||
else if (change.Key == "nick" && change.NewValue.has_value())
|
||||
extra_markup.push_back("Set their nickname to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_ROLE_UPDATE: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated roles for <b>" +
|
||||
target_user->GetEscapedString() + "</b>";
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "$remove" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back("<b>Removed</b> a role <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue.value()[0].at("name").get<std::string>()) +
|
||||
"</b>");
|
||||
} else if (change.Key == "$add" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back("<b>Added</b> a role <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue.value()[0].at("name").get<std::string>()) +
|
||||
"</b>");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_MOVE: {
|
||||
const auto channel = discord.GetChannel(*entry.Options->ChannelID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"moved <b>" +
|
||||
*entry.Options->Count +
|
||||
" user" +
|
||||
(*entry.Options->Count == "1" ? ""s : "s"s) +
|
||||
"</b> to <b>" +
|
||||
Glib::Markup::escape_text(*channel->Name) +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::MEMBER_DISCONNECT: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"disconnected <b>" +
|
||||
*entry.Options->Count +
|
||||
"</b> users from voice";
|
||||
} break;
|
||||
case AuditLogActionType::BOT_ADD: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"added <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b> to the server";
|
||||
} break;
|
||||
case AuditLogActionType::ROLE_CREATE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created the role <b>" +
|
||||
*entry.GetNewFromKey<std::string>("name") +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::ROLE_UPDATE: {
|
||||
const auto role = discord.GetRole(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated the role <b>" +
|
||||
(role.has_value() ? Glib::Markup::escape_text(role->Name) : entry.TargetID) +
|
||||
"</b>";
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "name" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back("Changed the name to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
} else if (change.Key == "color" && change.NewValue.has_value()) {
|
||||
const auto col = change.NewValue->get<int>();
|
||||
if (col == 0)
|
||||
extra_markup.push_back("Removed the color");
|
||||
else
|
||||
extra_markup.push_back("Set the color to <b>" +
|
||||
IntToCSSColor(col) +
|
||||
"</b>");
|
||||
} else if (change.Key == "permissions") {
|
||||
extra_markup.push_back("Updated the permissions");
|
||||
} else if (change.Key == "mentionable" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back(change.NewValue->get<bool>() ? "Mentionable" : "Not mentionable");
|
||||
} else if (change.Key == "hoist" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back(change.NewValue->get<bool>() ? "Not hoisted" : "Hoisted");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::ROLE_DELETE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"deleted the role <b>" +
|
||||
*entry.GetOldFromKey<std::string>("name") +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::INVITE_CREATE: {
|
||||
const auto code = *entry.GetNewFromKey<std::string>("code");
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created an invite <b>" + code + "</b>";
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "channel_id" && change.NewValue.has_value()) {
|
||||
const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>());
|
||||
if (!channel.has_value()) continue;
|
||||
extra_markup.push_back("For channel <b>#" +
|
||||
Glib::Markup::escape_text(*channel->Name) +
|
||||
"</b>");
|
||||
} else if (change.Key == "max_uses" && change.NewValue.has_value()) {
|
||||
const auto uses = change.NewValue->get<int>();
|
||||
if (uses == 0)
|
||||
extra_markup.push_back("Which has <b>unlimited</b> uses");
|
||||
else
|
||||
extra_markup.push_back("Which has <b>" + std::to_string(uses) + "</b> uses");
|
||||
} else if (change.Key == "temporary" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back("With temporary <b>"s +
|
||||
(change.NewValue->get<bool>() ? "on" : "off") +
|
||||
"</b>");
|
||||
} // no max_age cuz fuck time
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::INVITE_DELETE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"deleted an invite <b>" +
|
||||
*entry.GetOldFromKey<std::string>("code") +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::WEBHOOK_CREATE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created the webhook <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "channel_id" && change.NewValue.has_value()) {
|
||||
const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>());
|
||||
if (channel.has_value()) {
|
||||
extra_markup.push_back("With channel <b>#" +
|
||||
Glib::Markup::escape_text(*channel->Name) +
|
||||
"</b>");
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::WEBHOOK_UPDATE: {
|
||||
const WebhookData *webhookptr = nullptr;
|
||||
for (const auto &webhook : data.Webhooks) {
|
||||
if (webhook.ID == entry.TargetID)
|
||||
webhookptr = &webhook;
|
||||
}
|
||||
if (webhookptr != nullptr) {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated the webhook <b>" +
|
||||
Glib::Markup::escape_text(webhookptr->Name) +
|
||||
"</b>";
|
||||
} else {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated a webhook";
|
||||
}
|
||||
if (entry.Changes.has_value())
|
||||
for (const auto &change : *entry.Changes) {
|
||||
if (change.Key == "name" && change.NewValue.has_value()) {
|
||||
extra_markup.push_back("Changed the name to <b>" +
|
||||
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
|
||||
"</b>");
|
||||
} else if (change.Key == "avatar_hash") {
|
||||
extra_markup.push_back("Changed the avatar");
|
||||
} else if (change.Key == "channel_id" && change.NewValue.has_value()) {
|
||||
const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>());
|
||||
if (channel.has_value()) {
|
||||
extra_markup.push_back("Changed the channel to <b>#" +
|
||||
Glib::Markup::escape_text(*channel->Name) +
|
||||
"</b>");
|
||||
} else {
|
||||
extra_markup.push_back("Changed the channel");
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case AuditLogActionType::WEBHOOK_DELETE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"deleted the webhook <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::EMOJI_CREATE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"created the emoji <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::EMOJI_UPDATE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"updated the emoji <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
extra_markup.push_back("Changed the name from <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) +
|
||||
"</b> to <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) +
|
||||
"</b>");
|
||||
} break;
|
||||
case AuditLogActionType::EMOJI_DELETE: {
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"deleted the emoji <b>" +
|
||||
Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::MESSAGE_PIN: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"pinned a message by <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b>";
|
||||
} break;
|
||||
case AuditLogActionType::MESSAGE_UNPIN: {
|
||||
const auto target_user = discord.GetUser(entry.TargetID);
|
||||
markup = "<b>" + user.GetEscapedString() + "</b> " +
|
||||
"unpinned a message by <b>" +
|
||||
target_user->GetEscapedString() +
|
||||
"</b>";
|
||||
} break;
|
||||
default:
|
||||
markup = "<i>Unknown action</i>";
|
||||
break;
|
||||
}
|
||||
|
||||
label->set_markup(markup);
|
||||
expander->set_label_widget(*label);
|
||||
|
||||
if (entry.Reason.has_value()) {
|
||||
extra_markup.push_back("With reason <b>" +
|
||||
Glib::Markup::escape_text(*entry.Reason) +
|
||||
"</b>");
|
||||
}
|
||||
|
||||
if (extra_markup.size() == 1)
|
||||
expander->set_expanded(true);
|
||||
|
||||
auto contents = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
|
||||
for (const auto &extra : extra_markup) {
|
||||
auto extra_label = Gtk::manage(new Gtk::Label);
|
||||
extra_label->set_markup(extra);
|
||||
extra_label->set_halign(Gtk::ALIGN_START);
|
||||
extra_label->set_margin_start(25);
|
||||
extra_label->set_ellipsize(Pango::ELLIPSIZE_END);
|
||||
contents->add(*extra_label);
|
||||
}
|
||||
expander->add(*contents);
|
||||
expander->set_margin_bottom(5);
|
||||
expander->show_all();
|
||||
m_list.add(*expander);
|
||||
}
|
||||
}
|
||||
15
windows/guildsettings/auditlogpane.hpp
Normal file
15
windows/guildsettings/auditlogpane.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <gtkmm.h>
|
||||
#include "../../discord/objects.hpp"
|
||||
|
||||
class GuildSettingsAuditLogPane : public Gtk::ScrolledWindow {
|
||||
public:
|
||||
GuildSettingsAuditLogPane(Snowflake id);
|
||||
|
||||
private:
|
||||
Gtk::ListBox m_list;
|
||||
|
||||
void OnAuditLogFetch(const AuditLogData &data);
|
||||
|
||||
Snowflake GuildID;
|
||||
};
|
||||
@@ -6,7 +6,8 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
, GuildID(id)
|
||||
, m_pane_info(id)
|
||||
, m_pane_bans(id)
|
||||
, m_pane_invites(id) {
|
||||
, m_pane_invites(id)
|
||||
, m_pane_audit_log(id) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto guild = *discord.GetGuild(id);
|
||||
|
||||
@@ -24,6 +25,7 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
set_title(guild.Name);
|
||||
set_position(Gtk::WIN_POS_CENTER);
|
||||
get_style_context()->add_class("app-window");
|
||||
get_style_context()->add_class("app-popup");
|
||||
|
||||
if (guild.HasIcon()) {
|
||||
Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL(), sigc::mem_fun(*this, &GuildSettingsWindow::set_icon));
|
||||
@@ -38,6 +40,7 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
m_pane_info.show();
|
||||
m_pane_bans.show();
|
||||
m_pane_invites.show();
|
||||
m_pane_audit_log.show();
|
||||
|
||||
m_stack.set_transition_duration(100);
|
||||
m_stack.set_transition_type(Gtk::STACK_TRANSITION_TYPE_CROSSFADE);
|
||||
@@ -49,6 +52,7 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
m_stack.add(m_pane_info, "info", "Info");
|
||||
m_stack.add(m_pane_bans, "bans", "Bans");
|
||||
m_stack.add(m_pane_invites, "invites", "Invites");
|
||||
m_stack.add(m_pane_audit_log, "audit-log", "Audit Log");
|
||||
m_stack.show();
|
||||
|
||||
m_main.add(m_switcher);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "guildsettings/infopane.hpp"
|
||||
#include "guildsettings/banspane.hpp"
|
||||
#include "guildsettings/invitespane.hpp"
|
||||
#include "guildsettings/auditlogpane.hpp"
|
||||
|
||||
class GuildSettingsWindow : public Gtk::Window {
|
||||
public:
|
||||
@@ -17,6 +18,7 @@ private:
|
||||
GuildSettingsInfoPane m_pane_info;
|
||||
GuildSettingsBansPane m_pane_bans;
|
||||
GuildSettingsInvitesPane m_pane_invites;
|
||||
GuildSettingsAuditLogPane m_pane_audit_log;
|
||||
|
||||
Snowflake GuildID;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user