1
0
mirror of https://github.com/sciwhiz12/Janitor.git synced 2024-09-20 01:44:02 +00:00

Externalize messages to JSONs

For ref.: bot console has "reload messages" command to reload the messages from disk
This commit is contained in:
Arnold Alejo Nunag 2020-10-10 16:53:05 +08:00
parent 44a55d3962
commit 46eff3358e
Signed by: sciwhiz12
GPG Key ID: 622CF446534317E1
51 changed files with 1663 additions and 851 deletions

View File

@ -49,6 +49,11 @@ public class BotConsole {
bot.getTranslations().loadTranslations(); bot.getTranslations().loadTranslations();
break outer; break outer;
} }
case "messages": {
CONSOLE.info("Reloading messages");
bot.getMessages().loadMessages();
break outer;
}
} }
} }
default: default:
@ -71,8 +76,7 @@ public class BotConsole {
while (!scanner.hasNextLine()) { while (!scanner.hasNextLine()) {
try { try {
Thread.sleep(150); Thread.sleep(150);
} } catch (InterruptedException e) {
catch (InterruptedException e) {
CONSOLE.warn("Console thread is interrupted"); CONSOLE.warn("Console thread is interrupted");
continue outer; continue outer;
} }
@ -84,8 +88,7 @@ public class BotConsole {
} }
CONSOLE.debug("Received command: {}", input); CONSOLE.debug("Received command: {}", input);
BotConsole.this.parseCommand(input); BotConsole.this.parseCommand(input);
} } catch (Exception e) {
catch (Exception e) {
CONSOLE.error("Error while running console thread", e); CONSOLE.error("Error while running console thread", e);
} }
} }

View File

@ -9,8 +9,8 @@ import net.dv8tion.jda.api.entities.User;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.config.BotConfig; import sciwhiz12.janitor.config.BotConfig;
import sciwhiz12.janitor.msg.Messages; import sciwhiz12.janitor.msg.Messages;
import sciwhiz12.janitor.msg.Substitutions; import sciwhiz12.janitor.msg.TranslationMap;
import sciwhiz12.janitor.msg.Translations; import sciwhiz12.janitor.msg.substitution.SubstitutionMap;
import sciwhiz12.janitor.utils.Util; import sciwhiz12.janitor.utils.Util;
import java.nio.file.Path; import java.nio.file.Path;
@ -22,13 +22,13 @@ import static sciwhiz12.janitor.Logging.STATUS;
public class JanitorBot { public class JanitorBot {
private final JDA discord; private final JDA discord;
private final BotConfig config; private final BotConfig config;
private final Messages messages; private final BotConsole console;
private BotConsole console;
private final GuildStorage storage; private final GuildStorage storage;
private final GuildStorage.SavingThread storageSavingThread; private final GuildStorage.SavingThread storageSavingThread;
private final CommandRegistry cmdRegistry; private final CommandRegistry cmdRegistry;
private final Translations translations; private final TranslationMap translations;
private final Substitutions substitutions; private final SubstitutionMap substitutions;
private final Messages messages;
public JanitorBot(JDA discord, BotConfig config) { public JanitorBot(JDA discord, BotConfig config) {
this.config = config; this.config = config;
@ -36,9 +36,10 @@ public class JanitorBot {
this.console = new BotConsole(this, System.in); this.console = new BotConsole(this, System.in);
this.storage = new GuildStorage(this, Path.of(config.STORAGE_PATH.get())); this.storage = new GuildStorage(this, Path.of(config.STORAGE_PATH.get()));
this.cmdRegistry = new CommandRegistry(this, config.getCommandPrefix()); this.cmdRegistry = new CommandRegistry(this, config.getCommandPrefix());
this.translations = new Translations(this, config.getTranslationsFile()); this.translations = new TranslationMap(this, config.getTranslationsFile());
this.messages = new Messages(this); this.substitutions = new SubstitutionMap(this);
this.substitutions = new Substitutions(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.addEventListener(cmdRegistry);
discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!")); discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!"));
discord.getGuilds().forEach(Guild::loadMembers); discord.getGuilds().forEach(Guild::loadMembers);
@ -67,7 +68,9 @@ public class JanitorBot {
return this.config; return this.config;
} }
public Messages getMessages() { return this.messages; } public Messages getMessages() {
return messages;
}
public GuildStorage getStorage() { return this.storage; } public GuildStorage getStorage() { return this.storage; }
@ -75,7 +78,7 @@ public class JanitorBot {
return this.cmdRegistry; return this.cmdRegistry;
} }
public Translations getTranslations() { public TranslationMap getTranslations() {
return this.translations; return this.translations;
} }
@ -105,7 +108,7 @@ public class JanitorBot {
console.stop(); console.stop();
} }
public Substitutions getSubstitutions() { public SubstitutionMap getSubstitutions() {
return substitutions; return substitutions;
} }
} }

View File

@ -9,6 +9,7 @@ public class Logging {
public static final Marker STATUS = MarkerFactory.getMarker("STATUS"); public static final Marker STATUS = MarkerFactory.getMarker("STATUS");
public static final Marker COMMANDS = MarkerFactory.getMarker("COMMANDS"); public static final Marker COMMANDS = MarkerFactory.getMarker("COMMANDS");
public static final Marker TRANSLATIONS = MarkerFactory.getMarker("TRANSLATIONS"); 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 Marker STORAGE = MarkerFactory.getMarker("STORAGE");
public static final Logger JANITOR = LoggerFactory.getLogger("janitor"); public static final Logger JANITOR = LoggerFactory.getLogger("janitor");

View File

@ -11,6 +11,7 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.commands.util.ModerationHelper; import sciwhiz12.janitor.commands.util.ModerationHelper;
import sciwhiz12.janitor.msg.MessageHelper;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -60,45 +61,77 @@ public class BanCommand extends BaseCommand {
); );
} }
public int run(CommandContext<MessageReceivedEvent> ctx, int days, @Nullable String reason) throws CommandSyntaxException { int run(CommandContext<MessageReceivedEvent> ctx, int days, @Nullable String reason) throws CommandSyntaxException {
realRun(ctx, days, reason);
return 1;
}
void realRun(CommandContext<MessageReceivedEvent> ctx, int days, @Nullable String reason) throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { if (!ctx.getSource().isFromGuild()) {
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); messages().getRegularMessage("general/error/guild_only_command")
return; .apply(MessageHelper.user("performer", ctx.getSource().getAuthor()))
.send(getBot(), channel).queue();
return 1;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
final List<Member> members = getMembers("member", ctx).fromGuild(performer.getGuild()); final List<Member> 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 Member target = members.get(0);
if (guild.getSelfMember().equals(target)) if (guild.getSelfMember().equals(target)) {
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); messages().getRegularMessage("general/error/cannot_action_self")
else if (performer.equals(target)) .apply(MessageHelper.member("performer", performer))
channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue(); .send(getBot(), channel).queue();
else if (!guild.getSelfMember().hasPermission(BAN_PERMISSION))
channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, BAN_PERMISSION).build(getBot())).queue(); } else if (performer.equals(target)) {
else if (!guild.getSelfMember().canInteract(target)) messages().getRegularMessage("general/error/cannot_action_performer")
channel.sendMessage(messages().GENERAL.cannotInteract(performer, target).build(getBot())).queue(); .apply(MessageHelper.member("performer", performer))
else if (!performer.hasPermission(BAN_PERMISSION)) .send(getBot(), channel).queue();
channel.sendMessage(
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, BAN_PERMISSION).build(getBot())) } else if (!guild.getSelfMember().hasPermission(BAN_PERMISSION)) {
.queue(); messages().getRegularMessage("general/error/insufficient_permissions")
else if (!performer.canInteract(target)) .apply(MessageHelper.member("performer", performer))
channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue(); .with("required_permissions", BAN_PERMISSION::toString)
else .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() 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() .mapToResult()
.flatMap(res -> ModerationHelper.banUser(target.getGuild(), performer, target, days, reason) .flatMap(res ->
.flatMap(v -> channel.sendMessage( ModerationHelper.banUser(target.getGuild(), performer, target, days, reason)
messages().MODERATION.banUser(performer, target, reason, days, res.isSuccess()).build(getBot())))) .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(); .queue();
} }
return 1;
}
} }

View File

