#include "auditlogpane.hpp"
#include "abaddon.hpp"
using namespace std::string_literals;
GuildSettingsAuditLogPane::GuildSettingsAuditLogPane(Snowflake id)
: GuildID(id) {
signal_map().connect(sigc::mem_fun(*this, &GuildSettingsAuditLogPane::OnMap));
set_name("guild-audit-log-pane");
set_hexpand(true);
set_vexpand(true);
m_list.set_selection_mode(Gtk::SELECTION_NONE);
m_list.show();
add(m_list);
}
void GuildSettingsAuditLogPane::OnMap() {
if (m_requested) return;
m_requested = 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));
}
void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
auto &discord = Abaddon::Get().GetDiscordClient();
auto guild = *discord.GetGuild(GuildID);
for (const auto &entry : data.Entries) {
if (entry.TargetID == "") continue;
auto expander = Gtk::manage(new Gtk::Expander);
auto label = Gtk::manage(new Gtk::Label);
label->set_ellipsize(Pango::ELLIPSIZE_END);
Glib::ustring user_markup = "Unknown User";
if (entry.UserID.has_value()) {
if (auto user = discord.GetUser(*entry.UserID); user.has_value())
user_markup = discord.GetUser(*entry.UserID)->GetEscapedBoldString();
}
// spaghetti moment
Glib::ustring markup;
std::vector extra_markup;
switch (entry.Type) {
case AuditLogActionType::GUILD_UPDATE: {
markup =
user_markup +
" made changes to " +
Glib::Markup::escape_text(guild.Name) +
"";
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 " +
Glib::Markup::escape_text(new_name->get()) +
"");
else
extra_markup.push_back("Set the server name");
}
}
} break;
case AuditLogActionType::CHANNEL_CREATE: {
const auto type = *entry.GetNewFromKey("type");
markup = user_markup +
" created a " + (type == ChannelType::GUILD_VOICE ? "voice" : "text") +
" channel #" +
Glib::Markup::escape_text(*entry.GetNewFromKey("name")) +
"";
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 " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
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 = user_markup +
" made changes to #" +
Glib::Markup::escape_text(*target_channel->Name) +
"";
} else {
markup = user_markup +
" made changes to <#" +
entry.TargetID +
">";
}
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 " +
Glib::Markup::escape_text(change.OldValue->get()) +
" to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
else
extra_markup.push_back("Changed the name to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
} else if (change.Key == "topic") {
if (change.NewValue.has_value())
extra_markup.push_back("Changed the topic to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
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();
if (secs == 0)
extra_markup.push_back("Disabled slowmode");
else
extra_markup.push_back("Set slowmode to " +
std::to_string(secs) + " seconds");
}
}
} break;
case AuditLogActionType::CHANNEL_DELETE: {
markup = user_markup +
" removed #" +
Glib::Markup::escape_text(*entry.GetOldFromKey("name")) +
"";
} break;
case AuditLogActionType::CHANNEL_OVERWRITE_CREATE: {
const auto channel = discord.GetChannel(entry.TargetID);
if (channel.has_value()) {
markup = user_markup +
" created channel overrides for #" +
Glib::Markup::escape_text(*channel->Name) + "";
} else {
markup = user_markup +
" created channel overrides for <#" +
entry.TargetID + ">";
}
} break;
case AuditLogActionType::CHANNEL_OVERWRITE_UPDATE: {
const auto channel = discord.GetChannel(entry.TargetID);
if (channel.has_value()) {
markup = user_markup +
" updated channel overrides for #" +
Glib::Markup::escape_text(*channel->Name) + "";
} else {
markup = user_markup +
" updated channel overrides for <#" +
entry.TargetID + ">";
}
} break;
case AuditLogActionType::CHANNEL_OVERWRITE_DELETE: {
const auto channel = discord.GetChannel(entry.TargetID);
if (channel.has_value()) {
markup = user_markup +
" removed channel overrides for #" +
Glib::Markup::escape_text(*channel->Name) + "";
} else {
markup = user_markup +
" removed channel overrides for <#" +
entry.TargetID + ">";
}
} break;
case AuditLogActionType::MEMBER_KICK: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" kicked " +
target_user->GetEscapedString() +
"";
} break;
case AuditLogActionType::MEMBER_PRUNE: {
markup = user_markup +
" pruned " +
*entry.Options->MembersRemoved +
" members";
extra_markup.push_back("For " +
*entry.Options->DeleteMemberDays +
" days of inactivity");
} break;
case AuditLogActionType::MEMBER_BAN_ADD: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" banned " +
target_user->GetEscapedString() +
"";
} break;
case AuditLogActionType::MEMBER_BAN_REMOVE: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" removed the ban for " +
target_user->GetEscapedString() +
"";
} break;
case AuditLogActionType::MEMBER_UPDATE: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" updated " +
target_user->GetEscapedString() +
"";
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() ? "Deafened"s : "Undeafened"s) +
" them");
else if (change.Key == "mute" && change.NewValue.has_value())
extra_markup.push_back(
(change.NewValue->get() ? "Muted"s : "Unmuted"s) +
" them");
else if (change.Key == "nick" && change.NewValue.has_value())
extra_markup.push_back("Set their nickname to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
}
} break;
case AuditLogActionType::MEMBER_ROLE_UPDATE: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" updated roles for " +
target_user->GetEscapedString() + "";
if (entry.Changes.has_value())
for (const auto &change : *entry.Changes) {
if (change.Key == "$remove" && change.NewValue.has_value()) {
extra_markup.push_back("Removed a role " +
Glib::Markup::escape_text(change.NewValue.value()[0].at("name").get()) +
"");
} else if (change.Key == "$add" && change.NewValue.has_value()) {
extra_markup.push_back("Added a role " +
Glib::Markup::escape_text(change.NewValue.value()[0].at("name").get()) +
"");
}
}
} break;
case AuditLogActionType::MEMBER_MOVE: {
const auto channel = discord.GetChannel(*entry.Options->ChannelID);
markup = user_markup +
" moved " +
*entry.Options->Count +
" user" +
(*entry.Options->Count == "1" ? ""s : "s"s) +
" to " +
Glib::Markup::escape_text(*channel->Name) +
"";
} break;
case AuditLogActionType::MEMBER_DISCONNECT: {
markup = user_markup +
" disconnected " +
*entry.Options->Count +
" users from voice";
} break;
case AuditLogActionType::BOT_ADD: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" added " +
target_user->GetEscapedString() +
" to the server";
} break;
case AuditLogActionType::ROLE_CREATE: {
markup = user_markup +
" created the role " +
*entry.GetNewFromKey("name") +
"";
} break;
case AuditLogActionType::ROLE_UPDATE: {
const auto role = discord.GetRole(entry.TargetID);
markup = user_markup +
" updated the role " +
(role.has_value() ? Glib::Markup::escape_text(role->Name) : Glib::ustring(entry.TargetID)) +
"";
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 " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
} else if (change.Key == "color" && change.NewValue.has_value()) {
const auto col = change.NewValue->get();
if (col == 0)
extra_markup.push_back("Removed the color");
else
extra_markup.push_back("Set the color to " +
IntToCSSColor(col) +
"");
} 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() ? "Mentionable" : "Not mentionable");
} else if (change.Key == "hoist" && change.NewValue.has_value()) {
extra_markup.push_back(change.NewValue->get() ? "Not hoisted" : "Hoisted");
}
}
} break;
case AuditLogActionType::ROLE_DELETE: {
markup = user_markup +
" deleted the role " +
*entry.GetOldFromKey("name") +
"";
} break;
case AuditLogActionType::INVITE_CREATE: {
const auto code = *entry.GetNewFromKey("code");
markup = user_markup +
" created an invite " + code + "";
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());
if (!channel.has_value()) continue;
extra_markup.push_back("For channel #" +
Glib::Markup::escape_text(*channel->Name) +
"");
} else if (change.Key == "max_uses" && change.NewValue.has_value()) {
const auto uses = change.NewValue->get();
if (uses == 0)
extra_markup.push_back("Which has unlimited uses");
else
extra_markup.push_back("Which has " + std::to_string(uses) + " uses");
} else if (change.Key == "temporary" && change.NewValue.has_value()) {
extra_markup.push_back("With temporary "s +
(change.NewValue->get() ? "on" : "off") +
"");
} // no max_age cuz fuck time
}
} break;
case AuditLogActionType::INVITE_DELETE: {
markup = user_markup +
" deleted an invite " +
*entry.GetOldFromKey("code") +
"";
} break;
case AuditLogActionType::WEBHOOK_CREATE: {
markup = user_markup +
" created the webhook " +
Glib::Markup::escape_text(*entry.GetNewFromKey("name")) +
"";
for (const auto &change : *entry.Changes) {
if (change.Key == "channel_id" && change.NewValue.has_value()) {
const auto channel = discord.GetChannel(change.NewValue->get());
if (channel.has_value()) {
extra_markup.push_back("With channel #" +
Glib::Markup::escape_text(*channel->Name) +
"");
}
}
}
} 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 = user_markup +
" updated the webhook " +
Glib::Markup::escape_text(webhookptr->Name) +
"";
} else {
markup = user_markup +
" 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 " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
} 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());
if (channel.has_value()) {
extra_markup.push_back("Changed the channel to #" +
Glib::Markup::escape_text(*channel->Name) +
"");
} else {
extra_markup.push_back("Changed the channel");
}
}
}
} break;
case AuditLogActionType::WEBHOOK_DELETE: {
markup = user_markup +
" deleted the webhook " +
Glib::Markup::escape_text(*entry.GetOldFromKey("name")) +
"";
} break;
case AuditLogActionType::EMOJI_CREATE: {
markup = user_markup +
" created the emoji " +
Glib::Markup::escape_text(*entry.GetNewFromKey("name")) +
"";
} break;
case AuditLogActionType::EMOJI_UPDATE: {
markup = user_markup +
" updated the emoji " +
Glib::Markup::escape_text(*entry.GetOldFromKey("name")) +
"";
extra_markup.push_back("Changed the name from " +
Glib::Markup::escape_text(*entry.GetOldFromKey("name")) +
" to " +
Glib::Markup::escape_text(*entry.GetNewFromKey("name")) +
"");
} break;
case AuditLogActionType::EMOJI_DELETE: {
markup = user_markup +
" deleted the emoji " +
Glib::Markup::escape_text(*entry.GetOldFromKey("name")) +
"";
} break;
case AuditLogActionType::MESSAGE_DELETE: {
const auto channel = discord.GetChannel(*entry.Options->ChannelID);
const auto count = *entry.Options->Count;
if (channel.has_value()) {
markup = user_markup +
" deleted " + count + " messages in #" +
Glib::Markup::escape_text(*channel->Name) +
"";
} else {
markup = user_markup +
" deleted " + count + " messages";
}
} break;
case AuditLogActionType::MESSAGE_BULK_DELETE: {
const auto channel = discord.GetChannel(entry.TargetID);
if (channel.has_value()) {
markup = user_markup +
" deleted " +
*entry.Options->Count +
" messages in #" +
Glib::Markup::escape_text(*channel->Name) +
"";
} else {
markup = user_markup +
" deleted " +
*entry.Options->Count +
" messages";
}
} break;
case AuditLogActionType::MESSAGE_PIN: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" pinned a message by " +
target_user->GetEscapedString() +
"";
} break;
case AuditLogActionType::MESSAGE_UNPIN: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" unpinned a message by " +
target_user->GetEscapedString() +
"";
} break;
case AuditLogActionType::STAGE_INSTANCE_CREATE: {
const auto channel = discord.GetChannel(*entry.Options->ChannelID);
if (channel.has_value()) {
markup = user_markup +
" started the stage for " +
Glib::Markup::escape_text(*channel->Name) +
"";
} else {
markup = user_markup +
" started the stage for " +
std::to_string(*entry.Options->ChannelID) +
"";
}
if (entry.Changes.has_value()) {
for (const auto &change : *entry.Changes) {
if (change.Key == "topic" && change.NewValue.has_value()) {
extra_markup.push_back(
"Set the topic to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
} else if (change.Key == "privacy_level" && change.NewValue.has_value()) {
Glib::ustring str = Glib::Markup::escape_text(GetStagePrivacyDisplayString(change.NewValue->get()));
extra_markup.push_back(
"Set the privacy level to " +
str +
"");
}
}
}
} break;
case AuditLogActionType::STAGE_INSTANCE_UPDATE: {
const auto channel = discord.GetChannel(*entry.Options->ChannelID);
if (channel.has_value()) {
markup = user_markup +
" updated the stage for " +
Glib::Markup::escape_text(*channel->Name) +
"";
} else {
markup = user_markup +
" updated the stage for " +
std::to_string(*entry.Options->ChannelID) +
"";
}
if (entry.Changes.has_value()) {
for (const auto &change : *entry.Changes) {
if (change.Key == "topic" && change.NewValue.has_value()) {
extra_markup.push_back(
"Set the topic to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
} else if (change.Key == "privacy_level" && change.NewValue.has_value()) {
Glib::ustring str = Glib::Markup::escape_text(GetStagePrivacyDisplayString(change.NewValue->get()));
extra_markup.push_back(
"Set the privacy level to " +
str +
"");
}
}
}
} break;
case AuditLogActionType::STAGE_INSTANCE_DELETE: {
const auto channel = discord.GetChannel(*entry.Options->ChannelID);
if (channel.has_value()) {
markup = user_markup +
" ended the stage for " +
Glib::Markup::escape_text(*channel->Name) +
"";
} else {
markup = user_markup +
" ended the stage for " +
std::to_string(*entry.Options->ChannelID) +
"";
}
} break;
case AuditLogActionType::THREAD_CREATE: {
const auto channel = discord.GetChannel(entry.TargetID);
markup = user_markup +
" created a thread " +
(channel.has_value()
? Glib::Markup::escape_text(*channel->Name)
: Glib::ustring(*entry.GetNewFromKey("name"))) +
"";
if (entry.Changes.has_value()) {
for (const auto &change : *entry.Changes) {
if (change.Key == "name")
extra_markup.push_back("Set the name to " + Glib::Markup::escape_text(change.NewValue->get()) + "");
else if (change.Key == "archived")
extra_markup.push_back(change.NewValue->get() ? "Archived the thread" : "Unarchived the thread");
else if (change.Key == "auto_archive_duration")
extra_markup.push_back("Set auto archive duration to "s + std::to_string(change.NewValue->get()) + " minutes"s);
else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) {
const int secs = change.NewValue->get();
if (secs == 0)
extra_markup.push_back("Disabled slowmode");
else
extra_markup.push_back("Set slowmode to " +
std::to_string(secs) + " seconds");
} else if (change.Key == "locked")
extra_markup.push_back(change.NewValue->get() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators");
}
}
} break;
case AuditLogActionType::THREAD_UPDATE: {
const auto channel = discord.GetChannel(entry.TargetID);
markup = user_markup +
" made changes to the thread " +
(channel.has_value()
? Glib::Markup::escape_text(*channel->Name)
: Glib::ustring(entry.TargetID)) +
"";
for (const auto &change : *entry.Changes) {
if (change.Key == "name")
extra_markup.push_back(
"Changed the name from " +
Glib::Markup::escape_text(change.OldValue->get()) +
" to " +
Glib::Markup::escape_text(change.NewValue->get()) +
"");
else if (change.Key == "auto_archive_duration")
extra_markup.push_back("Set auto archive duration to "s + std::to_string(change.NewValue->get()) + " minutes"s);
else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) {
const int secs = change.NewValue->get();
if (secs == 0)
extra_markup.push_back("Disabled slowmode");
else
extra_markup.push_back("Set slowmode to " +
std::to_string(secs) +
" seconds");
} else if (change.Key == "locked")
extra_markup.push_back(change.NewValue->get() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators");
else if (change.Key == "archived")
extra_markup.push_back(change.NewValue->get() ? "Archived the thread" : "Unarchived the thread");
}
} break;
case AuditLogActionType::THREAD_DELETE: {
markup = user_markup +
" deleted the thread " + Glib::Markup::escape_text(*entry.GetOldFromKey("name")) + "";
} break;
default:
markup = "Unknown action";
break;
}
label->set_markup(markup);
expander->set_label_widget(*label);
if (entry.Reason.has_value()) {
extra_markup.push_back("With reason " +
Glib::Markup::escape_text(*entry.Reason) +
"");
}
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);
}
}