1
0
mirror of https://github.com/sciwhiz12/Janitor.git synced 2024-09-19 21:04:02 +00:00

Improve messages system (WIP)

Now uses a builder format, and allows for substitutions in the translation keys
(WIP) the Listing commands are unimplemented yet.

This is in preparation for moving to JSON-based messages in the future
This commit is contained in:
Arnold Alejo Nunag 2020-10-06 14:57:14 +08:00
parent eb50a1bab3
commit 8e972ba96a
Signed by: sciwhiz12
GPG Key ID: 622CF446534317E1
14 changed files with 836 additions and 491 deletions

View File

@ -9,6 +9,7 @@ import net.dv8tion.jda.api.entities.User;
import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.config.BotConfig;
import sciwhiz12.janitor.msg.Messages;
import sciwhiz12.janitor.msg.Substitutions;
import sciwhiz12.janitor.msg.Translations;
import sciwhiz12.janitor.utils.Util;
@ -25,17 +26,19 @@ public class JanitorBot {
private BotConsole console;
private final GuildStorage storage;
private final GuildStorage.SavingThread storageSavingThread;
private CommandRegistry cmdRegistry;
private Translations translations;
private final CommandRegistry cmdRegistry;
private final Translations translations;
private final Substitutions substitutions;
public JanitorBot(JDA discord, BotConfig config) {
this.config = config;
this.discord = discord;
this.console = new BotConsole(this, System.in);
this.storage = new GuildStorage(this, Path.of(config.STORAGE_PATH.get()));
this.cmdRegistry = new CommandRegistry(this, config.getCommandPrefix());
this.discord = discord;
this.translations = new Translations(this, config.getTranslationsFile());
this.messages = new Messages(this);
this.substitutions = new Substitutions(this);
discord.addEventListener(cmdRegistry);
discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!"));
discord.getGuilds().forEach(Guild::loadMembers);
@ -101,4 +104,8 @@ public class JanitorBot {
storage.save();
console.stop();
}
public Substitutions getSubstitutions() {
return substitutions;
}
}

View File

@ -68,7 +68,7 @@ public class BanCommand extends BaseCommand {
void realRun(CommandContext<MessageReceivedEvent> ctx, int days, @Nullable String reason) throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return;
}
final Guild guild = ctx.getSource().getGuild();
@ -79,24 +79,26 @@ public class BanCommand extends BaseCommand {
final Member target = members.get(0);
if (guild.getSelfMember().equals(target))
messages().GENERAL.cannotActionSelf(channel).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
else if (performer.equals(target))
messages().GENERAL.cannotActionPerformer(channel, performer).queue();
channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue();
else if (!guild.getSelfMember().hasPermission(BAN_PERMISSION))
messages().GENERAL.insufficientPermissions(channel, BAN_PERMISSION).queue();
channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, BAN_PERMISSION).build(getBot())).queue();
else if (!guild.getSelfMember().canInteract(target))
messages().GENERAL.cannotInteract(channel, target).queue();
channel.sendMessage(messages().GENERAL.cannotInteract(performer, target).build(getBot())).queue();
else if (!performer.hasPermission(BAN_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, BAN_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, BAN_PERMISSION).build(getBot()))
.queue();
else if (!performer.canInteract(target))
messages().MODERATION.ERRORS.cannotModerate(channel, performer, target).queue();
channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue();
else
target.getUser().openPrivateChannel()
.flatMap(dm -> messages().MODERATION.bannedDM(dm, performer, reason))
.flatMap(dm -> dm.sendMessage(messages().MODERATION.bannedDM(performer, target, reason).build(getBot())))
.mapToResult()
.flatMap(res -> ModerationHelper.banUser(target.getGuild(), performer, target, days, reason)
.flatMap(
v -> messages().MODERATION.banUser(channel, performer, target, reason, days, res.isSuccess())))
.flatMap(v -> channel.sendMessage(
messages().MODERATION.banUser(performer, target, reason, days, res.isSuccess()).build(getBot()))))
.queue();
}
}

View File

@ -50,7 +50,7 @@ public class KickCommand extends BaseCommand {
private int runWithReason(CommandContext<MessageReceivedEvent> ctx, @Nullable String reason) throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return 1;
}
final Guild guild = ctx.getSource().getGuild();
@ -61,24 +61,28 @@ public class KickCommand extends BaseCommand {
}
final Member target = members.get(0);
if (guild.getSelfMember().equals(target))
messages().GENERAL.cannotActionSelf(channel).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
else if (performer.equals(target))
messages().GENERAL.cannotActionPerformer(channel, performer).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
else if (!guild.getSelfMember().hasPermission(KICK_PERMISSION))
messages().GENERAL.insufficientPermissions(channel, KICK_PERMISSION).queue();
channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, KICK_PERMISSION).build(getBot())).queue();
else if (!guild.getSelfMember().canInteract(target))
messages().GENERAL.cannotInteract(channel, target).queue();
channel.sendMessage(messages().GENERAL.cannotInteract(performer, target).build(getBot())).queue();
else if (!performer.hasPermission(KICK_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, KICK_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, KICK_PERMISSION).build(getBot()))
.queue();
else if (!performer.canInteract(target))
messages().MODERATION.ERRORS.cannotModerate(channel, performer, target).queue();
channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue();
else
target.getUser().openPrivateChannel()
.flatMap(dm -> messages().MODERATION.kickedDM(dm, performer, target, reason))
.flatMap(dm -> dm.sendMessage(messages().MODERATION.kickedDM(performer, target, reason).build(getBot())))
.mapToResult()
.flatMap(res -> ModerationHelper.kickUser(target.getGuild(), performer, target, reason)
.flatMap(
v -> messages().MODERATION.kickUser(channel, performer, target, reason, res.isSuccess())))
.flatMap(v -> channel.sendMessage(
messages().MODERATION.kickUser(performer, target, reason, res.isSuccess()).build(getBot()))
)
)
.queue();
return 1;
}

View File

@ -90,32 +90,36 @@ public class NoteCommand extends BaseCommand {
}
private int addNote(CommandContext<MessageReceivedEvent> ctx, String noteContents) throws CommandSyntaxException {
final MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(ctx.getSource().getChannel());
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return 1;
}
final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
final Guild guild = performer.getGuild();
final MessageChannel channel = ctx.getSource().getChannel();
final List<Member> members = getMembers("target", ctx).fromGuild(guild);
if (members.size() < 1) return 1;
final Member target = members.get(0);
final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC);
if (guild.getSelfMember().equals(target))
messages().GENERAL.cannotActionSelf(channel).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
else if (performer.equals(target))
messages().GENERAL.cannotActionPerformer(channel, performer).queue();
channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue();
else if (!performer.hasPermission(NOTE_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, NOTE_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot()))
.queue();
else {
final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild);
final int maxAmount = config().NOTES_MAX_AMOUNT_PER_MOD.get();
if (storage.getAmountOfNotes(target.getUser()) >= maxAmount) {
messages().MODERATION.ERRORS.maxAmountOfNotes(channel, performer, target, maxAmount).queue();
channel.sendMessage(messages().MODERATION.ERRORS.maxAmountOfNotes(performer, target, maxAmount).build(getBot()))
.queue();
} else {
int noteID = storage.addNote(new NoteEntry(performer.getUser(), target.getUser(), dateTime, noteContents));
messages().MODERATION.addNote(channel, performer, target, noteContents, dateTime, noteID).queue();
final NoteEntry entry = new NoteEntry(performer.getUser(), target.getUser(), dateTime, noteContents);
int noteID = storage.addNote(entry);
channel.sendMessage(messages().MODERATION.addNote(performer, noteID, entry).build(getBot())).queue();
}
}
return 1;
@ -127,9 +131,9 @@ public class NoteCommand extends BaseCommand {
private int listNotes(CommandContext<MessageReceivedEvent> ctx, boolean filterTarget, ModeratorFilter modFilter)
throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel();
final MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return 1;
}
final Guild guild = ctx.getSource().getGuild();
@ -141,7 +145,7 @@ public class NoteCommand extends BaseCommand {
if (members.size() < 1) return 1;
final Member target = members.get(0);
if (guild.getSelfMember().equals(target)) {
messages().GENERAL.cannotActionSelf(channel).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
return 1;
}
predicate = predicate.and(e -> e.getValue().getTarget().getIdLong() == target.getIdLong());
@ -161,21 +165,24 @@ public class NoteCommand extends BaseCommand {
final OffsetDateTime dateTime = OffsetDateTime.now();
if (!performer.hasPermission(NOTE_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, NOTE_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot()))
.queue();
else
messages().MODERATION.noteList(channel, NoteStorage.get(getBot().getStorage(), guild)
.getNotes()
.entrySet().stream()
.filter(predicate)
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue))
).queue();
channel.sendMessage(messages().MODERATION.noteList(
NoteStorage.get(getBot().getStorage(), guild)
.getNotes()
.entrySet().stream()
.filter(predicate)
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue))
).build(getBot())).queue();
return 1;
}
private int removeNote(CommandContext<MessageReceivedEvent> ctx, int noteID) {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return 1;
}
final Guild guild = ctx.getSource().getGuild();
@ -184,16 +191,18 @@ public class NoteCommand extends BaseCommand {
final OffsetDateTime dateTime = OffsetDateTime.now();
if (!performer.hasPermission(NOTE_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, NOTE_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot()))
.queue();
else {
final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild);
@Nullable
final NoteEntry entry = storage.getNote(noteID);
if (entry == null)
messages().MODERATION.ERRORS.noNoteFound(channel, performer, noteID).queue();
channel.sendMessage(messages().MODERATION.ERRORS.noNoteFound(performer, noteID).build(getBot())).queue();
else {
storage.removeNote(noteID);
messages().MODERATION.removeNote(channel, performer, noteID, entry).queue();
channel.sendMessage(messages().MODERATION.removeNote(performer, noteID, entry).build(getBot())).queue();
}
}
return 1;