@ -12,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.commands.util.CommandHelper; import sciwhiz12.janitor.commands.util.CommandHelper;
import sciwhiz12.janitor.commands.util.ModerationHelper; import sciwhiz12.janitor.commands.util.ModerationHelper;
import sciwhiz12.janitor.msg.MessageHelper;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -50,40 +51,72 @@ public class KickCommand extends BaseCommand {
private int runWithReason(CommandContext<MessageReceivedEvent> ctx, @Nullable String reason) throws CommandSyntaxException { private int runWithReason(CommandContext<MessageReceivedEvent> ctx, @Nullable String reason) throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return 1;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
final List<Member> members = getMembers("member", ctx).fromGuild(performer.getGuild()); final List<Member> members = getMembers("member", ctx).fromGuild(performer.getGuild());
if (members.size() < 1) { if (members.size() < 1) { return 1; }
return 1;
}
final Member target = members.get(0); final Member target = members.get(0);
if (guild.getSelfMember().equals(target))
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); if (guild.getSelfMember().equals(target)) {
else if (performer.equals(target)) messages().getRegularMessage("general/error/cannot_action_self")
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); .apply(MessageHelper.member("performer", performer))
else if (!guild.getSelfMember().hasPermission(KICK_PERMISSION)) .send(getBot(), channel).queue();
channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, KICK_PERMISSION).build(getBot())).queue();
else if (!guild.getSelfMember().canInteract(target)) } else if (performer.equals(target)) {
channel.sendMessage(messages().GENERAL.cannotInteract(performer, target).build(getBot())).queue(); messages().getRegularMessage("general/error/cannot_action_performer")
else if (!performer.hasPermission(KICK_PERMISSION)) .apply(MessageHelper.member("performer", performer))
channel.sendMessage( .send(getBot(), channel).queue();
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, KICK_PERMISSION).build(getBot()))
.queue(); } else if (!guild.getSelfMember().hasPermission(KICK_PERMISSION)) {
else if (!performer.canInteract(target)) messages().getRegularMessage("general/error/insufficient_permissions")
channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue(); .apply(MessageHelper.member("performer", performer))
else .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() 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() .mapToResult()
.flatMap(res -> ModerationHelper.kickUser(target.getGuild(), performer, target, reason) .flatMap(res -> ModerationHelper.kickUser(target.getGuild(), performer, target, reason)
.flatMap(v -> channel.sendMessage( .flatMap(v -> messages().getRegularMessage("moderation/kick/info")
messages().MODERATION.kickUser(performer, target, reason, res.isSuccess()).build(getBot())) .apply(MessageHelper.member("performer", performer))
.apply(MessageHelper.member("target", target))
.with("private_message", () -> res.isSuccess() ? "" : "")
.with("reason", () -> reason)
.send(getBot(), channel)
) )
) )
.queue(); .queue();
}
return 1; return 1;
} }
} }

View File

@ -1,6 +1,5 @@
package sciwhiz12.janitor.commands.moderation; package sciwhiz12.janitor.commands.moderation;
import com.google.common.collect.ImmutableMap;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
@ -13,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.moderation.notes.NoteEntry; import sciwhiz12.janitor.moderation.notes.NoteEntry;
import sciwhiz12.janitor.moderation.notes.NoteStorage; import sciwhiz12.janitor.moderation.notes.NoteStorage;
import sciwhiz12.janitor.msg.MessageHelper;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
@ -92,7 +92,10 @@ public class NoteCommand extends BaseCommand {
private int addNote(CommandContext<MessageReceivedEvent> ctx, String noteContents) throws CommandSyntaxException { private int addNote(CommandContext<MessageReceivedEvent> ctx, String noteContents) throws CommandSyntaxException {
final MessageChannel channel = ctx.getSource().getChannel(); final MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return 1;
} }
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
@ -102,24 +105,41 @@ public class NoteCommand extends BaseCommand {
final Member target = members.get(0); final Member target = members.get(0);
final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC); final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC);
if (guild.getSelfMember().equals(target)) if (guild.getSelfMember().equals(target)) {
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); messages().getRegularMessage("general/error/cannot_action_self")
else if (performer.equals(target)) .apply(MessageHelper.member("performer", performer))
channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue(); .send(getBot(), channel).queue();
else if (!performer.hasPermission(NOTE_PERMISSION))
channel.sendMessage( } else if (performer.equals(target)) {
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) messages().getRegularMessage("general/error/cannot_action_performer")
.queue(); .apply(MessageHelper.member("performer", performer))
else { .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 NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild);
final int maxAmount = config().NOTES_MAX_AMOUNT_PER_MOD.get(); final int maxAmount = config().NOTES_MAX_AMOUNT_PER_MOD.get();
if (storage.getAmountOfNotes(target.getUser()) >= maxAmount) { if (storage.getAmountOfNotes(target.getUser()) >= maxAmount) {
channel.sendMessage(messages().MODERATION.ERRORS.maxAmountOfNotes(performer, target, maxAmount).build(getBot())) messages().getRegularMessage("moderation/error/insufficient_permissions")
.queue(); .apply(MessageHelper.member("performer", performer))
.apply(MessageHelper.member("target", target))
.with("notes_amount", () -> String.valueOf(maxAmount))
.send(getBot(), channel).queue();
} else { } else {
final NoteEntry entry = new NoteEntry(performer.getUser(), target.getUser(), dateTime, noteContents); final NoteEntry entry = new NoteEntry(performer.getUser(), target.getUser(), dateTime, noteContents);
int noteID = storage.addNote(entry); 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; return 1;
@ -133,7 +153,10 @@ public class NoteCommand extends BaseCommand {
throws CommandSyntaxException { throws CommandSyntaxException {
final MessageChannel channel = ctx.getSource().getChannel(); final MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return 1;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
@ -145,7 +168,10 @@ public class NoteCommand extends BaseCommand {
if (members.size() < 1) return 1; if (members.size() < 1) return 1;
final Member target = members.get(0); final Member target = members.get(0);
if (guild.getSelfMember().equals(target)) { 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; return 1;
} }
predicate = predicate.and(e -> e.getValue().getTarget().getIdLong() == target.getIdLong()); predicate = predicate.and(e -> e.getValue().getTarget().getIdLong() == target.getIdLong());
@ -163,43 +189,62 @@ public class NoteCommand extends BaseCommand {
case NONE: {} case NONE: {}
} }
if (!performer.hasPermission(NOTE_PERMISSION)) if (!performer.hasPermission(NOTE_PERMISSION)) {
channel.sendMessage( messages().getRegularMessage("moderation/error/insufficient_permissions")
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) .apply(MessageHelper.member("performer", performer))
.queue(); .with("required_permissions", NOTE_PERMISSION::toString)
else .send(getBot(), channel).queue();
channel.sendMessage(messages().MODERATION.noteList(
NoteStorage.get(getBot().getStorage(), guild) } else {
.getNotes() // channel.sendMessage(messages().MODERATION.noteList(
.entrySet().stream() // NoteStorage.get(getBot().getStorage(), guild)
.filter(predicate) // .getNotes()
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)) // .entrySet().stream()
).build(getBot())).queue(); // .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; return 1;
} }
private int removeNote(CommandContext<MessageReceivedEvent> ctx, int noteID) { private int removeNote(CommandContext<MessageReceivedEvent> ctx, int noteID) {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return 1;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
if (!performer.hasPermission(NOTE_PERMISSION)) if (!performer.hasPermission(NOTE_PERMISSION)) {
channel.sendMessage( messages().getRegularMessage("moderation/error/insufficient_permissions")
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, NOTE_PERMISSION).build(getBot())) .apply(MessageHelper.member("performer", performer))
.queue(); .with("required_permissions", NOTE_PERMISSION::toString)
else { .send(getBot(), channel).queue();
} else {
final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild); final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild);
@Nullable @Nullable
final NoteEntry entry = storage.getNote(noteID); final NoteEntry entry = storage.getNote(noteID);
if (entry == null) if (entry == null) {
channel.sendMessage(messages().MODERATION.ERRORS.noNoteFound(performer, noteID).build(getBot())).queue(); messages().getRegularMessage("moderation/note/add")
else { .apply(MessageHelper.member("performer", performer))
.with("note_id", () -> String.valueOf(noteID))
.send(getBot(), channel).queue();
} else {
storage.removeNote(noteID); 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; return 1;

View File

@ -12,6 +12,7 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.commands.util.ModerationHelper; import sciwhiz12.janitor.commands.util.ModerationHelper;
import sciwhiz12.janitor.msg.MessageHelper;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Locale; import java.util.Locale;
@ -49,7 +50,10 @@ public class UnbanCommand extends BaseCommand {
void realNamedRun(CommandContext<MessageReceivedEvent> ctx) { void realNamedRun(CommandContext<MessageReceivedEvent> ctx) {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
@ -62,10 +66,14 @@ public class UnbanCommand extends BaseCommand {
.startsWith(username)) .startsWith(username))
.collect(Collectors.toList())) .collect(Collectors.toList()))
.queue(bans -> { .queue(bans -> {
if (bans.size() > 1) if (bans.size() > 1) {
channel.sendMessage(messages().GENERAL.ambiguousMember(performer).build(getBot())).queue(); messages().getRegularMessage("general/error/ambiguous_member")
else if (bans.size() == 1) .apply(MessageHelper.user("performer", ctx.getSource().getAuthor()))
.send(getBot(), channel).queue();
} else if (bans.size() == 1) {
tryUnban(channel, guild, performer, bans.get(0).getUser()); tryUnban(channel, guild, performer, bans.get(0).getUser());
}
}); });
} }
@ -77,7 +85,10 @@ public class UnbanCommand extends BaseCommand {
void realIdRun(CommandContext<MessageReceivedEvent> ctx) { void realIdRun(CommandContext<MessageReceivedEvent> ctx) {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return;
} }
final Guild guild = ctx.getSource().getGuild(); 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) { void tryUnban(MessageChannel channel, Guild guild, Member performer, User target) {
if (!guild.getSelfMember().hasPermission(UNBAN_PERMISSION)) if (!guild.getSelfMember().hasPermission(UNBAN_PERMISSION)) {
channel.sendMessage(messages().GENERAL.insufficientPermissions(performer, UNBAN_PERMISSION).build(getBot())) messages().getRegularMessage("general/error/insufficient_permissions")
.queue(); .apply(MessageHelper.member("performer", performer))
else if (!performer.hasPermission(UNBAN_PERMISSION)) .with("required_permissions", UNBAN_PERMISSION::toString)
channel.sendMessage( .send(getBot(), channel).queue();
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, UNBAN_PERMISSION).build(getBot()))
.queue(); } else if (!performer.hasPermission(UNBAN_PERMISSION)) {
else 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) 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(); .queue();
} }
}
} }

