Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ and deliver them to a configured channel without the need to manually navigate t

With support for detecting more than 72 events, it can log changes made to: AutoMod Settings, Servers, Onboarding,
Invites, Members, Roles, Channels, Threads, Stages, Events, Polls, Messages, Boosts, Emojis, Stickers, Soundboard,
Integrations, Webhooks, Moderation Action and Unknown events.
Integrations, Webhooks, Moderation Action, Unusual DMs, Raids and Unknown events.

> Get it from here: https://discord.com/discovery/applications/1381658412550590475

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.eggy03</groupId>
<artifactId>papertrailbot</artifactId>
<version>4.0.0</version>
<version>4.1.0</version>
<name>PaperTrail Bot</name>
<description>A simple bot for logging all server and member actions</description>
<packaging>quarkus</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public final class ProjectInfo {
public static final String APPNAME = "PaperTrail";

@NonNull
public static final String VERSION = "v4.0.0";
public static final String VERSION = "v4.1.0";

@NonNull
public static final String PROJECT_ISSUE_LINK = "https://github.com/eggy03/PaperTrailBot/issues";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package io.github.eggy03.papertrail.bot.handlers.guild;

import io.github.eggy03.papertrail.sdk.client.AuditLogRegistrationClient;
import io.github.eggy03.papertrail.sdk.entity.AuditLogRegistrationEntity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.guild.SecurityIncidentActions;
import net.dv8tion.jda.api.entities.guild.SecurityIncidentDetections;
import net.dv8tion.jda.api.events.guild.update.GenericGuildUpdateEvent;
import net.dv8tion.jda.api.events.guild.update.GuildUpdateSecurityIncidentActionsEvent;
import net.dv8tion.jda.api.events.guild.update.GuildUpdateSecurityIncidentDetectionsEvent;
import net.dv8tion.jda.api.utils.MarkdownUtil;
import org.apache.commons.lang3.StringUtils;
import org.jspecify.annotations.Nullable;

import java.awt.Color;
import java.time.Instant;
import java.time.OffsetDateTime;

@ApplicationScoped
@Slf4j
public final class GuildSecurityIncidentEventHandler {

private final @NonNull AuditLogRegistrationClient client;

@Inject
public GuildSecurityIncidentEventHandler(@NonNull AuditLogRegistrationClient client) {
this.client = client;
}

@NonNull
private String getRegisteredGuildChannel(@NonNull String guildId) {
return client.getRegisteredGuild(guildId)
.map(AuditLogRegistrationEntity::getChannelId).orElse(StringUtils.EMPTY);

}

private void performChecksThenBuildAndSendEmbed(@NonNull GenericGuildUpdateEvent<?> event, @NonNull EmbedBuilder embedBuilder, @NonNull String channelIdToSendTo) {
if (!embedBuilder.isValidLength() || embedBuilder.isEmpty()) {
log.warn("Embed is empty or too long (current length: {}).", embedBuilder.length());
return;
}

TextChannel sendingChannel = event.getGuild().getTextChannelById(channelIdToSendTo);
if (sendingChannel != null && sendingChannel.canTalk()) {
sendingChannel.sendMessageEmbeds(embedBuilder.build()).queue();
}
}

private @NonNull String formatOffsetDateTime(@Nullable OffsetDateTime offsetDateTime) {
if (offsetDateTime == null) return "N/A";
return "<t:" + offsetDateTime.toEpochSecond() + ":f>";
}

public void handleGuildUpdateSecurityIncidentDetections(@NonNull GuildUpdateSecurityIncidentDetectionsEvent event) {
String channelIdToSendTo = getRegisteredGuildChannel(event.getGuild().getId());
if (channelIdToSendTo.isBlank()) return;

SecurityIncidentDetections oldSID = event.getOldSecurityIncidentDetections();
SecurityIncidentDetections newSID = event.getNewSecurityIncidentDetections();

OffsetDateTime oldDMSpamDetectedAt = oldSID.getTimeDetectedDmSpam();
OffsetDateTime newDMSpamDetectedAt = newSID.getTimeDetectedDmSpam();

OffsetDateTime oldRaidDetectedAt = oldSID.getTimeDetectedRaid();
OffsetDateTime newRaidDetectedAt = newSID.getTimeDetectedRaid();

if (oldDMSpamDetectedAt != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Incident Resolved");
eb.setDescription(MarkdownUtil.quoteBlock("Incident Type: DM Spam\nStatus: Resolved"));
eb.setColor(Color.GREEN);
eb.addField(MarkdownUtil.underline("DM Spam Started At"), formatOffsetDateTime(oldDMSpamDetectedAt), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}

if (newDMSpamDetectedAt != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Incident Detected");
eb.setDescription(MarkdownUtil.quoteBlock("Incident Type: DM Spam\nStatus: Detected"));
eb.setColor(Color.ORANGE);
eb.addField(MarkdownUtil.underline("DM Spam Detected At"), formatOffsetDateTime(newDMSpamDetectedAt), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}

if (oldRaidDetectedAt != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Incident Resolved");
eb.setDescription(MarkdownUtil.quoteBlock("Incident Type: RAID\nStatus: Resolved"));
eb.setColor(Color.GREEN);
eb.addField(MarkdownUtil.underline("Raid Was Detected At"), formatOffsetDateTime(oldRaidDetectedAt), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}

if (newRaidDetectedAt != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Incident Detected");
eb.setDescription(MarkdownUtil.quoteBlock("Incident Type: RAID\nStatus: Detected"));
eb.setColor(Color.ORANGE);
eb.addField(MarkdownUtil.underline("Raid Detected At"), formatOffsetDateTime(newRaidDetectedAt), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}
}

public void handleGuildUpdateSecurityIncidentActions(@NonNull GuildUpdateSecurityIncidentActionsEvent event) {
String channelIdToSendTo = getRegisteredGuildChannel(event.getGuild().getId());
if (channelIdToSendTo.isBlank()) return;

SecurityIncidentActions oldSIA = event.getOldSecurityIncidentActions();
SecurityIncidentActions newSIA = event.getNewSecurityIncidentActions();

OffsetDateTime oldDMDisabledUntil = oldSIA.getDirectMessagesDisabledUntil();
OffsetDateTime newDMDisabledUntil = newSIA.getDirectMessagesDisabledUntil();

OffsetDateTime oldInvitesPausedUntil = oldSIA.getInvitesDisabledUntil();
OffsetDateTime newInvitesPausedUntil = newSIA.getInvitesDisabledUntil();

if (oldDMDisabledUntil != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Action Disabled");
eb.setDescription(MarkdownUtil.quoteBlock("Action Type: DM Pause\nStatus: Disabled"));
eb.setColor(Color.GREEN);
eb.addField(MarkdownUtil.underline("DMs Were Paused Until"), formatOffsetDateTime(oldDMDisabledUntil), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}

if (newDMDisabledUntil != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Action Enabled");
eb.setDescription(MarkdownUtil.quoteBlock("Action Type: DM Pause\nStatus: Enabled"));
eb.setColor(Color.ORANGE);
eb.addField(MarkdownUtil.underline("DMs Paused Until"), formatOffsetDateTime(newDMDisabledUntil), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}

if (oldInvitesPausedUntil != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Action Disabled");
eb.setDescription(MarkdownUtil.quoteBlock("Action Type: Invite Pause\nStatus: Disabled"));
eb.setColor(Color.GREEN);
eb.addField(MarkdownUtil.underline("Invites Were Paused Until"), formatOffsetDateTime(oldInvitesPausedUntil), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}

if (newInvitesPausedUntil != null) {
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Audit Log Entry | Security Action Enabled");
eb.setDescription(MarkdownUtil.quoteBlock("Action Type: Invite Pause\nStatus: Enabled"));
eb.setColor(Color.ORANGE);
eb.addField(MarkdownUtil.underline("Invites Paused Until"), formatOffsetDateTime(newInvitesPausedUntil), false);
eb.setFooter(event.getGuild().getName());
eb.setTimestamp(Instant.now());

performChecksThenBuildAndSendEmbed(event, eb, channelIdToSendTo);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.eggy03.papertrail.bot.listeners.guild;

import io.github.eggy03.papertrail.bot.handlers.guild.GuildSecurityIncidentEventHandler;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.NonNull;
import net.dv8tion.jda.api.events.guild.update.GuildUpdateSecurityIncidentActionsEvent;
import net.dv8tion.jda.api.events.guild.update.GuildUpdateSecurityIncidentDetectionsEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;

@ApplicationScoped
public final class GuildSecurityIncidentEventListener extends ListenerAdapter {

private final @NonNull GuildSecurityIncidentEventHandler handler;

@Inject
public GuildSecurityIncidentEventListener(@NonNull GuildSecurityIncidentEventHandler handler) {
this.handler = handler;
}

@Override
public void onGuildUpdateSecurityIncidentDetections(@NonNull GuildUpdateSecurityIncidentDetectionsEvent event) {
handler.handleGuildUpdateSecurityIncidentDetections(event);
}

@Override
public void onGuildUpdateSecurityIncidentActions(@NonNull GuildUpdateSecurityIncidentActionsEvent event) {
handler.handleGuildUpdateSecurityIncidentActions(event);
}

}
Loading