diff --git a/src/main/java/sciwhiz12/janitor/JanitorBot.java b/src/main/java/sciwhiz12/janitor/JanitorBot.java index 241893e..79af96b 100644 --- a/src/main/java/sciwhiz12/janitor/JanitorBot.java +++ b/src/main/java/sciwhiz12/janitor/JanitorBot.java @@ -8,6 +8,8 @@ import net.dv8tion.jda.api.entities.PrivateChannel; import net.dv8tion.jda.api.entities.User; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.config.BotConfig; +import sciwhiz12.janitor.msg.Messages; +import sciwhiz12.janitor.msg.Translations; import sciwhiz12.janitor.utils.Util; import java.util.concurrent.CompletableFuture; @@ -18,14 +20,18 @@ import static sciwhiz12.janitor.Logging.STATUS; public class JanitorBot { private final JDA discord; private final BotConfig config; - private final BotConsole console; - private final CommandRegistry cmdRegistry; + private final Messages messages; + private BotConsole console; + private CommandRegistry cmdRegistry; + private Translations translations; public JanitorBot(JDA discord, BotConfig config) { this.config = config; this.console = new BotConsole(this, System.in); this.cmdRegistry = new CommandRegistry(this, config.getCommandPrefix()); this.discord = discord; + this.translations = new Translations(this, config.getTranslationsFile()); + this.messages = new Messages(this); discord.addEventListener(cmdRegistry); discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!")); discord.getGuilds().forEach(Guild::loadMembers); @@ -52,10 +58,16 @@ public class JanitorBot { return this.config; } + public Messages getMessages() { return this.messages; } + public CommandRegistry getCommandRegistry() { return this.cmdRegistry; } + public Translations getTranslations() { + return this.translations; + } + public void shutdown() { JANITOR.info(STATUS, "Shutting down!"); console.stop(); diff --git a/src/main/java/sciwhiz12/janitor/Logging.java b/src/main/java/sciwhiz12/janitor/Logging.java index a9c0c13..8cb8d42 100644 --- a/src/main/java/sciwhiz12/janitor/Logging.java +++ b/src/main/java/sciwhiz12/janitor/Logging.java @@ -8,6 +8,7 @@ import org.slf4j.MarkerFactory; public class Logging { public static final Marker STATUS = MarkerFactory.getMarker("STATUS"); public static final Marker COMMANDS = MarkerFactory.getMarker("COMMANDS"); + public static final Marker TRANSLATIONS = MarkerFactory.getMarker("TRANSLATIONS"); public static final Logger JANITOR = LoggerFactory.getLogger("janitor"); public static final Logger CONSOLE = LoggerFactory.getLogger("janitor.console"); diff --git a/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java b/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java index 2eacf40..61fa91a 100644 --- a/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java +++ b/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java @@ -74,7 +74,8 @@ public class CommandRegistry implements EventListener { } JANITOR.debug(COMMANDS, "Executing command."); dispatcher.execute(parseResults); - } catch (CommandSyntaxException ex) { + } + catch (CommandSyntaxException ex) { JANITOR.error(COMMANDS, "Error while parsing message and executing command", ex); } } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java index 6ac7a1b..e2bb62f 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java @@ -4,6 +4,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; @@ -12,11 +13,10 @@ import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.commands.util.CommandHelper; import sciwhiz12.janitor.commands.util.ModerationHelper; -import sciwhiz12.janitor.msg.General; -import sciwhiz12.janitor.msg.Moderation; import java.util.EnumSet; import java.util.List; +import java.util.Objects; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; @@ -50,20 +50,37 @@ public class KickCommand extends BaseCommand { private int runWithReason(CommandContext ctx, @Nullable String reason) throws CommandSyntaxException { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { - General.guildOnlyCommand(channel).queue(); + getBot().getMessages().GENERAL.guildOnlyCommand(channel).queue(); return 1; } - Member performer = ctx.getSource().getMember(); - if (performer == null) return 1; - List members = getMembers("member", ctx).fromGuild(performer.getGuild()); - Member target = members.get(0); - if (ModerationHelper.ensurePermissions(channel, performer, target, KICK_PERMISSION)) { - target.getUser().openPrivateChannel() - .flatMap(dm -> Moderation.kickedDM(dm, performer, target, reason)) - .flatMap(v -> ModerationHelper.kickUser(target.getGuild(), performer, target, reason)) - .flatMap(v -> Moderation.kickUser(ctx.getSource().getChannel(), performer, target, reason)) - .queue(); + final Guild guild = ctx.getSource().getGuild(); + final Member performer = Objects.requireNonNull(ctx.getSource().getMember()); + final List members = getMembers("member", ctx).fromGuild(performer.getGuild()); + if (members.size() < 1) { + return 1; } + final Member target = members.get(0); + if (!guild.getSelfMember().hasPermission(KICK_PERMISSION)) { + getBot().getMessages().GENERAL.insufficientPermissions(channel, KICK_PERMISSION).queue(); + return 1; + } + if (!guild.getSelfMember().canInteract(target)) { + getBot().getMessages().GENERAL.cannotInteract(channel, target).queue(); + return 1; + } + if (!performer.hasPermission(KICK_PERMISSION)) { + getBot().getMessages().MODERATION.performerInsufficientPermissions(channel, performer, KICK_PERMISSION).queue(); + return 1; + } + if (!performer.canInteract(target)) { + getBot().getMessages().MODERATION.cannotModerate(channel, performer, target).queue(); + return 1; + } + target.getUser().openPrivateChannel() + .flatMap(dm -> getBot().getMessages().MODERATION.kickedDM(dm, performer, target, reason)) + .flatMap(v -> ModerationHelper.kickUser(target.getGuild(), performer, target, reason)) + .flatMap(v -> getBot().getMessages().MODERATION.kickUser(channel, performer, target, reason)) + .queue(); return 1; } } diff --git a/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java index 22be29b..7aab24f 100644 --- a/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java +++ b/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java @@ -3,37 +3,15 @@ package sciwhiz12.janitor.commands.util; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.msg.General; - -import java.util.EnumSet; public class CommandHelper { public static LiteralArgumentBuilder literal(String command) { return LiteralArgumentBuilder.literal(command); } - public static RequiredArgumentBuilder argument(String command, ArgumentType argument) { + public static RequiredArgumentBuilder argument(String command, + ArgumentType argument) { return RequiredArgumentBuilder.argument(command, argument); } - - public static boolean canInteract(MessageChannel response, Member target) { - if (!target.getGuild().getSelfMember().canInteract(target)) { - General.cannotInteract(response, target).queue(); - return false; - } - return true; - } - - public static boolean hasPermission(MessageChannel response, Guild guild, EnumSet permissions) { - if (!guild.getSelfMember().hasPermission(permissions)) { - General.insufficientPermissions(response, permissions).queue(); - return false; - } - return true; - } } diff --git a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java index 6435241..d8b88c4 100644 --- a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java +++ b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java @@ -1,36 +1,18 @@ package sciwhiz12.janitor.commands.util; -import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; import org.checkerframework.checker.nullness.qual.Nullable; -import sciwhiz12.janitor.msg.General; -import sciwhiz12.janitor.msg.Moderation; -import java.util.EnumSet; +import static sciwhiz12.janitor.utils.Util.nameFor; public class ModerationHelper { public static AuditableRestAction kickUser(Guild guild, Member performer, Member target, @Nullable String reason) { StringBuilder auditReason = new StringBuilder(); - auditReason.append("Kicked by ").append(General.nameFor(performer.getUser())); + auditReason.append("Kicked by ").append(nameFor(performer.getUser())); if (reason != null) auditReason.append(" for reason: ").append(reason); return guild.kick(target, auditReason.toString()); } - - public static boolean ensurePermissions(MessageChannel channel, Member performer, Member target, EnumSet permissions) { - if (!CommandHelper.hasPermission(channel, target.getGuild(), permissions)) return false; - if (!CommandHelper.canInteract(channel, target)) return false; - if (!performer.hasPermission(permissions)) { - Moderation.performerInsufficientPermissions(channel, performer, permissions).queue(); - return false; - } - if (!performer.canInteract(target)) { - Moderation.cannotModerate(channel, performer, target).queue(); - return false; - } - return true; - } } diff --git a/src/main/java/sciwhiz12/janitor/config/BotConfig.java b/src/main/java/sciwhiz12/janitor/config/BotConfig.java index 2e68854..3868e73 100644 --- a/src/main/java/sciwhiz12/janitor/config/BotConfig.java +++ b/src/main/java/sciwhiz12/janitor/config/BotConfig.java @@ -3,6 +3,7 @@ package sciwhiz12.janitor.config; import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.file.FileWatcher; import com.electronwill.nightconfig.toml.TomlFormat; +import org.checkerframework.checker.nullness.qual.Nullable; import java.io.IOException; import java.nio.file.Path; @@ -15,6 +16,7 @@ public class BotConfig { public static final Path DEFAULT_CONFIG_PATH = Path.of("config.toml"); public static final String CLIENT_TOKEN = "discord.client_token"; public static final String OWNER_ID = "discord.owner_id"; + public static final String TRANSLATION_FILE_PATH = "messages.translation_file"; public static final String COMMAND_PREFIX = "commands.prefix"; private final BotOptions options; @@ -33,7 +35,8 @@ public class BotConfig { config.load(); // TODO: config spec FileWatcher.defaultInstance().addWatch(configPath, this::onFileChange); - } catch (IOException ex) { + } + catch (IOException ex) { JANITOR.error("Error while building config from file {}", configPath, ex); } } @@ -42,6 +45,13 @@ public class BotConfig { return config; } + @Nullable + public Path getTranslationsFile() { + return options.getTranslationsFile(). + or(() -> Optional.ofNullable(config.get(TRANSLATION_FILE_PATH)).map(Path::of)) + .orElse(null); + } + public Optional getToken() { return options.getToken().or(() -> config.getOptional(CLIENT_TOKEN)); } @@ -62,7 +72,8 @@ public class BotConfig { try { CONFIG.info("Reloading config due to file change {}", configPath); config.load(); - } catch (Exception ex) { + } + catch (Exception ex) { CONFIG.error("Error while reloading config from {}", configPath, ex); } } diff --git a/src/main/java/sciwhiz12/janitor/config/BotOptions.java b/src/main/java/sciwhiz12/janitor/config/BotOptions.java index 1e89b08..a61fb0c 100644 --- a/src/main/java/sciwhiz12/janitor/config/BotOptions.java +++ b/src/main/java/sciwhiz12/janitor/config/BotOptions.java @@ -13,6 +13,7 @@ import static joptsimple.util.PathProperties.*; public class BotOptions { private final OptionSet options; private final ArgumentAcceptingOptionSpec configPath; + private final ArgumentAcceptingOptionSpec translationsPath; private final ArgumentAcceptingOptionSpec token; private final ArgumentAcceptingOptionSpec prefix; private final ArgumentAcceptingOptionSpec owner; @@ -23,6 +24,10 @@ public class BotOptions { .accepts("config", "The path to the config file; defaults to 'config.toml'") .withRequiredArg() .withValuesConvertedBy(new PathConverter(FILE_EXISTING, READABLE, WRITABLE)); + this.translationsPath = parser + .accepts("translations", "The path to the translations file") + .withRequiredArg() + .withValuesConvertedBy(new PathConverter(FILE_EXISTING, READABLE)); this.token = parser .accepts("token", "The Discord token for the bot user") .withRequiredArg(); @@ -40,6 +45,10 @@ public class BotOptions { return configPath.valueOptional(options); } + public Optional getTranslationsFile() { + return translationsPath.valueOptional(options); + } + public Optional getToken() { return token.valueOptional(options); } diff --git a/src/main/java/sciwhiz12/janitor/msg/General.java b/src/main/java/sciwhiz12/janitor/msg/General.java deleted file mode 100644 index f3fea43..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/General.java +++ /dev/null @@ -1,67 +0,0 @@ -package sciwhiz12.janitor.msg; - -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.requests.RestAction; - -import java.util.EnumSet; -import java.util.stream.Collectors; - -public final class General { - public static final int FAILURE_COLOR = 0xF73132; - - private General() { - } - - public static RestAction guildOnlyCommand(MessageChannel channel) { - return channel.sendMessage( - new EmbedBuilder() - .setTitle("Guild only command!") - .setDescription("The previous command can only be run in a guild channel.") - .setColor(FAILURE_COLOR) - .build() - ); - } - - public static RestAction insufficientPermissions(MessageChannel channel, EnumSet permissions) { - return channel.sendMessage( - new EmbedBuilder() - .setTitle("I have insufficient permissions!") - .setDescription("I do not have sufficient permissions to carry out this action!\n" + - "Please contact your server admins if you believe this is in error.") - .addField(new MessageEmbed.Field( - "Required permissions", - permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), - false)) - .setColor(FAILURE_COLOR) - .build() - ); - } - - public static RestAction ambiguousMember(MessageChannel channel) { - return channel.sendMessage( - new EmbedBuilder() - .setTitle("Ambiguous member argument!") - .setDescription("The name you have specified is too ambiguous (leads to more than 1 member)!\n" + - "Please narrow down the specified name until it can uniquely identify a member of this guild.") - .setColor(FAILURE_COLOR) - .build() - ); - } - - public static RestAction cannotInteract(MessageChannel channel, Member target) { - return channel.sendMessage( - new EmbedBuilder() - .setTitle("Member is higher than me!") - .setDescription("Cannot perform action on the given member, as they higher up in the hierarchy than me.") - .addField("Target", nameFor(target.getUser()), true) - .setColor(General.FAILURE_COLOR) - .build() - ); - } - - public static String nameFor(User user) { - return user.getName().concat("#").concat(user.getDiscriminator()); - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/Messages.java b/src/main/java/sciwhiz12/janitor/msg/Messages.java new file mode 100644 index 0000000..5ff2767 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/Messages.java @@ -0,0 +1,142 @@ +package sciwhiz12.janitor.msg; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.restaction.MessageAction; +import org.checkerframework.checker.nullness.qual.Nullable; +import sciwhiz12.janitor.JanitorBot; + +import java.util.EnumSet; +import java.util.stream.Collectors; + +import static sciwhiz12.janitor.utils.Util.nameFor; + +public class Messages { + private final JanitorBot bot; + public final General GENERAL; + public final Moderation MODERATION; + + public Messages(JanitorBot bot) { + this.bot = bot; + this.GENERAL = new General(); + this.MODERATION = new Moderation(); + } + + public String translate(String key, Object... args) { + return bot.getTranslations().translate(key, args); + } + + public final class General { + public static final int FAILURE_COLOR = 0xF73132; + + private General() {} + + public RestAction guildOnlyCommand(MessageChannel channel) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle(translate("general.guild_only_command.title")) + .setDescription(translate("general.guild_only_command.desc")) + .setColor(FAILURE_COLOR) + .build() + ); + } + + public RestAction insufficientPermissions(MessageChannel channel, EnumSet permissions) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle(translate("general.insufficient_permissions.title")) + .setDescription(translate("general.insufficient_permissions.desc")) + .addField(new MessageEmbed.Field( + translate("general.insufficient_permissions.field.permissions"), + permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), + false)) + .setColor(FAILURE_COLOR) + .build() + ); + } + + public RestAction ambiguousMember(MessageChannel channel) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle(translate("general.ambiguous_member.title")) + .setDescription(translate("general.ambiguous_member.desc")) + .setColor(FAILURE_COLOR) + .build() + ); + } + + public RestAction cannotInteract(MessageChannel channel, Member target) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle(translate("general.cannot_interact.title")) + .setDescription(translate("general.cannot_interact.desc")) + .addField(translate("general.cannot_interact.field.target"), nameFor(target.getUser()), true) + .setColor(General.FAILURE_COLOR) + .build() + ); + } + } + + 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 Moderation() {} + + public MessageAction performerInsufficientPermissions(MessageChannel channel, Member performer, + EnumSet permissions) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle(translate("moderation.insufficient_permissions.title")) + .setDescription(translate("moderation.insufficient_permissions.desc")) + .addField(translate("moderation.insufficient_permissions.field.performer"), nameFor(performer.getUser()), + true) + .addField(new MessageEmbed.Field( + translate("moderation.insufficient_permissions.field.permissions"), + permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), + true)) + .setColor(General.FAILURE_COLOR) + .build() + ); + } + + public MessageAction cannotModerate(MessageChannel channel, Member performer, Member target) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle(translate("moderation.cannot_interact.title")) + .setDescription(translate("moderation.cannot_interact.desc")) + .addField(translate("moderation.cannot_interact.field.performer"), nameFor(performer.getUser()), true) + .addField(translate("moderation.cannot_interact.field.target"), nameFor(target.getUser()), true) + .setColor(General.FAILURE_COLOR) + .build() + ); + } + + public MessageAction kickUser(MessageChannel channel, Member performer, Member target, @Nullable String reason) { + final EmbedBuilder embed = new EmbedBuilder() + .setAuthor(translate("moderation.kick.info.author"), null, GAVEL_ICON_URL) + .addField(translate("moderation.kick.info.field.performer"), nameFor(performer.getUser()), true) + .addField(translate("moderation.kick.info.field.target"), nameFor(target.getUser()), true); + if (reason != null) + embed.addField(translate("moderation.kick.info.field.reason"), reason, false); + return channel.sendMessage(embed.setColor(MODERATION_COLOR).build()); + } + + public MessageAction kickedDM(MessageChannel channel, Member performer, Member target, @Nullable String reason) { + final EmbedBuilder embed = new EmbedBuilder() + .setAuthor(performer.getGuild().getName(), null, performer.getGuild().getIconUrl()) + .setTitle(translate("moderation.kick.dm.title")) + .addField(translate("moderation.kick.dm.field.performer"), nameFor(performer.getUser()), true); + if (reason != null) + embed.addField(translate("moderation.kick.dm.field.reason"), reason, false); + return channel.sendMessage(embed.setColor(MODERATION_COLOR).build()); + } + } + +} diff --git a/src/main/java/sciwhiz12/janitor/msg/Moderation.java b/src/main/java/sciwhiz12/janitor/msg/Moderation.java deleted file mode 100644 index 33ec91e..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/Moderation.java +++ /dev/null @@ -1,73 +0,0 @@ -package sciwhiz12.janitor.msg; - -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageChannel; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.requests.restaction.MessageAction; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.util.EnumSet; -import java.util.stream.Collectors; - -import static sciwhiz12.janitor.msg.General.nameFor; - -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 Moderation() { - } - - public static MessageAction performerInsufficientPermissions(MessageChannel channel, Member performer, EnumSet permissions) { - return channel.sendMessage( - new EmbedBuilder() - .setTitle("Insufficient permissions.") - .setDescription("The performer of this command has insufficient permissions to use this command.") - .addField("Performer", nameFor(performer.getUser()), true) - .addField(new MessageEmbed.Field( - "Required permissions", - permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), - true)) - .setColor(General.FAILURE_COLOR) - .build() - ); - } - - public static MessageAction cannotModerate(MessageChannel channel, Member performer, Member target) { - return channel.sendMessage( - new EmbedBuilder() - .setTitle("Cannot moderate Target.") - .setDescription("The performer of this command cannot moderate the target user, likely due to being lower in the role hierarchy.") - .addField("Performer", nameFor(performer.getUser()), true) - .addField("Target", nameFor(target.getUser()), true) - .setColor(General.FAILURE_COLOR) - .build() - ); - } - - public static MessageAction kickUser(MessageChannel channel, Member performer, Member target, @Nullable String reason) { - final EmbedBuilder embed = new EmbedBuilder() - .setAuthor("Kicked user from server.", null, GAVEL_ICON_URL) - .addField("Moderator", nameFor(performer.getUser()), true) - .addField("Target", nameFor(target.getUser()), true); - if (reason != null) - embed.addField("Reason", reason, false); - return channel.sendMessage( - embed.setColor(MODERATION_COLOR).build() - ); - } - - public static MessageAction kickedDM(MessageChannel channel, Member performer, Member target, @Nullable String reason) { - final EmbedBuilder embed = new EmbedBuilder() - .setAuthor(performer.getGuild().getName(), null, performer.getGuild().getIconUrl()) - .setTitle("You were kicked from this server.") - .addField("Moderator", nameFor(performer.getUser()), true); - if (reason != null) - embed.addField("Reason", reason, false); - return channel.sendMessage( - embed.setColor(MODERATION_COLOR).build() - ); - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/Translations.java b/src/main/java/sciwhiz12/janitor/msg/Translations.java new file mode 100644 index 0000000..a280e5a --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/Translations.java @@ -0,0 +1,77 @@ +package sciwhiz12.janitor.msg; + +import com.google.common.io.Resources; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import sciwhiz12.janitor.JanitorBot; + +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static sciwhiz12.janitor.Logging.JANITOR; +import static sciwhiz12.janitor.Logging.TRANSLATIONS; + +public class Translations { + private static final Gson GSON = new GsonBuilder().create(); + private static final String DEFAULT_TRANSLATIONS_RESOURCE = "english.json"; + private static final Type MAP_TYPE = new TypeToken>() {}.getType(); + + private final JanitorBot bot; + private final Path translationsFile; + private final Map translations = new HashMap<>(); + + public Translations(JanitorBot bot, Path translationsFile) { + this.bot = bot; + this.translationsFile = translationsFile; + loadTranslations(); + } + + void loadTranslations() { + if (translationsFile == null) { + JANITOR.info(TRANSLATIONS, "No translation file given, using default english translations"); + loadDefaultTranslations(); + return; + } + try { + JANITOR.debug(TRANSLATIONS, "Loading translations from file {}", translationsFile); + Map trans = GSON.fromJson(Files.newBufferedReader(translationsFile), MAP_TYPE); + translations.clear(); + translations.putAll(trans); + JANITOR.info(TRANSLATIONS, "Loaded {} translations from file {}", translations.size(), translationsFile); + } + catch (Exception e) { + JANITOR.error(TRANSLATIONS, "Error while loading translations from file {}", translationsFile, e); + loadDefaultTranslations(); + } + } + + void loadDefaultTranslations() { + try { + JANITOR.debug(TRANSLATIONS, "Loading default english translations"); + // noinspection UnstableApiUsage + Map trans = GSON.fromJson( + new InputStreamReader(Resources.getResource(DEFAULT_TRANSLATIONS_RESOURCE).openStream()), + MAP_TYPE); + translations.clear(); + translations.putAll(trans); + JANITOR.info(TRANSLATIONS, "Loaded {} default english translations", translations.size()); + } + catch (Exception e) { + JANITOR.error(TRANSLATIONS, "Error while loading default english translations", e); + } + } + + public Map getTranslationMap() { + return Collections.unmodifiableMap(translations); + } + + public String translate(String key, Object... args) { + return String.format(translations.getOrDefault(key, key), args); + } +} diff --git a/src/main/java/sciwhiz12/janitor/utils/Util.java b/src/main/java/sciwhiz12/janitor/utils/Util.java index a193111..47c8f5d 100644 --- a/src/main/java/sciwhiz12/janitor/utils/Util.java +++ b/src/main/java/sciwhiz12/janitor/utils/Util.java @@ -47,6 +47,10 @@ public class Util { return String.format("<%s%s>", prefix, entity.getId()); } + public static String nameFor(User user) { + return user.getName().concat("#").concat(user.getDiscriminator()); + } + public static BiConsumer handle(final Consumer success, final Consumer exceptionally) { return (suc, ex) -> { diff --git a/src/main/resources/default-config.toml b/src/main/resources/default-config.toml index 79f4b5a..00009fd 100644 --- a/src/main/resources/default-config.toml +++ b/src/main/resources/default-config.toml @@ -5,6 +5,10 @@ client_token = "NONE" # The id of the bot owner; used for sending status messages and allowing bot admin commands owner_id = 0 +[messages] +# The file containing the translation keys +# translation_file = "english.json" + [commands] # The prefix for commands prefix = "!" diff --git a/src/main/resources/english.json b/src/main/resources/english.json new file mode 100644 index 0000000..57d314f --- /dev/null +++ b/src/main/resources/english.json @@ -0,0 +1,27 @@ +{ + "general.guild_only_command.title": "Guild only command!", + "general.guild_only_command.desc": "The previous command can only be run in a guild channel.", + "general.insufficient_permissions.title": "I have insufficient permissions!", + "general.insufficient_permissions.desc": "I do not have sufficient permissions to carry out this action!\nPlease contact your server admins if you believe this is in error.", + "general.insufficient_permissions.field.permissions": "Required permissions", + "general.ambiguous_member.title": "Ambiguous member argument!", + "general.ambiguous_member.desc": "The name you have specified is too ambiguous (leads to more than 1 member)!\nPlease narrow down the specified name until it can uniquely identify a member of this guild.", + "general.cannot_interact.title": "Member is higher than me!", + "general.cannot_interact.desc": "Cannot perform action on the given member, likely due to me being lower in the role hierarchy.", + "general.cannot_interact.field.target": "Target", + "moderation.insufficient_permissions.title": "Insufficient permissions.", + "moderation.insufficient_permissions.desc": "The performer of this command has insufficient permissions to use this command.", + "moderation.insufficient_permissions.field.performer": "Performer", + "moderation.insufficient_permissions.field.permissions": "Required permissions", + "moderation.cannot_interact.title": "Cannot moderate Target.", + "moderation.cannot_interact.desc": "The performer of this command cannot moderate the target user, likely due to being lower in the role hierarchy.", + "moderation.cannot_interact.field.performer": "Performer", + "moderation.cannot_interact.field.target": "Target", + "moderation.kick.info.author": "Kicked user from server.", + "moderation.kick.info.field.performer": "Performer", + "moderation.kick.info.field.target": "Target", + "moderation.kick.info.field.reason": "Reason", + "moderation.kick.dm.title": "You were kicked from this server.", + "moderation.kick.dm.field.performer": "Moderator", + "moderation.kick.dm.field.reason": "Reason" +} \ No newline at end of file