View File

@ -12,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningEntry;
import sciwhiz12.janitor.moderation.warns.WarningStorage; import sciwhiz12.janitor.moderation.warns.WarningStorage;
import sciwhiz12.janitor.msg.MessageHelper;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Objects; import java.util.Objects;
@ -44,36 +45,54 @@ public class UnwarnCommand extends BaseCommand {
void realRun(CommandContext<MessageReceivedEvent> ctx) { void realRun(CommandContext<MessageReceivedEvent> ctx) {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { 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; return;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
int caseID = IntegerArgumentType.getInteger(ctx, "caseId"); int caseID = IntegerArgumentType.getInteger(ctx, "caseId");
if (!performer.hasPermission(WARN_PERMISSION)) if (!performer.hasPermission(WARN_PERMISSION)) {
channel.sendMessage( messages().getRegularMessage("moderation/error/insufficient_permissions")
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) .apply(MessageHelper.member("performer", performer))
.queue(); .with("required_permissions", WARN_PERMISSION::toString)
else { .send(getBot(), channel).queue();
} else {
final WarningStorage storage = WarningStorage.get(getBot().getStorage(), guild); final WarningStorage storage = WarningStorage.get(getBot().getStorage(), guild);
@Nullable @Nullable
final WarningEntry entry = storage.getWarning(caseID); final WarningEntry entry = storage.getWarning(caseID);
Member temp; Member temp;
if (entry == null) if (entry == null) {
channel.sendMessage(messages().MODERATION.ERRORS.noWarnWithID(performer, caseID).build(getBot())).queue(); messages().getRegularMessage("moderation/error/unwarn/no_case_found")
else if (entry.getWarned().getIdLong() == performer.getIdLong() .apply(MessageHelper.member("performer", performer))
&& !config().WARNINGS_REMOVE_SELF_WARNINGS.get()) .with("case_id", () -> String.valueOf(caseID))
channel.sendMessage(messages().MODERATION.ERRORS.cannotUnwarnSelf(performer, caseID, entry).build(getBot())) .send(getBot(), channel).queue();
.queue();
else if (config().WARNINGS_RESPECT_MOD_ROLES.get() } else if (entry.getWarned().getIdLong() == performer.getIdLong()
&& (temp = guild.getMember(entry.getPerformer())) != null && !config().WARNINGS_REMOVE_SELF_WARNINGS.get()) {
&& !performer.canInteract(temp)) messages().getRegularMessage("moderation/error/unwarn/cannot_unwarn_self")
channel.sendMessage( .apply(MessageHelper.member("performer", performer))
messages().MODERATION.ERRORS.cannotRemoveHigherModerated(performer, caseID, entry).build(getBot())).queue(); .apply(MessageHelper.warningEntry("warning_entry", caseID, entry))
else { .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); 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();
} }
} }
} }

View File

@ -12,6 +12,7 @@ import sciwhiz12.janitor.commands.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningEntry;
import sciwhiz12.janitor.moderation.warns.WarningStorage; import sciwhiz12.janitor.moderation.warns.WarningStorage;
import sciwhiz12.janitor.msg.MessageHelper;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
@ -44,48 +45,70 @@ public class WarnCommand extends BaseCommand {
); );
} }
public int run(CommandContext<MessageReceivedEvent> ctx, String reason) throws CommandSyntaxException { int run(CommandContext<MessageReceivedEvent> ctx, String reason) throws CommandSyntaxException {
realRun(ctx, reason);
return 1;
}
void realRun(CommandContext<MessageReceivedEvent> ctx, String reason) throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { if (!ctx.getSource().isFromGuild()) {
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); messages().getRegularMessage("general/error/guild_only_command")
return; .apply(MessageHelper.user("performer", ctx.getSource().getAuthor()))
.send(getBot(), channel).queue();
return 1;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
final List<Member> members = getMembers("member", ctx).fromGuild(performer.getGuild()); final List<Member> 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 Member target = members.get(0);
final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC); final OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC);
if (guild.getSelfMember().equals(target)) if (guild.getSelfMember().equals(target)) {
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); messages().getRegularMessage("general/error/cannot_action_self")
else if (performer.equals(target)) .apply(MessageHelper.member("performer", performer))
channel.sendMessage(messages().GENERAL.cannotActionPerformer(performer).build(getBot())).queue(); .send(getBot(), channel).queue();
else if (!performer.hasPermission(WARN_PERMISSION))
channel.sendMessage( } else if (performer.equals(target)) {
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) messages().getRegularMessage("general/error/cannot_action_performer")
.queue(); .apply(MessageHelper.member("performer", performer))
else if (!performer.canInteract(target)) .send(getBot(), channel).queue();
channel.sendMessage(messages().MODERATION.ERRORS.cannotInteract(performer, target).build(getBot())).queue();
else if (target.hasPermission(WARN_PERMISSION) && config().WARNINGS_PREVENT_WARNING_MODS.get()) } else if (!performer.hasPermission(WARN_PERMISSION)) {
channel.sendMessage(messages().MODERATION.ERRORS.cannotWarnMods(performer, target).build(getBot())).queue(); messages().getRegularMessage("moderation/error/insufficient_permissions")
else .apply(MessageHelper.member("performer", performer))
target.getUser().openPrivateChannel() .with("required_permissions", WARN_PERMISSION::toString)
.flatMap( .send(getBot(), channel).queue();
dm -> dm.sendMessage(messages().MODERATION.warnedDM(performer, target, reason, dateTime).build(getBot())))
.mapToResult() } else if (!performer.canInteract(target)) {
.flatMap(res -> { 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); WarningEntry entry = new WarningEntry(target.getUser(), performer.getUser(), dateTime, reason);
int caseId = WarningStorage.get(getBot().getStorage(), guild).addWarning(entry); int caseId = WarningStorage.get(getBot().getStorage(), guild).addWarning(entry);
return channel
.sendMessage(messages().MODERATION.warnUser(performer, caseId, entry, res.isSuccess()).build(getBot())); target.getUser().openPrivateChannel()
}) .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 -> 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(); .queue();
} }
return 1;
}
} }

