Skip to content

Commit 8dc3b07

Browse files
authored
Enhanced mod action dm message (#647)
* enhanced mod aciton dm message * removed unused fields * improvements * improved docs * code fixes * code fixes * fixes * CR
1 parent d839687 commit 8dc3b07

File tree

8 files changed

+134
-172
lines changed

8 files changed

+134
-172
lines changed

application/src/main/java/org/togetherjava/tjbot/features/moderation/BanCommand.java

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import net.dv8tion.jda.api.Permission;
44
import net.dv8tion.jda.api.entities.*;
5-
import net.dv8tion.jda.api.events.GenericEvent;
65
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
76
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
87
import net.dv8tion.jda.api.interactions.InteractionHook;
@@ -13,7 +12,6 @@
1312
import net.dv8tion.jda.api.requests.ErrorResponse;
1413
import net.dv8tion.jda.api.requests.RestAction;
1514
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
16-
import net.dv8tion.jda.api.utils.Result;
1715
import org.slf4j.Logger;
1816
import org.slf4j.LoggerFactory;
1917

@@ -45,6 +43,7 @@ public final class BanCommand extends SlashCommandAdapter {
4543
private static final String REASON_OPTION = "reason";
4644
private static final String COMMAND_NAME = "ban";
4745
private static final String ACTION_VERB = "ban";
46+
private static final String ACTION_TITLE = "Ban";
4847
@SuppressWarnings("StaticCollection")
4948
private static final List<String> DURATIONS = List.of(ModerationUtils.PERMANENT_DURATION,
5049
"1 hour", "3 hours", "1 day", "2 days", "3 days", "7 days", "30 days");
@@ -82,24 +81,16 @@ private static RestAction<Message> handleAlreadyBanned(Guild.Ban ban, Interactio
8281
return hook.sendMessage(message).setEphemeral(true);
8382
}
8483

85-
private static RestAction<Boolean> sendDm(ISnowflake target,
86-
@Nullable ModerationUtils.TemporaryData temporaryData, String reason, Guild guild,
87-
GenericEvent event) {
88-
String durationMessage =
89-
temporaryData == null ? "permanently" : "for " + temporaryData.duration();
90-
String dmMessage =
84+
private static RestAction<Boolean> sendDm(User target,
85+
@Nullable ModerationUtils.TemporaryData temporaryData, String reason, Guild guild) {
86+
String durationMessage = temporaryData == null ? "Permanently" : temporaryData.duration();
87+
String description =
9188
"""
92-
Hey there, sorry to tell you but unfortunately you have been banned %s from the server %s.
93-
If you think this was a mistake, please contact a moderator or admin of the server.
94-
The reason for the ban is: %s
95-
"""
96-
.formatted(durationMessage, guild.getName(), reason);
97-
98-
return event.getJDA()
99-
.openPrivateChannelById(target.getId())
100-
.flatMap(channel -> channel.sendMessage(dmMessage))
101-
.mapToResult()
102-
.map(Result::isSuccess);
89+
Hey there, sorry to tell you but unfortunately you have been banned from the server.
90+
If you think this was a mistake, please contact a moderator or admin of this server.""";
91+
92+
return ModerationUtils.sendModActionDm(ModerationUtils.getModActionEmbed(guild,
93+
ACTION_TITLE, description, reason, durationMessage, false), target);
10394
}
10495

10596
private static MessageEmbed sendFeedback(boolean hasSentDm, User target, Member author,
@@ -140,7 +131,7 @@ private static Optional<RestAction<Message>> handleNotAlreadyBannedResponse(
140131
private RestAction<Message> banUserFlow(User target, Member author,
141132
@Nullable ModerationUtils.TemporaryData temporaryData, String reason,
142133
int deleteHistoryDays, Guild guild, SlashCommandInteractionEvent event) {
143-
return sendDm(target, temporaryData, reason, guild, event)
134+
return sendDm(target, temporaryData, reason, guild)
144135
.flatMap(hasSentDm -> banUser(target, author, temporaryData, reason, deleteHistoryDays,
145136
guild).map(banResult -> hasSentDm))
146137
.map(hasSentDm -> sendFeedback(hasSentDm, target, author, temporaryData, reason))

application/src/main/java/org/togetherjava/tjbot/features/moderation/KickCommand.java

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
package org.togetherjava.tjbot.features.moderation;
22

33
import net.dv8tion.jda.api.Permission;
4-
import net.dv8tion.jda.api.entities.Guild;
5-
import net.dv8tion.jda.api.entities.ISnowflake;
6-
import net.dv8tion.jda.api.entities.Member;
7-
import net.dv8tion.jda.api.entities.MessageEmbed;
8-
import net.dv8tion.jda.api.events.GenericEvent;
4+
import net.dv8tion.jda.api.entities.*;
95
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
106
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
117
import net.dv8tion.jda.api.interactions.commands.OptionType;
128
import net.dv8tion.jda.api.requests.RestAction;
139
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
14-
import net.dv8tion.jda.api.utils.Result;
1510
import org.slf4j.Logger;
1611
import org.slf4j.LoggerFactory;
1712

@@ -36,6 +31,7 @@ public final class KickCommand extends SlashCommandAdapter {
3631
private static final String REASON_OPTION = "reason";
3732
private static final String COMMAND_NAME = "kick";
3833
private static final String ACTION_VERB = "kick";
34+
private static final String ACTION_TITLE = "Kick";
3935
private final ModerationActionsStore actionsStore;
4036

4137
/**
@@ -62,27 +58,23 @@ private void kickUserFlow(Member target, Member author, String reason, Guild gui
6258
SlashCommandInteractionEvent event) {
6359
event.deferReply().queue();
6460

65-
sendDm(target, reason, guild, event)
61+
sendDm(target.getUser(), reason, guild)
6662
.flatMap(hasSentDm -> kickUser(target, author, reason, guild)
6763
.map(kickResult -> hasSentDm))
6864
.map(hasSentDm -> sendFeedback(hasSentDm, target, author, reason))
6965
.flatMap(event.getHook()::sendMessageEmbeds)
7066
.queue();
7167
}
7268

73-
private static RestAction<Boolean> sendDm(ISnowflake target, String reason, Guild guild,
74-
GenericEvent event) {
75-
return event.getJDA()
76-
.openPrivateChannelById(target.getId())
77-
.flatMap(channel -> channel.sendMessage(
78-
"""
79-
Hey there, sorry to tell you but unfortunately you have been kicked from the server %s.
80-
If you think this was a mistake, please contact a moderator or admin of the server.
81-
The reason for the kick is: %s
82-
"""
83-
.formatted(guild.getName(), reason)))
84-
.mapToResult()
85-
.map(Result::isSuccess);
69+
private static RestAction<Boolean> sendDm(User target, String reason, Guild guild) {
70+
String description =
71+
"""
72+
Hey there, sorry to tell you but unfortunately you have been kicked from the server.
73+
If you think this was a mistake, please contact a moderator or admin of this server.""";
74+
75+
return ModerationUtils.sendModActionDm(
76+
ModerationUtils.getModActionEmbed(guild, ACTION_TITLE, description, reason, false),
77+
target);
8678
}
8779

8880
private AuditableRestAction<Void> kickUser(Member target, Member author, String reason,

application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java

Lines changed: 59 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import net.dv8tion.jda.api.EmbedBuilder;
44
import net.dv8tion.jda.api.Permission;
55
import net.dv8tion.jda.api.entities.*;
6-
import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel;
76
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
87
import net.dv8tion.jda.api.requests.RestAction;
98
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
9+
import net.dv8tion.jda.api.utils.Result;
10+
import net.dv8tion.jda.internal.requests.CompletedRestAction;
1011
import org.slf4j.Logger;
1112
import org.slf4j.LoggerFactory;
1213

@@ -20,11 +21,8 @@
2021
import java.time.Instant;
2122
import java.time.temporal.ChronoUnit;
2223
import java.time.temporal.TemporalUnit;
23-
import java.util.EnumSet;
2424
import java.util.Optional;
25-
import java.util.Set;
2625
import java.util.function.Predicate;
27-
import java.util.function.UnaryOperator;
2826
import java.util.regex.Pattern;
2927

3028
/**
@@ -52,25 +50,6 @@ private ModerationUtils() {
5250
*/
5351
public static final Color AMBIENT_COLOR = Color.decode("#895FE8");
5452

55-
/**
56-
* Actions with timely constraint, like being muted for 1 hour.
57-
*/
58-
private static final Set<ModerationAction> TEMPORARY_ACTIONS =
59-
EnumSet.of(ModerationAction.MUTE);
60-
/**
61-
* Actions with revoking previously made actions on the user, like unmuting the user after it
62-
* has been muted.
63-
*/
64-
private static final Set<ModerationAction> REVOKE_ACTIONS =
65-
EnumSet.of(ModerationAction.UNMUTE, ModerationAction.UNQUARANTINE);
66-
/**
67-
* Soft violations were the user still remains member of the guild, such as a warning
68-
*/
69-
private static final Set<ModerationAction> SOFT_ACTIONS =
70-
EnumSet.of(ModerationAction.WARN, ModerationAction.QUARANTINE);
71-
72-
73-
7453
/**
7554
* Checks whether the given reason is valid. If not, it will handle the situation and respond to
7655
* the user.
@@ -390,64 +369,71 @@ static Optional<TemporaryData> computeTemporaryData(String durationText) {
390369
}
391370

392371
/**
393-
* Wrapper to hold data relevant to temporary actions, for example the time it expires.
372+
* Creates a nice looking embed for the mod action taken.
394373
*
395-
* @param expiresAt the time the temporary action expires
396-
* @param duration a human-readable text representing the duration of the temporary action, such
397-
* as {@code "1 day"}.
374+
* @param guild the guild in which the action has been taken
375+
* @param actionTitle the mod action as title e.g, Ban
376+
* @param description a short description explaining the action
377+
* @param reason reason for the action taken
378+
* @param showModmailAdvice whether to advice on how to use the modmail command
379+
* @return the embed
398380
*/
399-
record TemporaryData(Instant expiresAt, String duration) {
381+
static RestAction<EmbedBuilder> getModActionEmbed(Guild guild, String actionTitle,
382+
String description, String reason, boolean showModmailAdvice) {
383+
EmbedBuilder modActionEmbed =
384+
new EmbedBuilder().setAuthor(guild.getName(), null, guild.getIconUrl())
385+
.setTitle(actionTitle)
386+
.setDescription(description)
387+
.addField("Reason", reason, false)
388+
.setColor(ModerationUtils.AMBIENT_COLOR);
389+
390+
if (!showModmailAdvice) {
391+
return new CompletedRestAction<>(guild.getJDA(), modActionEmbed);
392+
}
393+
394+
return MessageUtils.mentionGlobalSlashCommand(guild.getJDA(), ModMailCommand.COMMAND_NAME)
395+
.map(commandMention -> modActionEmbed.appendDescription(
396+
"%n%nTo get in touch with a moderator, you can use the %s command here in this chat. Your message will then be forwarded and a moderator will get back to you soon 😊"
397+
.formatted(commandMention)));
400398
}
401399

402400
/**
403-
* Gives out advice depending on the {@link ModerationAction} and the parameters passed into it.
401+
* Creates a nice looking embed for the mod action taken with duration
404402
*
405-
* @param action the action that is being performed, such as banning a user.
406-
* @param temporaryData if the action is a temporary action, such as a 1 hour mute.
407-
* @param additionalDescription any extra description that should be part of the message, if
408-
* desired
409-
* @param guild for which the action was triggered.
410-
* @param reason for the action.
411-
* @param textChannel for which messages are being sent to.
412-
*
413-
* @return the appropriate advice.
403+
* @param guild the guild in which the action has been taken
404+
* @param actionTitle the mod action itself
405+
* @param description a short description explaining the action
406+
* @param reason reason for the action taken
407+
* @param duration the duration of mod action
408+
* @param showModmailAdvice whether to advice on how to use the modmail command
409+
* @return the embed
414410
*/
415-
public static RestAction<Message> sendDmAdvice(ModerationAction action,
416-
@Nullable TemporaryData temporaryData, @Nullable String additionalDescription,
417-
Guild guild, String reason, PrivateChannel textChannel) {
418-
String additionalDescriptionInfix =
419-
additionalDescription == null ? "" : "\n" + additionalDescription;
420-
421-
if (REVOKE_ACTIONS.contains(action)) {
422-
return textChannel.sendMessage("""
423-
Hey there, you have been %s in the server %s.%s
424-
The reason for being %s is: %s
425-
""".formatted(action.getVerb(), guild.getName(), additionalDescriptionInfix,
426-
action.getVerb(), reason));
427-
}
428-
String durationMessage;
429-
if (SOFT_ACTIONS.contains(action)) {
430-
durationMessage = "";
431-
} else if (TEMPORARY_ACTIONS.contains(action)) {
432-
durationMessage =
433-
temporaryData == null ? " permanently" : " for " + temporaryData.duration();
434-
} else {
435-
throw new IllegalArgumentException(
436-
"Action '%s' is not supported by this method".formatted(action));
437-
}
411+
static RestAction<EmbedBuilder> getModActionEmbed(Guild guild, String actionTitle,
412+
String description, String reason, String duration, boolean showModmailAdvice) {
413+
return getModActionEmbed(guild, actionTitle, description, reason, showModmailAdvice)
414+
.map(embedBuilder -> embedBuilder.addField("Duration", duration, false));
415+
}
438416

439-
UnaryOperator<String> createDmMessage =
440-
commandMention -> """
441-
Hey there, sorry to tell you but unfortunately you have been %s%s in the server %s.%s
442-
To get in touch with a moderator, you can simply use the %s command here in this chat. \
443-
Your message will then be forwarded and a moderator will get back to you soon 😊
444-
The reason for being %s is: %s
445-
"""
446-
.formatted(action.getVerb(), durationMessage, guild.getName(),
447-
additionalDescriptionInfix, commandMention, action.getVerb(), reason);
417+
/**
418+
* @param embedBuilder rest action to generate embed from
419+
* @param target the user to send the generated embed
420+
* @return boolean rest action, weather the dm is sent successfully
421+
*/
422+
static RestAction<Boolean> sendModActionDm(RestAction<EmbedBuilder> embedBuilder, User target) {
423+
return embedBuilder.map(EmbedBuilder::build)
424+
.flatMap(embed -> target.openPrivateChannel()
425+
.flatMap(channel -> channel.sendMessageEmbeds(embed)))
426+
.mapToResult()
427+
.map(Result::isSuccess);
428+
}
448429

449-
return MessageUtils.mentionGlobalSlashCommand(guild.getJDA(), ModMailCommand.COMMAND_NAME)
450-
.map(createDmMessage)
451-
.flatMap(textChannel::sendMessage);
430+
/**
431+
* Wrapper to hold data relevant to temporary actions, for example the time it expires.
432+
*
433+
* @param expiresAt the time the temporary action expires
434+
* @param duration a human-readable text representing the duration of the temporary action, such
435+
* as {@code "1 day"}.
436+
*/
437+
record TemporaryData(Instant expiresAt, String duration) {
452438
}
453439
}

application/src/main/java/org/togetherjava/tjbot/features/moderation/MuteCommand.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package org.togetherjava.tjbot.features.moderation;
22

33
import net.dv8tion.jda.api.entities.*;
4-
import net.dv8tion.jda.api.events.GenericEvent;
54
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
65
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
76
import net.dv8tion.jda.api.interactions.commands.OptionType;
87
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
98
import net.dv8tion.jda.api.requests.RestAction;
109
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
11-
import net.dv8tion.jda.api.utils.Result;
1210
import org.slf4j.Logger;
1311
import org.slf4j.LoggerFactory;
1412

@@ -38,6 +36,7 @@ public final class MuteCommand extends SlashCommandAdapter {
3836
private static final String REASON_OPTION = "reason";
3937
private static final String COMMAND_NAME = "mute";
4038
private static final String ACTION_VERB = "mute";
39+
private static final String ACTION_TITLE = "Mute";
4140
@SuppressWarnings("StaticCollection")
4241
private static final List<String> DURATIONS = List.of("10 minutes", "30 minutes", "1 hour",
4342
"3 hours", "1 day", "3 days", "7 days", ModerationUtils.PERMANENT_DURATION);
@@ -70,16 +69,16 @@ private static void handleAlreadyMutedTarget(IReplyCallback event) {
7069
event.reply("The user is already muted.").setEphemeral(true).queue();
7170
}
7271

73-
private static RestAction<Boolean> sendDm(ISnowflake target,
74-
@Nullable ModerationUtils.TemporaryData temporaryData, String reason, Guild guild,
75-
GenericEvent event) {
76-
return event.getJDA()
77-
.openPrivateChannelById(target.getId())
78-
.flatMap(channel -> ModerationUtils.sendDmAdvice(ModerationAction.MUTE, temporaryData,
79-
"This means you can no longer send any messages in the server until you have been unmuted again.",
80-
guild, reason, channel))
81-
.mapToResult()
82-
.map(Result::isSuccess);
72+
private static RestAction<Boolean> sendDm(User target,
73+
@Nullable ModerationUtils.TemporaryData temporaryData, String reason, Guild guild) {
74+
String durationMessage = temporaryData == null ? "Permanent" : temporaryData.duration();
75+
String description =
76+
"""
77+
Hey there, sorry to tell you but unfortunately you have been muted.
78+
This means you can no longer send any messages in the server until you have been unmuted again.""";
79+
80+
return ModerationUtils.sendModActionDm(ModerationUtils.getModActionEmbed(guild,
81+
ACTION_TITLE, description, reason, durationMessage, true), target);
8382
}
8483

8584
private static MessageEmbed sendFeedback(boolean hasSentDm, Member target, Member author,
@@ -117,7 +116,7 @@ private void muteUserFlow(Member target, Member author,
117116
SlashCommandInteractionEvent event) {
118117
event.deferReply().queue();
119118

120-
sendDm(target, temporaryData, reason, guild, event)
119+
sendDm(target.getUser(), temporaryData, reason, guild)
121120
.flatMap(hasSentDm -> muteUser(target, author, temporaryData, reason, guild)
122121
.map(result -> hasSentDm))
123122
.map(hasSentDm -> sendFeedback(hasSentDm, target, author, temporaryData, reason))

0 commit comments

Comments
 (0)