From 8e972ba96a30394e24e7a5bfa0c0a25fc1719d84 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Tue, 6 Oct 2020 14:57:14 +0800 Subject: [PATCH 1/5] 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 --- .../java/sciwhiz12/janitor/JanitorBot.java | 13 +- .../commands/moderation/BanCommand.java | 22 +- .../commands/moderation/KickCommand.java | 24 +- .../commands/moderation/NoteCommand.java | 53 +- .../commands/moderation/UnbanCommand.java | 15 +- .../commands/moderation/UnwarnCommand.java | 16 +- .../commands/moderation/WarnCommand.java | 25 +- .../commands/moderation/WarnListCommand.java | 22 +- .../java/sciwhiz12/janitor/msg/General.java | 100 ++- .../sciwhiz12/janitor/msg/MessageBuilder.java | 103 ++++ .../java/sciwhiz12/janitor/msg/Messages.java | 79 ++- .../sciwhiz12/janitor/msg/Moderation.java | 567 +++++++++--------- .../sciwhiz12/janitor/msg/Substitutions.java | 59 ++ src/main/resources/english.json | 229 ++++--- 14 files changed, 836 insertions(+), 491 deletions(-) create mode 100644 src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/Substitutions.java diff --git a/src/main/java/sciwhiz12/janitor/JanitorBot.java b/src/main/java/sciwhiz12/janitor/JanitorBot.java index 4f59553..9e60dfa 100644 --- a/src/main/java/sciwhiz12/janitor/JanitorBot.java +++ b/src/main/java/sciwhiz12/janitor/JanitorBot.java @@ -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; + } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java index fdeab26..172fc39 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java @@ -68,7 +68,7 @@ public class BanCommand extends BaseCommand { void realRun(CommandContext 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(); } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java index 5996907..bbcb852 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java @@ -50,7 +50,7 @@ public class KickCommand extends BaseCommand { private int runWithReason(CommandContext 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; } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java index d73ff39..dca1e23 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java @@ -90,32 +90,36 @@ public class NoteCommand extends BaseCommand { } private int addNote(CommandContext 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 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 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 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; diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java index e909e7c..3dcee75 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java @@ -49,7 +49,7 @@ public class UnbanCommand extends BaseCommand { void realNamedRun(CommandContext 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 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(); } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java index 27eb3ec..86d4a61 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java @@ -45,7 +45,7 @@ public class UnwarnCommand extends BaseCommand { void realRun(CommandContext 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(); } } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java index 70199fc..3353d93 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java @@ -52,7 +52,7 @@ public class WarnCommand extends BaseCommand { void realRun(CommandContext 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(); } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java index bfa4daf..18799d3 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java @@ -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(); + } } diff --git a/src/main/java/sciwhiz12/janitor/msg/General.java b/src/main/java/sciwhiz12/janitor/msg/General.java index 2ad3a95..9ab322b 100644 --- a/src/main/java/sciwhiz12/janitor/msg/General.java +++ b/src/main/java/sciwhiz12/janitor/msg/General.java @@ -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 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 insufficientPermissions(MessageChannel channel, EnumSet 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 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 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 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 cannotActionSelf(MessageChannel channel) { - return channel.sendMessage( - messages.failureEmbed(translate("general.cannot_action_self.title")) - .setDescription(translate("general.cannot_action_self.desc")) - .build() - ); - } - - public RestAction 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); } } diff --git a/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java new file mode 100644 index 0000000..0a4fe73 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java @@ -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> substitutions; + + public MessageBuilder(EmbedBuilder embedBuilder, Map> 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 operator) { + operator.accept(embed()); + return this; + } + + public MessageBuilder apply(Consumer consumer) { + consumer.accept(this); + return this; + } + + public MessageBuilder with(final String argument, final Supplier 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> replaceMap = substitutions.createDefaultedMap(this.substitutions); + final UnaryOperator 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()); + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/Messages.java b/src/main/java/sciwhiz12/janitor/msg/Messages.java index 9b509d1..03b4f53 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Messages.java +++ b/src/main/java/sciwhiz12/janitor/msg/Messages.java @@ -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())); } } diff --git a/src/main/java/sciwhiz12/janitor/msg/Moderation.java b/src/main/java/sciwhiz12/janitor/msg/Moderation.java index f903876..3fe302b 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Moderation.java +++ b/src/main/java/sciwhiz12/janitor/msg/Moderation.java @@ -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 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 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 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 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 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 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); } } diff --git a/src/main/java/sciwhiz12/janitor/msg/Substitutions.java b/src/main/java/sciwhiz12/janitor/msg/Substitutions.java new file mode 100644 index 0000000..75fa7eb --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/Substitutions.java @@ -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> 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> 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> substitutions) { + return Substitutions.substitute( + text, + DefaultedMap.defaultedMap(substitutions, TransformerUtils.mapTransformer(defaultSubstitutions)) + ); + } + + public Map> createDefaultedMap(Map> custom) { + return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(defaultSubstitutions)); + } +} diff --git a/src/main/resources/english.json b/src/main/resources/english.json index 4de540a..abc3289 100644 --- a/src/main/resources/english.json +++ b/src/main/resources/english.json @@ -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}" } \ No newline at end of file From 44a55d3962e96fa46c06a5d11ccac85be6e29ba3 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Fri, 9 Oct 2020 15:21:12 +0800 Subject: [PATCH 2/5] Move from GSON to Jackson, cleanup code warnings, move to javax.annotation annotations --- build.gradle | 4 +- gradle.properties | 2 +- .../java/sciwhiz12/janitor/BotStartup.java | 4 +- .../java/sciwhiz12/janitor/GuildStorage.java | 6 +- .../commands/moderation/BanCommand.java | 2 +- .../commands/moderation/KickCommand.java | 2 +- .../commands/moderation/NoteCommand.java | 7 +- .../commands/moderation/UnwarnCommand.java | 5 +- .../commands/moderation/WarnListCommand.java | 3 - .../commands/util/ModerationHelper.java | 2 +- .../sciwhiz12/janitor/config/BotConfig.java | 2 +- .../janitor/moderation/notes/NoteEntry.java | 69 ++++++++++-------- .../janitor/moderation/notes/NoteStorage.java | 49 +++++++------ .../moderation/warns/WarningEntry.java | 72 ++++++++++--------- .../moderation/warns/WarningStorage.java | 49 +++++++------ .../sciwhiz12/janitor/msg/MessageBuilder.java | 1 - .../sciwhiz12/janitor/msg/Moderation.java | 2 +- .../sciwhiz12/janitor/msg/Translations.java | 14 ++-- .../sciwhiz12/janitor/storage/IStorage.java | 5 +- .../janitor/storage/JsonStorage.java | 33 +++++---- .../janitor/utils/StringReaderUtil.java | 2 - .../java/sciwhiz12/janitor/utils/Util.java | 4 +- 22 files changed, 179 insertions(+), 160 deletions(-) diff --git a/build.gradle b/build.gradle index be5573d..ac8d7b6 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,9 @@ dependencies { implementation group: 'com.electronwill.night-config', name: 'toml', version: nightconfig_version implementation group: 'net.sf.jopt-simple', name: 'jopt-simple', version: jopt_version implementation group: 'com.google.guava', name: 'guava', version: guava_version - implementation group: 'com.google.code.gson', name: 'gson', version: gson_version + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jackson_version + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: jackson_version + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jackson_version implementation group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: log4j_bridge_version implementation group: 'ch.qos.logback', name: 'logback-classic', version: logback_version implementation group: 'com.mojang', name: 'brigadier', version: brigadier_version diff --git a/gradle.properties b/gradle.properties index 5124242..90fffd0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ jda_version=4.2.0_207 nightconfig_version=3.6.3 jopt_version=6.0-alpha-3 guava_version=29.0-jre -gson_version=2.8.6 +jackson_version=2.11.2 log4j_bridge_version=2.13.3 logback_version=1.3.0-alpha5 brigadier_version=1.0.17 diff --git a/src/main/java/sciwhiz12/janitor/BotStartup.java b/src/main/java/sciwhiz12/janitor/BotStartup.java index 3dcfb0a..633d314 100644 --- a/src/main/java/sciwhiz12/janitor/BotStartup.java +++ b/src/main/java/sciwhiz12/janitor/BotStartup.java @@ -1,5 +1,6 @@ package sciwhiz12.janitor; +import com.google.common.base.Preconditions; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; @@ -12,7 +13,6 @@ import sciwhiz12.janitor.config.BotOptions; import java.util.EnumSet; -import static com.google.common.base.Preconditions.checkArgument; import static sciwhiz12.janitor.Logging.JANITOR; public class BotStartup { @@ -21,7 +21,7 @@ public class BotStartup { BotOptions options = new BotOptions(args); BotConfig config = new BotConfig(options); - checkArgument(!config.getToken().isEmpty(), "Supply a client token through config or command line"); + Preconditions.checkArgument(!config.getToken().isEmpty(), "Supply a client token through config or command line"); JANITOR.info("Building bot instance and connecting to Discord..."); diff --git a/src/main/java/sciwhiz12/janitor/GuildStorage.java b/src/main/java/sciwhiz12/janitor/GuildStorage.java index 9c98f7b..abeac53 100644 --- a/src/main/java/sciwhiz12/janitor/GuildStorage.java +++ b/src/main/java/sciwhiz12/janitor/GuildStorage.java @@ -1,8 +1,6 @@ package sciwhiz12.janitor; import com.google.common.base.Preconditions; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import net.dv8tion.jda.api.entities.Guild; import sciwhiz12.janitor.storage.IStorage; @@ -19,8 +17,6 @@ import java.util.function.Supplier; import static java.nio.file.StandardOpenOption.*; public class GuildStorage { - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().serializeNulls().create(); - private final JanitorBot bot; private final Path mainFolder; private final Map> guildStorage = new IdentityHashMap<>(); @@ -35,9 +31,9 @@ public class GuildStorage { return bot; } + @SuppressWarnings("unchecked") public T getOrCreate(Guild guild, String key, Supplier defaultSupplier) { final Map storageMap = guildStorage.computeIfAbsent(guild, g -> new HashMap<>()); - //noinspection unchecked return (T) storageMap.computeIfAbsent(key, k -> load(guild, key, defaultSupplier.get())); } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java index 172fc39..322210d 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java @@ -8,7 +8,6 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.util.ModerationHelper; @@ -16,6 +15,7 @@ import sciwhiz12.janitor.commands.util.ModerationHelper; import java.util.EnumSet; import java.util.List; import java.util.Objects; +import javax.annotation.Nullable; import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java index bbcb852..91e3e05 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java @@ -8,7 +8,6 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.util.CommandHelper; @@ -17,6 +16,7 @@ import sciwhiz12.janitor.commands.util.ModerationHelper; import java.util.EnumSet; import java.util.List; import java.util.Objects; +import javax.annotation.Nullable; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java index dca1e23..0e4c13c 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java @@ -9,7 +9,6 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.notes.NoteEntry; @@ -22,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; +import javax.annotation.Nullable; import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; @@ -160,10 +160,9 @@ public class NoteCommand extends BaseCommand { case PERFORMER: { predicate = predicate.and(e -> e.getValue().getPerformer().getIdLong() == performer.getIdLong()); } + case NONE: {} } - final OffsetDateTime dateTime = OffsetDateTime.now(); - if (!performer.hasPermission(NOTE_PERMISSION)) channel.sendMessage( messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) @@ -188,8 +187,6 @@ public class NoteCommand extends BaseCommand { final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); - final OffsetDateTime dateTime = OffsetDateTime.now(); - if (!performer.hasPermission(NOTE_PERMISSION)) channel.sendMessage( messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java index 86d4a61..b03b1d0 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java @@ -8,15 +8,14 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningStorage; -import java.time.OffsetDateTime; import java.util.EnumSet; import java.util.Objects; +import javax.annotation.Nullable; import static sciwhiz12.janitor.commands.util.CommandHelper.argument; import static sciwhiz12.janitor.commands.util.CommandHelper.literal; @@ -52,8 +51,6 @@ public class UnwarnCommand extends BaseCommand { final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); int caseID = IntegerArgumentType.getInteger(ctx, "caseId"); - final OffsetDateTime dateTime = OffsetDateTime.now(); - if (!performer.hasPermission(WARN_PERMISSION)) channel.sendMessage( messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java index 18799d3..edc61ab 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java @@ -14,7 +14,6 @@ import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningStorage; -import java.time.OffsetDateTime; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -88,8 +87,6 @@ public class WarnListCommand extends BaseCommand { predicate = predicate.and(e -> e.getValue().getPerformer().getIdLong() == mod.getIdLong()); } - final OffsetDateTime dateTime = OffsetDateTime.now(); - if (!performer.hasPermission(WARN_PERMISSION)) channel.sendMessage( messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) diff --git a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java index 14ea445..0895ece 100644 --- a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java +++ b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java @@ -4,11 +4,11 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; -import org.checkerframework.checker.nullness.qual.Nullable; import java.time.Instant; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; +import javax.annotation.Nullable; import static sciwhiz12.janitor.utils.Util.nameFor; diff --git a/src/main/java/sciwhiz12/janitor/config/BotConfig.java b/src/main/java/sciwhiz12/janitor/config/BotConfig.java index 8c73d3b..4d5a487 100644 --- a/src/main/java/sciwhiz12/janitor/config/BotConfig.java +++ b/src/main/java/sciwhiz12/janitor/config/BotConfig.java @@ -4,11 +4,11 @@ import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.file.FileNotFoundAction; import com.electronwill.nightconfig.core.file.FileWatcher; import com.electronwill.nightconfig.toml.TomlFormat; -import org.checkerframework.checker.nullness.qual.Nullable; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; +import javax.annotation.Nullable; import static sciwhiz12.janitor.Logging.CONFIG; import static sciwhiz12.janitor.Logging.JANITOR; diff --git a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java b/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java index 5d79756..94ded43 100644 --- a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java +++ b/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java @@ -1,18 +1,19 @@ package sciwhiz12.janitor.moderation.notes; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import net.dv8tion.jda.api.entities.User; import sciwhiz12.janitor.JanitorBot; -import java.lang.reflect.Type; +import java.io.IOException; import java.time.OffsetDateTime; import java.util.Objects; +import java.util.function.Supplier; public class NoteEntry { private final User performer; @@ -59,32 +60,42 @@ public class NoteEntry { return Objects.hash(getPerformer(), getTarget(), getDateTime(), getContents()); } - public static class Serializer implements JsonDeserializer, JsonSerializer { - private final JanitorBot bot; + public static class Serializer extends StdSerializer { + private static final long serialVersionUID = 1L; - public Serializer(JanitorBot bot) { + public Serializer() { + super(NoteEntry.class); + } + + @Override + public void serialize(NoteEntry value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("performer", value.getPerformer().getIdLong()); + gen.writeNumberField("target", value.getTarget().getIdLong()); + gen.writeStringField("dateTime", value.getDateTime().toString()); + gen.writeStringField("contents", value.getContents()); + gen.writeEndObject(); + } + } + + public static class Deserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + private final Supplier bot; + + public Deserializer(Supplier bot) { + super(NoteEntry.class); this.bot = bot; } @Override - public NoteEntry deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - final JsonObject obj = json.getAsJsonObject(); - final User performer = bot.getDiscord().retrieveUserById(obj.get("performer").getAsLong()).complete(); - final User target = bot.getDiscord().retrieveUserById(obj.get("target").getAsLong()).complete(); - final OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").getAsString()); - final String reason = obj.get("contents").getAsString(); - return new NoteEntry(performer, target, dateTime, reason); - } - - @Override - public JsonElement serialize(NoteEntry src, Type typeOfSrc, JsonSerializationContext context) { - final JsonObject obj = new JsonObject(); - obj.addProperty("performer", src.getPerformer().getId()); - obj.addProperty("target", src.getTarget().getId()); - obj.addProperty("dateTime", src.getDateTime().toString()); - obj.addProperty("contents", src.getContents()); - return obj; + public NoteEntry deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + final JsonNode obj = ctx.readTree(p); + User performer = bot.get().getDiscord().retrieveUserById(obj.get("performer").asLong()).complete(); + User target = bot.get().getDiscord().retrieveUserById(obj.get("target").asLong()).complete(); + OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").asText()); + String contents = obj.get("contents").asText(); + return new NoteEntry(performer, target, dateTime, contents); } } } diff --git a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java b/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java index c82d205..3101432 100644 --- a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java +++ b/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java @@ -1,40 +1,36 @@ package sciwhiz12.janitor.moderation.notes; import com.electronwill.nightconfig.core.utils.ObservedMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.node.ObjectNode; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.User; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.GuildStorage; import sciwhiz12.janitor.JanitorBot; import sciwhiz12.janitor.storage.JsonStorage; -import java.lang.reflect.Type; +import java.io.IOException; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nullable; public class NoteStorage extends JsonStorage { - private static final Type NOTE_MAP_TYPE = new TypeToken>() {}.getType(); + private static final TypeReference> NOTE_MAP_TYPE = new TypeReference<>() {}; public static final String STORAGE_KEY = "notes"; public static NoteStorage get(GuildStorage storage, Guild guild) { return storage.getOrCreate(guild, STORAGE_KEY, () -> new NoteStorage(storage.getBot())); } - private final Gson gson; private final JanitorBot bot; private int lastID = 1; private final Map notes = new ObservedMap<>(new HashMap<>(), this::markDirty); public NoteStorage(JanitorBot bot) { this.bot = bot; - this.gson = new GsonBuilder() - .registerTypeAdapter(NoteEntry.class, new NoteEntry.Serializer(bot)) - .create(); } public JanitorBot getBot() { @@ -52,8 +48,8 @@ public class NoteStorage extends JsonStorage { return notes.get(noteID); } - public NoteEntry removeNote(int noteID) { - return notes.remove(noteID); + public void removeNote(int noteID) { + notes.remove(noteID); } public int getAmountOfNotes(User target) { @@ -67,18 +63,27 @@ public class NoteStorage extends JsonStorage { } @Override - public JsonElement save() { - JsonObject obj = new JsonObject(); - obj.addProperty("lastNoteID", lastID); - obj.add("notes", gson.toJsonTree(notes)); + protected void initialize(ObjectMapper mapper) { + super.initialize(mapper); + mapper.registerModule( + new SimpleModule() + .addSerializer(NoteEntry.class, new NoteEntry.Serializer()) + .addDeserializer(NoteEntry.class, new NoteEntry.Deserializer(this::getBot)) + ); + } + + @Override + public JsonNode save(ObjectMapper mapper) { + final ObjectNode obj = mapper.createObjectNode(); + obj.put("lastNoteID", lastID); + obj.set("notes", mapper.valueToTree(notes)); return obj; } @Override - public void load(JsonElement in) { - final JsonObject obj = in.getAsJsonObject(); - lastID = obj.get("lastNoteID").getAsInt(); - final Map loaded = gson.fromJson(obj.get("notes"), NOTE_MAP_TYPE); + public void load(JsonNode in, ObjectMapper mapper) throws IOException { + lastID = in.get("lastNoteID").asInt(); + final Map loaded = mapper.readerFor(NOTE_MAP_TYPE).readValue(in.get("notes")); notes.clear(); notes.putAll(loaded); } diff --git a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java b/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java index e136c77..2623c2d 100644 --- a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java +++ b/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java @@ -1,18 +1,19 @@ package sciwhiz12.janitor.moderation.warns; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import net.dv8tion.jda.api.entities.User; import sciwhiz12.janitor.JanitorBot; -import java.lang.reflect.Type; +import java.io.IOException; import java.time.OffsetDateTime; import java.util.Objects; +import java.util.function.Supplier; import javax.annotation.Nullable; public class WarningEntry { @@ -62,35 +63,42 @@ public class WarningEntry { return Objects.hash(getPerformer(), getWarned(), getDateTime(), getReason()); } - public static class Serializer implements JsonDeserializer, JsonSerializer { - private final JanitorBot bot; + public static class Serializer extends StdSerializer { + private static final long serialVersionUID = 1L; - public Serializer(JanitorBot bot) { + public Serializer() { + super(WarningEntry.class); + } + + @Override + public void serialize(WarningEntry value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("performer", value.getPerformer().getIdLong()); + gen.writeNumberField("warned", value.getWarned().getIdLong()); + gen.writeStringField("dateTime", value.getDateTime().toString()); + gen.writeStringField("reason", value.getReason()); + gen.writeEndObject(); + } + } + + public static class Deserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + private final Supplier bot; + + public Deserializer(Supplier bot) { + super(WarningEntry.class); this.bot = bot; } @Override - public WarningEntry deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - final JsonObject obj = json.getAsJsonObject(); - final User warned = bot.getDiscord().retrieveUserById(obj.get("warned").getAsLong()).complete(); - final User performer = bot.getDiscord().retrieveUserById(obj.get("performer").getAsLong()).complete(); - final OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").getAsString()); - @Nullable - final String reason = obj.has("reason") ? obj.get("reason").getAsString() : null; - return new WarningEntry(warned, performer, dateTime, reason); - } - - @Override - public JsonElement serialize(WarningEntry src, Type typeOfSrc, JsonSerializationContext context) { - final JsonObject obj = new JsonObject(); - obj.addProperty("warned", src.getWarned().getId()); - obj.addProperty("performer", src.getPerformer().getId()); - obj.addProperty("dateTime", src.getDateTime().toString()); - if (src.getReason() != null) { - obj.addProperty("reason", src.getReason()); - } - return obj; + public WarningEntry deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + final JsonNode obj = ctx.readTree(p); + User performer = bot.get().getDiscord().retrieveUserById(obj.get("performer").asLong()).complete(); + User warned = bot.get().getDiscord().retrieveUserById(obj.get("warned").asLong()).complete(); + OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").asText()); + String contents = obj.get("reason").asText(); + return new WarningEntry(performer, warned, dateTime, contents); } } } diff --git a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java b/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java index b540c34..9dc21ab 100644 --- a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java +++ b/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java @@ -1,39 +1,35 @@ package sciwhiz12.janitor.moderation.warns; import com.electronwill.nightconfig.core.utils.ObservedMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.node.ObjectNode; import net.dv8tion.jda.api.entities.Guild; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.GuildStorage; import sciwhiz12.janitor.JanitorBot; import sciwhiz12.janitor.storage.JsonStorage; -import java.lang.reflect.Type; +import java.io.IOException; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nullable; public class WarningStorage extends JsonStorage { - private static final Type WARNING_MAP_TYPE = new TypeToken>() {}.getType(); + private static final TypeReference> WARNING_MAP_TYPE = new TypeReference<>() {}; public static final String STORAGE_KEY = "warnings"; public static WarningStorage get(GuildStorage storage, Guild guild) { return storage.getOrCreate(guild, STORAGE_KEY, () -> new WarningStorage(storage.getBot())); } - private final Gson gson; private final JanitorBot bot; private int lastID = 1; private final Map warnings = new ObservedMap<>(new HashMap<>(), this::markDirty); public WarningStorage(JanitorBot bot) { this.bot = bot; - this.gson = new GsonBuilder() - .registerTypeAdapter(WarningEntry.class, new WarningEntry.Serializer(bot)) - .create(); } public JanitorBot getBot() { @@ -51,8 +47,8 @@ public class WarningStorage extends JsonStorage { return warnings.get(caseID); } - public WarningEntry removeWarning(int caseID) { - return warnings.remove(caseID); + public void removeWarning(int caseID) { + warnings.remove(caseID); } public Map getWarnings() { @@ -60,18 +56,27 @@ public class WarningStorage extends JsonStorage { } @Override - public JsonElement save() { - JsonObject obj = new JsonObject(); - obj.addProperty("lastCaseID", lastID); - obj.add("warnings", gson.toJsonTree(warnings)); + protected void initialize(ObjectMapper mapper) { + super.initialize(mapper); + mapper.registerModule( + new SimpleModule() + .addSerializer(WarningEntry.class, new WarningEntry.Serializer()) + .addDeserializer(WarningEntry.class, new WarningEntry.Deserializer(this::getBot)) + ); + } + + @Override + public JsonNode save(ObjectMapper mapper) { + final ObjectNode obj = mapper.createObjectNode(); + obj.put("lastCaseID", lastID); + obj.set("warnings", mapper.valueToTree(warnings)); return obj; } @Override - public void load(JsonElement in) { - final JsonObject obj = in.getAsJsonObject(); - lastID = obj.get("lastCaseID").getAsInt(); - final Map loaded = gson.fromJson(obj.get("warnings"), WARNING_MAP_TYPE); + public void load(JsonNode in, ObjectMapper mapper) throws IOException { + lastID = in.get("lastCaseID").asInt(); + final Map loaded = mapper.convertValue(in.get("warnings"), WARNING_MAP_TYPE); warnings.clear(); warnings.putAll(loaded); } diff --git a/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java index 0a4fe73..1c4fb86 100644 --- a/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java +++ b/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java @@ -25,7 +25,6 @@ public class MessageBuilder { this(new EmbedBuilder(), new HashMap<>()); } - @SuppressWarnings("CopyConstructorMissesField") public MessageBuilder(MessageBuilder copy) { this(new EmbedBuilder(copy.embedBuilder), new HashMap<>(copy.substitutions)); } diff --git a/src/main/java/sciwhiz12/janitor/msg/Moderation.java b/src/main/java/sciwhiz12/janitor/msg/Moderation.java index 3fe302b..b4ea21d 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Moderation.java +++ b/src/main/java/sciwhiz12/janitor/msg/Moderation.java @@ -3,7 +3,6 @@ package sciwhiz12.janitor.msg; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; -import org.checkerframework.checker.nullness.qual.Nullable; import sciwhiz12.janitor.moderation.notes.NoteEntry; import sciwhiz12.janitor.moderation.warns.WarningEntry; @@ -12,6 +11,7 @@ import java.time.ZoneOffset; import java.util.EnumSet; import java.util.Map; import java.util.stream.Collectors; +import javax.annotation.Nullable; import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; diff --git a/src/main/java/sciwhiz12/janitor/msg/Translations.java b/src/main/java/sciwhiz12/janitor/msg/Translations.java index 5dda90d..0101dd3 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Translations.java +++ b/src/main/java/sciwhiz12/janitor/msg/Translations.java @@ -1,13 +1,11 @@ package sciwhiz12.janitor.msg; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.io.Resources; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; import sciwhiz12.janitor.JanitorBot; import java.io.InputStreamReader; -import java.lang.reflect.Type; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; @@ -18,13 +16,13 @@ import static sciwhiz12.janitor.Logging.JANITOR; import static sciwhiz12.janitor.Logging.TRANSLATIONS; public class Translations { - private static final Gson GSON = new GsonBuilder().create(); private static final String DEFAULT_TRANSLATIONS_RESOURCE = "english.json"; - private static final Type MAP_TYPE = new TypeToken>() {}.getType(); + private static final TypeReference> MAP_TYPE = new TypeReference<>() {}; private final JanitorBot bot; private final Path translationsFile; private final Map translations = new HashMap<>(); + private final ObjectMapper jsonMapper = new ObjectMapper(); public Translations(JanitorBot bot, Path translationsFile) { this.bot = bot; @@ -40,7 +38,7 @@ public class Translations { } try { JANITOR.debug(TRANSLATIONS, "Loading translations from file {}", translationsFile); - Map trans = GSON.fromJson(Files.newBufferedReader(translationsFile), MAP_TYPE); + Map trans = jsonMapper.readValue(Files.newBufferedReader(translationsFile), MAP_TYPE); translations.clear(); translations.putAll(trans); JANITOR.info(TRANSLATIONS, "Loaded {} translations from file {}", translations.size(), translationsFile); @@ -55,7 +53,7 @@ public class Translations { try { JANITOR.debug(TRANSLATIONS, "Loading default english translations"); // noinspection UnstableApiUsage - Map trans = GSON.fromJson( + Map trans = jsonMapper.readValue( new InputStreamReader(Resources.getResource(DEFAULT_TRANSLATIONS_RESOURCE).openStream()), MAP_TYPE); translations.clear(); diff --git a/src/main/java/sciwhiz12/janitor/storage/IStorage.java b/src/main/java/sciwhiz12/janitor/storage/IStorage.java index 6ee78c8..655a775 100644 --- a/src/main/java/sciwhiz12/janitor/storage/IStorage.java +++ b/src/main/java/sciwhiz12/janitor/storage/IStorage.java @@ -1,5 +1,6 @@ package sciwhiz12.janitor.storage; +import java.io.IOException; import java.io.Reader; import java.io.Writer; @@ -7,7 +8,7 @@ public interface IStorage { boolean dirty(); - void write(Writer output); + void write(Writer output) throws IOException; - void read(Reader input); + void read(Reader input) throws IOException; } diff --git a/src/main/java/sciwhiz12/janitor/storage/JsonStorage.java b/src/main/java/sciwhiz12/janitor/storage/JsonStorage.java index f0a8013..5763234 100644 --- a/src/main/java/sciwhiz12/janitor/storage/JsonStorage.java +++ b/src/main/java/sciwhiz12/janitor/storage/JsonStorage.java @@ -1,30 +1,35 @@ package sciwhiz12.janitor.storage; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import java.io.IOException; import java.io.Reader; import java.io.Writer; public abstract class JsonStorage extends AbstractStorage { - public static final Gson GSON = new GsonBuilder() - .serializeNulls() - .setPrettyPrinting() - .create(); + protected final ObjectMapper jsonMapper = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT) + .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); - public abstract JsonElement save(); + protected JsonStorage() { + initialize(jsonMapper); + } - public abstract void load(JsonElement object); + protected void initialize(ObjectMapper mapper) {} + + public abstract JsonNode save(ObjectMapper mapper); + + public abstract void load(JsonNode object, ObjectMapper mapper) throws IOException; @Override - public void write(Writer input) { - GSON.toJson(save(), input); + public void write(Writer input) throws IOException { + jsonMapper.writeTree(jsonMapper.createGenerator(input), save(jsonMapper)); } @Override - public void read(Reader input) { - load(JsonParser.parseReader(input)); + public void read(Reader input) throws IOException { + load(jsonMapper.readTree(input), jsonMapper); } } diff --git a/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java b/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java index a20b0cc..99bb739 100644 --- a/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java +++ b/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java @@ -37,8 +37,6 @@ public class StringReaderUtil { } private static final char SYNTAX_ESCAPE = '\\'; - private static final char SYNTAX_DOUBLE_QUOTE = '"'; - private static final char SYNTAX_SINGLE_QUOTE = '\''; public static String readStringUntil(StringReader reader, char terminator) throws CommandSyntaxException { final StringBuilder result = new StringBuilder(); boolean escaped = false; diff --git a/src/main/java/sciwhiz12/janitor/utils/Util.java b/src/main/java/sciwhiz12/janitor/utils/Util.java index 57ec9aa..fe22818 100644 --- a/src/main/java/sciwhiz12/janitor/utils/Util.java +++ b/src/main/java/sciwhiz12/janitor/utils/Util.java @@ -55,8 +55,8 @@ public class Util { return user.getName().concat("#").concat(user.getDiscriminator()); } - public static BiConsumer handle(final Consumer success, - final Consumer exceptionally) { + public static BiConsumer handle(final Consumer success, + final Consumer exceptionally) { return (suc, ex) -> { if (ex == null) { success.accept(suc); From 46eff3358e13b3a263a5e8241c9c9db18b2349ad Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sat, 10 Oct 2020 16:53:05 +0800 Subject: [PATCH 3/5] Externalize messages to JSONs For ref.: bot console has "reload messages" command to reload the messages from disk --- .../java/sciwhiz12/janitor/BotConsole.java | 11 +- .../java/sciwhiz12/janitor/JanitorBot.java | 27 +- src/main/java/sciwhiz12/janitor/Logging.java | 1 + .../commands/moderation/BanCommand.java | 89 +++-- .../commands/moderation/KickCommand.java | 77 ++-- .../commands/moderation/NoteCommand.java | 121 +++++-- .../commands/moderation/UnbanCommand.java | 49 ++- .../commands/moderation/UnwarnCommand.java | 57 ++- .../commands/moderation/WarnCommand.java | 83 +++-- .../commands/moderation/WarnListCommand.java | 59 ++-- .../commands/util/ModerationHelper.java | 6 +- .../sciwhiz12/janitor/config/BotConfig.java | 20 +- .../sciwhiz12/janitor/config/BotOptions.java | 10 + .../java/sciwhiz12/janitor/msg/General.java | 75 ---- .../sciwhiz12/janitor/msg/MessageHelper.java | 110 ++++++ .../java/sciwhiz12/janitor/msg/Messages.java | 168 +++++---- .../sciwhiz12/janitor/msg/Moderation.java | 333 ------------------ ...{Translations.java => TranslationMap.java} | 21 +- .../sciwhiz12/janitor/msg/json/IMessage.java | 9 + .../janitor/msg/json/ListingMessage.java | 61 ++++ .../janitor/msg/json/RegularMessage.java | 213 +++++++++++ .../msg/json/RegularMessageDeserializer.java | 93 +++++ .../msg/substitution/CustomSubstitutions.java | 21 ++ .../msg/substitution/ISubstitutor.java | 5 + .../SubstitutionMap.java} | 29 +- src/main/resources/english.json | 240 +++++-------- .../general/error/ambiguous_member.json | 5 + .../error/cannot_action_performer.json | 12 + .../general/error/cannot_action_self.json | 5 + .../general/error/cannot_interact.json | 12 + .../general/error/guild_only_command.json | 5 + .../error/insufficient_permissions.json | 12 + src/main/resources/messages/messages.json | 26 ++ .../resources/messages/moderation/ban/dm.json | 20 ++ .../messages/moderation/ban/info.json | 34 ++ .../moderation/error/cannot_interact.json | 17 + .../error/insufficient_permissions.json | 17 + .../error/note/max_amount_of_notes.json | 22 ++ .../moderation/error/note/no_note_found.json | 17 + .../unwarn/cannot_remove_higher_mod.json | 22 ++ .../error/unwarn/cannot_unwarn_self.json | 22 ++ .../error/unwarn/no_case_found.json | 17 + .../error/warn/cannot_warn_mods.json | 17 + .../messages/moderation/kick/dm.json | 20 ++ .../messages/moderation/kick/info.json | 29 ++ .../messages/moderation/note/add.json | 34 ++ .../messages/moderation/note/remove.json | 39 ++ .../messages/moderation/unban/info.json | 19 + .../messages/moderation/unwarn/info.json | 39 ++ .../messages/moderation/warn/dm.json | 25 ++ .../messages/moderation/warn/info.json | 39 ++ 51 files changed, 1663 insertions(+), 851 deletions(-) delete mode 100644 src/main/java/sciwhiz12/janitor/msg/General.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/MessageHelper.java delete mode 100644 src/main/java/sciwhiz12/janitor/msg/Moderation.java rename src/main/java/sciwhiz12/janitor/msg/{Translations.java => TranslationMap.java} (78%) create mode 100644 src/main/java/sciwhiz12/janitor/msg/json/IMessage.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java rename src/main/java/sciwhiz12/janitor/msg/{Substitutions.java => substitution/SubstitutionMap.java} (61%) create mode 100644 src/main/resources/messages/general/error/ambiguous_member.json create mode 100644 src/main/resources/messages/general/error/cannot_action_performer.json create mode 100644 src/main/resources/messages/general/error/cannot_action_self.json create mode 100644 src/main/resources/messages/general/error/cannot_interact.json create mode 100644 src/main/resources/messages/general/error/guild_only_command.json create mode 100644 src/main/resources/messages/general/error/insufficient_permissions.json create mode 100644 src/main/resources/messages/messages.json create mode 100644 src/main/resources/messages/moderation/ban/dm.json create mode 100644 src/main/resources/messages/moderation/ban/info.json create mode 100644 src/main/resources/messages/moderation/error/cannot_interact.json create mode 100644 src/main/resources/messages/moderation/error/insufficient_permissions.json create mode 100644 src/main/resources/messages/moderation/error/note/max_amount_of_notes.json create mode 100644 src/main/resources/messages/moderation/error/note/no_note_found.json create mode 100644 src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json create mode 100644 src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json create mode 100644 src/main/resources/messages/moderation/error/unwarn/no_case_found.json create mode 100644 src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json create mode 100644 src/main/resources/messages/moderation/kick/dm.json create mode 100644 src/main/resources/messages/moderation/kick/info.json create mode 100644 src/main/resources/messages/moderation/note/add.json create mode 100644 src/main/resources/messages/moderation/note/remove.json create mode 100644 src/main/resources/messages/moderation/unban/info.json create mode 100644 src/main/resources/messages/moderation/unwarn/info.json create mode 100644 src/main/resources/messages/moderation/warn/dm.json create mode 100644 src/main/resources/messages/moderation/warn/info.json diff --git a/src/main/java/sciwhiz12/janitor/BotConsole.java b/src/main/java/sciwhiz12/janitor/BotConsole.java index 4f2526a..ebb83c5 100644 --- a/src/main/java/sciwhiz12/janitor/BotConsole.java +++ b/src/main/java/sciwhiz12/janitor/BotConsole.java @@ -49,6 +49,11 @@ public class BotConsole { bot.getTranslations().loadTranslations(); break outer; } + case "messages": { + CONSOLE.info("Reloading messages"); + bot.getMessages().loadMessages(); + break outer; + } } } default: @@ -71,8 +76,7 @@ public class BotConsole { while (!scanner.hasNextLine()) { try { Thread.sleep(150); - } - catch (InterruptedException e) { + } catch (InterruptedException e) { CONSOLE.warn("Console thread is interrupted"); continue outer; } @@ -84,8 +88,7 @@ public class BotConsole { } CONSOLE.debug("Received command: {}", input); BotConsole.this.parseCommand(input); - } - catch (Exception e) { + } catch (Exception e) { CONSOLE.error("Error while running console thread", e); } } diff --git a/src/main/java/sciwhiz12/janitor/JanitorBot.java b/src/main/java/sciwhiz12/janitor/JanitorBot.java index 9e60dfa..5a62a62 100644 --- a/src/main/java/sciwhiz12/janitor/JanitorBot.java +++ b/src/main/java/sciwhiz12/janitor/JanitorBot.java @@ -9,8 +9,8 @@ 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.msg.TranslationMap; +import sciwhiz12.janitor.msg.substitution.SubstitutionMap; import sciwhiz12.janitor.utils.Util; import java.nio.file.Path; @@ -22,13 +22,13 @@ import static sciwhiz12.janitor.Logging.STATUS; public class JanitorBot { private final JDA discord; private final BotConfig config; - private final Messages messages; - private BotConsole console; + private final BotConsole console; private final GuildStorage storage; private final GuildStorage.SavingThread storageSavingThread; private final CommandRegistry cmdRegistry; - private final Translations translations; - private final Substitutions substitutions; + private final TranslationMap translations; + private final SubstitutionMap substitutions; + private final Messages messages; public JanitorBot(JDA discord, BotConfig config) { this.config = config; @@ -36,9 +36,10 @@ public class JanitorBot { 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.translations = new Translations(this, config.getTranslationsFile()); - this.messages = new Messages(this); - this.substitutions = new Substitutions(this); + this.translations = new TranslationMap(this, config.getTranslationsFile()); + this.substitutions = new SubstitutionMap(this); + this.messages = new Messages(this, config.getTranslationsFile()); + // TODO: find which of these can be loaded in parallel before the bot JDA is ready discord.addEventListener(cmdRegistry); discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!")); discord.getGuilds().forEach(Guild::loadMembers); @@ -67,7 +68,9 @@ public class JanitorBot { return this.config; } - public Messages getMessages() { return this.messages; } + public Messages getMessages() { + return messages; + } public GuildStorage getStorage() { return this.storage; } @@ -75,7 +78,7 @@ public class JanitorBot { return this.cmdRegistry; } - public Translations getTranslations() { + public TranslationMap getTranslations() { return this.translations; } @@ -105,7 +108,7 @@ public class JanitorBot { console.stop(); } - public Substitutions getSubstitutions() { + public SubstitutionMap getSubstitutions() { return substitutions; } } diff --git a/src/main/java/sciwhiz12/janitor/Logging.java b/src/main/java/sciwhiz12/janitor/Logging.java index dc38a81..90a570e 100644 --- a/src/main/java/sciwhiz12/janitor/Logging.java +++ b/src/main/java/sciwhiz12/janitor/Logging.java @@ -9,6 +9,7 @@ public class Logging { public static final Marker STATUS = MarkerFactory.getMarker("STATUS"); public static final Marker COMMANDS = MarkerFactory.getMarker("COMMANDS"); public static final Marker TRANSLATIONS = MarkerFactory.getMarker("TRANSLATIONS"); + public static final Marker MESSAGES = MarkerFactory.getMarker("MESSAGES"); public static final Marker STORAGE = MarkerFactory.getMarker("STORAGE"); public static final Logger JANITOR = LoggerFactory.getLogger("janitor"); diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java index 322210d..4c624db 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.util.ModerationHelper; +import sciwhiz12.janitor.msg.MessageHelper; import java.util.EnumSet; import java.util.List; @@ -60,45 +61,77 @@ public class BanCommand extends BaseCommand { ); } - public int run(CommandContext ctx, int days, @Nullable String reason) throws CommandSyntaxException { - realRun(ctx, days, reason); - return 1; - } - - void realRun(CommandContext ctx, int days, @Nullable String reason) throws CommandSyntaxException { + int run(CommandContext ctx, int days, @Nullable String reason) throws CommandSyntaxException { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); - return; + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + + return 1; } final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final List members = getMembers("member", ctx).fromGuild(performer.getGuild()); - if (members.size() < 1) return; + if (members.size() < 1) { return 1; } final Member target = members.get(0); - if (guild.getSelfMember().equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); - else if (performer.equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue(); - else if (!guild.getSelfMember().hasPermission(BAN_PERMISSION)) - channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, BAN_PERMISSION).build(getBot())).queue(); - else if (!guild.getSelfMember().canInteract(target)) - channel.sendMessage(messages().GENERAL.cannotInteract(performer, target).build(getBot())).queue(); - else if (!performer.hasPermission(BAN_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, BAN_PERMISSION).build(getBot())) - .queue(); - else if (!performer.canInteract(target)) - channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue(); - else + if (guild.getSelfMember().equals(target)) { + messages().getRegularMessage("general/error/cannot_action_self") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (performer.equals(target)) { + messages().getRegularMessage("general/error/cannot_action_performer") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (!guild.getSelfMember().hasPermission(BAN_PERMISSION)) { + messages().getRegularMessage("general/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", BAN_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else if (!guild.getSelfMember().canInteract(target)) { + messages().getRegularMessage("general/error/cannot_interact") + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + } else if (!performer.hasPermission(BAN_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", BAN_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else if (!performer.canInteract(target)) { + messages().getRegularMessage("moderation/error/cannot_interact") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + } else { target.getUser().openPrivateChannel() - .flatMap(dm -> dm.sendMessage(messages().MODERATION.bannedDM(performer, target, reason).build(getBot()))) + .flatMap(dm -> messages().getRegularMessage("moderation/ban/dm") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .with("reason", () -> reason) + .send(getBot(), dm) + ) .mapToResult() - .flatMap(res -> ModerationHelper.banUser(target.getGuild(), performer, target, days, reason) - .flatMap(v -> channel.sendMessage( - messages().MODERATION.banUser(performer, target, reason, days, res.isSuccess()).build(getBot())))) + .flatMap(res -> + ModerationHelper.banUser(target.getGuild(), performer, target, days, reason) + .flatMap(v -> messages().getRegularMessage("moderation/ban/info") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .with("private_message", () -> res.isSuccess() ? "✅" : "❌") + .with("delete_duration", () -> String.valueOf(days)) + .with("reason", () -> reason) + .send(getBot(), channel) + ) + ) .queue(); + } + return 1; } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java index 91e3e05..71d392b 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java @@ -12,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.util.CommandHelper; import sciwhiz12.janitor.commands.util.ModerationHelper; +import sciwhiz12.janitor.msg.MessageHelper; import java.util.EnumSet; import java.util.List; @@ -50,40 +51,72 @@ public class KickCommand extends BaseCommand { private int runWithReason(CommandContext ctx, @Nullable String reason) throws CommandSyntaxException { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return 1; } final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); + final List members = getMembers("member", ctx).fromGuild(performer.getGuild()); - if (members.size() < 1) { - return 1; - } + if (members.size() < 1) { return 1; } final Member target = members.get(0); - if (guild.getSelfMember().equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); - else if (performer.equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); - else if (!guild.getSelfMember().hasPermission(KICK_PERMISSION)) - channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, KICK_PERMISSION).build(getBot())).queue(); - else if (!guild.getSelfMember().canInteract(target)) - channel.sendMessage(messages().GENERAL.cannotInteract(performer, target).build(getBot())).queue(); - else if (!performer.hasPermission(KICK_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, KICK_PERMISSION).build(getBot())) - .queue(); - else if (!performer.canInteract(target)) - channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue(); - else + + if (guild.getSelfMember().equals(target)) { + messages().getRegularMessage("general/error/cannot_action_self") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (performer.equals(target)) { + messages().getRegularMessage("general/error/cannot_action_performer") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (!guild.getSelfMember().hasPermission(KICK_PERMISSION)) { + messages().getRegularMessage("general/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", KICK_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else if (!guild.getSelfMember().canInteract(target)) { + messages().getRegularMessage("general/error/cannot_interact") + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + } else if (!performer.hasPermission(KICK_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", KICK_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else if (!performer.canInteract(target)) { + messages().getRegularMessage("moderation/error/cannot_interact") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + } else { target.getUser().openPrivateChannel() - .flatMap(dm -> dm.sendMessage(messages().MODERATION.kickedDM(performer, target, reason).build(getBot()))) + .flatMap(dm -> messages().getRegularMessage("moderation/kick/dm") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .with("reason", () -> reason) + .send(getBot(), dm) + ) .mapToResult() .flatMap(res -> ModerationHelper.kickUser(target.getGuild(), performer, target, reason) - .flatMap(v -> channel.sendMessage( - messages().MODERATION.kickUser(performer, target, reason, res.isSuccess()).build(getBot())) + .flatMap(v -> messages().getRegularMessage("moderation/kick/info") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .with("private_message", () -> res.isSuccess() ? "✅" : "❌") + .with("reason", () -> reason) + .send(getBot(), channel) ) ) .queue(); + } return 1; } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java index 0e4c13c..8a0b9c2 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java @@ -1,6 +1,5 @@ package sciwhiz12.janitor.commands.moderation; -import com.google.common.collect.ImmutableMap; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -13,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.notes.NoteEntry; import sciwhiz12.janitor.moderation.notes.NoteStorage; +import sciwhiz12.janitor.msg.MessageHelper; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -92,7 +92,10 @@ public class NoteCommand extends BaseCommand { private int addNote(CommandContext ctx, String noteContents) throws CommandSyntaxException { final MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return 1; } final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); @@ -102,24 +105,41 @@ public class NoteCommand extends BaseCommand { final Member target = members.get(0); final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC); - if (guild.getSelfMember().equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); - else if (performer.equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue(); - else if (!performer.hasPermission(NOTE_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) - .queue(); - else { + if (guild.getSelfMember().equals(target)) { + messages().getRegularMessage("general/error/cannot_action_self") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (performer.equals(target)) { + messages().getRegularMessage("general/error/cannot_action_performer") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (!performer.hasPermission(NOTE_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", NOTE_PERMISSION::toString) + .send(getBot(), channel).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) { - channel.sendMessage(messages().MODERATION.ERRORS.maxAmountOfNotes(performer, target, maxAmount).build(getBot())) - .queue(); + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .with("notes_amount", () -> String.valueOf(maxAmount)) + .send(getBot(), channel).queue(); + } else { 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(); + + messages().getRegularMessage("moderation/note/add") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.noteEntry("note_entry", noteID, entry)) + .send(getBot(), channel).queue(); + } } return 1; @@ -133,7 +153,10 @@ public class NoteCommand extends BaseCommand { throws CommandSyntaxException { final MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return 1; } final Guild guild = ctx.getSource().getGuild(); @@ -145,7 +168,10 @@ public class NoteCommand extends BaseCommand { if (members.size() < 1) return 1; final Member target = members.get(0); if (guild.getSelfMember().equals(target)) { - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); + messages().getRegularMessage("general/error/cannot_interact") + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + return 1; } predicate = predicate.and(e -> e.getValue().getTarget().getIdLong() == target.getIdLong()); @@ -163,43 +189,62 @@ public class NoteCommand extends BaseCommand { case NONE: {} } - if (!performer.hasPermission(NOTE_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) - .queue(); - else - 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(); + if (!performer.hasPermission(NOTE_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", NOTE_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else { + // 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(); + messages().getRegularMessage("moderation/note/list") + .send(getBot(), channel).queue(); + // TODO: fix this + } return 1; } private int removeNote(CommandContext ctx, int noteID) { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return 1; } final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); - if (!performer.hasPermission(NOTE_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) - .queue(); - else { + if (!performer.hasPermission(NOTE_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", NOTE_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else { final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild); @Nullable final NoteEntry entry = storage.getNote(noteID); - if (entry == null) - channel.sendMessage(messages().MODERATION.ERRORS.noNoteFound(performer, noteID).build(getBot())).queue(); - else { + if (entry == null) { + messages().getRegularMessage("moderation/note/add") + .apply(MessageHelper.member("performer", performer)) + .with("note_id", () -> String.valueOf(noteID)) + .send(getBot(), channel).queue(); + + } else { storage.removeNote(noteID); - channel.sendMessage(messages().MODERATION.removeNote(performer, noteID, entry).build(getBot())).queue(); + + messages().getRegularMessage("moderation/note/remove") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.noteEntry("note_entry", noteID, entry)) + .send(getBot(), channel).queue(); } } return 1; diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java index 3dcee75..7cdcfa8 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java @@ -12,6 +12,7 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.util.ModerationHelper; +import sciwhiz12.janitor.msg.MessageHelper; import java.util.EnumSet; import java.util.Locale; @@ -49,7 +50,10 @@ public class UnbanCommand extends BaseCommand { void realNamedRun(CommandContext ctx) { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return; } final Guild guild = ctx.getSource().getGuild(); @@ -62,10 +66,14 @@ public class UnbanCommand extends BaseCommand { .startsWith(username)) .collect(Collectors.toList())) .queue(bans -> { - if (bans.size() > 1) - channel.sendMessage(messages().GENERAL.ambiguousMember(performer).build(getBot())).queue(); - else if (bans.size() == 1) + if (bans.size() > 1) { + messages().getRegularMessage("general/error/ambiguous_member") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + + } else if (bans.size() == 1) { tryUnban(channel, guild, performer, bans.get(0).getUser()); + } }); } @@ -77,7 +85,10 @@ public class UnbanCommand extends BaseCommand { void realIdRun(CommandContext ctx) { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return; } final Guild guild = ctx.getSource().getGuild(); @@ -97,16 +108,26 @@ public class UnbanCommand extends BaseCommand { } void tryUnban(MessageChannel channel, Guild guild, Member performer, User target) { - if (!guild.getSelfMember().hasPermission(UNBAN_PERMISSION)) - channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, UNBAN_PERMISSION).build(getBot())) - .queue(); - else if (!performer.hasPermission(UNBAN_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, UNBAN_PERMISSION).build(getBot())) - .queue(); - else + if (!guild.getSelfMember().hasPermission(UNBAN_PERMISSION)) { + messages().getRegularMessage("general/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", UNBAN_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else if (!performer.hasPermission(UNBAN_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", UNBAN_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else { ModerationHelper.unbanUser(guild, target) - .flatMap(v -> channel.sendMessage(messages().MODERATION.unbanUser(performer, target).build(getBot()))) + .flatMap(v -> messages().getRegularMessage("moderation/unban/info") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.user("target", target)) + .send(getBot(), channel) + ) .queue(); + } } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java index b03b1d0..0790e0b 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java @@ -12,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningStorage; +import sciwhiz12.janitor.msg.MessageHelper; import java.util.EnumSet; import java.util.Objects; @@ -44,36 +45,54 @@ public class UnwarnCommand extends BaseCommand { void realRun(CommandContext ctx) { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + return; } final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); int caseID = IntegerArgumentType.getInteger(ctx, "caseId"); - if (!performer.hasPermission(WARN_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) - .queue(); - else { + if (!performer.hasPermission(WARN_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", WARN_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else { final WarningStorage storage = WarningStorage.get(getBot().getStorage(), guild); @Nullable final WarningEntry entry = storage.getWarning(caseID); Member temp; - if (entry == null) - channel.sendMessage(messages().MODERATION.ERRORS.noWarnWithID(performer, caseID).build(getBot())).queue(); - else if (entry.getWarned().getIdLong() == performer.getIdLong() - && !config().WARNINGS_REMOVE_SELF_WARNINGS.get()) - 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)) - channel.sendMessage( - messages().MODERATION.ERRORS.cannotRemoveHigherModerated(performer, caseID, entry).build(getBot())).queue(); - else { + if (entry == null) { + messages().getRegularMessage("moderation/error/unwarn/no_case_found") + .apply(MessageHelper.member("performer", performer)) + .with("case_id", () -> String.valueOf(caseID)) + .send(getBot(), channel).queue(); + + } else if (entry.getWarned().getIdLong() == performer.getIdLong() + && !config().WARNINGS_REMOVE_SELF_WARNINGS.get()) { + messages().getRegularMessage("moderation/error/unwarn/cannot_unwarn_self") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.warningEntry("warning_entry", caseID, entry)) + .send(getBot(), channel).queue(); + + } else if (config().WARNINGS_RESPECT_MOD_ROLES.get() + && (temp = guild.getMember(entry.getPerformer())) != null && !performer.canInteract(temp)) { + messages().getRegularMessage("moderation/error/unwarn/cannot_remove_higher_mod") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.warningEntry("warning_entry", caseID, entry)) + .send(getBot(), channel).queue(); + + } else { storage.removeWarning(caseID); - channel.sendMessage(messages().MODERATION.unwarn(performer, caseID, entry).build(getBot())).queue(); + messages().getRegularMessage("moderation/unwarn/info") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.warningEntry("warning_entry", caseID, entry)) + .send(getBot(), channel).queue(); + } } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java index 3353d93..c0288a3 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java @@ -12,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningStorage; +import sciwhiz12.janitor.msg.MessageHelper; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -44,48 +45,70 @@ public class WarnCommand extends BaseCommand { ); } - public int run(CommandContext ctx, String reason) throws CommandSyntaxException { - realRun(ctx, reason); - return 1; - } - - void realRun(CommandContext ctx, String reason) throws CommandSyntaxException { + int run(CommandContext ctx, String reason) throws CommandSyntaxException { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); - return; + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + + return 1; } final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final List members = getMembers("member", ctx).fromGuild(performer.getGuild()); - if (members.size() < 1) return; + if (members.size() < 1) { return 1; } final Member target = members.get(0); final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC); - if (guild.getSelfMember().equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); - else if (performer.equals(target)) - channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue(); - else if (!performer.hasPermission(WARN_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) - .queue(); - else if (!performer.canInteract(target)) - channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue(); - else if (target.hasPermission(WARN_PERMISSION) && config().WARNINGS_PREVENT_WARNING_MODS.get()) - channel.sendMessage(messages().MODERATION.ERRORS.cannotWarnMods(performer, target).build(getBot())).queue(); - else + if (guild.getSelfMember().equals(target)) { + messages().getRegularMessage("general/error/cannot_action_self") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (performer.equals(target)) { + messages().getRegularMessage("general/error/cannot_action_performer") + .apply(MessageHelper.member("performer", performer)) + .send(getBot(), channel).queue(); + + } else if (!performer.hasPermission(WARN_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", WARN_PERMISSION::toString) + .send(getBot(), channel).queue(); + + } else if (!performer.canInteract(target)) { + messages().getRegularMessage("moderation/error/cannot_interact") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + } else if (target.hasPermission(WARN_PERMISSION) && config().WARNINGS_PREVENT_WARNING_MODS.get()) { + messages().getRegularMessage("moderation/error/warn/cannot_warn_mods") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + } else { + WarningEntry entry = new WarningEntry(target.getUser(), performer.getUser(), dateTime, reason); + int caseId = WarningStorage.get(getBot().getStorage(), guild).addWarning(entry); + target.getUser().openPrivateChannel() - .flatMap( - dm -> dm.sendMessage(messages().MODERATION.warnedDM(performer, target, reason, dateTime).build(getBot()))) + .flatMap(dm -> messages().getRegularMessage("moderation/warn/dm") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.warningEntry("warning_entry", caseId, entry)) + .send(getBot(), dm) + ) .mapToResult() - .flatMap(res -> { - 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())); - }) + .flatMap(res -> messages().getRegularMessage("moderation/warn/info") + .apply(MessageHelper.member("performer", performer)) + .apply(MessageHelper.warningEntry("warning_entry", caseId, entry)) + .with("private_message", () -> res.isSuccess() ? "✅" : "❌") + .send(getBot(), channel) + ) .queue(); + } + return 1; } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java index edc61ab..a30eaf8 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java @@ -1,6 +1,5 @@ package sciwhiz12.janitor.commands.moderation; -import com.google.common.collect.ImmutableMap; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -12,7 +11,7 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.warns.WarningEntry; -import sciwhiz12.janitor.moderation.warns.WarningStorage; +import sciwhiz12.janitor.msg.MessageHelper; import java.util.EnumSet; import java.util.List; @@ -53,18 +52,15 @@ public class WarnListCommand extends BaseCommand { .executes(ctx -> this.run(ctx, false, false)); } - public int run(CommandContext ctx, boolean filterTarget, boolean filterModerator) - throws CommandSyntaxException { - realRun(ctx, filterTarget, filterModerator); - return 1; - } - - void realRun(CommandContext ctx, boolean filterTarget, boolean filterModerator) + int run(CommandContext ctx, boolean filterTarget, boolean filterModerator) throws CommandSyntaxException { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); - return; + messages().getRegularMessage("general/error/guild_only_command") + .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .send(getBot(), channel).queue(); + + return 1; } final Guild guild = ctx.getSource().getGuild(); final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); @@ -72,33 +68,42 @@ public class WarnListCommand extends BaseCommand { if (filterTarget) { final List members = getMembers("target", ctx).fromGuild(performer.getGuild()); - if (members.size() < 1) return; + if (members.size() < 1) return 1; final Member target = members.get(0); if (guild.getSelfMember().equals(target)) { - channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); - return; + messages().getRegularMessage("general/error/cannot_interact") + .apply(MessageHelper.member("target", target)) + .send(getBot(), channel).queue(); + + return 1; } predicate = predicate.and(e -> e.getValue().getWarned().getIdLong() == target.getIdLong()); } if (filterModerator) { final List members = getMembers("moderator", ctx).fromGuild(performer.getGuild()); - if (members.size() < 1) return; + if (members.size() < 1) return 1; final Member mod = members.get(0); predicate = predicate.and(e -> e.getValue().getPerformer().getIdLong() == mod.getIdLong()); } - if (!performer.hasPermission(WARN_PERMISSION)) - channel.sendMessage( - messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) - .queue(); - else - 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(); + if (!performer.hasPermission(WARN_PERMISSION)) { + messages().getRegularMessage("moderation/error/insufficient_permissions") + .apply(MessageHelper.member("performer", performer)) + .with("required_permissions", WARN_PERMISSION::toString) + .send(getBot(), channel).queue(); + } else { + // 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(); + messages().getRegularMessage("moderation/warn/list") + .send(getBot(), channel).queue(); + // TODO: fix this + } + return 1; } } diff --git a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java index 0895ece..069d503 100644 --- a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java +++ b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java @@ -7,9 +7,9 @@ import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; import java.time.Instant; import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; import javax.annotation.Nullable; +import static sciwhiz12.janitor.msg.MessageHelper.DATE_TIME_FORMAT; import static sciwhiz12.janitor.utils.Util.nameFor; public class ModerationHelper { @@ -18,7 +18,7 @@ public class ModerationHelper { auditReason.append("Kicked by ") .append(nameFor(performer.getUser())) .append(" on ") - .append(Instant.now().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME)); + .append(Instant.now().atOffset(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); if (reason != null) auditReason.append(" for reason: ").append(reason); return guild.kick(target, auditReason.toString()); @@ -30,7 +30,7 @@ public class ModerationHelper { auditReason.append("Banned by ") .append(nameFor(performer.getUser())) .append(" on ") - .append(Instant.now().atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME)); + .append(Instant.now().atOffset(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); if (reason != null) auditReason.append(" for reason: ").append(reason); return guild.ban(target, deleteDuration, auditReason.toString()); diff --git a/src/main/java/sciwhiz12/janitor/config/BotConfig.java b/src/main/java/sciwhiz12/janitor/config/BotConfig.java index 4d5a487..6dc0fb7 100644 --- a/src/main/java/sciwhiz12/janitor/config/BotConfig.java +++ b/src/main/java/sciwhiz12/janitor/config/BotConfig.java @@ -23,6 +23,7 @@ public class BotConfig { public final CommentedConfigSpec.IntValue AUTOSAVE_INTERVAL; public final CommentedConfigSpec.ConfigValue CUSTOM_TRANSLATION_FILE; + public final CommentedConfigSpec.ConfigValue CUSTOM_MESSAGES_DIRECTORY; public final CommentedConfigSpec.ConfigValue COMMAND_PREFIX; @@ -67,6 +68,10 @@ public class BotConfig { .comment("A file which contains custom translation keys to load for messages.", "If blank, no file shall be loaded.") .define("messages.custom_translations", ""); + CUSTOM_MESSAGES_DIRECTORY = builder + .comment("A folder containing custom messages, with a 'messages.json' key file.", + "If blank, no folder shall be loaded and defaults will be used.") + .define("messages.custom_messages", ""); COMMAND_PREFIX = builder .comment("The prefix for commands.") @@ -115,8 +120,7 @@ public class BotConfig { spec.setConfig(config); // TODO: config spec FileWatcher.defaultInstance().addWatch(configPath, this::onFileChange); - } - catch (IOException ex) { + } catch (IOException ex) { JANITOR.error("Error while building config from file {}", configPath, ex); } } @@ -134,6 +138,15 @@ public class BotConfig { .orElse(null); } + @Nullable + public Path getMessagesFolder() { + return options.getMessagesFolder(). + or(() -> CUSTOM_MESSAGES_DIRECTORY.get().isBlank() ? + Optional.empty() : + Optional.of(Path.of(CUSTOM_MESSAGES_DIRECTORY.get()))) + .orElse(null); + } + public String getToken() { return options.getToken().orElse(CLIENT_TOKEN.get()); } @@ -157,8 +170,7 @@ public class BotConfig { CONFIG.info("Reloading config due to file change {}", configPath); config.load(); spec.setConfig(config); - } - catch (Exception ex) { + } catch (Exception ex) { CONFIG.error("Error while reloading config from {}", configPath, ex); } } diff --git a/src/main/java/sciwhiz12/janitor/config/BotOptions.java b/src/main/java/sciwhiz12/janitor/config/BotOptions.java index a61fb0c..6771b88 100644 --- a/src/main/java/sciwhiz12/janitor/config/BotOptions.java +++ b/src/main/java/sciwhiz12/janitor/config/BotOptions.java @@ -4,6 +4,7 @@ import joptsimple.ArgumentAcceptingOptionSpec; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.util.PathConverter; +import joptsimple.util.PathProperties; import java.nio.file.Path; import java.util.Optional; @@ -14,6 +15,7 @@ public class BotOptions { private final OptionSet options; private final ArgumentAcceptingOptionSpec configPath; private final ArgumentAcceptingOptionSpec translationsPath; + private final ArgumentAcceptingOptionSpec messagesFolder; private final ArgumentAcceptingOptionSpec token; private final ArgumentAcceptingOptionSpec prefix; private final ArgumentAcceptingOptionSpec owner; @@ -28,6 +30,10 @@ public class BotOptions { .accepts("translations", "The path to the translations file") .withRequiredArg() .withValuesConvertedBy(new PathConverter(FILE_EXISTING, READABLE)); + this.messagesFolder = parser + .accepts("translations", "The path to the custom messages folder") + .withRequiredArg() + .withValuesConvertedBy(new PathConverter(DIRECTORY_EXISTING, READABLE)); this.token = parser .accepts("token", "The Discord token for the bot user") .withRequiredArg(); @@ -49,6 +55,10 @@ public class BotOptions { return translationsPath.valueOptional(options); } + public Optional getMessagesFolder() { + return messagesFolder.valueOptional(options); + } + public Optional getToken() { return token.valueOptional(options); } diff --git a/src/main/java/sciwhiz12/janitor/msg/General.java b/src/main/java/sciwhiz12/janitor/msg/General.java deleted file mode 100644 index 9ab322b..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/General.java +++ /dev/null @@ -1,75 +0,0 @@ -package sciwhiz12.janitor.msg; - -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.User; - -import java.util.EnumSet; -import java.util.stream.Collectors; - -public final class General { - private final Messages messages; - - General(Messages messages) { - this.messages = messages; - } - - 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 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 MessageBuilder insufficientPermissions(final Member performer, final EnumSet 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 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 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 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); - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java b/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java new file mode 100644 index 0000000..ab96799 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java @@ -0,0 +1,110 @@ +package sciwhiz12.janitor.msg; + +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.moderation.notes.NoteEntry; +import sciwhiz12.janitor.moderation.warns.WarningEntry; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.util.function.Consumer; + +import static java.time.temporal.ChronoField.*; + +public class MessageHelper { + private MessageHelper() {} + + public static Consumer snowflake(String head, ISnowflake snowflake) { + return builder -> builder + .with(head + ".id", snowflake::getId) + .with(head + ".creation_datetime", () -> snowflake.getTimeCreated().format(DATE_TIME_FORMAT)); + } + + public static Consumer mentionable(String head, IMentionable mentionable) { + return builder -> builder + .apply(snowflake(head, mentionable)) + .with(head + ".mention", mentionable::getAsMention); + } + + public static Consumer role(String head, Role role) { + return builder -> builder + .apply(mentionable(head, role)) + .with(head + ".color_hex", () -> Integer.toHexString(role.getColorRaw())) + .with(head + ".name", role::getName) + .with(head + ".permissions", role.getPermissions()::toString); + } + + public static Consumer user(String head, User user) { + return builder -> builder + .apply(mentionable(head, user)) + .with(head + ".name", user::getName) + .with(head + ".discriminator", user::getDiscriminator) + .with(head + ".tag", user::getAsTag) + .with(head + ".flags", user.getFlags()::toString); + } + + public static Consumer guild(String head, Guild guild) { + return builder -> builder + .apply(snowflake(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) + .with(head + ".icon_url", guild::getIconUrl); + } + + public static Consumer member(String head, Member member) { + return builder -> builder + .apply(user(head, member.getUser())) + .apply(guild(head + ".guild", member.getGuild())) + .with(head + ".nickname", member::getNickname) + .with(head + ".effective_name", member::getEffectiveName) + .with(head + ".join_datetime", () -> member.getTimeJoined().format(DATE_TIME_FORMAT)) + .with(head + ".color", () -> String.valueOf(member.getColorRaw())); + } + + public static Consumer warningEntry(String head, int caseID, WarningEntry entry) { + return builder -> builder + .with(head + ".case_id", () -> String.valueOf(caseID)) + .apply(user(head + ".performer", entry.getPerformer())) + .apply(user(head + ".target", entry.getWarned())) + .with(head + ".date_time", () -> entry.getDateTime().format(DATE_TIME_FORMAT)) + .with(head + ".reason", entry::getReason); + } + + public static Consumer noteEntry(String head, int noteID, NoteEntry entry) { + return builder -> builder + .with(head + ".note_id", () -> String.valueOf(noteID)) + .apply(user(head + ".performer", entry.getPerformer())) + .apply(user(head + ".target", entry.getTarget())) + .with(head + ".date_time", () -> entry.getDateTime().format(DATE_TIME_FORMAT)) + .with(head + ".contents", entry::getContents); + } + + public static final DateTimeFormatter DATE_TIME_FORMAT = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .parseLenient() + .appendValue(YEAR, 4) // 2 digit year not handled + .appendLiteral('-') + .appendValue(MONTH_OF_YEAR, 2) + .appendLiteral('-') + .appendValue(DAY_OF_MONTH, 2) + .appendLiteral(' ') + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .optionalStart() + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .optionalEnd() + .appendLiteral(' ') + .appendOffset("+HHMM", "GMT") + .toFormatter(); +} diff --git a/src/main/java/sciwhiz12/janitor/msg/Messages.java b/src/main/java/sciwhiz12/janitor/msg/Messages.java index 03b4f53..114e60b 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Messages.java +++ b/src/main/java/sciwhiz12/janitor/msg/Messages.java @@ -1,97 +1,119 @@ package sciwhiz12.janitor.msg; -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 com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import net.dv8tion.jda.api.entities.MessageEmbed; import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.msg.json.RegularMessage; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; +import static com.google.common.io.Resources.getResource; +import static sciwhiz12.janitor.Logging.JANITOR; +import static sciwhiz12.janitor.Logging.MESSAGES; public class Messages { - public static final int FAILURE_COLOR = 0xF73132; + public static final String JSON_FILE_SUFFIX = ".json"; + public static final String MESSAGES_FILENAME = "messages"; + public static final String DEFAULT_MESSAGES_FOLDER = "messages/"; + public static final TypeReference> LIST_TYPE = new TypeReference<>() {}; private final JanitorBot bot; - public final General GENERAL; - public final Moderation MODERATION; + private final Path messagesFolder; + private final Map regularMessages = new HashMap<>(); + private final ObjectMapper jsonMapper = new ObjectMapper(); - public Messages(JanitorBot bot) { + public Messages(JanitorBot bot, Path messagesFolder) { this.bot = bot; - this.GENERAL = new General(this); - this.MODERATION = new Moderation(this); + this.messagesFolder = messagesFolder; + loadMessages(); } - public JanitorBot getBot() { - return bot; + public void loadMessages() { + boolean success = false; + + if (messagesFolder != null) { + JANITOR.debug(MESSAGES, "Loading messages from folder {}", messagesFolder); + success = loadMessages( + path -> Files.newBufferedReader(messagesFolder.resolve(path + JSON_FILE_SUFFIX)) + ); + } else { + JANITOR.info(MESSAGES, "No custom messages folder specified"); + } + + if (!success) { + JANITOR.info(MESSAGES, "Loading default messages"); + //noinspection UnstableApiUsage + loadMessages( + file -> new InputStreamReader(getResource(DEFAULT_MESSAGES_FOLDER + file + JSON_FILE_SUFFIX).openStream()) + ); + } } - public MessageBuilder message() { - final MessageBuilder builder = new MessageBuilder(); - builder.embed() - .setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); - return builder; + boolean loadMessages(FileOpener files) { + try (Reader keyReader = files.open(MESSAGES_FILENAME)) { + List keysList = jsonMapper.readValue(keyReader, LIST_TYPE); + regularMessages.clear(); + for (String messageKey : keysList) { + final String path = messageKey.replace("/", FileSystems.getDefault().getSeparator()); + try (Reader reader = files.open(path)) { + final JsonNode tree = jsonMapper.readTree(reader); + if ("regular".equals(tree.path("type").asText("regular"))) { + regularMessages.put(messageKey, jsonMapper.convertValue(tree, RegularMessage.class)); + } else { + JANITOR.warn(MESSAGES, "Unknown message type {} for {}", tree.path("type").asText(), messageKey); + } + } catch (Exception e) { + JANITOR.error(MESSAGES, "Error while loading message {}", path, e); + } + } + JANITOR.info(MESSAGES, "Loaded {} messages", regularMessages.size()); + return true; + } catch (Exception e) { + JANITOR.error(MESSAGES, "Error while loading messages", e); + return false; + } } - public MessageBuilder failure() { - final MessageBuilder builder = message(); - builder.embed() - .setColor(FAILURE_COLOR); - return builder; + public Map getRegularMessages() { + return Collections.unmodifiableMap(regularMessages); } - 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 RegularMessageBuilder getRegularMessage(String messageKey) { + final RegularMessage msg = regularMessages.get(messageKey); + if (msg == null) { + JANITOR.warn(MESSAGES, "Attempted to get unknown message with key {}", messageKey); + return new RegularMessageBuilder(UNKNOWN_MESSAGE).with("key", () -> messageKey); + } + return new RegularMessageBuilder(msg); } - public MessageBuilder mentionable(MessageBuilder builder, String head, IMentionable mentionable) { - return builder - .apply(b -> snowflake(b, head, mentionable)) - .with(head + ".mention", mentionable::getAsMention); + interface FileOpener { + Reader open(String filePath) throws IOException; } - 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())); - } + public static final RegularMessage UNKNOWN_MESSAGE = new RegularMessage( + "UNKNOWN MESSAGE!", + null, + "A message was tried to be looked up, but was not found. Please report this to your bot maintainer/administrator.", + String.valueOf(0xFF0000), + null, + null, + null, + null, + null, + null, + null, + Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)) + ); } diff --git a/src/main/java/sciwhiz12/janitor/msg/Moderation.java b/src/main/java/sciwhiz12/janitor/msg/Moderation.java deleted file mode 100644 index b4ea21d..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/Moderation.java +++ /dev/null @@ -1,333 +0,0 @@ -package sciwhiz12.janitor.msg; - -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.User; -import sciwhiz12.janitor.moderation.notes.NoteEntry; -import sciwhiz12.janitor.moderation.warns.WarningEntry; - -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.EnumSet; -import java.util.Map; -import java.util.stream.Collectors; -import javax.annotation.Nullable; - -import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; - -public final class Moderation { - public static final int MODERATION_COLOR = 0xF1BD25; - public static final String GAVEL_ICON_URL = "https://cdn.discordapp.com/attachments/738478941760782526" + - "/760463743330549760/gavel.png"; - - private final Messages messages; - public final Errors ERRORS; - - Moderation(Messages messages) { - this.messages = messages; - ERRORS = new Errors(); - } - - public MessageBuilder moderation() { - return messages.message() - .embed(embed -> embed - .setColor(MODERATION_COLOR) - .setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)) - ); - } - - public MessageBuilder moderation(String author) { - return moderation() - .embed(embed -> embed.setAuthor(author, null, GAVEL_ICON_URL)); - } - - public class Errors { - private Errors() {} - - public MessageBuilder performerInsufficientPermissions(final Member performer, final EnumSet 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") - ) - .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 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 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 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); - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/Translations.java b/src/main/java/sciwhiz12/janitor/msg/TranslationMap.java similarity index 78% rename from src/main/java/sciwhiz12/janitor/msg/Translations.java rename to src/main/java/sciwhiz12/janitor/msg/TranslationMap.java index 0101dd3..d89790d 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Translations.java +++ b/src/main/java/sciwhiz12/janitor/msg/TranslationMap.java @@ -11,11 +11,16 @@ import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; +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; import static sciwhiz12.janitor.Logging.JANITOR; import static sciwhiz12.janitor.Logging.TRANSLATIONS; -public class Translations { +public class TranslationMap { + public static final Pattern TRANSLATION_REGEX = Pattern.compile("<(.+?)>", CASE_INSENSITIVE); private static final String DEFAULT_TRANSLATIONS_RESOURCE = "english.json"; private static final TypeReference> MAP_TYPE = new TypeReference<>() {}; @@ -24,7 +29,7 @@ public class Translations { private final Map translations = new HashMap<>(); private final ObjectMapper jsonMapper = new ObjectMapper(); - public Translations(JanitorBot bot, Path translationsFile) { + public TranslationMap(JanitorBot bot, Path translationsFile) { this.bot = bot; this.translationsFile = translationsFile; loadTranslations(); @@ -42,8 +47,7 @@ public class Translations { translations.clear(); translations.putAll(trans); JANITOR.info(TRANSLATIONS, "Loaded {} translations from file {}", translations.size(), translationsFile); - } - catch (Exception e) { + } catch (Exception e) { JANITOR.error(TRANSLATIONS, "Error while loading translations from file {}", translationsFile, e); loadDefaultTranslations(); } @@ -59,8 +63,7 @@ public class Translations { translations.clear(); translations.putAll(trans); JANITOR.info(TRANSLATIONS, "Loaded {} default english translations", translations.size()); - } - catch (Exception e) { + } catch (Exception e) { JANITOR.error(TRANSLATIONS, "Error while loading default english translations", e); } } @@ -69,7 +72,9 @@ public class Translations { return Collections.unmodifiableMap(translations); } - public String translate(String key, Object... args) { - return String.format(translations.getOrDefault(key, key), args); + public String translate(String text) { + final Matcher matcher = TRANSLATION_REGEX.matcher(text); + return matcher.replaceAll( + matchResult -> quoteReplacement(translations.getOrDefault(matchResult.group(1), matchResult.group(0)))); } } diff --git a/src/main/java/sciwhiz12/janitor/msg/json/IMessage.java b/src/main/java/sciwhiz12/janitor/msg/json/IMessage.java new file mode 100644 index 0000000..97535d5 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/json/IMessage.java @@ -0,0 +1,9 @@ +package sciwhiz12.janitor.msg.json; + +import net.dv8tion.jda.api.EmbedBuilder; +import sciwhiz12.janitor.msg.substitution.ISubstitutor; +import sciwhiz12.janitor.msg.TranslationMap; + +public interface IMessage { + EmbedBuilder create(TranslationMap translations, ISubstitutor substitutions); +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java b/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java new file mode 100644 index 0000000..82c39c6 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java @@ -0,0 +1,61 @@ +package sciwhiz12.janitor.msg.json; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import net.dv8tion.jda.api.entities.MessageEmbed; + +import java.time.OffsetDateTime; + +public class ListingMessage { + protected final String url; + protected final String title; + protected final String description; + protected final OffsetDateTime timestamp; + protected final int color; + protected final MessageEmbed.Thumbnail thumbnail; + protected final MessageEmbed.AuthorInfo author; + protected final MessageEmbed.Footer footer; + protected final MessageEmbed.ImageInfo image; + protected final Multimap fields; + + @Deprecated + public ListingMessage() { + this(null, null, null, null, 0, null, null, null, null, null); + } + + public ListingMessage(MessageEmbed embed) { + this(embed.getUrl(), + embed.getTitle(), + embed.getDescription(), + embed.getTimestamp(), + embed.getColorRaw(), + embed.getThumbnail(), + embed.getAuthor(), + embed.getFooter(), + embed.getImage(), + Multimaps.index(embed.getFields(), k -> FieldPlacement.BEFORE)); + } + + public ListingMessage(String url, String title, String description, OffsetDateTime timestamp, int color, + MessageEmbed.Thumbnail thumbnail, MessageEmbed.AuthorInfo author, MessageEmbed.Footer footer, + MessageEmbed.ImageInfo image, Multimap fields) { + this.url = url; + this.title = title; + this.description = description; + this.timestamp = timestamp; + this.color = color; + this.thumbnail = thumbnail; + this.author = author; + this.footer = footer; + this.image = image; + this.fields = fields; + } + + public enum ListingType { + DESCRIPTION, FIELDS + } + + public enum FieldPlacement { + BEFORE, AFTER; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java b/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java new file mode 100644 index 0000000..873b015 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java @@ -0,0 +1,213 @@ +package sciwhiz12.janitor.msg.json; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.primitives.Ints; +import joptsimple.internal.Strings; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import sciwhiz12.janitor.msg.substitution.ISubstitutor; +import sciwhiz12.janitor.msg.TranslationMap; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; +import java.util.function.Function; +import javax.annotation.Nullable; + +@JsonDeserialize(using = RegularMessageDeserializer.class) +public class RegularMessage implements IMessage { + @Nullable + protected final String title; + @Nullable + protected final String url; + @Nullable + protected final String description; + @Nullable + protected final String color; + @Nullable + protected final String authorName; + @Nullable + protected final String authorUrl; + @Nullable + protected final String authorIconUrl; + @Nullable + protected final String footerText; + @Nullable + protected final String footerIconUrl; + @Nullable + protected final String imageUrl; + @Nullable + protected final String thumbnailUrl; + protected final List fields; + + public RegularMessage( + @Nullable String title, + @Nullable String url, + @Nullable String description, + @Nullable String color, + @Nullable String authorName, + @Nullable String authorUrl, + @Nullable String authorIconUrl, + @Nullable String footerText, + @Nullable String footerIconUrl, + @Nullable String imageUrl, + @Nullable String thumbnailUrl, + List fields + ) { + this.title = title; + this.url = url; + this.description = description; + this.color = color; + this.authorName = authorName; + this.authorUrl = authorUrl; + this.authorIconUrl = authorIconUrl; + this.footerText = footerText; + this.footerIconUrl = footerIconUrl; + this.imageUrl = imageUrl; + this.thumbnailUrl = thumbnailUrl; + this.fields = new ArrayList<>(fields); + } + + @Nullable + public String getTitle() { + return title; + } + + @Nullable + public String getUrl() { + return url; + } + + @Nullable + public String getDescription() { + return description; + } + + @Nullable + public String getColor() { + return color; + } + + @Nullable + public String getAuthorName() { + return authorName; + } + + @Nullable + public String getAuthorUrl() { + return authorUrl; + } + + @Nullable + public String getAuthorIconUrl() { + return authorIconUrl; + } + + @Nullable + public String getFooterText() { + return footerText; + } + + @Nullable + public String getFooterIconUrl() { + return footerIconUrl; + } + + @Nullable + public String getImageUrl() { + return imageUrl; + } + + @Nullable + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public List getFields() { + return Collections.unmodifiableList(fields); + } + + @Override + public String toString() { + return new StringJoiner(", ", RegularMessage.class.getSimpleName() + "[", "]") + .add("title='" + title + "'") + .add("url='" + url + "'") + .add("description='" + description + "'") + .add("color='" + color + "'") + .add("authorName='" + authorName + "'") + .add("authorUrl='" + authorUrl + "'") + .add("authorIconUrl='" + authorIconUrl + "'") + .add("footerText='" + footerText + "'") + .add("footerIconUrl='" + footerIconUrl + "'") + .add("imageUrl='" + imageUrl + "'") + .add("thumbnailUrl='" + thumbnailUrl + "'") + .add("fields=" + fields) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RegularMessage that = (RegularMessage) o; + return Objects.equals(title, that.title) && + Objects.equals(url, that.url) && + Objects.equals(description, that.description) && + Objects.equals(color, that.color) && + Objects.equals(authorName, that.authorName) && + Objects.equals(authorUrl, that.authorUrl) && + Objects.equals(authorIconUrl, that.authorIconUrl) && + Objects.equals(footerText, that.footerText) && + Objects.equals(footerIconUrl, that.footerIconUrl) && + Objects.equals(imageUrl, that.imageUrl) && + Objects.equals(thumbnailUrl, that.thumbnailUrl) && + fields.equals(that.fields); + } + + @Override + public int hashCode() { + return Objects + .hash(title, url, description, color, authorName, authorUrl, authorIconUrl, footerText, footerIconUrl, imageUrl, + thumbnailUrl, fields); + } + + @Override + public EmbedBuilder create(TranslationMap translations, ISubstitutor substitutions) { + final Function func = str -> str != null ? substitutions.substitute(translations.translate(str)) : null; + final EmbedBuilder builder = new EmbedBuilder(); + builder.setTitle(func.apply(title), func.apply(url)); + builder.setColor(parseColor(substitutions.substitute(color))); + builder.setAuthor(func.apply(authorName), func.apply(authorUrl), func.apply(authorIconUrl)); + builder.setDescription(func.apply(description)); + builder.setImage(func.apply(imageUrl)); + builder.setThumbnail(func.apply(thumbnailUrl)); + builder.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); + builder.setFooter(func.apply(footerText), func.apply(footerIconUrl)); + for (MessageEmbed.Field field : fields) { + builder.addField(func.apply(field.getName()), func.apply(field.getValue()), field.isInline()); + } + return builder; + } + + private static int parseColor(String str) { + if (Strings.isNullOrEmpty(str)) return Role.DEFAULT_COLOR_RAW; + if (str.startsWith("0x")) { + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str.substring(2), 16); + if (res != null) { + return res; + } + } + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str, 10); + if (res != null) { + return res; + } + return Role.DEFAULT_COLOR_RAW; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java b/src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java new file mode 100644 index 0000000..36857e3 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java @@ -0,0 +1,93 @@ +package sciwhiz12.janitor.msg.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import net.dv8tion.jda.api.entities.MessageEmbed; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; + +public class RegularMessageDeserializer extends StdDeserializer { + public RegularMessageDeserializer() { + super(RegularMessage.class); + } + + @Override + public RegularMessage deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + + final JsonNode node = ctx.readTree(p); + + String title = null; + String url = null; + String description = node.path("description").asText(null); + String color = node.path("color").asText(null); + String authorName = null; + String authorUrl = null; + String authorIconUrl = null; + String footerText = null; + String footerIconUrl = null; + String imageUrl = node.path("image").asText(null); + String thumbnailUrl = node.path("thumbnail").asText(null); + List fields = readFields(node); + + // Title + if (node.path("title").isTextual()) { + title = node.path("title").asText(); + } else if (node.path("title").path("text").isTextual()) { + title = node.path("title").path("text").asText(); + url = node.path("title").path("url").asText(null); + } + + // Author + if (node.path("author").isTextual()) { + authorName = node.path("author").asText(); + } else if (node.path("author").path("name").isTextual()) { + authorName = node.path("author").path("name").asText(); + authorUrl = node.path("author").path("url").asText(null); + authorIconUrl = node.path("author").path("icon_url").asText(null); + } + + // Footer + if (node.path("footer").isTextual()) { + footerText = node.path("footer").asText(); + } else if (node.path("footer").path("text").isTextual()) { + footerText = node.path("footer").path("text").asText(); + footerIconUrl = node.path("footer").path("icon_url").asText(null); + } + + return new RegularMessage(title, url, description, color, authorName, authorUrl, + authorIconUrl, footerText, footerIconUrl, imageUrl, thumbnailUrl, fields); + } + + public static List readFields(JsonNode node) { + if (node.path("fields").isArray()) { + final ArrayList fields = new ArrayList<>(); + for (int i = 0; i < node.path("fields").size(); i++) { + final MessageEmbed.Field field = readField(node.path("fields").path(i)); + if (field != null) { + fields.add(field); + } + } + return fields; + } + return Collections.emptyList(); + } + + @Nullable + public static MessageEmbed.Field readField(JsonNode fieldNode) { + if (fieldNode.path("name").isTextual() && fieldNode.path("value").isTextual()) { + return new MessageEmbed.Field( + fieldNode.path("name").asText(), + fieldNode.path("value").asText(), + fieldNode.path("inline").asBoolean(false) + ); + } + return null; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java b/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java new file mode 100644 index 0000000..bc45d6c --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java @@ -0,0 +1,21 @@ +package sciwhiz12.janitor.msg.substitution; + +import java.util.Map; +import java.util.function.Supplier; + +public class CustomSubstitutions implements ISubstitutor { + private final Map> map; + + public CustomSubstitutions(Map> map) { + this.map = map; + } + + @Override + public String substitute(String text) { + return SubstitutionMap.substitute(text, map); + } + + public Map> getMap() { + return map; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java b/src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java new file mode 100644 index 0000000..907b51d --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java @@ -0,0 +1,5 @@ +package sciwhiz12.janitor.msg.substitution; + +public interface ISubstitutor { + String substitute(String text); +} diff --git a/src/main/java/sciwhiz12/janitor/msg/Substitutions.java b/src/main/java/sciwhiz12/janitor/msg/substitution/SubstitutionMap.java similarity index 61% rename from src/main/java/sciwhiz12/janitor/msg/Substitutions.java rename to src/main/java/sciwhiz12/janitor/msg/substitution/SubstitutionMap.java index 75fa7eb..00c2d5d 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Substitutions.java +++ b/src/main/java/sciwhiz12/janitor/msg/substitution/SubstitutionMap.java @@ -1,9 +1,11 @@ -package sciwhiz12.janitor.msg; +package sciwhiz12.janitor.msg.substitution; import org.apache.commons.collections4.TransformerUtils; import org.apache.commons.collections4.map.DefaultedMap; import sciwhiz12.janitor.JanitorBot; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; @@ -12,8 +14,9 @@ import java.util.regex.Pattern; import static java.util.regex.Matcher.quoteReplacement; import static java.util.regex.Pattern.CASE_INSENSITIVE; +import static sciwhiz12.janitor.msg.MessageHelper.DATE_TIME_FORMAT; -public class Substitutions { +public class SubstitutionMap implements ISubstitutor { public static final Pattern ARGUMENT_REGEX = Pattern.compile("\\$\\{(.+?)}", CASE_INSENSITIVE); public static final Pattern NULL_ARGUMENT_REGEX = Pattern.compile("nullcheck;(.+?);(.+)", CASE_INSENSITIVE); @@ -22,7 +25,8 @@ public class Substitutions { 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(); + final String grp1 = nullMatcher.group(1); + final String str = arguments.containsKey(grp1) ? arguments.get(grp1).get() : null; return str != null ? quoteReplacement(str) : quoteReplacement(arguments.getOrDefault(nullMatcher.group(2), () -> nullMatcher.group(2)).get()); @@ -34,8 +38,13 @@ public class Substitutions { private final JanitorBot bot; private final Map> defaultSubstitutions = new HashMap<>(); - public Substitutions(JanitorBot bot) { + public SubstitutionMap(JanitorBot bot) { this.bot = bot; + defaultSubstitutions.put("time.now", () -> OffsetDateTime.now(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); + defaultSubstitutions.put("moderation.color", () -> "0xF1BD25"); + defaultSubstitutions.put("moderation.icon_url", + () -> "https://cdn.discordapp.com/attachments/738478941760782526/760463743330549760/gavel.png"); + defaultSubstitutions.put("general.error.color", () -> "0xF73132"); } public JanitorBot getBot() { @@ -43,17 +52,19 @@ public class Substitutions { } public String substitute(String text) { - return Substitutions.substitute(text, defaultSubstitutions); + return SubstitutionMap.substitute(text, defaultSubstitutions); } public String with(String text, Map> substitutions) { - return Substitutions.substitute( - text, - DefaultedMap.defaultedMap(substitutions, TransformerUtils.mapTransformer(defaultSubstitutions)) - ); + return SubstitutionMap.substitute(text, createDefaultedMap(substitutions)); + } + + public CustomSubstitutions with(Map> customSubstitutions) { + return new CustomSubstitutions(createDefaultedMap(customSubstitutions)); } public Map> createDefaultedMap(Map> custom) { return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(defaultSubstitutions)); } + } diff --git a/src/main/resources/english.json b/src/main/resources/english.json index abc3289..fef3c78 100644 --- a/src/main/resources/english.json +++ b/src/main/resources/english.json @@ -1,182 +1,118 @@ { - "general.guild_only_command.title": "Guild only command!", - "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.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.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.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.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}", + "general.error.guild_only_command.title": "Guild only command!", + "general.error.guild_only_command.description": "This command can only be run in a guild channel.", + "general.error.ambiguous_member.title": "Ambiguous member argument!", + "general.error.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.error.insufficient_permissions.title": "I have insufficient permissions!", + "general.error.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.error.insufficient_permissions.field.permissions": "Required permissions", + "general.error.cannot_interact.title": "Member is higher than me!", + "general.error.cannot_interact.description": "Cannot perform action on the given member, likely due to me being lower in the role hierarchy.", + "general.error.cannot_interact.field.target": "Target", + "general.error.cannot_action_self.title": "Cannot act against myself!", + "general.error.cannot_action_self.description": "Cannot perform action against myself, as that would be counter-intuitive.", + "general.error.cannot_action_performer.title": "Performer cannot act against self!", + "general.error.cannot_action_performer.description": "You cannot perform this action against yourself.", + "general.error.cannot_action_performer.field.performer": "Performer/Target", "moderation.insufficient_permissions.title": "Insufficient 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.insufficient_permissions.field.performer": "Performer", + "moderation.insufficient_permissions.field.permissions": "Required permissions", "moderation.cannot_interact.title": "Cannot moderate 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.cannot_interact.field.performer": "Performer", + "moderation.cannot_interact.field.target": "Target", "moderation.kick.info.author": "Kicked user from server.", - "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.performer": "Performer", + "moderation.kick.info.field.target": "Target", + "moderation.kick.info.field.private_message": "Sent DM", "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.name": "Moderator", - "moderation.kick.dm.field.performer.value": "${performer.mention}", + "moderation.kick.dm.field.performer": "Moderator", "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.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.performer": "Performer", + "moderation.ban.info.field.target": "Target", + "moderation.ban.info.field.private_message": "Sent DM", "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.info.field.reason.name": "Reason", + "moderation.ban.info.field.reason.value": "${nullcheck;reason;_No reason specified._}", "moderation.ban.dm.title": "You were banned from this server.", - "moderation.ban.dm.field.performer.name": "Moderator", - "moderation.ban.dm.field.performer.value": "${performer.mention}", + "moderation.ban.dm.field.performer": "Moderator", "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.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.unban.info.field.performer": "Performer", + "moderation.unban.info.field.target": "Target", "moderation.warn.info.author": "Warned user.", - "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.performer": "Performer", + "moderation.warn.info.field.target": "Target", + "moderation.warn.info.field.case_id": "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.info.field.private_message": "Sent DM", + "moderation.warn.info.field.date_time": "Date & Time", "moderation.warn.dm.title": "You were warned by a moderator.", - "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.performer": "Moderator", + "moderation.warn.dm.field.date_time": "Date & Time", "moderation.warn.dm.field.reason.name": "Reason", - "moderation.warn.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}", + "moderation.warn.dm.field.reason.value": "${nullcheck;warning_entry.reason;_No reason specified._}", + "moderation.unwarn.info.author": "Removed warning from user.", + "moderation.unwarn.info.field.performer": "Performer", + "moderation.unwarn.info.field.original_target": "Original Target", + "moderation.unwarn.info.field.original_performer": "Original Performer", + "moderation.unwarn.info.field.case_id": "Case ID", + "moderation.unwarn.info.field.date_time": "Date & Time", + "moderation.unwarn.info.field.reason.name": "Reason", + "moderation.unwarn.info.field.reason.value": "${nullcheck;warning_entry.reason;_No reason specified._}", + "moderation.unwarn.no_case_found.title": "No warning found.", + "moderation.unwarn.no_case_found.description": "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.cannot_unwarn_self.title": "Cannot remove warning from self.", + "moderation.unwarn.cannot_unwarn_self.description": "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.warn.cannot_warn_mods.title": "Cannot warn moderators.", + "moderation.warn.cannot_warn_mods.description": "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_remove_higher_mod.title": "Cannot remove warning issued by higher-ranked moderator.", + "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": "Performer", + "moderation.warn.cannot_remove_higher_mod.field.original_performer": "Original Performer", + "moderation.warn.cannot_remove_higher_mod.field.case_id": "Case ID", + "moderation.note.max_amount_of_notes.title": "Max notes reached.", + "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": "Performer", + "moderation.note.max_amount_of_notes.field.target": "Target", + "moderation.note.max_amount_of_notes.field.amount": "(Max.) Amount", + "moderation.note.no_note_found.title": "No note found.", + "moderation.note.no_note_found.description": "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.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.remove.author": "Removed note.", + "moderation.note.remove.field.performer": "Performer", + "moderation.note.remove.field.original_performer": "Original Performer", + "moderation.note.remove.field.original_target": "Original Target", + "moderation.note.remove.field.note_id": "Note ID", + "moderation.note.remove.field.date_time": "Date & Time", + "moderation.note.remove.field.contents": "Text", "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.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.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.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.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.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.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.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.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.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}" + "moderation.note.list.entry": "**#%1$s**: for %2$s by %3$s %n - _Date & Time:_ %4$s %n - _Text:_ %5$s" } \ No newline at end of file diff --git a/src/main/resources/messages/general/error/ambiguous_member.json b/src/main/resources/messages/general/error/ambiguous_member.json new file mode 100644 index 0000000..f078f45 --- /dev/null +++ b/src/main/resources/messages/general/error/ambiguous_member.json @@ -0,0 +1,5 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "" +} \ No newline at end of file diff --git a/src/main/resources/messages/general/error/cannot_action_performer.json b/src/main/resources/messages/general/error/cannot_action_performer.json new file mode 100644 index 0000000..8913518 --- /dev/null +++ b/src/main/resources/messages/general/error/cannot_action_performer.json @@ -0,0 +1,12 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/general/error/cannot_action_self.json b/src/main/resources/messages/general/error/cannot_action_self.json new file mode 100644 index 0000000..a43ec63 --- /dev/null +++ b/src/main/resources/messages/general/error/cannot_action_self.json @@ -0,0 +1,5 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "" +} \ No newline at end of file diff --git a/src/main/resources/messages/general/error/cannot_interact.json b/src/main/resources/messages/general/error/cannot_interact.json new file mode 100644 index 0000000..a4eea52 --- /dev/null +++ b/src/main/resources/messages/general/error/cannot_interact.json @@ -0,0 +1,12 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${target.mention}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/general/error/guild_only_command.json b/src/main/resources/messages/general/error/guild_only_command.json new file mode 100644 index 0000000..b46d90c --- /dev/null +++ b/src/main/resources/messages/general/error/guild_only_command.json @@ -0,0 +1,5 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "" +} \ No newline at end of file diff --git a/src/main/resources/messages/general/error/insufficient_permissions.json b/src/main/resources/messages/general/error/insufficient_permissions.json new file mode 100644 index 0000000..1d3f29c --- /dev/null +++ b/src/main/resources/messages/general/error/insufficient_permissions.json @@ -0,0 +1,12 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${required_permissions}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/messages.json b/src/main/resources/messages/messages.json new file mode 100644 index 0000000..5322c01 --- /dev/null +++ b/src/main/resources/messages/messages.json @@ -0,0 +1,26 @@ +[ + "general/error/ambiguous_member", + "general/error/guild_only_command", + "general/error/insufficient_permissions", + "general/error/cannot_interact", + "general/error/cannot_action_self", + "general/error/cannot_action_performer", + "moderation/error/cannot_interact", + "moderation/error/insufficient_permissions", + "moderation/kick/info", + "moderation/kick/dm", + "moderation/ban/info", + "moderation/ban/dm", + "moderation/unban/info", + "moderation/warn/info", + "moderation/warn/dm", + "moderation/unwarn/info", + "moderation/error/unwarn/no_case_found", + "moderation/error/unwarn/cannot_unwarn_self", + "moderation/error/unwarn/cannot_remove_higher_mod", + "moderation/error/warn/cannot_warn_mods", + "moderation/error/note/max_amount_of_notes", + "moderation/error/note/no_note_found", + "moderation/note/add", + "moderation/note/remove" +] \ No newline at end of file diff --git a/src/main/resources/messages/moderation/ban/dm.json b/src/main/resources/messages/moderation/ban/dm.json new file mode 100644 index 0000000..3798ed0 --- /dev/null +++ b/src/main/resources/messages/moderation/ban/dm.json @@ -0,0 +1,20 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "${performer.guild.name}", + "icon_url": "${performer.guild.icon_url}" + }, + "title": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/ban/info.json b/src/main/resources/messages/moderation/ban/info.json new file mode 100644 index 0000000..306ee27 --- /dev/null +++ b/src/main/resources/messages/moderation/ban/info.json @@ -0,0 +1,34 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${target.mention}", + "inline": true + }, + { + "name": "", + "value": "${private_message}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": true + }, + { + "name": "", + "value": "", + "inline": false + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/cannot_interact.json b/src/main/resources/messages/moderation/error/cannot_interact.json new file mode 100644 index 0000000..63cb40a --- /dev/null +++ b/src/main/resources/messages/moderation/error/cannot_interact.json @@ -0,0 +1,17 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${target.mention}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/insufficient_permissions.json b/src/main/resources/messages/moderation/error/insufficient_permissions.json new file mode 100644 index 0000000..a2479f2 --- /dev/null +++ b/src/main/resources/messages/moderation/error/insufficient_permissions.json @@ -0,0 +1,17 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${required_permissions}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/note/max_amount_of_notes.json b/src/main/resources/messages/moderation/error/note/max_amount_of_notes.json new file mode 100644 index 0000000..a451a0d --- /dev/null +++ b/src/main/resources/messages/moderation/error/note/max_amount_of_notes.json @@ -0,0 +1,22 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${target.mention}", + "inline": true + }, + { + "name": "", + "value": "${notes_amount}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/note/no_note_found.json b/src/main/resources/messages/moderation/error/note/no_note_found.json new file mode 100644 index 0000000..70b2bf3 --- /dev/null +++ b/src/main/resources/messages/moderation/error/note/no_note_found.json @@ -0,0 +1,17 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${note_id}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json b/src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json new file mode 100644 index 0000000..fd3982d --- /dev/null +++ b/src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json @@ -0,0 +1,22 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.case_id}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json b/src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json new file mode 100644 index 0000000..69f09a3 --- /dev/null +++ b/src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json @@ -0,0 +1,22 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.case_id}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/unwarn/no_case_found.json b/src/main/resources/messages/moderation/error/unwarn/no_case_found.json new file mode 100644 index 0000000..2914238 --- /dev/null +++ b/src/main/resources/messages/moderation/error/unwarn/no_case_found.json @@ -0,0 +1,17 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${case_id}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json b/src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json new file mode 100644 index 0000000..4b2f418 --- /dev/null +++ b/src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json @@ -0,0 +1,17 @@ +{ + "color": "${general.error.color}", + "title": "", + "description": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${target.mention}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/kick/dm.json b/src/main/resources/messages/moderation/kick/dm.json new file mode 100644 index 0000000..89a7ca0 --- /dev/null +++ b/src/main/resources/messages/moderation/kick/dm.json @@ -0,0 +1,20 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "${performer.guild.name}", + "icon_url": "${performer.guild.icon_url}" + }, + "title": "", + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/kick/info.json b/src/main/resources/messages/moderation/kick/info.json new file mode 100644 index 0000000..f3288b8 --- /dev/null +++ b/src/main/resources/messages/moderation/kick/info.json @@ -0,0 +1,29 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${target.mention}", + "inline": true + }, + { + "name": "", + "value": "${private_message}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": false + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/note/add.json b/src/main/resources/messages/moderation/note/add.json new file mode 100644 index 0000000..71fc360 --- /dev/null +++ b/src/main/resources/messages/moderation/note/add.json @@ -0,0 +1,34 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${note_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.target.mention}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.note_id}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.date_time}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.contents}", + "inline": false + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/note/remove.json b/src/main/resources/messages/moderation/note/remove.json new file mode 100644 index 0000000..69b176b --- /dev/null +++ b/src/main/resources/messages/moderation/note/remove.json @@ -0,0 +1,39 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.target.mention}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.note_id}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.date_time}", + "inline": true + }, + { + "name": "", + "value": "${note_entry.contents}", + "inline": false + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/unban/info.json b/src/main/resources/messages/moderation/unban/info.json new file mode 100644 index 0000000..6efe8a3 --- /dev/null +++ b/src/main/resources/messages/moderation/unban/info.json @@ -0,0 +1,19 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${target.mention}", + "inline": true + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/unwarn/info.json b/src/main/resources/messages/moderation/unwarn/info.json new file mode 100644 index 0000000..6d2b347 --- /dev/null +++ b/src/main/resources/messages/moderation/unwarn/info.json @@ -0,0 +1,39 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.target.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.case_id}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.date_time}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": false + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/warn/dm.json b/src/main/resources/messages/moderation/warn/dm.json new file mode 100644 index 0000000..1139d25 --- /dev/null +++ b/src/main/resources/messages/moderation/warn/dm.json @@ -0,0 +1,25 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "${performer.guild.name}", + "icon_url": "${performer.guild.icon_url}" + }, + "title": "", + "fields": [ + { + "name": "", + "value": "${warning_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.date_time}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": false + } + ] +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/warn/info.json b/src/main/resources/messages/moderation/warn/info.json new file mode 100644 index 0000000..ed55bb7 --- /dev/null +++ b/src/main/resources/messages/moderation/warn/info.json @@ -0,0 +1,39 @@ +{ + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "fields": [ + { + "name": "", + "value": "${warning_entry.performer.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.target.mention}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.case_id}", + "inline": true + }, + { + "name": "", + "value": "${private_message}", + "inline": true + }, + { + "name": "", + "value": "${warning_entry.date_time}", + "inline": true + }, + { + "name": "", + "value": "", + "inline": false + } + ] +} \ No newline at end of file From f861308e4da27a15c9c402384fd3eaafb361fa15 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Sat, 10 Oct 2020 16:53:40 +0800 Subject: [PATCH 4/5] Forgot to commit two files --- .../sciwhiz12/janitor/msg/MessageBuilder.java | 102 ------------------ .../janitor/msg/RegularMessageBuilder.java | 53 +++++++++ 2 files changed, 53 insertions(+), 102 deletions(-) delete mode 100644 src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java diff --git a/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java deleted file mode 100644 index 1c4fb86..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/MessageBuilder.java +++ /dev/null @@ -1,102 +0,0 @@ -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> substitutions; - - public MessageBuilder(EmbedBuilder embedBuilder, Map> substitutions) { - this.embedBuilder = embedBuilder; - this.substitutions = substitutions; - } - - public MessageBuilder() { - this(new EmbedBuilder(), new HashMap<>()); - } - - public MessageBuilder(MessageBuilder copy) { - this(new EmbedBuilder(copy.embedBuilder), new HashMap<>(copy.substitutions)); - } - - public EmbedBuilder embed() { - return embedBuilder; - } - - public MessageBuilder embed(Consumer operator) { - operator.accept(embed()); - return this; - } - - public MessageBuilder apply(Consumer consumer) { - consumer.accept(this); - return this; - } - - public MessageBuilder with(final String argument, final Supplier 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> replaceMap = substitutions.createDefaultedMap(this.substitutions); - final UnaryOperator 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()); - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java new file mode 100644 index 0000000..76dab18 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java @@ -0,0 +1,53 @@ +package sciwhiz12.janitor.msg; + +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.requests.restaction.MessageAction; +import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.msg.json.RegularMessage; +import sciwhiz12.janitor.msg.substitution.SubstitutionMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class RegularMessageBuilder { + private final RegularMessage message; + private final Map> customSubstitutions; + + public RegularMessageBuilder(RegularMessage message, Map> customSubstitutions) { + this.message = message; + this.customSubstitutions = customSubstitutions; + } + + public RegularMessageBuilder(RegularMessage message) { + this(message, new HashMap<>()); + } + + public RegularMessageBuilder(RegularMessageBuilder copy) { + this(copy.message, new HashMap<>(copy.customSubstitutions)); + } + + public RegularMessageBuilder apply(Consumer consumer) { + consumer.accept(this); + return this; + } + + public RegularMessageBuilder with(final String argument, final Supplier value) { + customSubstitutions.put(argument, value); + return this; + } + + public MessageEmbed build(TranslationMap translations, SubstitutionMap substitutions) { + return message.create(translations, substitutions.with(customSubstitutions)).build(); + } + + public MessageEmbed build(JanitorBot bot) { + return build(bot.getTranslations(), bot.getSubstitutions()); + } + + public MessageAction send(JanitorBot bot, MessageChannel channel) { + return channel.sendMessage(build(bot)); + } +} From 09aa6a269c3d682ced3edb732bb234280dd19c10 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Fri, 16 Oct 2020 18:12:50 +0800 Subject: [PATCH 5/5] Add listing messages --- .../java/sciwhiz12/janitor/JanitorBot.java | 9 +- .../janitor/commands/misc/HelloCommand.java | 28 +- .../commands/moderation/NoteCommand.java | 41 ++- .../commands/moderation/WarnListCommand.java | 33 +- .../janitor/msg/ListingMessageBuilder.java | 161 ++++++++++ .../sciwhiz12/janitor/msg/MessageHelper.java | 17 +- .../java/sciwhiz12/janitor/msg/Messages.java | 78 +++-- .../janitor/msg/RegularMessageBuilder.java | 3 +- .../sciwhiz12/janitor/msg/TranslationMap.java | 4 + .../janitor/msg/emote/ReactionManager.java | 43 +++ .../janitor/msg/emote/ReactionMessage.java | 112 +++++++ .../sciwhiz12/janitor/msg/json/IMessage.java | 9 - .../janitor/msg/json/ListingMessage.java | 281 +++++++++++++++--- .../msg/json/ListingMessageDeserializer.java | 117 ++++++++ .../janitor/msg/json/RegularMessage.java | 5 +- .../msg/substitution/CustomSubstitutions.java | 29 +- .../substitution/IHasCustomSubstitutions.java | 10 + src/main/resources/english.json | 7 +- src/main/resources/messages/messages.json | 4 +- .../messages/moderation/note/list.json | 13 + .../messages/moderation/warn/list.json | 13 + 21 files changed, 893 insertions(+), 124 deletions(-) create mode 100644 src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java delete mode 100644 src/main/java/sciwhiz12/janitor/msg/json/IMessage.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java create mode 100644 src/main/java/sciwhiz12/janitor/msg/substitution/IHasCustomSubstitutions.java create mode 100644 src/main/resources/messages/moderation/note/list.json create mode 100644 src/main/resources/messages/moderation/warn/list.json diff --git a/src/main/java/sciwhiz12/janitor/JanitorBot.java b/src/main/java/sciwhiz12/janitor/JanitorBot.java index 5a62a62..a97ae21 100644 --- a/src/main/java/sciwhiz12/janitor/JanitorBot.java +++ b/src/main/java/sciwhiz12/janitor/JanitorBot.java @@ -10,6 +10,7 @@ import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.config.BotConfig; import sciwhiz12.janitor.msg.Messages; import sciwhiz12.janitor.msg.TranslationMap; +import sciwhiz12.janitor.msg.emote.ReactionManager; import sciwhiz12.janitor.msg.substitution.SubstitutionMap; import sciwhiz12.janitor.utils.Util; @@ -29,6 +30,7 @@ public class JanitorBot { private final TranslationMap translations; private final SubstitutionMap substitutions; private final Messages messages; + private final ReactionManager reactions; public JanitorBot(JDA discord, BotConfig config) { this.config = config; @@ -39,8 +41,9 @@ public class JanitorBot { this.translations = new TranslationMap(this, config.getTranslationsFile()); this.substitutions = new SubstitutionMap(this); this.messages = new Messages(this, config.getTranslationsFile()); + this.reactions = new ReactionManager(this); // TODO: find which of these can be loaded in parallel before the bot JDA is ready - discord.addEventListener(cmdRegistry); + discord.addEventListener(cmdRegistry, reactions); discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!")); discord.getGuilds().forEach(Guild::loadMembers); JANITOR.info("Ready!"); @@ -82,6 +85,10 @@ public class JanitorBot { return this.translations; } + public ReactionManager getReactionManager() { + return this.reactions; + } + public void shutdown() { JANITOR.info(STATUS, "Shutting down!"); getConfig().getOwnerID() diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java b/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java index b8425ad..2625da9 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java @@ -24,10 +24,9 @@ public class HelloCommand extends BaseCommand { public LiteralArgumentBuilder getNode() { return literal("greet") - .then( - argument("member", GuildMemberArgument.member()) - .executes(this::run) - ); + .then(argument("member", GuildMemberArgument.member()) + .executes(this::run) + ); } int run(final CommandContext ctx) throws CommandSyntaxException { @@ -35,11 +34,22 @@ public class HelloCommand extends BaseCommand { final List memberList = getMembers("member", ctx).fromGuild(ctx.getSource().getGuild()); if (memberList.size() == 1) { final Member member = memberList.get(0); - ctx.getSource().getChannel().sendMessage("Hello " + member.getAsMention() + "!") - .queue( - success -> JANITOR.debug("Sent greeting message to {}, on cmd of {}", Util.toString(member.getUser()), Util.toString(ctx.getSource().getAuthor())), - err -> JANITOR.error("Error while sending greeting message to {}, on cmd of {}", Util.toString(member.getUser()), Util.toString(ctx.getSource().getAuthor())) - ); + ctx.getSource().getChannel().sendMessage("Hello " + member.getAsMention() + "!").queue( + success -> { + JANITOR.debug("Sent greeting message to {}, on cmd of {}", Util.toString(member.getUser()), + Util.toString(ctx.getSource().getAuthor())); + getBot().getReactionManager().newMessage(success) + .add("\u274C", (msg, event) -> success.delete() + .flatMap(v -> event.getChannel() + .deleteMessageById(ctx.getSource().getMessageIdLong())) + .queue() + ) + .owner(ctx.getSource().getAuthor().getIdLong()) + .create(); + }, + err -> JANITOR.error("Error while sending greeting message to {}, on cmd of {}", + Util.toString(member.getUser()), Util.toString(ctx.getSource().getAuthor())) + ); } } return 1; diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java index 8a0b9c2..7bde650 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java @@ -1,5 +1,6 @@ package sciwhiz12.janitor.commands.moderation; +import com.google.common.collect.ImmutableList; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -16,6 +17,7 @@ import sciwhiz12.janitor.msg.MessageHelper; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -32,6 +34,7 @@ import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; import static sciwhiz12.janitor.commands.moderation.NoteCommand.ModeratorFilter.*; import static sciwhiz12.janitor.commands.util.CommandHelper.argument; import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.msg.MessageHelper.*; public class NoteCommand extends BaseCommand { public static EnumSet NOTE_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); @@ -93,7 +96,7 @@ public class NoteCommand extends BaseCommand { final MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { messages().getRegularMessage("general/error/guild_only_command") - .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .apply(user("performer", ctx.getSource().getAuthor())) .send(getBot(), channel).queue(); return 1; @@ -137,7 +140,7 @@ public class NoteCommand extends BaseCommand { messages().getRegularMessage("moderation/note/add") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.noteEntry("note_entry", noteID, entry)) + .apply(noteEntry("note_entry", noteID, entry)) .send(getBot(), channel).queue(); } @@ -154,7 +157,7 @@ public class NoteCommand extends BaseCommand { final MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { messages().getRegularMessage("general/error/guild_only_command") - .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .apply(user("performer", ctx.getSource().getAuthor())) .send(getBot(), channel).queue(); return 1; @@ -196,16 +199,24 @@ public class NoteCommand extends BaseCommand { .send(getBot(), channel).queue(); } else { - // 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(); - messages().getRegularMessage("moderation/note/list") - .send(getBot(), channel).queue(); - // TODO: fix this + messages().>getListingMessage("moderation/note/list") + .apply(MessageHelper.member("performer", performer)) + .amountPerPage(8) + .setEntryApplier((entry, subs) -> subs + .with("note_entry.note_id", () -> String.valueOf(entry.getKey())) + .apply(user("note_entry.performer", entry.getValue().getPerformer())) + .apply(user("note_entry.target", entry.getValue().getTarget())) + .with("note_entry.date_time", () -> entry.getValue().getDateTime().format(DATE_TIME_FORMAT)) + .with("note_entry.contents", entry.getValue()::getContents) + ) + .build(channel, getBot(), ctx.getSource().getMessage(), + NoteStorage.get(getBot().getStorage(), guild) + .getNotes() + .entrySet().stream() + .filter(predicate) + .sorted(Comparator.>comparingInt(Map.Entry::getKey).reversed()) + .collect(ImmutableList.toImmutableList()) + ); } return 1; } @@ -214,7 +225,7 @@ public class NoteCommand extends BaseCommand { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { messages().getRegularMessage("general/error/guild_only_command") - .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .apply(user("performer", ctx.getSource().getAuthor())) .send(getBot(), channel).queue(); return 1; @@ -243,7 +254,7 @@ public class NoteCommand extends BaseCommand { messages().getRegularMessage("moderation/note/remove") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.noteEntry("note_entry", noteID, entry)) + .apply(noteEntry("note_entry", noteID, entry)) .send(getBot(), channel).queue(); } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java index a30eaf8..0439c6e 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java @@ -1,5 +1,6 @@ package sciwhiz12.janitor.commands.moderation; +import com.google.common.collect.ImmutableList; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -11,8 +12,10 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.moderation.warns.WarningEntry; +import sciwhiz12.janitor.moderation.warns.WarningStorage; import sciwhiz12.janitor.msg.MessageHelper; +import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -23,6 +26,8 @@ import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMember import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; import static sciwhiz12.janitor.commands.util.CommandHelper.argument; import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.msg.MessageHelper.DATE_TIME_FORMAT; +import static sciwhiz12.janitor.msg.MessageHelper.user; public class WarnListCommand extends BaseCommand { public static final EnumSet WARN_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); @@ -93,16 +98,24 @@ public class WarnListCommand extends BaseCommand { .send(getBot(), channel).queue(); } else { - // 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(); - messages().getRegularMessage("moderation/warn/list") - .send(getBot(), channel).queue(); - // TODO: fix this + messages().>getListingMessage("moderation/warn/list") + .apply(MessageHelper.member("performer", performer)) + .amountPerPage(8) + .setEntryApplier((entry, subs) -> subs + .with("warning_entry.case_id", () -> String.valueOf(entry.getKey())) + .apply(user("warning_entry.performer", entry.getValue().getPerformer())) + .apply(user("warning_entry.warned", entry.getValue().getWarned())) + .with("warning_entry.date_time", () -> entry.getValue().getDateTime().format(DATE_TIME_FORMAT)) + .with("warning_entry.reason", entry.getValue()::getReason) + ) + .build(channel, getBot(), ctx.getSource().getMessage(), + WarningStorage.get(getBot().getStorage(), guild) + .getWarnings() + .entrySet().stream() + .filter(predicate) + .sorted(Comparator.>comparingInt(Map.Entry::getKey).reversed()) + .collect(ImmutableList.toImmutableList()) + ); } return 1; } diff --git a/src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java new file mode 100644 index 0000000..855b4ab --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java @@ -0,0 +1,161 @@ +package sciwhiz12.janitor.msg; + +import com.google.common.collect.ImmutableList; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageEmbed; +import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.msg.json.ListingMessage; +import sciwhiz12.janitor.msg.substitution.CustomSubstitutions; +import sciwhiz12.janitor.msg.substitution.IHasCustomSubstitutions; +import sciwhiz12.janitor.msg.substitution.SubstitutionMap; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class ListingMessageBuilder implements IHasCustomSubstitutions> { + private final ListingMessage message; + private final Map> customSubstitutions; + private int amountPerPage = 10; + private BiConsumer entryApplier = (entry, sub) -> {}; + + public ListingMessageBuilder(ListingMessage message, Map> customSubstitutions) { + this.message = message; + this.customSubstitutions = customSubstitutions; + } + + public ListingMessageBuilder(ListingMessage message) { + this(message, new HashMap<>()); + } + + public ListingMessageBuilder amountPerPage(int amountPerPage) { + this.amountPerPage = amountPerPage; + return this; + } + + public ListingMessageBuilder setEntryApplier(BiConsumer entryApplier) { + this.entryApplier = entryApplier; + return this; + } + + public ListingMessageBuilder apply(Consumer> consumer) { + consumer.accept(this); + return this; + } + + public ListingMessageBuilder with(final String argument, final Supplier value) { + this.customSubstitutions.put(argument, value); + return this; + } + + public void build(MessageChannel channel, TranslationMap translations, SubstitutionMap globalSubstitutions, + Message triggerMessage, List entries) { + + final CustomSubstitutions customSubs = globalSubstitutions.with(customSubstitutions); + final ImmutableList list = ImmutableList.copyOf(entries); + final PagedMessage pagedMessage = new PagedMessage(message, list, amountPerPage); + + channel.sendMessage(pagedMessage.createMessage(translations, customSubs, entryApplier)) + .queue(listMsg -> translations.getBot().getReactionManager().newMessage(listMsg) + .owner(triggerMessage.getAuthor().getIdLong()) + .removeEmotes(true) + .add("\u2b05", (msg, event) -> { // PREVIOUS + if (pagedMessage.advancePage(PageDirection.PREVIOUS)) { + event.retrieveMessage() + .flatMap(eventMsg -> eventMsg.editMessage( + pagedMessage.createMessage(translations, customSubs, entryApplier)) + ) + .queue(); + } + }) + .add("\u274c", (msg, event) -> { // CLOSE + event.getChannel().deleteMessageById(event.getMessageIdLong()) + .flatMap(v -> !triggerMessage.isFromGuild() || + event.getGuild().getSelfMember() + .hasPermission(triggerMessage.getTextChannel(), + Permission.MESSAGE_MANAGE), + v -> triggerMessage.delete()) + .queue(); + }) + .add("\u27a1", (msg, event) -> { // NEXT + if (pagedMessage.advancePage(PageDirection.NEXT)) { + event.retrieveMessage() + .flatMap(eventMsg -> eventMsg.editMessage( + pagedMessage.createMessage(translations, customSubs, entryApplier)) + ) + .queue(); + } + }) + .create() + ); + } + + public void build(MessageChannel channel, JanitorBot bot, Message triggerMessage, List entries) { + build(channel, bot.getTranslations(), bot.getSubstitutions(), triggerMessage, entries); + } + + class PagedMessage { + private final ListingMessage message; + private final ImmutableList list; + private final int maxPages; + private final int amountPerPage; + private int currentPage = 0; + private int lastPage = -1; + private EmbedBuilder cachedMessage; + + PagedMessage(ListingMessage message, ImmutableList list, int amountPerPage) { + this.message = message; + this.list = list; + this.amountPerPage = amountPerPage; + this.maxPages = Math.floorDiv(list.size(), ListingMessageBuilder.this.amountPerPage); + } + + public int getMaxPages() { + return maxPages; + } + + public int getCurrentPage() { + return currentPage; + } + + public boolean advancePage(PageDirection direction) { + if (direction == PageDirection.PREVIOUS && currentPage > 0) { + currentPage -= 1; + return true; + } else if (direction == PageDirection.NEXT && currentPage < maxPages) { + currentPage += 1; + return true; + } + return false; + } + + public MessageEmbed createMessage(TranslationMap translations, CustomSubstitutions substitutions, + BiConsumer applier) { + if (currentPage != lastPage) { + cachedMessage = message.create( + translations, + substitutions.with(new HashMap<>()) + .with("page.max", () -> String.valueOf(maxPages + 1)) + .with("page.current", () -> String.valueOf(currentPage + 1)), + list.stream() + .skip(currentPage * amountPerPage) + .limit(amountPerPage) + .collect(Collectors.toList()), + applier); + lastPage = currentPage; + } + return cachedMessage.build(); + } + } + + enum PageDirection { + PREVIOUS, NEXT + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java b/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java index ab96799..2a54033 100644 --- a/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java +++ b/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java @@ -8,6 +8,7 @@ import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.User; import sciwhiz12.janitor.moderation.notes.NoteEntry; import sciwhiz12.janitor.moderation.warns.WarningEntry; +import sciwhiz12.janitor.msg.substitution.IHasCustomSubstitutions; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -18,19 +19,19 @@ import static java.time.temporal.ChronoField.*; public class MessageHelper { private MessageHelper() {} - public static Consumer snowflake(String head, ISnowflake snowflake) { + public static > Consumer snowflake(String head, ISnowflake snowflake) { return builder -> builder .with(head + ".id", snowflake::getId) .with(head + ".creation_datetime", () -> snowflake.getTimeCreated().format(DATE_TIME_FORMAT)); } - public static Consumer mentionable(String head, IMentionable mentionable) { + public static > Consumer mentionable(String head, IMentionable mentionable) { return builder -> builder .apply(snowflake(head, mentionable)) .with(head + ".mention", mentionable::getAsMention); } - public static Consumer role(String head, Role role) { + public static > Consumer role(String head, Role role) { return builder -> builder .apply(mentionable(head, role)) .with(head + ".color_hex", () -> Integer.toHexString(role.getColorRaw())) @@ -38,7 +39,7 @@ public class MessageHelper { .with(head + ".permissions", role.getPermissions()::toString); } - public static Consumer user(String head, User user) { + public static > Consumer user(String head, User user) { return builder -> builder .apply(mentionable(head, user)) .with(head + ".name", user::getName) @@ -47,7 +48,7 @@ public class MessageHelper { .with(head + ".flags", user.getFlags()::toString); } - public static Consumer guild(String head, Guild guild) { + public static > Consumer guild(String head, Guild guild) { return builder -> builder .apply(snowflake(head, guild)) .with(head + ".name", guild::getName) @@ -60,7 +61,7 @@ public class MessageHelper { .with(head + ".icon_url", guild::getIconUrl); } - public static Consumer member(String head, Member member) { + public static > Consumer member(String head, Member member) { return builder -> builder .apply(user(head, member.getUser())) .apply(guild(head + ".guild", member.getGuild())) @@ -70,7 +71,7 @@ public class MessageHelper { .with(head + ".color", () -> String.valueOf(member.getColorRaw())); } - public static Consumer warningEntry(String head, int caseID, WarningEntry entry) { + public static > Consumer warningEntry(String head, int caseID, WarningEntry entry) { return builder -> builder .with(head + ".case_id", () -> String.valueOf(caseID)) .apply(user(head + ".performer", entry.getPerformer())) @@ -79,7 +80,7 @@ public class MessageHelper { .with(head + ".reason", entry::getReason); } - public static Consumer noteEntry(String head, int noteID, NoteEntry entry) { + public static > Consumer noteEntry(String head, int noteID, NoteEntry entry) { return builder -> builder .with(head + ".note_id", () -> String.valueOf(noteID)) .apply(user(head + ".performer", entry.getPerformer())) diff --git a/src/main/java/sciwhiz12/janitor/msg/Messages.java b/src/main/java/sciwhiz12/janitor/msg/Messages.java index 114e60b..c8342e8 100644 --- a/src/main/java/sciwhiz12/janitor/msg/Messages.java +++ b/src/main/java/sciwhiz12/janitor/msg/Messages.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import net.dv8tion.jda.api.entities.MessageEmbed; import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.msg.json.ListingMessage; import sciwhiz12.janitor.msg.json.RegularMessage; import java.io.IOException; @@ -31,6 +32,7 @@ public class Messages { private final JanitorBot bot; private final Path messagesFolder; private final Map regularMessages = new HashMap<>(); + private final Map listingMessages = new HashMap<>(); private final ObjectMapper jsonMapper = new ObjectMapper(); public Messages(JanitorBot bot, Path messagesFolder) { @@ -39,13 +41,17 @@ public class Messages { loadMessages(); } + public JanitorBot getBot() { + return bot; + } + public void loadMessages() { boolean success = false; if (messagesFolder != null) { JANITOR.debug(MESSAGES, "Loading messages from folder {}", messagesFolder); success = loadMessages( - path -> Files.newBufferedReader(messagesFolder.resolve(path + JSON_FILE_SUFFIX)) + path -> Files.newBufferedReader(messagesFolder.resolve(path + JSON_FILE_SUFFIX)) ); } else { JANITOR.info(MESSAGES, "No custom messages folder specified"); @@ -55,7 +61,7 @@ public class Messages { JANITOR.info(MESSAGES, "Loading default messages"); //noinspection UnstableApiUsage loadMessages( - file -> new InputStreamReader(getResource(DEFAULT_MESSAGES_FOLDER + file + JSON_FILE_SUFFIX).openStream()) + file -> new InputStreamReader(getResource(DEFAULT_MESSAGES_FOLDER + file + JSON_FILE_SUFFIX).openStream()) ); } } @@ -68,8 +74,11 @@ public class Messages { final String path = messageKey.replace("/", FileSystems.getDefault().getSeparator()); try (Reader reader = files.open(path)) { final JsonNode tree = jsonMapper.readTree(reader); - if ("regular".equals(tree.path("type").asText("regular"))) { + final String type = tree.path("type").asText("regular"); + if ("regular".equals(type)) { regularMessages.put(messageKey, jsonMapper.convertValue(tree, RegularMessage.class)); + } else if ("listing".equals(type)) { + listingMessages.put(messageKey, jsonMapper.convertValue(tree, ListingMessage.class)); } else { JANITOR.warn(MESSAGES, "Unknown message type {} for {}", tree.path("type").asText(), messageKey); } @@ -92,28 +101,61 @@ public class Messages { public RegularMessageBuilder getRegularMessage(String messageKey) { final RegularMessage msg = regularMessages.get(messageKey); if (msg == null) { - JANITOR.warn(MESSAGES, "Attempted to get unknown message with key {}", messageKey); - return new RegularMessageBuilder(UNKNOWN_MESSAGE).with("key", () -> messageKey); + JANITOR.warn(MESSAGES, "Attempted to get unknown regular message with key {}", messageKey); + return new RegularMessageBuilder(UNKNOWN_REGULAR_MESSAGE).with("key", () -> messageKey); } return new RegularMessageBuilder(msg); } + public Map getListingMessages() { + return listingMessages; + } + + public ListingMessageBuilder getListingMessage(String messageKey) { + final ListingMessage msg = listingMessages.get(messageKey); + if (msg == null) { + JANITOR.warn(MESSAGES, "Attempted to get unknown listing message with key {}", messageKey); + return new ListingMessageBuilder(UNKNOWN_LISTING_MESSAGE).with("key", () -> messageKey); + } + return new ListingMessageBuilder<>(msg); + } + interface FileOpener { Reader open(String filePath) throws IOException; } - public static final RegularMessage UNKNOWN_MESSAGE = new RegularMessage( - "UNKNOWN MESSAGE!", - null, - "A message was tried to be looked up, but was not found. Please report this to your bot maintainer/administrator.", - String.valueOf(0xFF0000), - null, - null, - null, - null, - null, - null, - null, - Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)) + public static final RegularMessage UNKNOWN_REGULAR_MESSAGE = new RegularMessage( + "UNKNOWN MESSAGE!", + null, + "A regular message was tried to be looked up, but was not found. Please report this to your bot " + + "maintainer/administrator.", + String.valueOf(0xFF0000), + null, + null, + null, + null, + null, + null, + null, + Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)) + ); + + public static final ListingMessage UNKNOWN_LISTING_MESSAGE = new ListingMessage( + "UNKNOWN MESSAGE!", + null, + "A listing message was tried to be looked up, but was not found. " + + "Please report this to your bot maintainer/administrator.", + String.valueOf(0xFF0000), + null, + null, + null, + null, + null, + null, + null, + null, + new ListingMessage.DescriptionEntry(null, ""), + Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)), + Collections.emptyList() ); } diff --git a/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java index 76dab18..37cad71 100644 --- a/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java +++ b/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java @@ -5,6 +5,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.requests.restaction.MessageAction; import sciwhiz12.janitor.JanitorBot; import sciwhiz12.janitor.msg.json.RegularMessage; +import sciwhiz12.janitor.msg.substitution.IHasCustomSubstitutions; import sciwhiz12.janitor.msg.substitution.SubstitutionMap; import java.util.HashMap; @@ -12,7 +13,7 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; -public class RegularMessageBuilder { +public class RegularMessageBuilder implements IHasCustomSubstitutions { private final RegularMessage message; private final Map> customSubstitutions; diff --git a/src/main/java/sciwhiz12/janitor/msg/TranslationMap.java b/src/main/java/sciwhiz12/janitor/msg/TranslationMap.java index d89790d..543488a 100644 --- a/src/main/java/sciwhiz12/janitor/msg/TranslationMap.java +++ b/src/main/java/sciwhiz12/janitor/msg/TranslationMap.java @@ -77,4 +77,8 @@ public class TranslationMap { return matcher.replaceAll( matchResult -> quoteReplacement(translations.getOrDefault(matchResult.group(1), matchResult.group(0)))); } + + public JanitorBot getBot() { + return bot; + } } diff --git a/src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java b/src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java new file mode 100644 index 0000000..17204a8 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java @@ -0,0 +1,43 @@ +package sciwhiz12.janitor.msg.emote; + +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.message.MessageDeleteEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import sciwhiz12.janitor.JanitorBot; + +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nonnull; + +public class ReactionManager extends ListenerAdapter { + private final JanitorBot bot; + private final Map messageMap = new HashMap<>(); + + public ReactionManager(JanitorBot bot) { + this.bot = bot; + } + + public ReactionMessage newMessage(Message message) { + if (messageMap.containsKey(message.getIdLong())) { + throw new IllegalArgumentException("Reaction message already exists for message with id " + message.getIdLong()); + } + final ReactionMessage msg = new ReactionMessage(bot, message); + messageMap.put(message.getIdLong(), msg); + return msg; + } + + public void removeMessage(long messageID) { + bot.getDiscord().removeEventListener(messageMap.remove(messageID)); + } + + public Map getRegisteredMessages() { + return messageMap; + } + + @Override + public void onMessageDelete(@Nonnull MessageDeleteEvent event) { + if (messageMap.containsKey(event.getMessageIdLong())) { + bot.getDiscord().removeEventListener(messageMap.get(event.getMessageIdLong())); + } + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java b/src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java new file mode 100644 index 0000000..6d4e924 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java @@ -0,0 +1,112 @@ +package sciwhiz12.janitor.msg.emote; + +import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageReaction.ReactionEmote; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.exceptions.ErrorHandler; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.requests.ErrorResponse; +import sciwhiz12.janitor.JanitorBot; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import javax.annotation.Nonnull; + +import static net.dv8tion.jda.api.Permission.MESSAGE_MANAGE; + +public class ReactionMessage extends ListenerAdapter { + private final JanitorBot bot; + private final Message message; + private final Map emotes = new LinkedHashMap<>(); + private boolean removeEmotes = true; + private long ownerID; + private boolean onlyOwner; + + public ReactionMessage(JanitorBot bot, Message message, boolean onlyOwner, long ownerID) { + this.bot = bot; + this.message = message; + this.ownerID = ownerID; + this.onlyOwner = onlyOwner; + } + + public ReactionMessage(JanitorBot bot, Message message) { + this(bot, message, false, 0); + } + + public ReactionMessage add(ReactionEmote emote, IReactionListener listener) { + emotes.put(emote, listener); + return this; + } + + public ReactionMessage add(String emote, IReactionListener listener) { + return add(ReactionEmote.fromUnicode(emote, bot.getDiscord()), listener); + } + + public ReactionMessage add(Emote emote, IReactionListener listener) { + return add(ReactionEmote.fromCustom(emote), listener); + } + + public ReactionMessage removeEmotes(boolean remove) { + this.removeEmotes = remove; + return this; + } + + public ReactionMessage owner(long ownerID) { + this.ownerID = ownerID; + this.onlyOwner = true; + return this; + } + + public void create() { + for (ReactionEmote reaction : emotes.keySet()) { + if (reaction.isEmote()) { + message.addReaction(reaction.getEmote()).queue(); + } else { + message.addReaction(reaction.getEmoji()).queue(); + } + } + bot.getDiscord().addEventListener(this); + } + + @Override + public void onMessageReactionAdd(@Nonnull MessageReactionAddEvent event) { + if (event.getMessageIdLong() != message.getIdLong()) return; + if (event.getUserIdLong() == bot.getDiscord().getSelfUser().getIdLong()) return; + if (onlyOwner && event.getUserIdLong() != ownerID) return; + + emotes.keySet().stream() + .filter(emote -> event.getReactionEmote().equals(emote)) + .forEach(emote -> emotes.get(emote).accept(this, event)); + + if (removeEmotes && (!event.isFromGuild() + || event.getGuild().getSelfMember().hasPermission(event.getTextChannel(), MESSAGE_MANAGE))) { + event.retrieveUser() + .flatMap(user -> event.getReaction().removeReaction(user)) + .queue(null, new ErrorHandler().ignore(ErrorResponse.UNKNOWN_MESSAGE)); + } + } + + public JanitorBot getBot() { + return bot; + } + + public long getOwnerID() { + return ownerID; + } + + public boolean isOwnerOnly() { + return onlyOwner; + } + + public Map getListeners() { + return Collections.unmodifiableMap(emotes); + } + + @FunctionalInterface + public interface IReactionListener extends BiConsumer { + void accept(ReactionMessage message, MessageReactionAddEvent event); + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/IMessage.java b/src/main/java/sciwhiz12/janitor/msg/json/IMessage.java deleted file mode 100644 index 97535d5..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/json/IMessage.java +++ /dev/null @@ -1,9 +0,0 @@ -package sciwhiz12.janitor.msg.json; - -import net.dv8tion.jda.api.EmbedBuilder; -import sciwhiz12.janitor.msg.substitution.ISubstitutor; -import sciwhiz12.janitor.msg.TranslationMap; - -public interface IMessage { - EmbedBuilder create(TranslationMap translations, ISubstitutor substitutions); -} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java b/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java index 82c39c6..50365f2 100644 --- a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java +++ b/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java @@ -1,61 +1,254 @@ package sciwhiz12.janitor.msg.json; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.common.primitives.Ints; +import joptsimple.internal.Strings; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import sciwhiz12.janitor.msg.TranslationMap; +import sciwhiz12.janitor.msg.substitution.CustomSubstitutions; +import sciwhiz12.janitor.msg.substitution.ISubstitutor; import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; +import javax.annotation.Nullable; +@JsonDeserialize(using = ListingMessageDeserializer.class) public class ListingMessage { - protected final String url; - protected final String title; - protected final String description; - protected final OffsetDateTime timestamp; - protected final int color; - protected final MessageEmbed.Thumbnail thumbnail; - protected final MessageEmbed.AuthorInfo author; - protected final MessageEmbed.Footer footer; - protected final MessageEmbed.ImageInfo image; - protected final Multimap fields; + @Nullable protected final String title; + @Nullable protected final String url; + @Nullable protected final String description; + @Nullable protected final String color; + @Nullable protected final String authorName; + @Nullable protected final String authorUrl; + @Nullable protected final String authorIconUrl; + @Nullable protected final String footerText; + @Nullable protected final String footerIconUrl; + @Nullable protected final String imageUrl; + @Nullable protected final String thumbnailUrl; + @Nullable protected final String emptyText; + protected final Entry entry; + protected final List beforeFields; + protected final List afterFields; - @Deprecated - public ListingMessage() { - this(null, null, null, null, 0, null, null, null, null, null); - } - - public ListingMessage(MessageEmbed embed) { - this(embed.getUrl(), - embed.getTitle(), - embed.getDescription(), - embed.getTimestamp(), - embed.getColorRaw(), - embed.getThumbnail(), - embed.getAuthor(), - embed.getFooter(), - embed.getImage(), - Multimaps.index(embed.getFields(), k -> FieldPlacement.BEFORE)); - } - - public ListingMessage(String url, String title, String description, OffsetDateTime timestamp, int color, - MessageEmbed.Thumbnail thumbnail, MessageEmbed.AuthorInfo author, MessageEmbed.Footer footer, - MessageEmbed.ImageInfo image, Multimap fields) { - this.url = url; + public ListingMessage( + @Nullable String title, + @Nullable String url, + @Nullable String description, + @Nullable String color, + @Nullable String authorName, + @Nullable String authorUrl, + @Nullable String authorIconUrl, + @Nullable String footerText, + @Nullable String footerIconUrl, + @Nullable String imageUrl, + @Nullable String thumbnailUrl, + @Nullable String emptyText, + Entry entry, + List beforeFields, + List afterFields + ) { this.title = title; + this.url = url; this.description = description; - this.timestamp = timestamp; this.color = color; - this.thumbnail = thumbnail; - this.author = author; - this.footer = footer; - this.image = image; - this.fields = fields; + this.authorName = authorName; + this.authorUrl = authorUrl; + this.authorIconUrl = authorIconUrl; + this.footerText = footerText; + this.footerIconUrl = footerIconUrl; + this.imageUrl = imageUrl; + this.thumbnailUrl = thumbnailUrl; + this.emptyText = emptyText; + this.entry = entry; + this.beforeFields = beforeFields; + this.afterFields = afterFields; } - public enum ListingType { - DESCRIPTION, FIELDS + @Nullable + public String getTitle() { + return title; } - public enum FieldPlacement { - BEFORE, AFTER; + @Nullable + public String getUrl() { + return url; + } + + @Nullable + public String getDescription() { + return description; + } + + @Nullable + public String getColor() { + return color; + } + + @Nullable + public String getAuthorName() { + return authorName; + } + + @Nullable + public String getAuthorUrl() { + return authorUrl; + } + + @Nullable + public String getAuthorIconUrl() { + return authorIconUrl; + } + + @Nullable + public String getFooterText() { + return footerText; + } + + @Nullable + public String getFooterIconUrl() { + return footerIconUrl; + } + + @Nullable + public String getImageUrl() { + return imageUrl; + } + + @Nullable + public String getThumbnailUrl() { + return thumbnailUrl; + } + + @Nullable + public String getEmptyText() { + return emptyText; + } + + public Entry getEntry() { + return entry; + } + + public List getBeforeFields() { + return beforeFields; + } + + public List getAfterFields() { + return afterFields; + } + + public EmbedBuilder create( + TranslationMap translations, + ISubstitutor global, + Iterable iterable, + BiConsumer entryApplier + ) { + final Function func = str -> str != null ? global.substitute(translations.translate(str)) : null; + final EmbedBuilder builder = new EmbedBuilder(); + builder.setTitle(func.apply(title), func.apply(url)); + builder.setColor(parseColor(global.substitute(color))); + builder.setAuthor(func.apply(authorName), func.apply(authorUrl), func.apply(authorIconUrl)); + builder.setDescription(func.apply(description)); + builder.setImage(func.apply(imageUrl)); + builder.setThumbnail(func.apply(thumbnailUrl)); + builder.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); + builder.setFooter(func.apply(footerText), func.apply(footerIconUrl)); + for (MessageEmbed.Field field : beforeFields) { + builder.addField(func.apply(field.getName()), func.apply(field.getValue()), field.isInline()); + } + + final CustomSubstitutions entrySubs = new CustomSubstitutions(); + final Function entryFunc = str -> str != null ? entrySubs.substitute(func.apply(str)) : null; + int count = 0; + for (T listEntry : iterable) { + entryApplier.accept(listEntry, entrySubs); + if (entry instanceof FieldEntry) { + FieldEntry fieldEntry = (FieldEntry) entry; + builder.addField( + entryFunc.apply(fieldEntry.getFieldName()), + entryFunc.apply(fieldEntry.getFieldValue()), + fieldEntry.isInline() + ); + } else if (entry instanceof DescriptionEntry) { + DescriptionEntry descEntry = (DescriptionEntry) entry; + builder.getDescriptionBuilder().append(entryFunc.apply(descEntry.getDescription())); + builder.getDescriptionBuilder().append(descEntry.getJoiner()); + } + count++; + } + if (count < 1) { + builder.getDescriptionBuilder().append(func.apply(emptyText)); + } + + for (MessageEmbed.Field field : afterFields) { + builder.addField(func.apply(field.getName()), func.apply(field.getValue()), field.isInline()); + } + return builder; + } + + private static int parseColor(String str) { + if (Strings.isNullOrEmpty(str)) return Role.DEFAULT_COLOR_RAW; + if (str.startsWith("0x")) { + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str.substring(2), 16); + if (res != null) { + return res; + } + } + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str, 10); + if (res != null) { + return res; + } + return Role.DEFAULT_COLOR_RAW; + } + + public interface Entry {} + + public static class DescriptionEntry implements Entry { + public static final String DEFAULT_JOINER = "\n"; + private final String joiner; + private final String descriptionEntry; + + public DescriptionEntry(@Nullable String joiner, String descriptionEntry) { + this.joiner = joiner != null ? joiner : DEFAULT_JOINER; + this.descriptionEntry = descriptionEntry; + } + + public String getJoiner() { + return joiner; + } + + public String getDescription() { + return descriptionEntry; + } + } + + public static class FieldEntry implements Entry { + private final String fieldName; + private final String fieldValue; + private final boolean inline; + + public FieldEntry(String fieldName, String fieldValue, boolean inline) { + this.fieldName = fieldName; + this.fieldValue = fieldValue; + this.inline = inline; + } + + public String getFieldName() { + return fieldName; + } + + public String getFieldValue() { + return fieldValue; + } + + public boolean isInline() { + return inline; + } } } diff --git a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java b/src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java new file mode 100644 index 0000000..8e5c69b --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java @@ -0,0 +1,117 @@ +package sciwhiz12.janitor.msg.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; + +public class ListingMessageDeserializer extends StdDeserializer { + public ListingMessageDeserializer() { + super(ListingMessage.class); + } + + @Override + public ListingMessage deserialize(JsonParser p, DeserializationContext ctx) + throws IOException { + + final JsonNode root = ctx.readTree(p); + + String title = null; + String url = null; + String description = root.path("description").asText(null); + String color = root.path("color").asText(null); + String authorName = null; + String authorUrl = null; + String authorIconUrl = null; + String footerText = null; + String footerIconUrl = null; + String imageUrl = root.path("image").asText(null); + String thumbnailUrl = root.path("thumbnail").asText(null); + String emptyText = root.path("empty").asText(null); + List beforeFields = readFields(root.path("fields").path("before")); + List afterFields = readFields(root.path("fields").path("after")); + + // Title + if (root.path("title").isTextual()) { + title = root.path("title").asText(); + } else if (root.path("title").path("text").isTextual()) { + title = root.path("title").path("text").asText(); + url = root.path("title").path("url").asText(null); + } + + // Author + if (root.path("author").isTextual()) { + authorName = root.path("author").asText(); + } else if (root.path("author").path("name").isTextual()) { + authorName = root.path("author").path("name").asText(); + authorUrl = root.path("author").path("url").asText(null); + authorIconUrl = root.path("author").path("icon_url").asText(null); + } + + // Footer + if (root.path("footer").isTextual()) { + footerText = root.path("footer").asText(); + } else if (root.path("footer").path("text").isTextual()) { + footerText = root.path("footer").path("text").asText(); + footerIconUrl = root.path("footer").path("icon_url").asText(null); + } + + // ENTRY + final ListingMessage.Entry entry = readEntry(root.path("entry")); + + return new ListingMessage(title, url, description, color, authorName, authorUrl, authorIconUrl, footerText, + footerIconUrl, imageUrl, thumbnailUrl, emptyText, entry, beforeFields, afterFields); + } + + public static ListingMessage.Entry readEntry(JsonNode root) { + switch (root.path("type").asText()) { + case "field": { + return new ListingMessage.FieldEntry( + root.path("name").asText(EmbedBuilder.ZERO_WIDTH_SPACE), + root.path("value").asText(EmbedBuilder.ZERO_WIDTH_SPACE), + root.path("inline").asBoolean(false) + ); + } + default: + case "description": { + return new ListingMessage.DescriptionEntry( + root.path("joiner").asText(null), + root.path("text").asText()); + } + } + } + + public static List readFields(JsonNode node) { + if (node.isArray()) { + final ArrayList fields = new ArrayList<>(); + for (int i = 0; i < node.size(); i++) { + final MessageEmbed.Field field = readField(node.path(i)); + if (field != null) { + fields.add(field); + } + } + return fields; + } + return Collections.emptyList(); + } + + @Nullable + public static MessageEmbed.Field readField(JsonNode fieldNode) { + if (fieldNode.path("name").isTextual() && fieldNode.path("value").isTextual()) { + return new MessageEmbed.Field( + fieldNode.path("name").asText(), + fieldNode.path("value").asText(), + fieldNode.path("inline").asBoolean(false) + ); + } + return null; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java b/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java index 873b015..10472d6 100644 --- a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java +++ b/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java @@ -6,8 +6,8 @@ import joptsimple.internal.Strings; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.Role; -import sciwhiz12.janitor.msg.substitution.ISubstitutor; import sciwhiz12.janitor.msg.TranslationMap; +import sciwhiz12.janitor.msg.substitution.ISubstitutor; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -20,7 +20,7 @@ import java.util.function.Function; import javax.annotation.Nullable; @JsonDeserialize(using = RegularMessageDeserializer.class) -public class RegularMessage implements IMessage { +public class RegularMessage { @Nullable protected final String title; @Nullable @@ -176,7 +176,6 @@ public class RegularMessage implements IMessage { thumbnailUrl, fields); } - @Override public EmbedBuilder create(TranslationMap translations, ISubstitutor substitutions) { final Function func = str -> str != null ? substitutions.substitute(translations.translate(str)) : null; final EmbedBuilder builder = new EmbedBuilder(); diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java b/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java index bc45d6c..bcbf9d0 100644 --- a/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java +++ b/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java @@ -1,20 +1,47 @@ package sciwhiz12.janitor.msg.substitution; +import org.apache.commons.collections4.TransformerUtils; +import org.apache.commons.collections4.map.DefaultedMap; + +import java.util.HashMap; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Supplier; -public class CustomSubstitutions implements ISubstitutor { +public class CustomSubstitutions implements ISubstitutor, IHasCustomSubstitutions { private final Map> map; public CustomSubstitutions(Map> map) { this.map = map; } + public CustomSubstitutions() { + this(new HashMap<>()); + } + @Override public String substitute(String text) { return SubstitutionMap.substitute(text, map); } + public CustomSubstitutions apply(Consumer consumer) { + consumer.accept(this); + return this; + } + + public CustomSubstitutions with(final String argument, final Supplier value) { + map.put(argument, value); + return this; + } + + public CustomSubstitutions with(Map> customSubstitutions) { + return new CustomSubstitutions(createDefaultedMap(customSubstitutions)); + } + + public Map> createDefaultedMap(Map> custom) { + return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(map)); + } + public Map> getMap() { return map; } diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/IHasCustomSubstitutions.java b/src/main/java/sciwhiz12/janitor/msg/substitution/IHasCustomSubstitutions.java new file mode 100644 index 0000000..93f772c --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/substitution/IHasCustomSubstitutions.java @@ -0,0 +1,10 @@ +package sciwhiz12.janitor.msg.substitution; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public interface IHasCustomSubstitutions> { + T with(String argument, Supplier value); + + T apply(Consumer consumer); +} diff --git a/src/main/resources/english.json b/src/main/resources/english.json index fef3c78..1803669 100644 --- a/src/main/resources/english.json +++ b/src/main/resources/english.json @@ -110,9 +110,8 @@ "moderation.note.remove.field.contents": "Text", "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.note.list.author": "Listing of Notes", + "moderation.warnlist.entry": "**Case #${warning_entry.case_id}**: Warned ${warning_entry.warned.mention} by ${warning_entry.performer.mention} \n - _Date & Time:_ ${warning_entry.date_time} \n - _Reason:_ ${nullcheck;warning_entry.reason;_No reason specified._}", + "moderation.note.list.author": "Listing of Notes (Page ${page.current}/${page.max})", "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.list.entry": "**#${note_entry.note_id}**: for ${note_entry.target.mention} by ${note_entry.performer.mention} \n - _Date & Time:_ ${note_entry.date_time} \n - _Text:_ ${note_entry.contents}" } \ No newline at end of file diff --git a/src/main/resources/messages/messages.json b/src/main/resources/messages/messages.json index 5322c01..fe64a1b 100644 --- a/src/main/resources/messages/messages.json +++ b/src/main/resources/messages/messages.json @@ -14,6 +14,7 @@ "moderation/unban/info", "moderation/warn/info", "moderation/warn/dm", + "moderation/warn/list", "moderation/unwarn/info", "moderation/error/unwarn/no_case_found", "moderation/error/unwarn/cannot_unwarn_self", @@ -22,5 +23,6 @@ "moderation/error/note/max_amount_of_notes", "moderation/error/note/no_note_found", "moderation/note/add", - "moderation/note/remove" + "moderation/note/remove", + "moderation/note/list" ] \ No newline at end of file diff --git a/src/main/resources/messages/moderation/note/list.json b/src/main/resources/messages/moderation/note/list.json new file mode 100644 index 0000000..db23518 --- /dev/null +++ b/src/main/resources/messages/moderation/note/list.json @@ -0,0 +1,13 @@ +{ + "type": "listing", + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "entry": { + "type": "description", + "text": "" + }, + "empty": "" +} \ No newline at end of file diff --git a/src/main/resources/messages/moderation/warn/list.json b/src/main/resources/messages/moderation/warn/list.json new file mode 100644 index 0000000..b915a59 --- /dev/null +++ b/src/main/resources/messages/moderation/warn/list.json @@ -0,0 +1,13 @@ +{ + "type": "listing", + "color": "${moderation.color}", + "author": { + "name": "", + "icon_url": "${moderation.icon_url}" + }, + "entry": { + "type": "description", + "text": "" + }, + "empty": "" +} \ No newline at end of file