View File

@ -1,6 +1,5 @@
package sciwhiz12.janitor.commands.moderation; package sciwhiz12.janitor.commands.moderation;
import com.google.common.collect.ImmutableMap;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; 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.BaseCommand;
import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.CommandRegistry;
import sciwhiz12.janitor.moderation.warns.WarningEntry; import sciwhiz12.janitor.moderation.warns.WarningEntry;
import sciwhiz12.janitor.moderation.warns.WarningStorage; import sciwhiz12.janitor.msg.MessageHelper;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -53,18 +52,15 @@ public class WarnListCommand extends BaseCommand {
.executes(ctx -> this.run(ctx, false, false)); .executes(ctx -> this.run(ctx, false, false));
} }
public int run(CommandContext<MessageReceivedEvent> ctx, boolean filterTarget, boolean filterModerator) int run(CommandContext<MessageReceivedEvent> ctx, boolean filterTarget, boolean filterModerator)
throws CommandSyntaxException {
realRun(ctx, filterTarget, filterModerator);
return 1;
}
void realRun(CommandContext<MessageReceivedEvent> ctx, boolean filterTarget, boolean filterModerator)
throws CommandSyntaxException { throws CommandSyntaxException {
MessageChannel channel = ctx.getSource().getChannel(); MessageChannel channel = ctx.getSource().getChannel();
if (!ctx.getSource().isFromGuild()) { if (!ctx.getSource().isFromGuild()) {
channel.sendMessage(messages().GENERAL.guildOnlyCommand(ctx.getSource().getAuthor()).build(getBot())).queue(); messages().getRegularMessage("general/error/guild_only_command")
return; .apply(MessageHelper.user("performer", ctx.getSource().getAuthor()))
.send(getBot(), channel).queue();
return 1;
} }
final Guild guild = ctx.getSource().getGuild(); final Guild guild = ctx.getSource().getGuild();
final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); final Member performer = Objects.requireNonNull(ctx.getSource().getMember());
@ -72,33 +68,42 @@ public class WarnListCommand extends BaseCommand {
if (filterTarget) { if (filterTarget) {
final List<Member> members = getMembers("target", ctx).fromGuild(performer.getGuild()); final List<Member> members = getMembers("target", ctx).fromGuild(performer.getGuild());
if (members.size() < 1) return; if (members.size() < 1) return 1;
final Member target = members.get(0); final Member target = members.get(0);
if (guild.getSelfMember().equals(target)) { if (guild.getSelfMember().equals(target)) {
channel.sendMessage(messages().GENERAL.cannotActionSelf(performer).build(getBot())).queue(); messages().getRegularMessage("general/error/cannot_interact")
return; .apply(MessageHelper.member("target", target))
.send(getBot(), channel).queue();
return 1;
} }
predicate = predicate.and(e -> e.getValue().getWarned().getIdLong() == target.getIdLong()); predicate = predicate.and(e -> e.getValue().getWarned().getIdLong() == target.getIdLong());
} }
if (filterModerator) { if (filterModerator) {
final List<Member> members = getMembers("moderator", ctx).fromGuild(performer.getGuild()); final List<Member> members = getMembers("moderator", ctx).fromGuild(performer.getGuild());
if (members.size() < 1) return; if (members.size() < 1) return 1;
final Member mod = members.get(0); final Member mod = members.get(0);
predicate = predicate.and(e -> e.getValue().getPerformer().getIdLong() == mod.getIdLong()); predicate = predicate.and(e -> e.getValue().getPerformer().getIdLong() == mod.getIdLong());
} }
if (!performer.hasPermission(WARN_PERMISSION)) if (!performer.hasPermission(WARN_PERMISSION)) {
channel.sendMessage( messages().getRegularMessage("moderation/error/insufficient_permissions")
messages().MODERATION.ERRORS.performerInsufficientPermissions(performer, WARN_PERMISSION).build(getBot())) .apply(MessageHelper.member("performer", performer))
.queue(); .with("required_permissions", WARN_PERMISSION::toString)
else .send(getBot(), channel).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();
} 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;
} }
} }

View File

@ -7,9 +7,9 @@ import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static sciwhiz12.janitor.msg.MessageHelper.DATE_TIME_FORMAT;
import static sciwhiz12.janitor.utils.Util.nameFor; import static sciwhiz12.janitor.utils.Util.nameFor;
public class ModerationHelper { public class ModerationHelper {
@ -18,7 +18,7 @@ public class ModerationHelper {
auditReason.append("Kicked by ") auditReason.append("Kicked by ")
.append(nameFor(performer.getUser())) .append(nameFor(performer.getUser()))
.append(" on ") .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) if (reason != null)
auditReason.append(" for reason: ").append(reason); auditReason.append(" for reason: ").append(reason);
return guild.kick(target, auditReason.toString()); return guild.kick(target, auditReason.toString());
@ -30,7 +30,7 @@ public class ModerationHelper {
auditReason.append("Banned by ") auditReason.append("Banned by ")
.append(nameFor(performer.getUser())) .append(nameFor(performer.getUser()))
.append(" on ") .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) if (reason != null)
auditReason.append(" for reason: ").append(reason); auditReason.append(" for reason: ").append(reason);
return guild.ban(target, deleteDuration, auditReason.toString()); return guild.ban(target, deleteDuration, auditReason.toString());

View File

@ -23,6 +23,7 @@ public class BotConfig {
public final CommentedConfigSpec.IntValue AUTOSAVE_INTERVAL; public final CommentedConfigSpec.IntValue AUTOSAVE_INTERVAL;
public final CommentedConfigSpec.ConfigValue<String> CUSTOM_TRANSLATION_FILE; public final CommentedConfigSpec.ConfigValue<String> CUSTOM_TRANSLATION_FILE;
public final CommentedConfigSpec.ConfigValue<String> CUSTOM_MESSAGES_DIRECTORY;
public final CommentedConfigSpec.ConfigValue<String> COMMAND_PREFIX; public final CommentedConfigSpec.ConfigValue<String> COMMAND_PREFIX;
@ -67,6 +68,10 @@ public class BotConfig {
.comment("A file which contains custom translation keys to load for messages.", .comment("A file which contains custom translation keys to load for messages.",
"If blank, no file shall be loaded.") "If blank, no file shall be loaded.")
.define("messages.custom_translations", ""); .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 COMMAND_PREFIX = builder
.comment("The prefix for commands.") .comment("The prefix for commands.")
@ -115,8 +120,7 @@ public class BotConfig {
spec.setConfig(config); spec.setConfig(config);
// TODO: config spec // TODO: config spec
FileWatcher.defaultInstance().addWatch(configPath, this::onFileChange); FileWatcher.defaultInstance().addWatch(configPath, this::onFileChange);
} } catch (IOException ex) {
catch (IOException ex) {
JANITOR.error("Error while building config from file {}", configPath, ex); JANITOR.error("Error while building config from file {}", configPath, ex);
} }
} }
@ -134,6 +138,15 @@ public class BotConfig {
.orElse(null); .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() { public String getToken() {
return options.getToken().orElse(CLIENT_TOKEN.get()); return options.getToken().orElse(CLIENT_TOKEN.get());
} }
@ -157,8 +170,7 @@ public class BotConfig {
CONFIG.info("Reloading config due to file change {}", configPath); CONFIG.info("Reloading config due to file change {}", configPath);
config.load(); config.load();
spec.setConfig(config); spec.setConfig(config);
} } catch (Exception ex) {
catch (Exception ex) {
CONFIG.error("Error while reloading config from {}", configPath, ex); CONFIG.error("Error while reloading config from {}", configPath, ex);
} }
} }

View File

