2525import  java .time .*;
2626import  java .time .temporal .ChronoUnit ;
2727import  java .time .temporal .TemporalAccessor ;
28- import  java .util .*;
2928import  java .util .List ;
29+ import  java .util .*;
3030import  java .util .concurrent .ScheduledExecutorService ;
3131import  java .util .concurrent .TimeUnit ;
3232import  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 \n Reason: "  + 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 \n Reason: "  + 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