From 09aa6a269c3d682ced3edb732bb234280dd19c10 Mon Sep 17 00:00:00 2001 From: Arnold Alejo Nunag Date: Fri, 16 Oct 2020 18:12:50 +0800 Subject: [PATCH] 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