@ -4,6 +4,7 @@ import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser; import joptsimple.OptionParser;
import joptsimple.OptionSet; import joptsimple.OptionSet;
import joptsimple.util.PathConverter; import joptsimple.util.PathConverter;
import joptsimple.util.PathProperties;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Optional; import java.util.Optional;
@ -14,6 +15,7 @@ public class BotOptions {
private final OptionSet options; private final OptionSet options;
private final ArgumentAcceptingOptionSpec<Path> configPath; private final ArgumentAcceptingOptionSpec<Path> configPath;
private final ArgumentAcceptingOptionSpec<Path> translationsPath; private final ArgumentAcceptingOptionSpec<Path> translationsPath;
private final ArgumentAcceptingOptionSpec<Path> messagesFolder;
private final ArgumentAcceptingOptionSpec<String> token; private final ArgumentAcceptingOptionSpec<String> token;
private final ArgumentAcceptingOptionSpec<String> prefix; private final ArgumentAcceptingOptionSpec<String> prefix;
private final ArgumentAcceptingOptionSpec<Long> owner; private final ArgumentAcceptingOptionSpec<Long> owner;
@ -28,6 +30,10 @@ public class BotOptions {
.accepts("translations", "The path to the translations file") .accepts("translations", "The path to the translations file")
.withRequiredArg() .withRequiredArg()
.withValuesConvertedBy(new PathConverter(FILE_EXISTING, READABLE)); .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 this.token = parser
.accepts("token", "The Discord token for the bot user") .accepts("token", "The Discord token for the bot user")
.withRequiredArg(); .withRequiredArg();
@ -49,6 +55,10 @@ public class BotOptions {
return translationsPath.valueOptional(options); return translationsPath.valueOptional(options);
} }
public Optional<Path> getMessagesFolder() {
return messagesFolder.valueOptional(options);
}
public Optional<String> getToken() { public Optional<String> getToken() {
return token.valueOptional(options); return token.valueOptional(options);
} }

View File

@ -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<Permission> permissions) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.with("required_permissions", () -> permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")))
.embed(embed -> embed
.setTitle("general.insufficient_permissions.title")
.setDescription("general.insufficient_permissions.description")
)
.field("general.insufficient_permissions.field.permissions", true);
}
public 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);
}
}

View File

@ -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<RegularMessageBuilder> 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<RegularMessageBuilder> mentionable(String head, IMentionable mentionable) {
return builder -> builder
.apply(snowflake(head, mentionable))
.with(head + ".mention", mentionable::getAsMention);
}
public static Consumer<RegularMessageBuilder> 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<RegularMessageBuilder> 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<RegularMessageBuilder> 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<RegularMessageBuilder> 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<RegularMessageBuilder> 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<RegularMessageBuilder> 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();
}

View File

@ -1,97 +1,119 @@
package sciwhiz12.janitor.msg; package sciwhiz12.janitor.msg;
import net.dv8tion.jda.api.entities.Guild; import com.fasterxml.jackson.core.type.TypeReference;
import net.dv8tion.jda.api.entities.IMentionable; import com.fasterxml.jackson.databind.JsonNode;
import net.dv8tion.jda.api.entities.ISnowflake; import com.fasterxml.jackson.databind.ObjectMapper;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.User;
import sciwhiz12.janitor.JanitorBot; import sciwhiz12.janitor.JanitorBot;
import sciwhiz12.janitor.msg.json.RegularMessage;
import java.time.OffsetDateTime; import java.io.IOException;
import java.time.ZoneOffset; 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 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<String>> LIST_TYPE = new TypeReference<>() {};
private final JanitorBot bot; private final JanitorBot bot;
public final General GENERAL; private final Path messagesFolder;
public final Moderation MODERATION; private final Map<String, RegularMessage> regularMessages = new HashMap<>();
private final ObjectMapper jsonMapper = new ObjectMapper();
public Messages(JanitorBot bot) { public Messages(JanitorBot bot, Path messagesFolder) {
this.bot = bot; this.bot = bot;
this.GENERAL = new General(this); this.messagesFolder = messagesFolder;
this.MODERATION = new Moderation(this); loadMessages();
} }
public JanitorBot getBot() { public void loadMessages() {
return bot; 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");
} }
public MessageBuilder message() { if (!success) {
final MessageBuilder builder = new MessageBuilder(); JANITOR.info(MESSAGES, "Loading default messages");
builder.embed() //noinspection UnstableApiUsage
.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); loadMessages(
return builder; file -> new InputStreamReader(getResource(DEFAULT_MESSAGES_FOLDER + file + JSON_FILE_SUFFIX).openStream())
);
}
} }
public MessageBuilder failure() { boolean loadMessages(FileOpener files) {
final MessageBuilder builder = message(); try (Reader keyReader = files.open(MESSAGES_FILENAME)) {
builder.embed() List<String> keysList = jsonMapper.readValue(keyReader, LIST_TYPE);
.setColor(FAILURE_COLOR); regularMessages.clear();
return builder; 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 snowflake(MessageBuilder builder, String head, ISnowflake snowflake) { public Map<String, RegularMessage> getRegularMessages() {
return builder return Collections.unmodifiableMap(regularMessages);
.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) { public RegularMessageBuilder getRegularMessage(String messageKey) {
return builder final RegularMessage msg = regularMessages.get(messageKey);
.apply(b -> snowflake(b, head, mentionable)) if (msg == null) {
.with(head + ".mention", mentionable::getAsMention); 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 role(MessageBuilder builder, String head, Role role) { interface FileOpener {
return builder Reader open(String filePath) throws IOException;
.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) { public static final RegularMessage UNKNOWN_MESSAGE = new RegularMessage(
return builder "UNKNOWN MESSAGE!",
.apply(b -> mentionable(b, head, user)) null,
.with(head + ".name", user::getName) "A message was tried to be looked up, but was not found. Please report this to your bot maintainer/administrator.",
.with(head + ".discriminator", user::getDiscriminator) String.valueOf(0xFF0000),
.with(head + ".tag", user::getAsTag) null,
.with(head + ".flags", user.getFlags()::toString); null,
} null,
null,
public MessageBuilder guild(MessageBuilder builder, String head, Guild guild) { null,
return builder null,
.apply(b -> snowflake(b, head, guild)) null,
.with(head + ".name", guild::getName) Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false))
.with(head + ".description", guild::getDescription) );
.with(head + ".voice_region", guild.getRegion()::toString)
.with(head + ".boost.tier", guild.getBoostTier()::toString)
.with(head + ".boost.count", () -> String.valueOf(guild.getBoostCount()))
.with(head + ".locale", guild.getLocale()::toString)
.with(head + ".verification_level", guild.getVerificationLevel()::toString);
}
public MessageBuilder member(MessageBuilder builder, String head, Member member) {
return builder
.apply(b -> user(b, head, member.getUser()))
.apply(b -> guild(b, head + ".guild", member.getGuild()))
.with(head + ".nickname", member::getNickname)
.with(head + ".effective_name", member::getEffectiveName)
.with(head + ".join_datetime", () -> member.getTimeJoined().format(RFC_1123_DATE_TIME))
.with(head + ".color", () -> String.valueOf(member.getColorRaw()));
}
} }

View File

@ -1,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<Permission> permissions) {
return messages.failure()
.apply(builder -> messages.member(builder, "performer", performer))
.with("required_permissions",
() -> permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")))
.embed(embed -> embed
.setTitle("moderation.insufficient_permissions.title")
.setDescription("moderation.insufficient_permissions.description")
)
.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<Integer, WarningEntry> displayWarnings) {
// return channel.sendMessage(
// moderationEmbed(translate("moderation.warnlist.author"))
// .setDescription(displayWarnings.size() > 0 ? displayWarnings.entrySet().stream()
// .sorted(Collections.reverseOrder(Comparator.comparingInt(Map.Entry::getKey)))
// .limit(10)
// .map(entry ->
// translate("moderation.warnlist.entry",
// entry.getKey(),
// entry.getValue().getWarned().getAsMention(),
// entry.getValue().getPerformer().getAsMention(),
// entry.getValue().getDateTime().format(RFC_1123_DATE_TIME),
// entry.getValue().getReason() != null
// ? entry.getValue().getReason()
// : translate("moderation.warnlist.entry.no_reason"))
// )
// .collect(Collectors.joining("\n"))
// : translate("moderation.warnlist.empty"))
// .build()
// );
return moderation()
.embed(embed -> embed.setTitle("NO OP, CURRENTLY IN PROGRESS"));
}
public MessageBuilder unwarn(final Member performer, final int caseID, final WarningEntry entry) {
return moderation("moderation.unwarn.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> warningEntry(builder, "warning_entry", caseID, entry))
.field("moderation.unwarn.field.performer", true)
.field("moderation.unwarn.field.case_id", true)
.field("moderation.unwarn.field.original_performer", true)
.field("moderation.unwarn.field.original_target", true)
.field("moderation.unwarn.field.date_time", true)
.field("moderation.unwarn.field.reason", true);
}
public void noteEntry(MessageBuilder builder, String head, int noteID, NoteEntry entry) {
builder
.with(head + ".note_id", () -> String.valueOf(noteID))
.apply(b -> messages.user(b, head + ".performer", entry.getPerformer()))
.apply(b -> messages.user(b, head + ".target", entry.getTarget()))
.with(head + ".date_time", () -> entry.getDateTime().format(RFC_1123_DATE_TIME))
.with(head + ".contents", entry::getContents);
}
public MessageBuilder addNote(final Member performer, final int noteID, final NoteEntry entry) {
return moderation("moderation.note.add.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> noteEntry(builder, "note", noteID, entry))
.field("moderation.note.add.field.performer", true)
.field("moderation.note.add.field.target", true)
.field("moderation.note.add.field.note_id", true)
.field("moderation.note.add.field.date_time", true)
.field("moderation.note.add.field.contents", true);
}
public MessageBuilder noteList(final Map<Integer, NoteEntry> displayNotes) {
// return channel.sendMessage(moderationEmbed(translate("moderation.note.list.author"))
// .setDescription(displayNotes.size() > 0 ? displayNotes.entrySet().stream()
// .sorted(Collections.reverseOrder(Comparator.comparingInt(Map.Entry::getKey)))
// .limit(10)
// .map(entry ->
// translate("moderation.note.list.entry",
// entry.getKey(),
// entry.getValue().getTarget().getAsMention(),
// entry.getValue().getPerformer().getAsMention(),
// entry.getValue().getDateTime().format(RFC_1123_DATE_TIME),
// entry.getValue().getContents())
// )
// .collect(Collectors.joining("\n"))
// : translate("moderation.note.list.empty"))
// .build()
// );
return moderation()
.embed(embed -> embed.setTitle("NO OP, CURRENTLY IN PROGRESS"));
}
public MessageBuilder removeNote(final Member performer, final int noteID, final NoteEntry entry) {
return moderation("moderation.note.remove.author")
.apply(builder -> messages.member(builder, "performer", performer))
.apply(builder -> noteEntry(builder, "note", noteID, entry))
.field("moderation.note.remove.field.performer", true)
.field("moderation.note.remove.field.case_id", true)
.field("moderation.note.remove.field.original_performer", true)
.field("moderation.note.remove.field.original_target", true)
.field("moderation.note.remove.field.date_time", true)
.field("moderation.note.remove.field.contents", true);
}
}

View File

@ -11,11 +11,16 @@ import java.nio.file.Path;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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.JANITOR;
import static sciwhiz12.janitor.Logging.TRANSLATIONS; 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 String DEFAULT_TRANSLATIONS_RESOURCE = "english.json";
private static final TypeReference<Map<String, String>> MAP_TYPE = new TypeReference<>() {}; private static final TypeReference<Map<String, String>> MAP_TYPE = new TypeReference<>() {};
@ -24,7 +29,7 @@ public class Translations {
private final Map<String, String> translations = new HashMap<>(); private final Map<String, String> translations = new HashMap<>();
private final ObjectMapper jsonMapper = new ObjectMapper(); private final ObjectMapper jsonMapper = new ObjectMapper();
public Translations(JanitorBot bot, Path translationsFile) { public TranslationMap(JanitorBot bot, Path translationsFile) {
this.bot = bot; this.bot = bot;
this.translationsFile = translationsFile; this.translationsFile = translationsFile;
loadTranslations(); loadTranslations();
@ -42,8 +47,7 @@ public class Translations {
translations.clear(); translations.clear();
translations.putAll(trans); translations.putAll(trans);
JANITOR.info(TRANSLATIONS, "Loaded {} translations from file {}", translations.size(), translationsFile); 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); JANITOR.error(TRANSLATIONS, "Error while loading translations from file {}", translationsFile, e);
loadDefaultTranslations(); loadDefaultTranslations();
} }
@ -59,8 +63,7 @@ public class Translations {
translations.clear(); translations.clear();
translations.putAll(trans); translations.putAll(trans);
JANITOR.info(TRANSLATIONS, "Loaded {} default english translations", translations.size()); 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); JANITOR.error(TRANSLATIONS, "Error while loading default english translations", e);
} }
} }
@ -69,7 +72,9 @@ public class Translations {
return Collections.unmodifiableMap(translations); return Collections.unmodifiableMap(translations);
} }
public String translate(String key, Object... args) { public String translate(String text) {
return String.format(translations.getOrDefault(key, key), args); final Matcher matcher = TRANSLATION_REGEX.matcher(text);
return matcher.replaceAll(
matchResult -> quoteReplacement(translations.getOrDefault(matchResult.group(1), matchResult.group(0))));
} }
} }

