Skip to content

Commit 7ba661d

Browse files
authored
Skipping audit logs against bot message deletions (#395)
* Skipping audit logs against bot message deletions * Bugfix with broken RestAction pipeline
1 parent aa68787 commit 7ba661d

File tree

1 file changed

+45
-33
lines changed

1 file changed

+45
-33
lines changed

application/src/main/java/org/togetherjava/tjbot/routines/ModAuditLogRoutine.java

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
import java.time.*;
2626
import java.time.temporal.ChronoUnit;
2727
import java.time.temporal.TemporalAccessor;
28-
import java.util.*;
2928
import java.util.List;
29+
import java.util.*;
3030
import java.util.concurrent.ScheduledExecutorService;
3131
import java.util.concurrent.TimeUnit;
3232
import java.util.function.BiFunction;
@@ -70,33 +70,15 @@ public ModAuditLogRoutine(@NotNull Database database, @NotNull Config config) {
7070
this.database = database;
7171
}
7272

73-
private static @NotNull RestAction<MessageEmbed> handleAction(@NotNull Action action,
73+
private static @NotNull RestAction<AuditLogMessage> handleAction(@NotNull Action action,
7474
@NotNull AuditLogEntry entry) {
7575
User author = Objects.requireNonNull(entry.getUser());
76-
return getTargetTagFromEntry(entry).map(targetTag -> createMessage(author, action,
77-
targetTag, entry.getReason(), entry.getTimeCreated()));
76+
return getTargetFromEntryOrNull(entry).map(target -> new AuditLogMessage(author, action,
77+
target, entry.getReason(), entry.getTimeCreated()));
7878
}
7979

80-
private static @NotNull MessageEmbed createMessage(@NotNull User author, @NotNull Action action,
81-
@NotNull String targetTag, @Nullable String reason,
82-
@NotNull TemporalAccessor timestamp) {
83-
String description = "%s **%s**.".formatted(action.getVerb(), targetTag);
84-
if (reason != null && !reason.isBlank()) {
85-
description += "\n\nReason: " + reason;
86-
}
87-
return new EmbedBuilder().setAuthor(author.getAsTag(), null, author.getAvatarUrl())
88-
.setDescription(description)
89-
.setTimestamp(timestamp)
90-
.setColor(AMBIENT_COLOR)
91-
.build();
92-
}
93-
94-
private static RestAction<String> getTargetTagFromEntry(@NotNull AuditLogEntry entry) {
95-
// If the target is null, the user got deleted in the meantime
96-
return entry.getJDA()
97-
.retrieveUserById(entry.getTargetIdLong())
98-
.onErrorMap(error -> null)
99-
.map(target -> target == null ? "(user unknown)" : target.getAsTag());
80+
private static RestAction<User> getTargetFromEntryOrNull(@NotNull AuditLogEntry entry) {
81+
return entry.getJDA().retrieveUserById(entry.getTargetIdLong()).onErrorMap(error -> null);
10082
}
10183

10284
private static boolean isSnowflakeAfter(@NotNull ISnowflake snowflake,
@@ -178,34 +160,40 @@ private static boolean isSnowflakeAfter(@NotNull ISnowflake snowflake,
178160
@NotNull AuditLogEntry entry) {
179161
// NOTE Temporary bans are realized as permanent bans with automated unban,
180162
// hence we can not differentiate a permanent or a temporary ban here
181-
return Optional.of(handleAction(Action.BAN, entry));
163+
return Optional.of(handleAction(Action.BAN, entry).map(AuditLogMessage::toEmbed));
182164
}
183165

184166
private static @NotNull Optional<RestAction<MessageEmbed>> handleUnbanEntry(
185167
@NotNull AuditLogEntry entry) {
186-
return Optional.of(handleAction(Action.UNBAN, entry));
168+
return Optional.of(handleAction(Action.UNBAN, entry).map(AuditLogMessage::toEmbed));
187169
}
188170

189171
private static @NotNull Optional<RestAction<MessageEmbed>> handleKickEntry(
190172
@NotNull AuditLogEntry entry) {
191-
return Optional.of(handleAction(Action.KICK, entry));
173+
return Optional.of(handleAction(Action.KICK, entry).map(AuditLogMessage::toEmbed));
192174
}
193175

194176
private static @NotNull Optional<RestAction<MessageEmbed>> handleMuteEntry(
195177
@NotNull AuditLogEntry entry) {
196178
// NOTE Temporary mutes are realized as permanent mutes with automated unmute,
197179
// hence we can not differentiate a permanent or a temporary mute here
198-
return Optional.of(handleAction(Action.MUTE, entry));
180+
return Optional.of(handleAction(Action.MUTE, entry).map(AuditLogMessage::toEmbed));
199181
}
200182

201183
private static @NotNull Optional<RestAction<MessageEmbed>> handleUnmuteEntry(
202184
@NotNull AuditLogEntry entry) {
203-
return Optional.of(handleAction(Action.UNMUTE, entry));
185+
return Optional.of(handleAction(Action.UNMUTE, entry).map(AuditLogMessage::toEmbed));
204186
}
205187

206188
private static @NotNull Optional<RestAction<MessageEmbed>> handleMessageDeleteEntry(
207189
@NotNull AuditLogEntry entry) {
208-
return Optional.of(handleAction(Action.MESSAGE_DELETION, entry));
190+
return Optional.of(handleAction(Action.MESSAGE_DELETION, entry).map(message -> {
191+
if (message.target() != null && message.target().isBot()) {
192+
// Message deletions against bots should be skipped. Cancel action.
193+
return null;
194+
}
195+
return message.toEmbed();
196+
}));
209197
}
210198

211199
@Override
@@ -274,8 +262,7 @@ private void handleAuditLogs(@NotNull MessageChannel auditLogChannel,
274262
.sorted(Comparator.comparing(TimeUtil::getTimeCreated))
275263
.map(entry -> handleAuditLog(auditLogChannel, entry))
276264
.flatMap(Optional::stream)
277-
.reduce((firstMessage, secondMessage) -> firstMessage.flatMap(result -> secondMessage))
278-
.ifPresent(RestAction::queue);
265+
.forEach(RestAction::queue);
279266

280267
database.write(context -> {
281268
var entry = context.newRecord(ModAuditLogGuildProcess.MOD_AUDIT_LOG_GUILD_PROCESS);
@@ -298,7 +285,13 @@ private Optional<RestAction<Message>> handleAuditLog(@NotNull MessageChannel aud
298285
case MESSAGE_DELETE -> handleMessageDeleteEntry(entry);
299286
default -> Optional.empty();
300287
};
301-
return maybeMessage.map(message -> message.flatMap(auditLogChannel::sendMessageEmbeds));
288+
// It can have 3 states:
289+
// * empty optional - entry is irrelevant and should not be logged
290+
// * has RestAction but that will contain null - entry was relevant at first, but at
291+
// query-time we found out that it is irrelevant
292+
// * has RestAction but will contain a message - entry is relevant, log the message
293+
return maybeMessage
294+
.map(message -> message.flatMap(Objects::nonNull, auditLogChannel::sendMessageEmbeds));
302295
}
303296

304297
private @NotNull Optional<RestAction<MessageEmbed>> handleRoleUpdateEntry(
@@ -359,4 +352,23 @@ String getVerb() {
359352
return verb;
360353
}
361354
}
355+
356+
private record AuditLogMessage(@NotNull User author, @NotNull Action action,
357+
@Nullable User target, @Nullable String reason, @NotNull TemporalAccessor timestamp) {
358+
@NotNull
359+
MessageEmbed toEmbed() {
360+
String targetTag = target == null ? "(user unknown)" : target.getAsTag();
361+
String description = "%s **%s**.".formatted(action.getVerb(), targetTag);
362+
363+
if (reason != null && !reason.isBlank()) {
364+
description += "\n\nReason: " + reason;
365+
}
366+
367+
return new EmbedBuilder().setAuthor(author.getAsTag(), null, author.getAvatarUrl())
368+
.setDescription(description)
369+
.setTimestamp(timestamp)
370+
.setColor(AMBIENT_COLOR)
371+
.build();
372+
}
373+
}
362374
}

0 commit comments

Comments
 (0)