View File

@ -49,7 +49,7 @@ public class UnbanCommand extends BaseCommand {
void realNamedRun(CommandContext<MessageReceivedEvent> ctx) {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return;
}
final Guild guild = ctx.getSource().getGuild();
@ -63,7 +63,7 @@ public class UnbanCommand extends BaseCommand {
.collect(Collectors.toList()))
.queue(bans -> {
if (bans.size() > 1)
messages().GENERAL.ambiguousMember(channel).queue();
channel.sendMessage(messages().GENERAL.ambiguousMember(performer).build(getBot())).queue();
else if (bans.size() == 1)
tryUnban(channel, guild, performer, bans.get(0).getUser());
});
@ -77,7 +77,7 @@ public class UnbanCommand extends BaseCommand {
void realIdRun(CommandContext<MessageReceivedEvent> ctx) {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return;
}
final Guild guild = ctx.getSource().getGuild();
@ -98,12 +98,15 @@ public class UnbanCommand extends BaseCommand {
void tryUnban(MessageChannel channel, Guild guild, Member performer, User target) {
if (!guild.getSelfMember().hasPermission(UNBAN_PERMISSION))
messages().GENERAL.insufficientPermissions(channel, UNBAN_PERMISSION).queue();
channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, UNBAN_PERMISSION).build(getBot()))
.queue();
else if (!performer.hasPermission(UNBAN_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, UNBAN_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, UNBAN_PERMISSION).build(getBot()))
.queue();
else
ModerationHelper.unbanUser(guild, target)
.flatMap(v -> messages().MODERATION.unbanUser(channel, performer, target))
.flatMap(v -> channel.sendMessage(messages().MODERATION.unbanUser(performer, target).build(getBot())))
.queue();
}
}

View File

@ -45,7 +45,7 @@ public class UnwarnCommand extends BaseCommand {
void realRun(CommandContext<MessageReceivedEvent> ctx) {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return;
}
final Guild guild = ctx.getSource().getGuild();
@ -55,24 +55,28 @@ public class UnwarnCommand extends BaseCommand {
final OffsetDateTime dateTime = OffsetDateTime.now();
if (!performer.hasPermission(WARN_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, WARN_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot()))
.queue();
else {
final WarningStorage storage = WarningStorage.get(getBot().getStorage(), guild);
@Nullable
final WarningEntry entry = storage.getWarning(caseID);
Member temp;
if (entry == null)
messages().MODERATION.ERRORS.noWarnWithID(channel, performer, caseID).queue();
channel.sendMessage(messages().MODERATION.ERRORS.noWarnWithID(performer, caseID).build(getBot())).queue();
else if (entry.getWarned().getIdLong() == performer.getIdLong()
&& !config().WARNINGS_REMOVE_SELF_WARNINGS.get())
messages().MODERATION.ERRORS.cannotUnwarnSelf(channel, performer, caseID, entry).queue();
channel.sendMessage(messages().MODERATION.ERRORS.cannotUnwarnSelf(performer, caseID, entry).build(getBot()))
.queue();
else if (config().WARNINGS_RESPECT_MOD_ROLES.get()
&& (temp = guild.getMember(entry.getPerformer())) != null
&& !performer.canInteract(temp))
messages().MODERATION.ERRORS.cannotRemoveHigherModerated(channel, performer, caseID, entry).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.cannotRemoveHigherModerated(performer, caseID, entry).build(getBot())).queue();
else {
storage.removeWarning(caseID);
messages().MODERATION.unwarn(channel, performer, caseID, entry).queue();
channel.sendMessage(messages().MODERATION.unwarn(performer, caseID, entry).build(getBot())).queue();
}
}
}

View File

@ -52,7 +52,7 @@ public class WarnCommand extends BaseCommand {
void realRun(CommandContext<MessageReceivedEvent> ctx, String reason) throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return;
}
final Guild guild = ctx.getSource().getGuild();
@ -64,24 +64,27 @@ public class WarnCommand extends BaseCommand {
final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC);
if (guild.getSelfMember().equals(target))
messages().GENERAL.cannotActionSelf(channel).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
else if (performer.equals(target))
messages().GENERAL.cannotActionPerformer(channel, performer).queue();
channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue();
else if (!performer.hasPermission(WARN_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, WARN_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot()))
.queue();
else if (!performer.canInteract(target))
messages().MODERATION.ERRORS.cannotModerate(channel, performer, target).queue();
channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue();
else if (target.hasPermission(WARN_PERMISSION) && config().WARNINGS_PREVENT_WARNING_MODS.get())
messages().MODERATION.ERRORS.cannotWarnMods(channel, performer, target).queue();
channel.sendMessage(messages().MODERATION.ERRORS.cannotWarnMods(performer, target).build(getBot())).queue();
else
target.getUser().openPrivateChannel()
.flatMap(dm -> messages().MODERATION.warnDM(dm, performer, target, reason, dateTime))
.flatMap(
dm -> dm.sendMessage(messages().MODERATION.warnedDM(performer, target, reason, dateTime).build(getBot())))
.mapToResult()
.flatMap(res -> {
int caseId = WarningStorage.get(getBot().getStorage(), guild)
.addWarning(new WarningEntry(target.getUser(), performer.getUser(), dateTime, reason));
return messages().MODERATION
.warnUser(channel, performer, target, reason, dateTime, caseId, res.isSuccess());
WarningEntry entry = new WarningEntry(target.getUser(), performer.getUser(), dateTime, reason);
int caseId = WarningStorage.get(getBot().getStorage(), guild).addWarning(entry);
return channel
.sendMessage(messages().MODERATION.warnUser(performer, caseId, entry, res.isSuccess()).build(getBot()));
})
.queue();
}

View File

@ -64,7 +64,7 @@ public class WarnListCommand extends BaseCommand {
throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) {
messages().GENERAL.guildOnlyCommand(channel).queue();
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue();
return;
}
final Guild guild = ctx.getSource().getGuild();
@ -76,7 +76,7 @@ public class WarnListCommand extends BaseCommand {
if (members.size() < 1) return;
final Member target = members.get(0);
if (guild.getSelfMember().equals(target)) {
messages().GENERAL.cannotActionSelf(channel).queue();
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue();
return;
}
predicate = predicate.and(e -> e.getValue().getWarned().getIdLong() == target.getIdLong());
@ -91,13 +91,17 @@ public class WarnListCommand extends BaseCommand {
final OffsetDateTime dateTime = OffsetDateTime.now();
if (!performer.hasPermission(WARN_PERMISSION))
messages().MODERATION.ERRORS.performerInsufficientPermissions(channel, performer, WARN_PERMISSION).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot()))
.queue();
else
messages().MODERATION.warnList(channel, WarningStorage.get(getBot().getStorage(), guild)
.getWarnings()
.entrySet().stream()
.filter(predicate)
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue))
).queue();
channel.sendMessage(messages().MODERATION.warnList(
WarningStorage.get(getBot().getStorage(), guild)
.getWarnings()
.entrySet().stream()
.filter(predicate)
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue))
).build(getBot())).queue();
}
}

View File