View File

@ -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);
}

View File

@ -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<FieldPlacement, MessageEmbed.Field> 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<FieldPlacement, MessageEmbed.Field> 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;
}
}

View File

@ -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<MessageEmbed.Field> 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<MessageEmbed.Field> 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<MessageEmbed.Field> 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<String, String> 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;
}
}

View File

@ -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<RegularMessage> {
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<MessageEmbed.Field> 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<MessageEmbed.Field> readFields(JsonNode node) {
if (node.path("fields").isArray()) {
final ArrayList<MessageEmbed.Field> 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;
}
}

View File

@ -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<String, Supplier<String>> map;
public CustomSubstitutions(Map<String, Supplier<String>> map) {
this.map = map;
}
@Override
public String substitute(String text) {
return SubstitutionMap.substitute(text, map);
}
public Map<String, Supplier<String>> getMap() {
return map;
}
}

View File

@ -0,0 +1,5 @@
package sciwhiz12.janitor.msg.substitution;
public interface ISubstitutor {
String substitute(String text);
}

View File

@ -1,9 +1,11 @@
package sciwhiz12.janitor.msg; package sciwhiz12.janitor.msg.substitution;
import org.apache.commons.collections4.TransformerUtils; import org.apache.commons.collections4.TransformerUtils;
import org.apache.commons.collections4.map.DefaultedMap; import org.apache.commons.collections4.map.DefaultedMap;
import sciwhiz12.janitor.JanitorBot; import sciwhiz12.janitor.JanitorBot;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; 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.Matcher.quoteReplacement;
import static java.util.regex.Pattern.CASE_INSENSITIVE; 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 ARGUMENT_REGEX = Pattern.compile("\\$\\{(.+?)}", CASE_INSENSITIVE);
public static final Pattern NULL_ARGUMENT_REGEX = Pattern.compile("nullcheck;(.+?);(.+)", 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 -> { return matcher.replaceAll(matchResult -> {
final Matcher nullMatcher = NULL_ARGUMENT_REGEX.matcher(matchResult.group(1)); final Matcher nullMatcher = NULL_ARGUMENT_REGEX.matcher(matchResult.group(1));
if (nullMatcher.matches()) { 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 ? return str != null ?
quoteReplacement(str) : quoteReplacement(str) :
quoteReplacement(arguments.getOrDefault(nullMatcher.group(2), () -> nullMatcher.group(2)).get()); quoteReplacement(arguments.getOrDefault(nullMatcher.group(2), () -> nullMatcher.group(2)).get());
@ -34,8 +38,13 @@ public class Substitutions {
private final JanitorBot bot; private final JanitorBot bot;
private final Map<String, Supplier<String>> defaultSubstitutions = new HashMap<>(); private final Map<String, Supplier<String>> defaultSubstitutions = new HashMap<>();
public Substitutions(JanitorBot bot) { public SubstitutionMap(JanitorBot bot) {
this.bot = 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() { public JanitorBot getBot() {
@ -43,17 +52,19 @@ public class Substitutions {
} }
public String substitute(String text) { public String substitute(String text) {
return Substitutions.substitute(text, defaultSubstitutions); return SubstitutionMap.substitute(text, defaultSubstitutions);
} }
public String with(String text, Map<String, Supplier<String>> substitutions) { public String with(String text, Map<String, Supplier<String>> substitutions) {
return Substitutions.substitute( return SubstitutionMap.substitute(text, createDefaultedMap(substitutions));
text, }
DefaultedMap.defaultedMap(substitutions, TransformerUtils.mapTransformer(defaultSubstitutions))
); public CustomSubstitutions with(Map<String, Supplier<String>> customSubstitutions) {
return new CustomSubstitutions(createDefaultedMap(customSubstitutions));
} }
public Map<String, Supplier<String>> createDefaultedMap(Map<String, Supplier<String>> custom) { public Map<String, Supplier<String>> createDefaultedMap(Map<String, Supplier<String>> custom) {
return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(defaultSubstitutions)); return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(defaultSubstitutions));
} }
} }

View File

@ -1,182 +1,118 @@
{ {
"general.guild_only_command.title": "Guild only command!", "general.error.guild_only_command.title": "Guild only command!",
"general.guild_only_command.description": "This command can only be run in a guild channel.", "general.error.guild_only_command.description": "This command can only be run in a guild channel.",
"general.ambiguous_member.title": "Ambiguous member argument!", "general.error.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.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.insufficient_permissions.title": "I have insufficient permissions!", "general.error.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.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.insufficient_permissions.field.permissions.name": "Required permissions", "general.error.insufficient_permissions.field.permissions": "Required permissions",
"general.insufficient_permissions.field.permissions.value": "${required_permissions}", "general.error.cannot_interact.title": "Member is higher than me!",
"general.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.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.cannot_interact.field.target.name": "Target", "general.error.cannot_action_self.title": "Cannot act against myself!",
"general.cannot_interact.field.target.value": "${target.mention}", "general.error.cannot_action_self.description": "Cannot perform action against myself, as that would be counter-intuitive.",
"general.cannot_action_self.title": "Cannot act against myself!", "general.error.cannot_action_performer.title": "Performer cannot act against self!",
"general.cannot_action_self.description": "Cannot perform action against myself, as that would be counter-intuitive.", "general.error.cannot_action_performer.description": "You cannot perform this action against yourself.",
"general.cannot_action_performer.title": "Performer cannot act against self!", "general.error.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.title": "Insufficient permissions.",
"moderation.insufficient_permissions.description": "The performer of this command has insufficient permissions to use this command.", "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": "Performer",
"moderation.insufficient_permissions.field.performer.value": "${performer.mention}", "moderation.insufficient_permissions.field.permissions": "Required permissions",
"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.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.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": "Performer",
"moderation.cannot_interact.field.performer.value": "${performer.mention}", "moderation.cannot_interact.field.target": "Target",
"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.author": "Kicked user from server.",
"moderation.kick.info.field.performer.name": "Performer", "moderation.kick.info.field.performer": "Performer",
"moderation.kick.info.field.performer.value": "${performer.mention}", "moderation.kick.info.field.target": "Target",
"moderation.kick.info.field.target.name": "Target", "moderation.kick.info.field.private_message": "Sent DM",
"moderation.kick.info.field.target.value": "${target.mention}",
"moderation.kick.info.field.reason.name": "Reason", "moderation.kick.info.field.reason.name": "Reason",
"moderation.kick.info.field.reason.value": "${nullcheck;reason;_No reason specified._}", "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.title": "You were kicked from this server.",
"moderation.kick.dm.field.performer.name": "Moderator", "moderation.kick.dm.field.performer": "Moderator",
"moderation.kick.dm.field.performer.value": "${performer.mention}",
"moderation.kick.dm.field.reason.name": "Reason", "moderation.kick.dm.field.reason.name": "Reason",
"moderation.kick.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}", "moderation.kick.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.ban.info.author": "Banned user from server.", "moderation.ban.info.author": "Banned user from server.",
"moderation.ban.info.field.performer.name": "Performer", "moderation.ban.info.field.performer": "Performer",
"moderation.ban.info.field.performer.value": "${performer.mention}", "moderation.ban.info.field.target": "Target",
"moderation.ban.info.field.target.name": "Target", "moderation.ban.info.field.private_message": "Sent DM",
"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.name": "Message Deletion",
"moderation.ban.info.field.delete_duration.value": "${delete_duration} day(s)", "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.title": "You were banned from this server.",
"moderation.ban.dm.field.performer.name": "Moderator", "moderation.ban.dm.field.performer": "Moderator",
"moderation.ban.dm.field.performer.value": "${performer.mention}",
"moderation.ban.dm.field.reason.name": "Reason", "moderation.ban.dm.field.reason.name": "Reason",
"moderation.ban.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}", "moderation.ban.dm.field.reason.value": "${nullcheck;reason;_No reason specified._}",
"moderation.unban.info.author": "Unbanned user from server.", "moderation.unban.info.author": "Unbanned user from server.",
"moderation.unban.info.field.performer.name": "Performer", "moderation.unban.info.field.performer": "Performer",
"moderation.unban.info.field.performer.value": "${performer.mention}", "moderation.unban.info.field.target": "Target",
"moderation.unban.info.field.target.name": "Target",
"moderation.unban.info.field.target.value": "${target.mention}",
"moderation.warn.info.author": "Warned user.", "moderation.warn.info.author": "Warned user.",
"moderation.warn.info.field.performer.name": "Performer", "moderation.warn.info.field.performer": "Performer",
"moderation.warn.info.field.performer.value": "${warning_entry.performer.mention}", "moderation.warn.info.field.target": "Target",
"moderation.warn.info.field.target.name": "Target", "moderation.warn.info.field.case_id": "Case ID",
"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.name": "Reason",
"moderation.warn.info.field.reason.value": "${nullcheck;warning_entry.reason;_No reason specified._}", "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 DM",
"moderation.warn.info.field.private_message.sent.value": "✅", "moderation.warn.info.field.date_time": "Date & Time",
"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.title": "You were warned by a moderator.",
"moderation.warn.dm.field.performer.name": "Moderator", "moderation.warn.dm.field.performer": "Moderator",
"moderation.warn.dm.field.performer.value": "${performer.mention}", "moderation.warn.dm.field.date_time": "Date & Time",
"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.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.author": "Listing of Warnings",
"moderation.warnlist.empty": "**_No warnings logged matching your query._**", "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": "**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.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.author": "Listing of Notes",
"moderation.note.list.empty": "**_No recorded notes matching your query._**", "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": "**#%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}"
} }

View File

@ -0,0 +1,5 @@
{
"color": "${general.error.color}",
"title": "<general.error.ambiguous_member.title>",
"description": "<general.error.ambiguous_member.description>"
}

View File

@ -0,0 +1,12 @@
{
"color": "${general.error.color}",
"title": "<general.error.cannot_action_performer.title>",
"description": "<general.error.cannot_action_performer.description>",
"fields": [
{
"name": "<general.error.cannot_action_performer.field.performer>",
"value": "${performer.mention}",
"inline": true
}
]
}

View File

@ -0,0 +1,5 @@
{
"color": "${general.error.color}",
"title": "<general.error.cannot_action_self.title>",
"description": "<general.error.cannot_action_self.description>"
}

View File

@ -0,0 +1,12 @@
{
"color": "${general.error.color}",
"title": "<general.error.cannot_interact.title>",
"description": "<general.error.cannot_interact.description>",
"fields": [
{
"name": "<general.error.cannot_interact.field.target>",
"value": "${target.mention}",
"inline": true
}
]
}

View File

@ -0,0 +1,5 @@
{
"color": "${general.error.color}",
"title": "<general.error.guild_only_command.title>",
"description": "<general.error.guild_only_command.description>"
}

View File

@ -0,0 +1,12 @@
{
"color": "${general.error.color}",
"title": "<general.error.insufficient_permissions.title>",
"description": "<general.error.insufficient_permissions.description>",
"fields": [
{
"name": "<general.error.insufficient_permissions.field.permissions>",
"value": "${required_permissions}",
"inline": true
}
]
}

View File

@ -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"
]

View File

@ -0,0 +1,20 @@
{
"color": "${moderation.color}",
"author": {
"name": "${performer.guild.name}",
"icon_url": "${performer.guild.icon_url}"
},
"title": "<moderation.ban.dm.title>",
"fields": [
{
"name": "<moderation.ban.dm.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.ban.dm.field.reason.name>",
"value": "<moderation.ban.dm.field.reason.value>",
"inline": true
}
]
}

View File

@ -0,0 +1,34 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.ban.info.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.ban.info.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.ban.info.field.target>",
"value": "${target.mention}",
"inline": true
},
{
"name": "<moderation.ban.info.field.private_message>",
"value": "${private_message}",
"inline": true
},
{
"name": "<moderation.ban.info.field.delete_duration.name>",
"value": "<moderation.ban.info.field.delete_duration.value>",
"inline": true
},
{
"name": "<moderation.ban.info.field.reason.name>",
"value": "<moderation.ban.info.field.reason.value>",
"inline": false
}
]
}

View File

@ -0,0 +1,17 @@
{
"color": "${general.error.color}",
"title": "<moderation.cannot_interact.title>",
"description": "<moderation.cannot_interact.description>",
"fields": [
{
"name": "<moderation.cannot_interact.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.cannot_interact.field.target>",
"value": "${target.mention}",
"inline": true
}
]
}

View File

@ -0,0 +1,17 @@
{
"color": "${general.error.color}",
"title": "<moderation.insufficient_permissions.title>",
"description": "<moderation.insufficient_permissions.description>",
"fields": [
{
"name": "<moderation.insufficient_permissions.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.insufficient_permissions.field.permissions>",
"value": "${required_permissions}",
"inline": true
}
]
}

View File

@ -0,0 +1,22 @@
{
"color": "${general.error.color}",
"title": "<moderation.note.max_amount_of_notes.title>",
"description": "<moderation.note.max_amount_of_notes.description>",
"fields": [
{
"name": "<moderation.note.max_amount_of_notes.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.note.max_amount_of_notes.field.target>",
"value": "${target.mention}",
"inline": true
},
{
"name": "<moderation.note.max_amount_of_notes.field.amount>",
"value": "${notes_amount}",
"inline": true
}
]
}

View File

@ -0,0 +1,17 @@
{
"color": "${general.error.color}",
"title": "<moderation.note.no_note_found.title>",
"description": "<moderation.note.no_note_found.description>",
"fields": [
{
"name": "<moderation.note.no_note_found.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.note.no_note_found.field.note_id>",
"value": "${note_id}",
"inline": true
}
]
}

View File

@ -0,0 +1,22 @@
{
"color": "${general.error.color}",
"title": "<moderation.warn.cannot_remove_higher_mod.title>",
"description": "<moderation.warn.cannot_remove_higher_mod.description>",
"fields": [
{
"name": "<moderation.warn.cannot_remove_higher_mod.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.warn.cannot_remove_higher_mod.field.original_performer>",
"value": "${warning_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.warn.cannot_remove_higher_mod.field.case_id>",
"value": "${warning_entry.case_id}",
"inline": true
}
]
}

View File

@ -0,0 +1,22 @@
{
"color": "${general.error.color}",
"title": "<moderation.unwarn.cannot_unwarn_self.title>",
"description": "<moderation.unwarn.cannot_unwarn_self.description>",
"fields": [
{
"name": "<moderation.unwarn.cannot_unwarn_self.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.unwarn.cannot_unwarn_self.field.original_performer>",
"value": "${warning_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.unwarn.cannot_unwarn_self.field.case_id>",
"value": "${warning_entry.case_id}",
"inline": true
}
]
}

View File

@ -0,0 +1,17 @@
{
"color": "${general.error.color}",
"title": "<moderation.unwarn.no_case_found.title>",
"description": "<moderation.unwarn.no_case_found.description>",
"fields": [
{
"name": "<moderation.unwarn.no_case_found.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.unwarn.no_case_found.field.case_id>",
"value": "${case_id}",
"inline": true
}
]
}

View File

@ -0,0 +1,17 @@
{
"color": "${general.error.color}",
"title": "<moderation.warn.cannot_warn_mods.title>",
"description": "<moderation.warn.cannot_warn_mods.description>",
"fields": [
{
"name": "<moderation.warn.cannot_warn_mods.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.warn.cannot_warn_mods.field.target>",
"value": "${target.mention}",
"inline": true
}
]
}

View File

@ -0,0 +1,20 @@
{
"color": "${moderation.color}",
"author": {
"name": "${performer.guild.name}",
"icon_url": "${performer.guild.icon_url}"
},
"title": "<moderation.kick.dm.title>",
"fields": [
{
"name": "<moderation.kick.dm.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.kick.dm.field.reason.name>",
"value": "<moderation.kick.dm.field.reason.value>",
"inline": true
}
]
}

View File

@ -0,0 +1,29 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.kick.info.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.kick.info.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.kick.info.field.target>",
"value": "${target.mention}",
"inline": true
},
{
"name": "<moderation.kick.info.field.private_message>",
"value": "${private_message}",
"inline": true
},
{
"name": "<moderation.kick.info.field.reason.name>",
"value": "<moderation.kick.info.field.reason.value>",
"inline": false
}
]
}