@ -2,10 +2,7 @@ package sciwhiz12.janitor.msg;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.entities.User;
import java.util.EnumSet;
import java.util.stream.Collectors;
@ -17,63 +14,62 @@ public final class General {
this.messages = messages;
}
private String translate(String key, Object... args) {
return messages.translate(key, args);
public MessageBuilder guildOnlyCommand(final User performer) {
return messages.failure()
.apply(builder -> messages.user(builder, "performer", performer))
.embed(embed -> embed
.setTitle("general.guild_only_command.title")
.setDescription("general.guild_only_command.description")
);
}
public RestAction<Message> guildOnlyCommand(MessageChannel channel) {
return channel.sendMessage(
messages.failureEmbed(translate("general.guild_only_command.title"))
.setDescription(translate("general.guild_only_command.desc"))
.build()
);
public MessageBuilder ambiguousMember(final Member performer) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.embed(embed -> embed
.setTitle("general.ambiguous_member.title")
.setDescription("general.ambiguous_member.description")
);
}
public RestAction<Message> insufficientPermissions(MessageChannel channel, EnumSet<Permission> permissions) {
return channel.sendMessage(
messages.failureEmbed(translate("general.insufficient_permissions.title"))
.setDescription(translate("general.insufficient_permissions.desc"))
.addField(new MessageEmbed.Field(
translate("general.insufficient_permissions.field.permissions"),
permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")),
false))
.build()
);
public MessageBuilder insufficientPermissions(final Member performer, final EnumSet<Permission> permissions) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.with("required_permissions", () -> permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")))
.embed(embed -> embed
.setTitle("general.insufficient_permissions.title")
.setDescription("general.insufficient_permissions.description")
)
.field("general.insufficient_permissions.field.permissions", true);
}
public RestAction<Message> ambiguousMember(MessageChannel channel) {
return channel.sendMessage(
messages.failureEmbed(translate("general.ambiguous_member.title"))
.setDescription(translate("general.ambiguous_member.desc"))
.build()
);
public MessageBuilder cannotInteract(final Member performer, final Member target) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.embed(embed -> embed
.setTitle("general.cannot_interact.title")
.setDescription("general.cannot_interact.description")
)
.field("general.cannot_interact.field.target", true);
}
public RestAction<Message> cannotInteract(MessageChannel channel, Member target) {
return channel.sendMessage(
messages.failureEmbed(translate("general.cannot_interact.title"))
.setDescription(translate("general.cannot_interact.desc"))
.addField(translate("general.cannot_interact.field.target"), target.getAsMention(), true)
.build()
);
public MessageBuilder cannotActionSelf(final Member performer) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.embed(embed -> embed
.setTitle("general.cannot_action_self.title")
.setDescription("general.cannot_action_self.description")
);
}
public RestAction<Message> cannotActionSelf(MessageChannel channel) {
return channel.sendMessage(
messages.failureEmbed(translate("general.cannot_action_self.title"))
.setDescription(translate("general.cannot_action_self.desc"))
.build()
);
}
public RestAction<Message> cannotActionPerformer(MessageChannel channel, Member performer) {
return channel.sendMessage(
messages.failureEmbed(translate("general.cannot_action_performer.title"))
.setDescription(translate("general.cannot_action_performer.desc"))
.addField(translate("general.cannot_action_performer.field.performer"),
performer.getUser().getAsMention(),
true)
.build()
);
public MessageBuilder cannotActionPerformer(final Member performer) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.embed(embed -> embed
.setTitle("general.cannot_action_performer.title")
.setDescription("general.cannot_action_performer.description")
)
.field("general.cannot_action_performer.field.performer", true);
}
}

View File

@ -0,0 +1,103 @@
package sciwhiz12.janitor.msg;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import sciwhiz12.janitor.JanitorBot;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import static sciwhiz12.janitor.msg.Substitutions.substitute;
public class MessageBuilder {
private final EmbedBuilder embedBuilder;
private final Map<String, Supplier<String>> substitutions;
public MessageBuilder(EmbedBuilder embedBuilder, Map<String, Supplier<String>> substitutions) {
this.embedBuilder = embedBuilder;
this.substitutions = substitutions;
}
public MessageBuilder() {
this(new EmbedBuilder(), new HashMap<>());
}
@SuppressWarnings("CopyConstructorMissesField")
public MessageBuilder(MessageBuilder copy) {
this(new EmbedBuilder(copy.embedBuilder), new HashMap<>(copy.substitutions));
}
public EmbedBuilder embed() {
return embedBuilder;
}
public MessageBuilder embed(Consumer<EmbedBuilder> operator) {
operator.accept(embed());
return this;
}
public MessageBuilder apply(Consumer<MessageBuilder> consumer) {
consumer.accept(this);
return this;
}
public MessageBuilder with(final String argument, final Supplier<String> value) {
substitutions.put(argument, value);
return this;
}
public MessageBuilder field(final String head, final boolean inline) {
embedBuilder.addField(head + ".name", head + ".value", inline);
return this;
}
public MessageBuilder blankField(final boolean inline) {
embedBuilder.addBlankField(inline);
return this;
}
public MessageEmbed build(Translations translations, Substitutions substitutions) {
EmbedBuilder realEmbed = new EmbedBuilder();
MessageEmbed tempEmbed = embed().build();
final Map<String, Supplier<String>> replaceMap = substitutions.createDefaultedMap(this.substitutions);
final UnaryOperator<String> replacer = str -> substitute(translations.translate(str), replaceMap);
realEmbed.setColor(tempEmbed.getColorRaw());
realEmbed.setTimestamp(tempEmbed.getTimestamp());
if (tempEmbed.getTitle() != null)
realEmbed.setTitle(replacer.apply(tempEmbed.getTitle()), tempEmbed.getUrl());
if (tempEmbed.getThumbnail() != null)
realEmbed.setThumbnail(tempEmbed.getThumbnail().getUrl());
if (tempEmbed.getAuthor() != null)
realEmbed.setAuthor(
replacer.apply(tempEmbed.getAuthor().getName()),
tempEmbed.getAuthor().getUrl(),
tempEmbed.getAuthor().getIconUrl()
);
if (tempEmbed.getFooter() != null)
realEmbed.setFooter(
replacer.apply(tempEmbed.getFooter().getText()),
tempEmbed.getFooter().getIconUrl()
);
if (tempEmbed.getImage() != null)
realEmbed.setImage(tempEmbed.getImage().getUrl());
if (tempEmbed.getDescription() != null)
realEmbed.setDescription(replacer.apply(tempEmbed.getDescription()));
for (MessageEmbed.Field field : tempEmbed.getFields())
realEmbed.addField(
replacer.apply(field.getName()),
replacer.apply(field.getValue()),
field.isInline()
);
return realEmbed.build();
}
public MessageEmbed build(JanitorBot bot) {
return build(bot.getTranslations(), bot.getSubstitutions());
}
}

View File

@ -1,11 +1,18 @@
package sciwhiz12.janitor.msg;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.IMentionable;
import net.dv8tion.jda.api.entities.ISnowflake;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.User;
import sciwhiz12.janitor.JanitorBot;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
public class Messages {
public static final int FAILURE_COLOR = 0xF73132;
@ -19,14 +26,72 @@ public class Messages {
this.MODERATION = new Moderation(this);
}
public String translate(String key, Object... args) {
return bot.getTranslations().translate(key, args);
public JanitorBot getBot() {
return bot;
}
public EmbedBuilder failureEmbed(String title) {
return new EmbedBuilder()
.setTitle(title)
.setColor(FAILURE_COLOR)
public MessageBuilder message() {
final MessageBuilder builder = new MessageBuilder();
builder.embed()
.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC));
return builder;
}
public MessageBuilder failure() {
final MessageBuilder builder = message();
builder.embed()
.setColor(FAILURE_COLOR);
return builder;
}
public MessageBuilder snowflake(MessageBuilder builder, String head, ISnowflake snowflake) {
return builder
.with(head + ".id", snowflake::getId)
.with(head + ".creation_datetime", () -> snowflake.getTimeCreated().format(RFC_1123_DATE_TIME));
}
public MessageBuilder mentionable(MessageBuilder builder, String head, IMentionable mentionable) {
return builder
.apply(b -> snowflake(b, head, mentionable))
.with(head + ".mention", mentionable::getAsMention);
}
public MessageBuilder role(MessageBuilder builder, String head, Role role) {
return builder
.apply(b -> mentionable(b, head, role))
.with(head + ".color_hex", () -> Integer.toHexString(role.getColorRaw()))
.with(head + ".name", role::getName)
.with(head + ".permissions", role.getPermissions()::toString);
}
public MessageBuilder user(MessageBuilder builder, String head, User user) {
return builder
.apply(b -> mentionable(b, head, user))
.with(head + ".name", user::getName)
.with(head + ".discriminator", user::getDiscriminator)
.with(head + ".tag", user::getAsTag)
.with(head + ".flags", user.getFlags()::toString);
}
public MessageBuilder guild(MessageBuilder builder, String head, Guild guild) {
return builder
.apply(b -> snowflake(b, head, guild))
.with(head + ".name", guild::getName)
.with(head + ".description", guild::getDescription)
.with(head + ".voice_region", guild.getRegion()::toString)
.with(head + ".boost.tier", guild.getBoostTier()::toString)
.with(head + ".boost.count", () -> String.valueOf(guild.getBoostCount()))
.with(head + ".locale", guild.getLocale()::toString)
.with(head + ".verification_level", guild.getVerificationLevel()::toString);
}
public MessageBuilder member(MessageBuilder builder, String head, Member member) {
return builder
.apply(b -> user(b, head, member.getUser()))
.apply(b -> guild(b, head + ".guild", member.getGuild()))
.with(head + ".nickname", member::getNickname)
.with(head + ".effective_name", member::getEffectiveName)
.with(head + ".join_datetime", () -> member.getTimeJoined().format(RFC_1123_DATE_TIME))
.with(head + ".color", () -> String.valueOf(member.getColorRaw()));
}
}

View File

@ -1,20 +1,14 @@
package sciwhiz12.janitor.msg;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.requests.restaction.MessageAction;
import org.checkerframework.checker.nullness.qual.Nullable;
import sciwhiz12.janitor.moderation.notes.NoteEntry;
import sciwhiz12.janitor.moderation.warns.WarningEntry;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.Map;
import java.util.stream.Collectors;
@ -34,285 +28,306 @@ public final class Moderation {
ERRORS = new Errors();
}
private String translate(String key, Object... args) {
return messages.translate(key, args);
public MessageBuilder moderation() {
return messages.message()
.embed(embed -> embed
.setColor(MODERATION_COLOR)
.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC))
);
}
public EmbedBuilder moderationEmbed() {
return new EmbedBuilder()
.setColor(MODERATION_COLOR)
.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC));
}
public EmbedBuilder moderationEmbed(String author) {
return moderationEmbed()
.setAuthor(author, null, GAVEL_ICON_URL);
public MessageBuilder moderation(String author) {
return moderation()
.embed(embed -> embed.setAuthor(author, null, GAVEL_ICON_URL));
}
public class Errors {
private Errors() {}
public MessageAction performerInsufficientPermissions(MessageChannel channel, Member performer,
EnumSet<Permission> permissions) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.insufficient_permissions.title"))
.setDescription(translate("moderation.insufficient_permissions.desc"))
.addField(
translate("moderation.insufficient_permissions.field.performer"),
performer.getAsMention(),
true)
.addField(new MessageEmbed.Field(
translate("moderation.insufficient_permissions.field.permissions"),
permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), true))
.build()
);
}
public MessageAction cannotModerate(MessageChannel channel, Member performer, Member target) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.cannot_interact.title"))
.setDescription(translate("moderation.cannot_interact.desc"))
.addField(translate("moderation.cannot_interact.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.cannot_interact.field.target"), target.getAsMention(), true)
.build()
);
}
public MessageAction cannotWarnMods(MessageChannel channel, Member performer, Member target) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.warn.cannot_warn_mods.title"))
.setDescription(translate("moderation.warn.cannot_warn_mods.desc"))
.addField(translate("moderation.warn.cannot_warn_mods.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.warn.cannot_warn_mods.field.target"), target.getAsMention(), true).build()
);
}
public MessageAction cannotRemoveHigherModerated(MessageChannel channel, Member performer, int caseID,
WarningEntry entry) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.unwarn.cannot_remove_higher_mod.title"))
.setDescription(translate("moderation.unwarn.cannot_remove_higher_mod.desc"))
.addField(translate("moderation.unwarn.cannot_remove_higher_mod.field.performer"), performer.getAsMention(),
true)
.addField(translate("moderation.unwarn.cannot_remove_higher_mod.field.original_performer"),
entry.getPerformer().getAsMention(), true)
.addField(translate("moderation.unwarn.cannot_remove_higher_mod.field.case_id"), String.valueOf(caseID),
true)
.build()
);
}
public MessageAction maxAmountOfNotes(MessageChannel channel, Member performer, Member target, int amount) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.note.max_amount_of_notes.title"))
.setDescription(translate("moderation.note.max_amount_of_notes.desc"))
.addField(translate("moderation.note.max_amount_of_notes.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.note.max_amount_of_notes.field.target"), target.getAsMention(), true)
.addField(translate("moderation.note.max_amount_of_notes.field.amount"), String.valueOf(amount), true)
.build()
);
}
public MessageAction noNoteFound(MessageChannel channel, Member performer, int noteID) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.note.no_note_found.title"))
.setDescription(translate("moderation.note.no_note_found.desc"))
.addField(translate("moderation.note.no_note_found.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.note.no_note_found.field.note_id"), String.valueOf(noteID), true)
.build()
);
}
public MessageAction noWarnWithID(MessageChannel channel, Member performer, int caseID) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.unwarn.no_case_found.title"))
.setDescription(translate("moderation.unwarn.no_case_found.desc"))
.addField(translate("moderation.unwarn.no_case_found.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.unwarn.no_case_found.field.case_id"), String.valueOf(caseID), true).build()
);
}
public MessageAction cannotUnwarnSelf(MessageChannel channel, Member performer, int caseID, WarningEntry entry) {
return channel.sendMessage(
messages.failureEmbed(translate("moderation.unwarn.cannot_unwarn_self.title"))
.setDescription(translate("moderation.unwarn.cannot_unwarn_self.desc"))
.addField(translate("moderation.unwarn.cannot_unwarn_self.field.performer"), performer.getAsMention(),
true)
.addField(translate("moderation.unwarn.cannot_unwarn_self.field.original_performer"),
entry.getPerformer().getAsMention(), true)
.addField(translate("moderation.unwarn.cannot_unwarn_self.field.case_id"), String.valueOf(caseID), true)
.build()
);
}
}
public MessageAction kickUser(MessageChannel channel, Member performer, Member target, @Nullable String reason,
boolean sentDM) {
return channel.sendMessage(
moderationEmbed(translate("moderation.kick.info.author"))
.addField(translate("moderation.kick.info.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.kick.info.field.target"), target.getAsMention(), true)
.addField(translate("moderation.kick.info.field.sent_private_message"), sentDM ? "" : "", true)
.addField(reason != null ? translate("moderation.kick.info.field.reason") : null, reason, false)
.build()
);
}
public MessageAction kickedDM(MessageChannel channel, Member performer, Member target, @Nullable String reason) {
return channel.sendMessage(
moderationEmbed()
.setAuthor(performer.getGuild().getName(), null, performer.getGuild().getIconUrl())
.setTitle(translate("moderation.kick.dm.title"))
.addField(translate("moderation.kick.dm.field.performer"), performer.getUser().getAsMention(), true)
.addField(reason != null ? translate("moderation.kick.dm.field.reason") : null, reason, false)
.build()
);
}
public MessageAction banUser(MessageChannel channel, Member performer, Member target, @Nullable String reason,
int deletionDays, boolean sentDM) {
return channel.sendMessage(
moderationEmbed(translate("moderation.ban.info.author"))
.addField(translate("moderation.ban.info.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.ban.info.field.target"), target.getAsMention(), true)
.addField(translate("moderation.ban.info.field.sent_private_message"), sentDM ? "" : "", true)
.addField(deletionDays != 0 ?
new MessageEmbed.Field(translate("moderation.ban.info.field.delete_duration"),
translate("moderation.ban.info.field.delete_duration.value", String.valueOf(deletionDays)), true)
: null)
.addField(reason != null ? translate("moderation.ban.info.field.reason") : null, reason, false)
.build()
);
}
public MessageAction bannedDM(MessageChannel channel, Member performer, @Nullable String reason) {
return channel.sendMessage(
moderationEmbed()
.setAuthor(performer.getGuild().getName(), null, performer.getGuild().getIconUrl())
.setTitle(translate("moderation.ban.dm.title"))
.addField(translate("moderation.ban.dm.field.performer"), performer.getAsMention(), true)
.addField(reason != null ? translate("moderation.ban.dm.field.reason") : null, reason, false)
.build()
);
}
public MessageAction unbanUser(MessageChannel channel, Member performer, User target) {
return channel.sendMessage(
moderationEmbed(translate("moderation.unban.info.author"))
.addField(translate("moderation.unban.info.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.unban.info.field.target"), target.getAsMention(), true)
.build()
);
}
public MessageAction warnUser(MessageChannel channel, Member performer, Member target, String reason,
OffsetDateTime dateTime, int caseID, boolean sentDM) {
return channel.sendMessage(
moderationEmbed(translate("moderation.warn.info.author"))
.addField(translate("moderation.warn.info.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.warn.info.field.target"), target.getAsMention(), true)
.addField(translate("moderation.warn.info.field.sent_private_message"), sentDM ? "" : "", true)
.addField(translate("moderation.warn.info.field.case_id"), String.valueOf(caseID), true)
.addField(translate("moderation.warn.info.field.date_time"),
dateTime.format(RFC_1123_DATE_TIME), true)
.addField(translate("moderation.warn.info.field.reason"), reason, false).build()
);
}
public MessageAction warnDM(MessageChannel channel, Member performer, Member target, String reason,
OffsetDateTime dateTime) {
return channel.sendMessage(
moderationEmbed()
.setAuthor(performer.getGuild().getName(), null, performer.getGuild().getIconUrl())
.setTitle(translate("moderation.warn.dm.title"))
.addField(translate("moderation.warn.dm.field.performer"), performer.getUser().getAsMention(), true)
.addField(translate("moderation.warn.dm.field.date_time"),
dateTime.format(RFC_1123_DATE_TIME), true)
.addField(translate("moderation.warn.dm.field.reason"), reason, false)
.build()
);
}
public MessageAction warnList(MessageChannel channel, Map<Integer, WarningEntry> displayWarnings) {
return channel.sendMessage(
moderationEmbed(translate("moderation.warnlist.author"))
.setDescription(displayWarnings.size() > 0 ? displayWarnings.entrySet().stream()
.sorted(Collections.reverseOrder(Comparator.comparingInt(Map.Entry::getKey)))
.limit(10)
.map(entry ->
translate("moderation.warnlist.entry",
entry.getKey(),
entry.getValue().getWarned().getAsMention(),
entry.getValue().getPerformer().getAsMention(),
entry.getValue().getDateTime().format(RFC_1123_DATE_TIME),
entry.getValue().getReason() != null
? entry.getValue().getReason()
: translate("moderation.warnlist.entry.no_reason"))
)
.collect(Collectors.joining("\n"))
: translate("moderation.warnlist.empty"))
.build()
);
}
public MessageAction unwarn(MessageChannel channel, Member performer, int caseID, WarningEntry entry) {
return channel.sendMessage(
moderationEmbed(translate("moderation.unwarn.author"))
.addField(translate("moderation.unwarn.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.unwarn.field.case_id"), String.valueOf(caseID), true)
.addField(translate("moderation.unwarn.field.original_target"), entry.getWarned().getAsMention(), true)
.addField(translate("moderation.unwarn.field.original_performer"), entry.getPerformer().getAsMention(), true)
.addField(translate("moderation.unwarn.field.date_time"), entry.getDateTime().format(RFC_1123_DATE_TIME), true)
.addField(entry.getReason() != null ? translate("moderation.unwarn.field.reason") : null, entry.getReason(),
false)
.build()
);
}
public MessageAction addNote(MessageChannel channel, Member performer, Member target, String contents,
OffsetDateTime dateTime, int noteID) {
return channel.sendMessage(
moderationEmbed(translate("moderation.note.add.author"))
.addField(translate("moderation.note.add.field.performer"), performer.getUser().getAsMention(), true)
.addField(translate("moderation.note.add.field.target"), target.getUser().getAsMention(), true)
.addField(translate("moderation.note.add.field.note_id"), String.valueOf(noteID), true)
.addField(translate("moderation.note.add.field.date_time"), dateTime.format(RFC_1123_DATE_TIME), true)
.addField(translate("moderation.note.add.field.contents"), contents, false)
.build()
);
}
public MessageAction noteList(MessageChannel channel, Map<Integer, NoteEntry> displayNotes) {
return channel.sendMessage(moderationEmbed(translate("moderation.note.list.author"))
.setDescription(displayNotes.size() > 0 ? displayNotes.entrySet().stream()
.sorted(Collections.reverseOrder(Comparator.comparingInt(Map.Entry::getKey)))
.limit(10)
.map(entry ->
translate("moderation.note.list.entry",
entry.getKey(),
entry.getValue().getTarget().getAsMention(),
entry.getValue().getPerformer().getAsMention(),
entry.getValue().getDateTime().format(RFC_1123_DATE_TIME),
entry.getValue().getContents())
public MessageBuilder performerInsufficientPermissions(final Member performer, final EnumSet<Permission> permissions) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.with("required_permissions",
() -> permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")))
.embed(embed -> embed
.setTitle("moderation.insufficient_permissions.title")
.setDescription("moderation.insufficient_permissions.description")
)
.collect(Collectors.joining("\n"))
: translate("moderation.note.list.empty"))
.build()
);
.field("moderation.insufficient_permissions.field.performer", true)
.field("moderation.insufficient_permissions.field.required_permissions", true);
}
public MessageBuilder cannotInteract(final Member performer, final Member target) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.embed(embed -> embed
.setTitle("moderation.cannot_interact.title")
.setDescription("moderation.cannot_interact.description")
)
.field("moderation.cannot_interact.field.performer", true)
.field("moderation.cannot_interact.field.target", true);
}
public MessageBuilder cannotWarnMods(final Member performer, final Member target) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.embed(embed -> embed
.setTitle("moderation.warn.cannot_warn_mods.title")
.setDescription("moderation.warn.cannot_warn_mods.description")
)
.field("moderation.warn.cannot_warn_mods.field.performer", true)
.field("moderation.warn.cannot_warn_mods.field.target", true);
}
public MessageBuilder cannotRemoveHigherModerated(final Member performer, final int caseID, final WarningEntry entry) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> warningEntry(builder, "warning_entry", caseID, entry))
.embed(embed -> embed
.setTitle("moderation.unwarn.cannot_remove_higher_mod.title")
.setDescription("moderation.unwarn.cannot_remove_higher_mod.description")
)
.field("moderation.unwarn.cannot_remove_higher_mod.field.performer", true)
.field("moderation.unwarn.cannot_remove_higher_mod.field.target", true);
}
public MessageBuilder maxAmountOfNotes(final Member performer, final Member target, final int amount) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.with("notes_amount", () -> String.valueOf(amount))
.embed(embed -> embed
.setTitle("moderation.note.max_amount_of_notes.title")
.setDescription("moderation.note.max_amount_of_notes.description")
)
.field("moderation.note.max_amount_of_notes.field.performer", true)
.field("moderation.note.max_amount_of_notes.field.target", true)
.field("moderation.note.max_amount_of_notes.field.amount", true);
}
public MessageBuilder noNoteFound(final Member performer, final int noteID) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.with("note_id", () -> String.valueOf(noteID))
.embed(embed -> embed
.setTitle("moderation.note.no_note_found.title")
.setDescription("moderation.note.no_note_found.description")
)
.field("moderation.note.no_note_found.field.performer", true)
.field("moderation.note.no_note_found.field.note_id", true);
}
public MessageBuilder noWarnWithID(final Member performer, final int caseID) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.with("case_id", () -> String.valueOf(caseID))
.embed(embed -> embed
.setTitle("moderation.unwarn.no_case_found.title")
.setDescription("moderation.unwarn.no_case_found.description")
)
.field("moderation.unwarn.no_case_found.field.performer", true)
.field("moderation.unwarn.no_case_found.field.note_id", true);
}
public MessageBuilder cannotUnwarnSelf(final Member performer, final int caseID, final WarningEntry entry) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> warningEntry(builder, "warning_entry", caseID, entry))
.embed(embed -> embed
.setTitle("moderation.unwarn.cannot_unwarn_self.title")
.setDescription("moderation.unwarn.cannot_unwarn_self.description")
)
.field("moderation.unwarn.cannot_unwarn_self.field.performer", true)
.field("moderation.unwarn.cannot_unwarn_self.field.original_performer", true)
.field("moderation.unwarn.cannot_unwarn_self.field.target", true);
}
}
public MessageAction removeNote(MessageChannel channel, Member performer, int noteID, NoteEntry entry) {
return channel.sendMessage(
moderationEmbed(translate("moderation.note.remove.author"))
.addField(translate("moderation.note.remove.field.performer"), performer.getAsMention(), true)
.addField(translate("moderation.note.remove.field.note_id"), String.valueOf(noteID), true)
.addField(translate("moderation.note.remove.field.original_target"), entry.getTarget().getAsMention(), true)
.addField(translate("moderation.note.remove.field.original_performer"), entry.getPerformer().getAsMention(),
true)
.addField(translate("moderation.note.remove.field.date_time"), entry.getDateTime().format(RFC_1123_DATE_TIME),
true)
.addField(translate("moderation.note.remove.field.contents"), entry.getContents(), false)
.build()
);
public MessageBuilder kickUser(final Member performer, final Member target, final @Nullable String reason,
final boolean sentDM) {
return moderation("moderation.kick.info.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.with("reason", () -> reason)
.field("moderation.kick.info.field.performer", true)
.field("moderation.kick.info.field.target", true)
.field("moderation.kick.info.field.private_message." + (sentDM ? "sent" : "unsent"), true)
.field("moderation.kick.info.field.reason", true);
}
public MessageBuilder kickedDM(final Member performer, final Member target, final @Nullable String reason) {
return moderation()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.with("reason", () -> reason)
.embed(embed -> embed
.setTitle("moderation.kick.dm.title")
.setAuthor("moderation.kick.dm.author", null, performer.getGuild().getIconUrl())
)
.field("moderation.kick.dm.field.performer", true)
.field("moderation.kick.dm.field.reason", true);
}
public MessageBuilder banUser(final Member performer, final Member target, final @Nullable String reason,
final int deletionDays, final boolean sentDM) {
return moderation("moderation.ban.info.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.with("delete_duration", () -> String.valueOf(deletionDays))
.with("reason", () -> reason)
.field("moderation.ban.info.field.performer", true)
.field("moderation.ban.info.field.target", true)
.field("moderation.ban.info.field.private_message." + (sentDM ? "sent" : "unsent"), true)
.field("moderation.ban.info.field.delete_duration", true)
.field("moderation.ban.info.field.reason", true);
}
public MessageBuilder bannedDM(final Member performer, final Member target, @Nullable final String reason) {
return moderation()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.with("reason", () -> reason)
.embed(embed -> embed
.setTitle("moderation.ban.dm.title")
.setAuthor("moderation.ban.dm.author", null, performer.getGuild().getIconUrl())
)
.field("moderation.ban.dm.field.performer", true)
.field("moderation.ban.dm.field.reason", true);
}
public MessageBuilder unbanUser(final Member performer, final User target) {
return moderation("moderation.unban.info.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.user(builder, "target", target))
.field("moderation.unban.info.field.performer", true)
.field("moderation.unban.info.field.target", true);
}
public void warningEntry(MessageBuilder builder, String head, int caseID, WarningEntry entry) {
builder
.with(head + ".case_id", () -> String.valueOf(caseID))
.apply(b -> messages.user(b, head + ".performer", entry.getPerformer()))
.apply(b -> messages.user(b, head + ".target", entry.getWarned()))
.with(head + ".date_time", () -> entry.getDateTime().format(RFC_1123_DATE_TIME))
.with(head + ".reason", entry::getReason);
}
public MessageBuilder warnUser(final Member performer, final int caseID, final WarningEntry entry, final boolean sentDM) {
return moderation("moderation.warn.info.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> warningEntry(builder, "warning_entry", caseID, entry))
.field("moderation.warn.info.field.performer", true)
.field("moderation.warn.info.field.target", true)
.field("moderation.warn.info.field.private_message." + (sentDM ? "sent" : "unsent"), true)
.field("moderation.warn.info.field.date_time", true)
.field("moderation.warn.info.field.case_id", true)
.field("moderation.warn.info.field.reason", true);
}
public MessageBuilder warnedDM(final Member performer, final Member target, final String reason,
final OffsetDateTime dateTime) {
return moderation()
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> messages.member(builder, "target", target))
.with("date_time", () -> dateTime.format(RFC_1123_DATE_TIME))
.with("reason", () -> reason)
.embed(embed -> embed
.setTitle("moderation.warn.dm.title")
.setAuthor("moderation.warn.dm.author", null, performer.getGuild().getIconUrl())
)
.field("moderation.warn.dm.field.performer", true)
.field("moderation.warn.dm.field.date_time", true)
.field("moderation.warn.dm.field.reason", true);
}
public MessageBuilder warnList(final Map<Integer, WarningEntry> displayWarnings) {
// return channel.sendMessage(
// moderationEmbed(translate("moderation.warnlist.author"))
// .setDescription(displayWarnings.size() > 0 ? displayWarnings.entrySet().stream()
// .sorted(Collections.reverseOrder(Comparator.comparingInt(Map.Entry::getKey)))
// .limit(10)
// .map(entry ->
// translate("moderation.warnlist.entry",
// entry.getKey(),
// entry.getValue().getWarned().getAsMention(),
// entry.getValue().getPerformer().getAsMention(),
// entry.getValue().getDateTime().format(RFC_1123_DATE_TIME),
// entry.getValue().getReason() != null
// ? entry.getValue().getReason()
// : translate("moderation.warnlist.entry.no_reason"))
// )
// .collect(Collectors.joining("\n"))
// : translate("moderation.warnlist.empty"))
// .build()
// );
return moderation()
.embed(embed -> embed.setTitle("NO OP, CURRENTLY IN PROGRESS"));
}
public MessageBuilder unwarn(final Member performer, final int caseID, final WarningEntry entry) {
return moderation("moderation.unwarn.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> warningEntry(builder, "warning_entry", caseID, entry))
.field("moderation.unwarn.field.performer", true)
.field("moderation.unwarn.field.case_id", true)
.field("moderation.unwarn.field.original_performer", true)
.field("moderation.unwarn.field.original_target", true)
.field("moderation.unwarn.field.date_time", true)
.field("moderation.unwarn.field.reason", true);
}
public void noteEntry(MessageBuilder builder, String head, int noteID, NoteEntry entry) {
builder
.with(head + ".note_id", () -> String.valueOf(noteID))
.apply(b -> messages.user(b, head + ".performer", entry.getPerformer()))
.apply(b -> messages.user(b, head + ".target", entry.getTarget()))
.with(head + ".date_time", () -> entry.getDateTime().format(RFC_1123_DATE_TIME))
.with(head + ".contents", entry::getContents);
}
public MessageBuilder addNote(final Member performer, final int noteID, final NoteEntry entry) {
return moderation("moderation.note.add.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> noteEntry(builder, "note", noteID, entry))
.field("moderation.note.add.field.performer", true)
.field("moderation.note.add.field.target", true)
.field("moderation.note.add.field.note_id", true)
.field("moderation.note.add.field.date_time", true)
.field("moderation.note.add.field.contents", true);
}
public MessageBuilder noteList(final Map<Integer, NoteEntry> displayNotes) {
// return channel.sendMessage(moderationEmbed(translate("moderation.note.list.author"))
// .setDescription(displayNotes.size() > 0 ? displayNotes.entrySet().stream()
// .sorted(Collections.reverseOrder(Comparator.comparingInt(Map.Entry::getKey)))
// .limit(10)
// .map(entry ->
// translate("moderation.note.list.entry",
// entry.getKey(),
// entry.getValue().getTarget().getAsMention(),
// entry.getValue().getPerformer().getAsMention(),
// entry.getValue().getDateTime().format(RFC_1123_DATE_TIME),
// entry.getValue().getContents())
// )
// .collect(Collectors.joining("\n"))
// : translate("moderation.note.list.empty"))
// .build()
// );
return moderation()
.embed(embed -> embed.setTitle("NO OP, CURRENTLY IN PROGRESS"));
}
public MessageBuilder removeNote(final Member performer, final int noteID, final NoteEntry entry) {
return moderation("moderation.note.remove.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> noteEntry(builder, "note", noteID, entry))
.field("moderation.note.remove.field.performer", true)
.field("moderation.note.remove.field.case_id", true)
.field("moderation.note.remove.field.original_performer", true)
.field("moderation.note.remove.field.original_target", true)
.field("moderation.note.remove.field.date_time", true)
.field("moderation.note.remove.field.contents", true);
}
}

View File

@ -0,0 +1,59 @@
package sciwhiz12.janitor.msg;
import org.apache.commons.collections4.TransformerUtils;
import org.apache.commons.collections4.map.DefaultedMap;
import sciwhiz12.janitor.JanitorBot;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.regex.Matcher.quoteReplacement;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
public class Substitutions {
public static final Pattern ARGUMENT_REGEX = Pattern.compile("\\$\\{(.+?)}", CASE_INSENSITIVE);
public static final Pattern NULL_ARGUMENT_REGEX = Pattern.compile("nullcheck;(.+?);(.+)", CASE_INSENSITIVE);
public static String substitute(String text, Map<String, Supplier<String>> arguments) {
final Matcher matcher = ARGUMENT_REGEX.matcher(text);
return matcher.replaceAll(matchResult -> {
final Matcher nullMatcher = NULL_ARGUMENT_REGEX.matcher(matchResult.group(1));
if (nullMatcher.matches()) {
final String str = arguments.get(nullMatcher.group(1)).get();
return str != null ?
quoteReplacement(str) :
quoteReplacement(arguments.getOrDefault(nullMatcher.group(2), () -> nullMatcher.group(2)).get());
}
return quoteReplacement(arguments.getOrDefault(matchResult.group(1), () -> matchResult.group(0)).get());
});
}
private final JanitorBot bot;
private final Map<String, Supplier<String>> defaultSubstitutions = new HashMap<>();
public Substitutions(JanitorBot bot) {
this.bot = bot;
}
public JanitorBot getBot() {
return bot;
}
public String substitute(String text) {
return Substitutions.substitute(text, defaultSubstitutions);
}
public String with(String text, Map<String, Supplier<String>> substitutions) {
return Substitutions.substitute(
text,
DefaultedMap.defaultedMap(substitutions, TransformerUtils.mapTransformer(defaultSubstitutions))
);
}
public Map<String, Supplier<String>> createDefaultedMap(Map<String, Supplier<String>> custom) {
return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(defaultSubstitutions));
}
}

View File

@ -1,111 +1,182 @@
{
"general.guild_only_command.title": "Guild only command!",
"general.guild_only_command.desc": "This command can only be run in a guild channel.",
"general.insufficient_permissions.title": "I have insufficient permissions!",
"general.insufficient_permissions.desc": "I do not have sufficient permissions to carry out this action!\nPlease contact your server admins if you believe this is in error.",
"general.insufficient_permissions.field.permissions": "Required permissions",
"general.guild_only_command.description": "This command can only be run in a guild channel.",
"general.ambiguous_member.title": "Ambiguous member argument!",
"general.ambiguous_member.desc": "The name you have specified is too ambiguous (leads to more than 1 member)!\nPlease narrow down the specified name until it can uniquely identify a member of this guild.",
"general.ambiguous_member.description": "The name you have specified is too ambiguous (leads to more than 1 member)!\nPlease narrow down the specified name until it can uniquely identify a member of this guild.",
"general.insufficient_permissions.title": "I have insufficient permissions!",
"general.insufficient_permissions.description": "I do not have sufficient permissions to carry out this action!\nPlease contact your server admins if you believe this is in error.",
"general.insufficient_permissions.field.permissions.name": "Required permissions",
"general.insufficient_permissions.field.permissions.value": "${required_permissions}",
"general.cannot_interact.title": "Member is higher than me!",
"general.cannot_interact.desc": "Cannot perform action on the given member, likely due to me being lower in the role hierarchy.",
"general.cannot_interact.field.target": "Target",
"general.cannot_interact.description": "Cannot perform action on the given member, likely due to me being lower in the role hierarchy.",
"general.cannot_interact.field.target.name": "Target",
"general.cannot_interact.field.target.value": "${target.mention}",
"general.cannot_action_self.title": "Cannot act against myself!",
"general.cannot_action_self.desc": "Cannot perform action against myself, as that would be counter-intuitive.",
"general.cannot_action_self.description": "Cannot perform action against myself, as that would be counter-intuitive.",
"general.cannot_action_performer.title": "Performer cannot act against self!",
"general.cannot_action_performer.desc": "You cannot perform this action against yourself.",
"general.cannot_action_performer.field.performer": "Performer/Target",
"general.cannot_action_performer.description": "You cannot perform this action against yourself.",
"general.cannot_action_performer.field.performer.name": "Performer/Target",
"general.cannot_action_performer.field.performer.value": "${performer.mention}",
"moderation.insufficient_permissions.title": "Insufficient permissions.",
"moderation.insufficient_permissions.desc": "The performer of this command has insufficient permissions to use this command.",
"moderation.insufficient_permissions.field.performer": "Performer",
"moderation.insufficient_permissions.field.permissions": "Required permissions",
"moderation.insufficient_permissions.description": "The performer of this command has insufficient permissions to use this command.",
"moderation.insufficient_permissions.field.performer.name": "Performer",
"moderation.insufficient_permissions.field.performer.value": "${performer.mention}",
"moderation.insufficient_permissions.field.permissions.name": "Required permissions",
"moderation.insufficient_permissions.field.permissions.value": "${required_permissions}",
"moderation.cannot_interact.title": "Cannot moderate Target.",
"moderation.cannot_interact.desc": "The performer of this command cannot moderate the target user, likely due to being lower in the role hierarchy.",
"moderation.cannot_interact.field.performer": "Performer",
"moderation.cannot_interact.field.target": "Target",
"moderation.cannot_interact.description": "The performer of this command cannot moderate the target user, likely due to being lower in the role hierarchy.",
"moderation.cannot_interact.field.performer.name": "Performer",
"moderation.cannot_interact.field.performer.value": "${performer.mention}",
"moderation.cannot_interact.field.target.name": "Target",
"moderation.cannot_interact.field.target.value": "${target.mention}",
"moderation.kick.info.author": "Kicked user from server.",
"moderation.kick.info.field.performer": "Performer",
"moderation.kick.info.field.target": "Target",
"moderation.kick.info.field.reason": "Reason",
"moderation.kick.info.field.sent_private_message": "Sent DM",
"moderation.kick.info.field.performer.name": "Performer",
"moderation.kick.info.field.performer.value": "${performer.mention}",
"moderation.kick.info.field.target.name": "Target",
"moderation.kick.info.field.target.value": "${target.mention}",
"moderation.kick.info.field.reason.name": "Reason",
"moderation.kick.info.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.kick.info.field.private_message.sent.name": "Sent DM",
"moderation.kick.info.field.private_message.sent.value": "✅",
"moderation.kick.info.field.private_message.unsent.name": "Sent DM",
"moderation.kick.info.field.private_message.unsent.value": "❌",
"moderation.kick.dm.author": "${performer.guild.name}",
"moderation.kick.dm.title": "You were kicked from this server.",
"moderation.kick.dm.field.performer": "Moderator",
"moderation.kick.dm.field.reason": "Reason",
"moderation.kick.dm.field.performer.name": "Moderator",
"moderation.kick.dm.field.performer.value": "${performer.mention}",
"moderation.kick.dm.field.reason.name": "Reason",
"moderation.kick.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.ban.info.author": "Banned user from server.",
"moderation.ban.info.field.performer": "Performer",
"moderation.ban.info.field.target": "Target",
"moderation.ban.info.field.reason": "Reason",
"moderation.ban.info.field.sent_private_message": "Sent DM",
"moderation.ban.info.field.delete_duration": "Message Deletion",
"moderation.ban.info.field.delete_duration.value": "%s day(s)",
"moderation.ban.info.field.performer.name": "Performer",
"moderation.ban.info.field.performer.value": "${performer.mention}",
"moderation.ban.info.field.target.name": "Target",
"moderation.ban.info.field.target.value": "${target.mention}",
"moderation.ban.info.field.reason.name": "Reason",
"moderation.ban.info.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.ban.info.field.private_message.sent.name": "Sent DM",
"moderation.ban.info.field.private_message.sent.value": "✅",
"moderation.ban.info.field.private_message.unsent.name": "Sent DM",
"moderation.ban.info.field.private_message.unsent.value": "❌",
"moderation.ban.info.field.delete_duration.name": "Message Deletion",
"moderation.ban.info.field.delete_duration.value": "${delete_duration} day(s)",
"moderation.ban.dm.author": "${performer.guild.name}",
"moderation.ban.dm.title": "You were banned from this server.",
"moderation.ban.dm.field.performer": "Moderator",
"moderation.ban.dm.field.reason": "Reason",
"moderation.ban.dm.field.performer.name": "Moderator",
"moderation.ban.dm.field.performer.value": "${performer.mention}",
"moderation.ban.dm.field.reason.name": "Reason",
"moderation.ban.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.unban.info.author": "Unbanned user from server.",
"moderation.unban.info.field.performer": "Performer",
"moderation.unban.info.field.target": "Target",
"moderation.unban.info.field.performer.name": "Performer",
"moderation.unban.info.field.performer.value": "${performer.mention}",
"moderation.unban.info.field.target.name": "Target",
"moderation.unban.info.field.target.value": "${target.mention}",
"moderation.warn.info.author": "Warned user.",
"moderation.warn.info.field.performer": "Performer",
"moderation.warn.info.field.target": "Target",
"moderation.warn.info.field.sent_private_message": "Sent DM",
"moderation.warn.info.field.case_id": "Case ID",
"moderation.warn.info.field.date_time": "Date & Time",
"moderation.warn.info.field.reason": "Reason",
"moderation.warn.info.field.performer.name": "Performer",
"moderation.warn.info.field.performer.value": "${warning_entry.performer.mention}",
"moderation.warn.info.field.target.name": "Target",
"moderation.warn.info.field.target.value": "${warning_entry.target.mention}",
"moderation.warn.info.field.case_id.name": "Case ID",
"moderation.warn.info.field.case_id.value": "${warning_entry.case_id}",
"moderation.warn.info.field.reason.name": "Reason",
"moderation.warn.info.field.reason.value": "${nullcheck;warning_entry.reason;_No reason specified._}",
"moderation.warn.info.field.private_message.sent.name": "Sent DM",
"moderation.warn.info.field.private_message.sent.value": "✅",
"moderation.warn.info.field.private_message.unsent.name": "Sent DM",
"moderation.warn.info.field.private_message.unsent.value": "❌",
"moderation.warn.info.field.date_time.name": "Date & Time",
"moderation.warn.info.field.date_time.value": "${warning_entry.date_time}",
"moderation.warn.dm.author": "${performer.guild.name}",
"moderation.warn.dm.title": "You were warned by a moderator.",
"moderation.warn.dm.field.performer": "Moderator",
"moderation.warn.dm.field.date_time": "Date & Time",
"moderation.warn.dm.field.reason": "Reason",
"moderation.warn.dm.field.performer.name": "Moderator",
"moderation.warn.dm.field.performer.value": "${performer.mention}",
"moderation.warn.dm.field.date_time.name": "Date & Time",
"moderation.warn.dm.field.date_time.value": "${date_time}",
"moderation.warn.dm.field.reason.name": "Reason",
"moderation.warn.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.warnlist.author": "Listing of Warnings",
"moderation.warnlist.empty": "**_No warnings logged matching your query._**",
"moderation.warnlist.entry": "**Case #%1$s**: Warned %2$s by %3$s %n - _Date & Time:_ %4$s %n - _Reason:_ %5$s",
"moderation.warnlist.entry.no_reason": "_no reason specified_",
"moderation.unwarn.author": "Removed warning from user.",
"moderation.unwarn.field.performer": "Performer",
"moderation.unwarn.field.original_target": "Original Target",
"moderation.unwarn.field.original_performer": "Original Performer",
"moderation.unwarn.field.case_id": "Case ID",
"moderation.unwarn.field.date_time": "Date & Time",
"moderation.unwarn.field.reason": "Reason",
"moderation.unwarn.field.performer.name": "Performer",
"moderation.unwarn.field.performer.value": "${performer.mention}",
"moderation.unwarn.field.original_target.name": "Original Target",
"moderation.unwarn.field.original_target.value": "${warning_entry.target.mention}",
"moderation.unwarn.field.original_performer.name": "Original Performer",
"moderation.unwarn.field.original_performer.value": "${warning_entry.performer.mention}",
"moderation.unwarn.field.case_id.name": "Case ID",
"moderation.unwarn.field.case_id.value": "${warning_entry.case_id}",
"moderation.unwarn.field.date_time.name": "Date & Time",
"moderation.unwarn.field.date_time.value": "${warning_entry.date_time}",
"moderation.unwarn.field.reason.name": "Reason",
"moderation.unwarn.field.reason.value": "${nullcheck;warning_entry.reason;_No reason specified._}",
"moderation.unwarn.no_case_found.title": "No warning found.",
"moderation.unwarn.no_case_found.desc": "No warning with that case ID was found.",
"moderation.unwarn.no_case_found.field.performer": "Performer",
"moderation.unwarn.no_case_found.field.case_id": "Case ID",
"moderation.unwarn.no_case_found.description": "No warning with that case ID was found.",
"moderation.unwarn.no_case_found.field.performer.name": "Performer",
"moderation.unwarn.no_case_found.field.performer.value": "${performer.mention}",
"moderation.unwarn.no_case_found.field.case_id.name": "Case ID",
"moderation.unwarn.no_case_found.field.case_id.value": "${case_id}",
"moderation.unwarn.cannot_unwarn_self.title": "Cannot remove warning from self.",
"moderation.unwarn.cannot_unwarn_self.desc": "Performer cannot remove a warning from themselves.",
"moderation.unwarn.cannot_unwarn_self.field.performer": "Performer/Original Target",
"moderation.unwarn.cannot_unwarn_self.field.original_performer": "Original Performer",
"moderation.unwarn.cannot_unwarn_self.field.case_id": "Case ID",
"moderation.unwarn.cannot_unwarn_self.description": "Performer cannot remove a warning from themselves.",
"moderation.unwarn.cannot_unwarn_self.field.performer.name": "Performer/Original Target",
"moderation.unwarn.cannot_unwarn_self.field.performer.value": "${performer.mention}",
"moderation.unwarn.cannot_unwarn_self.field.original_performer.name": "Original Performer",
"moderation.unwarn.cannot_unwarn_self.field.original_performer.value": "${warning_entry.performer.mention}",
"moderation.unwarn.cannot_unwarn_self.field.case_id.name": "Case ID",
"moderation.unwarn.cannot_unwarn_self.field.case_id.value": "${warning_entry.case_id}",
"moderation.warn.cannot_warn_mods.title": "Cannot warn moderators.",
"moderation.warn.cannot_warn_mods.desc": "Moderators cannot issue warnings to other moderators.",
"moderation.warn.cannot_warn_mods.field.performer": "Performer",
"moderation.warn.cannot_warn_mods.field.target": "Target",
"moderation.warn.cannot_warn_mods.description": "Moderators cannot issue warnings to other moderators.",
"moderation.warn.cannot_warn_mods.field.performer.name": "Performer",
"moderation.warn.cannot_warn_mods.field.performer.value": "${performer.mention}",
"moderation.warn.cannot_warn_mods.field.target.name": "Target",
"moderation.warn.cannot_warn_mods.field.target.value": "${target.mention}",
"moderation.warn.cannot_remove_higher_mod.title": "Cannot remove warning issued by higher-ranked moderator.",
"moderation.warn.cannot_remove_higher_mod.desc": "The performer cannot remove this warning, as this was issued by a higher-ranking moderator.",
"moderation.warn.cannot_remove_higher_mod.field.performer": "Performer",
"moderation.warn.cannot_remove_higher_mod.field.original_performer": "Original Performer",
"moderation.warn.cannot_remove_higher_mod.field.case_id": "Case ID",
"moderation.warn.cannot_remove_higher_mod.description": "The performer cannot remove this warning, as this was issued by a higher-ranking moderator.",
"moderation.warn.cannot_remove_higher_mod.field.performer.name": "Performer",
"moderation.warn.cannot_remove_higher_mod.field.performer.value": "${performer.mention}",
"moderation.warn.cannot_remove_higher_mod.field.original_performer.name": "Original Performer",
"moderation.warn.cannot_remove_higher_mod.field.original_performer.value": "${warning_entry.performer.mention}",
"moderation.warn.cannot_remove_higher_mod.field.case_id.name": "Case ID",
"moderation.warn.cannot_remove_higher_mod.field.case_id.value": "${warning_entry.case_id}",
"moderation.note.max_amount_of_notes.title": "Max notes reached.",
"moderation.note.max_amount_of_notes.desc": "The performer has reached the maximum amount of notes for the target user.",
"moderation.note.max_amount_of_notes.field.performer": "Performer",
"moderation.note.max_amount_of_notes.field.target": "Target",
"moderation.note.max_amount_of_notes.field.amount": "(Max.) Amount",
"moderation.note.max_amount_of_notes.description": "The performer has reached the maximum amount of notes for the target user.",
"moderation.note.max_amount_of_notes.field.performer.name": "Performer",
"moderation.note.max_amount_of_notes.field.performer.value": "${performer.mention}",
"moderation.note.max_amount_of_notes.field.target.name": "Target",
"moderation.note.max_amount_of_notes.field.target.value": "${target.mention}",
"moderation.note.max_amount_of_notes.field.amount.name": "(Max.) Amount",
"moderation.note.max_amount_of_notes.field.amount.value": "${notes_amount}",
"moderation.note.no_note_found.title": "No note found.",
"moderation.note.no_note_found.desc": "No note with that note ID was found.",
"moderation.note.no_note_found.field.performer": "Performer",
"moderation.note.no_note_found.field.note_id": "Note ID",
"moderation.note.no_note_found.description": "No note with that note ID was found.",
"moderation.note.no_note_found.field.performer.name": "Performer",
"moderation.note.no_note_found.field.performer.value": "${performer.mention}",
"moderation.note.no_note_found.field.note_id.name": "Note ID",
"moderation.note.no_note_found.field.note_id.value": "${note_id}",
"moderation.note.list.author": "Listing of Notes",
"moderation.note.list.empty": "**_No recorded notes matching your query._**",
"moderation.note.list.entry": "**#%1$s**: for %2$s by %3$s %n - _Date & Time:_ %4$s %n - _Text:_ %5$s",
"moderation.note.add.author": "Recorded note for user.",
"moderation.note.add.field.performer": "Performer",
"moderation.note.add.field.target": "Target",
"moderation.note.add.field.note_id": "Note ID",
"moderation.note.add.field.date_time": "Date & Time",
"moderation.note.add.field.contents": "Text",
"moderation.note.add.field.performer.name": "Performer",
"moderation.note.add.field.performer.value": "${note.performer.mention}",
"moderation.note.add.field.target.name": "Target",
"moderation.note.add.field.target.value": "${note.target.mention}",
"moderation.note.add.field.note_id.name": "Note ID",
"moderation.note.add.field.note_id.value": "${note.note_id}",
"moderation.note.add.field.date_time.name": "Date & Time",
"moderation.note.add.field.date_time.value": "${note.date_time}",
"moderation.note.add.field.contents.name": "Text",
"moderation.note.add.field.contents.value": "${note.contents}",
"moderation.note.remove.author": "Removed note.",
"moderation.note.remove.field.performer": "Performer",
"moderation.note.remove.field.original_target": "Original Target",
"moderation.note.remove.field.original_performer": "Original Performer",
"moderation.note.remove.field.note_id": "Note ID",
"moderation.note.remove.field.date_time": "Date & Time",
"moderation.note.remove.field.contents": "Text"
"moderation.note.remove.field.performer.name": "Performer",
"moderation.note.remove.field.performer.value": "${performer.mention}",
"moderation.note.remove.field.original_performer.name": "Original Performer",
"moderation.note.remove.field.original_performer.value": "${note.performer.mention}",
"moderation.note.remove.field.original_target.name": "Original Target",
"moderation.note.remove.field.original_target.value": "${note.target.mention}",
"moderation.note.remove.field.note_id.name": "Note ID",
"moderation.note.remove.field.note_id.value": "${note.note_id}",
"moderation.note.remove.field.date_time.name": "Date & Time",
"moderation.note.remove.field.date_time.value": "${note.date_time}",
"moderation.note.remove.field.contents.name": "Text",
"moderation.note.remove.field.contents.value": "${note.contents}"
}