View File

@ -0,0 +1,34 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.note.add.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.note.add.field.performer>",
"value": "${note_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.note.add.field.target>",
"value": "${note_entry.target.mention}",
"inline": true
},
{
"name": "<moderation.note.add.field.note_id>",
"value": "${note_entry.note_id}",
"inline": true
},
{
"name": "<moderation.note.add.field.date_time>",
"value": "${note_entry.date_time}",
"inline": true
},
{
"name": "<moderation.note.add.field.contents>",
"value": "${note_entry.contents}",
"inline": false
}
]
}

View File

@ -0,0 +1,39 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.note.remove.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.note.remove.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.note.remove.field.original_performer>",
"value": "${note_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.note.remove.field.original_target>",
"value": "${note_entry.target.mention}",
"inline": true
},
{
"name": "<moderation.note.remove.field.note_id>",
"value": "${note_entry.note_id}",
"inline": true
},
{
"name": "<moderation.note.remove.field.date_time>",
"value": "${note_entry.date_time}",
"inline": true
},
{
"name": "<moderation.note.remove.field.contents>",
"value": "${note_entry.contents}",
"inline": false
}
]
}

View File

@ -0,0 +1,19 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.unban.info.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.unban.info.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.unban.info.field.target>",
"value": "${target.mention}",
"inline": true
}
]
}

View File

@ -0,0 +1,39 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.unwarn.info.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.unwarn.info.field.performer>",
"value": "${performer.mention}",
"inline": true
},
{
"name": "<moderation.unwarn.info.field.original_target>",
"value": "${warning_entry.target.mention}",
"inline": true
},
{
"name": "<moderation.unwarn.info.field.original_performer>",
"value": "${warning_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.unwarn.info.field.case_id>",
"value": "${warning_entry.case_id}",
"inline": true
},
{
"name": "<moderation.unwarn.info.field.date_time>",
"value": "${warning_entry.date_time}",
"inline": true
},
{
"name": "<moderation.unwarn.info.field.reason.name>",
"value": "<moderation.unwarn.info.field.reason.value>",
"inline": false
}
]
}

View File

@ -0,0 +1,25 @@
{
"color": "${moderation.color}",
"author": {
"name": "${performer.guild.name}",
"icon_url": "${performer.guild.icon_url}"
},
"title": "<moderation.warn.dm.title>",
"fields": [
{
"name": "<moderation.warn.dm.field.performer>",
"value": "${warning_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.warn.dm.field.date_time>",
"value": "${warning_entry.date_time}",
"inline": true
},
{
"name": "<moderation.warn.dm.field.reason.name>",
"value": "<moderation.warn.dm.field.reason.value>",
"inline": false
}
]
}

View File

@ -0,0 +1,39 @@
{
"color": "${moderation.color}",
"author": {
"name": "<moderation.warn.info.author>",
"icon_url": "${moderation.icon_url}"
},
"fields": [
{
"name": "<moderation.warn.info.field.performer>",
"value": "${warning_entry.performer.mention}",
"inline": true
},
{
"name": "<moderation.warn.info.field.target>",
"value": "${warning_entry.target.mention}",
"inline": true
},
{
"name": "<moderation.warn.info.field.case_id>",
"value": "${warning_entry.case_id}",
"inline": true
},
{
"name": "<moderation.warn.info.field.private_message>",
"value": "${private_message}",
"inline": true
},
{
"name": "<moderation.warn.info.field.date_time>",
"value": "${warning_entry.date_time}",
"inline": true
},
{
"name": "<moderation.warn.info.field.reason.name>",
"value": "<moderation.warn.info.field.reason.value>",
"inline": false
}
]
}