From 1de5b1fe01ce2ac1456815ecd7713c0cf081cd9e Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Fri, 27 Feb 2026 18:51:51 -0800 Subject: [PATCH 01/22] Started Code Transition --- build.gradle.kts | 1 + modules/i18n/build.gradle.kts | 9 + .../com/beanbeanjuice/cafebot/i18n/I18N.java | 228 ++++++++++++++++++ .../cafebot/i18n/YamlControl.java | 69 ++++++ .../resources/i18n/en/command/balance.yml | 14 ++ .../main/resources/i18n/en/command/donate.yml | 28 +++ .../src/main/resources/i18n/en/generic.yml | 14 ++ .../i18n/src/main/resources/i18n/en/info.yml | 4 + .../src/main/resources/i18n/en_GB/info.yml | 3 + .../i18n/src/main/resources/i18n/messages.yml | 177 ++++++++++++++ .../cafebot/i18n/FormattingTest.java | 224 +++++++++++++++++ .../beanbeanjuice/cafebot/i18n/KeyTest.java | 58 +++++ .../test/resources/junit-platform.properties | 8 + settings.gradle.kts | 4 +- .../cafebot/commands/cafe/BalanceCommand.java | 32 ++- .../cafebot/commands/cafe/DonateCommand.java | 142 ++++++----- .../cafebot/commands/cafe/MenuCommand.java | 7 +- .../cafebot/commands/cafe/ServeCommand.java | 116 +++++---- .../cafebot/commands/fun/AiCommand.java | 5 +- .../cafebot/commands/fun/AvatarCommand.java | 5 +- .../cafebot/commands/fun/BannerCommand.java | 5 +- .../commands/fun/EightBallCommand.java | 5 +- .../cafebot/commands/fun/SnipeCommand.java | 7 +- .../fun/birthday/BirthdayCommand.java | 4 +- .../fun/birthday/BirthdayGetSubCommand.java | 90 +++---- .../birthday/BirthdayRemoveSubCommand.java | 11 +- .../fun/birthday/BirthdaySetSubCommand.java | 47 ++-- .../commands/fun/meme/IMemeSubCommand.java | 7 +- .../fun/meme/MemeCoffeeSubCommand.java | 4 +- .../commands/fun/meme/MemeCommand.java | 4 +- .../fun/meme/MemeRandomSubCommand.java | 4 +- .../commands/fun/meme/MemeTeaSubCommand.java | 4 +- .../fun/rate/RateCaffeinatedSubCommand.java | 17 +- .../commands/fun/rate/RateCommand.java | 4 +- .../commands/fun/rate/RateGaySubCommand.java | 17 +- .../fun/rate/RateInsaneSubCommand.java | 17 +- .../commands/fun/rate/RatePoorSubCommand.java | 17 +- .../commands/fun/rate/RateSimpSubCommand.java | 17 +- .../fun/rate/RateSmartSubCommand.java | 17 +- .../commands/games/CoinFlipCommand.java | 5 +- .../games/CountingStatisticsCommand.java | 10 +- .../cafebot/commands/games/RollCommand.java | 5 +- .../commands/games/TicTacToeCommand.java | 5 +- .../commands/games/game/GameCommand.java | 2 +- .../games/game/GameStatsSubCommand.java | 5 +- .../commands/generic/BotDonateCommand.java | 5 +- .../commands/generic/BotInviteCommand.java | 5 +- .../commands/generic/BotUpvoteCommand.java | 5 +- .../commands/generic/BugReportCommand.java | 5 +- .../commands/generic/DefineCommand.java | 5 +- .../commands/generic/EmbedCommand.java | 5 +- .../commands/generic/FeatureCommand.java | 5 +- .../cafebot/commands/generic/HelpCommand.java | 5 +- .../cafebot/commands/generic/InfoCommand.java | 5 +- .../cafebot/commands/generic/PingCommand.java | 29 ++- .../commands/generic/RemoveMyDataCommand.java | 5 +- .../commands/generic/StatsCommand.java | 5 +- .../commands/generic/SupportCommand.java | 5 +- .../commands/generic/VersionCommand.java | 5 +- .../cafebot/commands/generic/WhoCommand.java | 5 +- .../calendar/CalendarAddSubCommand.java | 6 +- .../generic/calendar/CalendarCommand.java | 2 +- .../calendar/CalendarDeleteSubCommand.java | 5 +- .../calendar/CalendarGetSubCommand.java | 10 +- .../calendar/CalendarListSubCommand.java | 6 +- .../generic/twitch/TwitchAddSubCommand.java | 5 +- .../generic/twitch/TwitchCommand.java | 2 +- .../generic/twitch/TwitchListSubCommand.java | 5 +- .../twitch/TwitchRemoveSubCommand.java | 5 +- .../commands/interaction/AmazedCommand.java | 5 +- .../commands/interaction/AskCommand.java | 5 +- .../commands/interaction/BiteCommand.java | 5 +- .../commands/interaction/BlushCommand.java | 5 +- .../commands/interaction/BonkCommand.java | 5 +- .../commands/interaction/BoopCommand.java | 5 +- .../commands/interaction/CryCommand.java | 5 +- .../commands/interaction/CuddleCommand.java | 5 +- .../commands/interaction/DabCommand.java | 5 +- .../commands/interaction/DanceCommand.java | 5 +- .../commands/interaction/DieCommand.java | 5 +- .../commands/interaction/GreetCommand.java | 5 +- .../commands/interaction/HeadPatCommand.java | 5 +- .../commands/interaction/HideCommand.java | 5 +- .../commands/interaction/HmphCommand.java | 5 +- .../commands/interaction/HugCommand.java | 5 +- .../commands/interaction/KissCommand.java | 5 +- .../commands/interaction/LickCommand.java | 5 +- .../commands/interaction/LoveCommand.java | 5 +- .../commands/interaction/NomCommand.java | 5 +- .../interaction/NoseBleedCommand.java | 5 +- .../commands/interaction/OkCommand.java | 5 +- .../commands/interaction/PokeCommand.java | 5 +- .../commands/interaction/PoutCommand.java | 5 +- .../commands/interaction/PunchCommand.java | 5 +- .../commands/interaction/RageCommand.java | 5 +- .../commands/interaction/ShootCommand.java | 5 +- .../commands/interaction/ShushCommand.java | 5 +- .../commands/interaction/SlapCommand.java | 5 +- .../commands/interaction/SleepCommand.java | 5 +- .../commands/interaction/SmileCommand.java | 5 +- .../commands/interaction/StabCommand.java | 5 +- .../commands/interaction/StareCommand.java | 5 +- .../commands/interaction/ThrowCommand.java | 5 +- .../commands/interaction/TickleCommand.java | 5 +- .../commands/interaction/UWUCommand.java | 5 +- .../commands/interaction/WaveCommand.java | 5 +- .../commands/interaction/WinkCommand.java | 5 +- .../commands/interaction/YellCommand.java | 5 +- .../generic/InteractionBlockSubCommand.java | 5 +- .../generic/InteractionCommand.java | 2 +- .../generic/InteractionStatusSubCommand.java | 5 +- .../generic/InteractionUnblockSubCommand.java | 5 +- .../commands/moderation/ClearChatCommand.java | 5 +- .../airport/AirportMessageCommand.java | 2 +- .../AirportMessageRemoveSubCommand.java | 5 +- .../airport/AirportMessageSetSubCommand.java | 5 +- .../commands/settings/bind/BindCommand.java | 2 +- .../settings/bind/BindListSubCommand.java | 5 +- .../settings/bind/BindRemoveSubCommand.java | 5 +- .../settings/bind/BindSetSubCommand.java | 5 +- .../settings/channels/ChannelCommand.java | 2 +- .../channels/ChannelListSubCommand.java | 7 +- .../channels/ChannelRemoveSubCommand.java | 5 +- .../channels/ChannelSetSubCommand.java | 5 +- .../commands/settings/polls/PollCommand.java | 2 +- .../settings/polls/PollCreateSubCommand.java | 5 +- .../settings/polls/PollDeleteSubCommand.java | 5 +- .../settings/raffles/RaffleCommand.java | 2 +- .../raffles/RaffleCreateSubCommand.java | 5 +- .../raffles/RaffleDeleteSubCommand.java | 5 +- .../commands/settings/roles/RoleCommand.java | 2 +- .../settings/roles/RoleListSubCommand.java | 5 +- .../settings/roles/RoleRemoveSubCommand.java | 5 +- .../settings/roles/RoleSetSubCommand.java | 5 +- .../commands/social/ConfessCommand.java | 4 +- .../commands/social/MemberCountCommand.java | 5 +- .../utility/commands/CommandContext.java | 15 ++ .../utility/commands/CommandHandler.java | 110 +++++++-- .../cafebot/utility/commands/ICommand.java | 6 +- .../cafebot/utility/commands/ISubCommand.java | 4 +- .../cafebot/utility/handlers/HelpHandler.java | 24 +- .../cafebot/utility/helper/Helper.java | 12 + .../utility/listeners/HelpListener.java | 6 +- .../com/beanbeanjuice/utility/LocaleTest.java | 17 ++ 144 files changed, 1637 insertions(+), 495 deletions(-) create mode 100644 modules/i18n/build.gradle.kts create mode 100644 modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java create mode 100644 modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java create mode 100644 modules/i18n/src/main/resources/i18n/en/command/balance.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/command/donate.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/generic.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/info.yml create mode 100644 modules/i18n/src/main/resources/i18n/en_GB/info.yml create mode 100644 modules/i18n/src/main/resources/i18n/messages.yml create mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java create mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java create mode 100644 modules/i18n/src/test/resources/junit-platform.properties create mode 100644 src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java create mode 100644 src/test/java/com/beanbeanjuice/utility/LocaleTest.java diff --git a/build.gradle.kts b/build.gradle.kts index d1a5cc888..76b071159 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -100,6 +100,7 @@ tasks.clean { dependencies { implementation(project(":modules:cafeBot-api-wrapper")) implementation(project(":modules:meme-api-wrapper")) + implementation(project(":modules:i18n")) implementation("net.dv8tion:JDA:6.3.1") { exclude(module = "opus-java") } diff --git a/modules/i18n/build.gradle.kts b/modules/i18n/build.gradle.kts new file mode 100644 index 000000000..f5989db53 --- /dev/null +++ b/modules/i18n/build.gradle.kts @@ -0,0 +1,9 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +version = "0.0.0" + +dependencies { + implementation("org.yaml:snakeyaml:2.5") // https://mvnrepository.com/artifact/org.yaml/snakeyaml +} + +tasks.withType { } diff --git a/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java new file mode 100644 index 000000000..0f8f1a2db --- /dev/null +++ b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java @@ -0,0 +1,228 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.jetbrains.annotations.NotNull; +import org.yaml.snakeyaml.Yaml; + +import java.io.InputStream; +import java.util.*; + +public class I18N extends ResourceBundle { + + private final Map> loadedFiles = new HashMap<>(); + private final Locale locale; + private final ClassLoader classLoader; + private final Yaml yaml = new Yaml(); + + public I18N(Locale locale, ClassLoader classLoader) { + this.locale = locale; + this.classLoader = classLoader; + } + + @Override + protected Object handleGetObject(@NotNull String key) { + // Parse the key: "commands.help.description" + // -> file: "commands/help", nested key: "description" + + int lastDotIndex = key.lastIndexOf('.'); + if (lastDotIndex == -1) { + // Try to load from root level files + Object result = loadFromFile(key, ""); + if (result != null) { + return result; + } + } else { + String filePath = key.substring(0, lastDotIndex); + String nestedKey = key.substring(lastDotIndex + 1); + + // Try to resolve as nested path first + Object result = loadFromFile(filePath, nestedKey); + if (result != null) { + return result; + } + + // If not found, try moving more parts to the file path + // e.g., if "commands.help" didn't work, try "commands" with "help.description" + int previousDotIndex = filePath.lastIndexOf('.'); + while (previousDotIndex != -1) { + filePath = filePath.substring(0, previousDotIndex); + nestedKey = key.substring(previousDotIndex + 1); + result = loadFromFile(filePath, nestedKey); + if (result != null) { + return result; + } + previousDotIndex = filePath.lastIndexOf('.'); + } + + // Try root level with full key as nested path + result = loadFromFile("", key); + if (result != null) { + return result; + } + } + + // Not found in this locale + // If we have a parent (fallback), return null to let parent handle it + if (parent != null) { + return null; + } + + // We're the root bundle (English) and key not found - return the key itself + return key; + } + + private Object loadFromFile(String filePath, String nestedKey) { + // Convert dot notation to folder path + String folderPath = filePath.replace('.', '/'); + + // Build locale string: "en_GB", "en_US", or just "en" + String localeString = locale.getLanguage(); + if (!locale.getCountry().isEmpty()) { + localeString += "_" + locale.getCountry(); + } + + // Include locale in cache key to avoid conflicts + String fileKey = localeString + ":" + (folderPath.isEmpty() ? "" : folderPath); + + // Check if file is already loaded + Map fileData = loadedFiles.get(fileKey); + + if (fileData == null) { + // Try to load the file: "i18n/en_GB/info.yml" + String resourcePath = "i18n/" + localeString + "/" + + (folderPath.isEmpty() ? "" : folderPath + ".yml"); + + fileData = loadYamlFile(resourcePath); + + if (fileData == null && !folderPath.isEmpty()) { + // Maybe it's in a parent directory file + // e.g., "commands.yml" contains "help" section + int lastSlash = folderPath.lastIndexOf('/'); + if (lastSlash != -1) { + String parentPath = folderPath.substring(0, lastSlash); + String section = folderPath.substring(lastSlash + 1); + resourcePath = "i18n/" + localeString + "/" + parentPath + ".yml"; + Map parentData = loadYamlFile(resourcePath); + if (parentData != null && parentData.containsKey(section)) { + Object sectionData = parentData.get(section); + if (sectionData instanceof Map) { + fileData = flatten((Map) sectionData); + } + } + } + } + + if (fileData != null) { + loadedFiles.put(fileKey, fileData); + } else { + loadedFiles.put(fileKey, Collections.emptyMap()); // Cache miss + } + } + + if (fileData == null || fileData.isEmpty()) { + return null; + } + + // Get nested key from flattened data + return fileData.get(nestedKey); + } + + private Map loadYamlFile(String resourcePath) { + try (InputStream stream = classLoader.getResourceAsStream(resourcePath)) { + if (stream == null) { + return null; + } + Map raw = yaml.load(stream); + return flatten(raw); + } catch (Exception e) { + return null; + } + } + + @Override + public @NotNull Enumeration getKeys() { + // This is complex with lazy loading - for now return empty or loaded keys + Set keys = new HashSet<>(); + + for (Map.Entry> entry : loadedFiles.entrySet()) { + String prefix = entry.getKey().replace('/', '.'); + for (String key : entry.getValue().keySet()) { + if (prefix.isEmpty()) { + keys.add(key); + } else { + keys.add(prefix + "." + key); + } + } + } + + if (parent != null) { + parent.getKeys().asIterator().forEachRemaining(keys::add); + } + + return Collections.enumeration(keys); + } + + private Map flatten(Map source) { + Map result = new HashMap<>(); + flatten("", source, result); + return result; + } + + private void flatten(String prefix, Map source, Map result) { + for (var entry : source.entrySet()) { + String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey(); + Object value = entry.getValue(); + + if (value instanceof Map map) { + flatten(key, (Map) map, result); + } else { + result.put(key, value.toString()); + } + } + } + + /** + * Gets the I18N bundle for the specified locale. + * @param locale The {@link Locale} to get the bundle for. + * @return The {@link I18N} bundle instance. + */ + public static I18N getBundle(Locale locale) { + return (I18N) ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + } + + /** + * Gets the I18N bundle for English (default). + * @return The {@link I18N} bundle instance. + */ + public static I18N getBundle() { + return getBundle(Locale.ENGLISH); + } + + /** + * Gets the description based on the language file and path. + * @param path The {@link String path} to search for. + * @return The proper {@link String description} or the {@link String path} if not found. + */ + public static String getStringFromLanguageFile(String path) { + try { + return getStringFromLanguageFile(Locale.ENGLISH, path); + } catch (MissingResourceException e) { + return path; + } + } + + /** + * Gets the description based on the language file and path. + * @param locale The {@link Locale} specifying which language file to use. If not found, defaults to {@link Locale english}. + * @param path The {@link String path} to search for. + * @return The proper {@link String description} or the {@link String path} if not found. + */ + public static String getStringFromLanguageFile(Locale locale, String path) { + try { + ResourceBundle bundle = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + return bundle.getString(path); + } catch (MissingResourceException e) { + return path; + } + } + +} diff --git a/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java new file mode 100644 index 000000000..1d8512445 --- /dev/null +++ b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java @@ -0,0 +1,69 @@ +package com.beanbeanjuice.cafebot.i18n; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; + +public class YamlControl extends ResourceBundle.Control { + + public static final YamlControl INSTANCE = new YamlControl(); + private YamlControl() {} + + @Override + public List getFormats(String baseName) { + return List.of("yaml"); + } + + @Override + public ResourceBundle newBundle( + String baseName, + Locale locale, + String format, + ClassLoader loader, + boolean reload + ) throws IOException { + // Build locale string: "en_GB", "en_US", or just "en" + String localeString = locale.getLanguage(); + if (!locale.getCountry().isEmpty()) { + localeString += "_" + locale.getCountry(); + } + + // Check if at least one YAML file exists for this locale + // Try a common base file to see if the locale directory exists + String testPath = "i18n/" + localeString + "/"; + + // We need to check if ANY file exists for this locale + // Try some common paths + boolean localeExists = false; + String[] commonFiles = {"info.yml", "generic.yml", "commands.yml"}; + + for (String file : commonFiles) { + try (InputStream stream = loader.getResourceAsStream(testPath + file)) { + if (stream != null) { + localeExists = true; + break; + } + } + } + + // If no files exist for this locale, return null so ResourceBundle tries fallback + if (!localeExists) { + return null; + } + + // Create and return the bundle + return new I18N(locale, loader); + } + + @Override + public Locale getFallbackLocale(String baseName, Locale locale) { + // Always fall back to English, unless we're already in English + if (locale.equals(Locale.ENGLISH)) { + return null; // No further fallback + } + return Locale.ENGLISH; + } + +} diff --git a/modules/i18n/src/main/resources/i18n/en/command/balance.yml b/modules/i18n/src/main/resources/i18n/en/command/balance.yml new file mode 100644 index 000000000..3125ce53d --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/balance.yml @@ -0,0 +1,14 @@ +description: "Get your balance!" +error: + title: "Error Getting Balance" + description: "There was some sort of error getting your coin balance." +embed: + title: "cafeCoin Balance" + orders: + bought: "Orders Bought" + received: "Orders Received" + description: "{user} has a current balance of `{balance}` cC (cafeCoins)!" + footer: "To learn how to make money, do /help!" +arguments: + user: + description: "The user you want to get the balance of." diff --git a/modules/i18n/src/main/resources/i18n/en/command/donate.yml b/modules/i18n/src/main/resources/i18n/en/command/donate.yml new file mode 100644 index 000000000..aa54fe1e5 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/donate.yml @@ -0,0 +1,28 @@ +description: "Donate your cafeCoins to another user!" +embed: + donation: + title: "Money up!" + description: | + Aww!~ + + {donator} just donated **{amount} bC** to {donatee}! + footer: "If you want to donate too, do \"/donate!\"" + cooldown: + title: "Donation Cooldown" + description: That user was last donated to at . You need to wait before they are able to receive another donation. + footer: "They can only be donated to once per hour!" + balance: + title: "Not Enough Cafe Coins!" + description: | + Umm.. you only have {balance} which is *not* enough to donate {payment}. + + If you don't have money to pay then why are you even trying to donate? Do you even have money to order from the store? Ugh... I guess I might give you some money if you serve some customers... + footer: "To get some money, do \"/serve\"!" + self: + title: "Self Donation" + description: "You're joking... right? You can't donate to yourself. <:cafeBot_angry:1171726164092518441>" +arguments: + user: + description: "The user you want to donate to." + amount: + description: "The amount of cafeCoins you want to donate!" diff --git a/modules/i18n/src/main/resources/i18n/en/generic.yml b/modules/i18n/src/main/resources/i18n/en/generic.yml new file mode 100644 index 000000000..c1fe8f7f9 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/generic.yml @@ -0,0 +1,14 @@ +error: + uncaught: + title: "Uncaught Error!" + message: | + I-... something went... seriously wrong... <:cafeBot_sad:1171726165040447518> + + I tried to do that and short-circuited... this is the error I got... + + ``` + {uncaught_error} + ``` + generic: + title: "Error" + message: "There was an error! I... I'll try to let me boss know... please try again later and make a report on the GitHub if it is not fixed soon with `/bug`!" diff --git a/modules/i18n/src/main/resources/i18n/en/info.yml b/modules/i18n/src/main/resources/i18n/en/info.yml new file mode 100644 index 000000000..41c95e25b --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/info.yml @@ -0,0 +1,4 @@ +bot: + name: "cafeBot" + greeting: "Hello, world!" + discord-locale: ENGLISH_US diff --git a/modules/i18n/src/main/resources/i18n/en_GB/info.yml b/modules/i18n/src/main/resources/i18n/en_GB/info.yml new file mode 100644 index 000000000..b10cdab23 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en_GB/info.yml @@ -0,0 +1,3 @@ +bot: + greeting: "Hello, eh?!" + discord-locale: ENGLISH_US diff --git a/modules/i18n/src/main/resources/i18n/messages.yml b/modules/i18n/src/main/resources/i18n/messages.yml new file mode 100644 index 000000000..c18c83cd6 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/messages.yml @@ -0,0 +1,177 @@ +#bot: +# name: "cafeBot" +# greeting: "Hello, world!" +# discord-locale: ENGLISH_US +# +#error: +# uncaught: +# title: "Uncaught Error!" +# message: | +# I-... something went... seriously wrong... <:cafeBot_sad:1171726165040447518> +# +# I tried to do that and short-circuited... this is the error I got... +# +# ``` +# {uncaught_error} +# ``` +# generic: +# title: "Error" +# message: "There was an error! I... I'll try to let me boss know... please try again later and make a report on the GitHub if it is not fixed soon with `/bug`!" +# +#command: +# +# menu: +# description: "Hungry? Check the menu out!" +# +# serve: +# description: "Serve some words to customers to earn some Cafe Coins!" +# embed: +# success: +# title: "Order up!" +# description: "{user} has served {word} for **{reward}** CC! They now have **{balance}**!" +# description_other: "{user} has served {word} to {customer} for **{reward}** CC! They now have **{balance}**!" +# footer: "Type \"/serve\" to get some money!" +# error: +# word: +# title: "Invalid Word" +# description: "Umm... I don't think that's a real word... make sure you use a real, single, **non-banned** english word." +# time: +# title: "Cannot serve!" +# description: | +# You last served a word at . +# +# You need to wait an hour before you last served in order to serve again. +# +# arguments: +# word: +# description: "Any english word!" +# customer: +# description: "The user you want to serve the word to!" +# +# ping: +# description: "Pong!" +# embed: +# description: "Hello!~ Would you like to order some coffee?" +# shard: "Your shard ID is {SHARD_ID}! Keep note of this in case you need some help..." +# author: "Author: beanbeanjuice - https://github.com/beanbeanjuice/cafeBot" +# arguments: +# word: +# description: "Any word you want repeated back to you." +# number: +# description: "An integer to repeat back to you." +# +# birthday: +# description: "Get someone's birthday or change your own!" +# subcommand: +# get: +# description: "Get someone's birthday!" +# embed: +# other: +# title: "🎂 {user}'s Birthday" +# description: "Their birthday is on **{month} {day}** ({timezone})." +# self: +# title: "🎂 Your Birthday" +# description: "Your birthday is on **{month} {day}** ({timezone})." +# error: +# title: "Error Getting Birthday" +# description: "<:cafeBot_sad:1171726165040447518> Sorry... there was an error getting their birthday. It might not be set, you should tell them to set it with `/birthday`!" +# arguments: +# user: +# description: "The user who's birthday you want to see." +# +# set: +# description: "Edit your birthday!" +# embed: +# error: +# title: "Error Setting Birthday" +# description: "I... I don't know what happened... the computer's not letting me put your birthday in!" +# success: +# title: "🎂 Birthday Set" +# description: "You have successfully set your birthday to **{month} {day}** ({timezone})." +# arguments: +# month: +# description: "The month you were born!" +# day: +# description: "The day you were born!" +# timezone: +# description: "Your current timezone! Start typing to see available options." +# year: +# description: "The year you were born in!" +# +# remove: +# description: "Remove your birthday... :c" +# embed: +# title: "Birthday Removed 🥺" +# description: "<:cafeBot_sad:1171726165040447518> Your birthday has been removed... but I know it's sometimes better to keep things private..." +# +# meme: +# description: "Get a meme!" +# error: +# embed: +# title: "Failed Getting Meme" +# description: "I... I'm so sorry... I tripped and fell when getting your meme..." +# subcommand: +# coffee: +# description: "Get a coffee meme!" +# random: +# description: "Get a random meme!" +# tea: +# description: "Get a tea meme!" +# +# rate: +# description: "Rate something!" +# subcommand: +# caffeinated: +# description: "Rate how caffeinated you, or someone else, is!" +# embed: +# title: "☕ Caffeine Rating ☕" +# description: "{user} is {percent}% caffeinated! " +# arguments: +# user: +# description: "The person who's caffeine levels you want to see." +# +# gay: +# description: "Rate how ✨ gay ✨ someone is!" +# embed: +# title: "🏳️‍🌈 Gay Rating 🌈" +# description: "{user} is {percent}% gay! <:bloatedBlush:1154475611893547218>" +# arguments: +# user: +# description: "The person who's gayness you want to see." +# +# insane: +# description: "See how insane someone is!" +# embed: +# title: "‼️ Insane Rating ‼️" +# description: "{user} is {percent}% insane! " +# arguments: +# user: +# description: "The person who's insanity you want to see." +# +# poor: +# description: "Rate how poor someone is!" +# embed: +# title: "💸 Poor Rating 💸️" +# description: "{user} is {percent}% poor! 🤢" +# arguments: +# user: +# description: "The person who's poor level you want to see." +# +# simp: +# description: "Rate how much of a simp someone is!" +# embed: +# title: "😍 Simp Rating 😍" +# description: "{user} is {percent}% simp! <:flushed_nervous:841923862202548224>" +# arguments: +# user: +# description: "The person who's simp level you want to see." +# +# smart: +# description: "Rate how smart someone is!" +# embed: +# title: "<:smartPeepo:1000248538376196280> Smart Rating <:smartPeepo:1000248538376196280>" +# description: "{user} is {percent}% smart! 👩‍🎓" +# arguments: +# user: +# description: "The person who's smart levels you want to see." +# diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java new file mode 100644 index 000000000..ca2b89f62 --- /dev/null +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java @@ -0,0 +1,224 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.*; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class FormattingTest { + + private static final String I18N_PATH = "i18n"; + private static final String ENGLISH_LOCALE = "en"; + + @Test + @DisplayName("all translations only contain keys present in English") + void allTranslationsOnlyContainEnglishKeys() throws IOException, URISyntaxException { + // Get all locale directories + Set locales = getLocaleDirectories(); + locales.remove(ENGLISH_LOCALE); // We'll compare against English + + // Get all English files and their keys + Map> englishKeysPerFile = loadAllKeysForLocale(ENGLISH_LOCALE); + + List errors = new ArrayList<>(); + + for (String locale : locales) { + Map> localeKeysPerFile = loadAllKeysForLocale(locale); + + for (Map.Entry> entry : localeKeysPerFile.entrySet()) { + String filePath = entry.getKey(); + Set localeKeys = entry.getValue(); + Set englishKeys = englishKeysPerFile.get(filePath); + + if (englishKeys == null) { + // This will be caught by the file structure test + continue; + } + + // Check for keys in locale that don't exist in English + Set extraKeys = new HashSet<>(localeKeys); + extraKeys.removeAll(englishKeys); + + if (!extraKeys.isEmpty()) { + errors.add(String.format("Locale '%s' file '%s' contains extra keys not in English: %s", + locale, filePath, extraKeys)); + } + } + } + + if (!errors.isEmpty()) { + Assertions.fail("Translation key validation failed:\n" + String.join("\n", errors)); + } + } + + @Test + @DisplayName("all translations only contain files present in English") + void allTranslationsOnlyContainEnglishFiles() throws IOException, URISyntaxException { + // Get all locale directories + Set locales = getLocaleDirectories(); + locales.remove(ENGLISH_LOCALE); + + // Get all English file paths + Set englishFiles = getAllFilesForLocale(ENGLISH_LOCALE); + + List errors = new ArrayList<>(); + + for (String locale : locales) { + Set localeFiles = getAllFilesForLocale(locale); + + // Check for files in locale that don't exist in English + Set extraFiles = new HashSet<>(localeFiles); + extraFiles.removeAll(englishFiles); + + if (!extraFiles.isEmpty()) { + errors.add(String.format("Locale '%s' contains extra files not in English: %s", + locale, extraFiles)); + } + } + + if (!errors.isEmpty()) { + Assertions.fail("File structure validation failed:\n" + String.join("\n", errors)); + } + } + + @Test + @DisplayName("all translations contain bot.discord-locale in info.yml") + void allTranslationsContainDiscordLocale() throws IOException, URISyntaxException { + // Get all locale directories + Set locales = getLocaleDirectories(); + + List errors = new ArrayList<>(); + + for (String locale : locales) { + String resourcePath = I18N_PATH + "/" + locale + "/info.yml"; + Set keys = loadKeysFromFile(resourcePath); + + if (keys.isEmpty()) { + errors.add(String.format("Locale '%s' is missing info.yml file", locale)); + } else if (!keys.contains("bot.discord-locale")) { + errors.add(String.format("Locale '%s' is missing 'bot.discord-locale' in info.yml", locale)); + } + } + + if (!errors.isEmpty()) { + Assertions.fail("Discord locale validation failed:\n" + String.join("\n", errors)); + } + } + + private Set getLocaleDirectories() throws IOException, URISyntaxException { + ClassLoader classLoader = getClass().getClassLoader(); + URI uri = classLoader.getResource(I18N_PATH).toURI(); + + if (uri.getScheme().equals("jar")) { + try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path path = fileSystem.getPath("/" + I18N_PATH); + try (Stream paths = Files.list(path)) { + return paths + .filter(Files::isDirectory) + .map(p -> p.getFileName().toString()) + .collect(Collectors.toSet()); + } + } + } else { + Path path = Paths.get(uri); + try (Stream paths = Files.list(path)) { + return paths + .filter(Files::isDirectory) + .map(p -> p.getFileName().toString()) + .collect(Collectors.toSet()); + } + } + } + + private Set getAllFilesForLocale(String locale) throws IOException, URISyntaxException { + Set files = new HashSet<>(); + String localePath = I18N_PATH + "/" + locale; + + ClassLoader classLoader = getClass().getClassLoader(); + URI uri = classLoader.getResource(localePath).toURI(); + + if (uri.getScheme().equals("jar")) { + try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path path = fileSystem.getPath("/" + localePath); + collectYamlFiles(path, "", files); + } + } else { + Path path = Paths.get(uri); + collectYamlFiles(path, "", files); + } + + return files; + } + + private void collectYamlFiles(Path dir, String prefix, Set files) throws IOException { + try (Stream paths = Files.list(dir)) { + List pathList = paths.collect(Collectors.toList()); + for (Path path : pathList) { + String fileName = path.getFileName().toString(); + String relativePath = prefix.isEmpty() ? fileName : prefix + "/" + fileName; + + if (Files.isDirectory(path)) { + collectYamlFiles(path, relativePath, files); + } else if (fileName.endsWith(".yml") || fileName.endsWith(".yaml")) { + files.add(relativePath); + } + } + } + } + + private Map> loadAllKeysForLocale(String locale) throws IOException, URISyntaxException { + Map> keysPerFile = new HashMap<>(); + Set files = getAllFilesForLocale(locale); + + for (String file : files) { + String resourcePath = I18N_PATH + "/" + locale + "/" + file; + Set keys = loadKeysFromFile(resourcePath); + keysPerFile.put(file, keys); + } + + return keysPerFile; + } + + private Set loadKeysFromFile(String resourcePath) throws IOException { + ClassLoader classLoader = getClass().getClassLoader(); + try (InputStream stream = classLoader.getResourceAsStream(resourcePath)) { + if (stream == null) { + return Collections.emptySet(); + } + + Yaml yaml = new Yaml(); + Map data = yaml.load(stream); + + if (data == null) { + return Collections.emptySet(); + } + + Set keys = new HashSet<>(); + flattenKeys("", data, keys); + return keys; + } + } + + private void flattenKeys(String prefix, Map source, Set keys) { + for (Map.Entry entry : source.entrySet()) { + String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey(); + Object value = entry.getValue(); + + if (value instanceof Map map) { + flattenKeys(key, (Map) map, keys); + } else { + keys.add(key); + } + } + } + +} diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java new file mode 100644 index 000000000..dd35af307 --- /dev/null +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java @@ -0,0 +1,58 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Locale; + +public class KeyTest { + + @Test + @DisplayName("can get default messages") + public void canGetDefaultMessages() { + I18N bundle = I18N.getBundle(); + Assertions.assertEquals("Hello, world!", bundle.getString("info.bot.greeting")); + } + + @Test + @DisplayName("different language works") + public void differentLanguageWorks() { + I18N bundle = I18N.getBundle(Locale.UK); + Assertions.assertEquals("Hello, eh?!", bundle.getString("info.bot.greeting")); + } + + @Test + @DisplayName("fallback works if language file exists but does not contain key") + public void fallbackWorksIfLanguageDoesNotContainKey() { + I18N bundle = I18N.getBundle(Locale.UK); + Assertions.assertEquals("cafeBot", bundle.getString("info.bot.name")); + } + + @Test + @DisplayName("fallback works if language file does not exist") + public void fallbackWorksIfLanguageDoesNotExist() { + I18N bundle = I18N.getBundle(Locale.TRADITIONAL_CHINESE); + + Assertions.assertEquals("Hello, world!", bundle.getString("info.bot.greeting")); + } + + @Test + @DisplayName("returns path if does not exist") + public void returnsPathIfDoesNotExist() { + I18N bundle = I18N.getBundle(); + String result = bundle.getString("path.that.does.not.exist"); + + Assertions.assertEquals("path.that.does.not.exist", result); + } + + @Test + @DisplayName("returns string if does exist") + public void returnsPathIfDoesExist() { + I18N bundle = I18N.getBundle(); + String result = bundle.getString("info.bot.name"); + + Assertions.assertEquals("cafeBot", result); + } + +} diff --git a/modules/i18n/src/test/resources/junit-platform.properties b/modules/i18n/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..ff11acf87 --- /dev/null +++ b/modules/i18n/src/test/resources/junit-platform.properties @@ -0,0 +1,8 @@ +# Enable parallel execution +junit.jupiter.execution.parallel.enabled = true + +# Classes can run in parallel +junit.jupiter.execution.parallel.mode.classes.default = concurrent + +# Methods inside a class run sequentially +junit.jupiter.execution.parallel.mode.default = same_thread diff --git a/settings.gradle.kts b/settings.gradle.kts index a96369303..baaa9ad87 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,8 +2,10 @@ rootProject.name = "cafeBot" include( "modules:cafeBot-api-wrapper", - "modules:meme-api-wrapper" + "modules:meme-api-wrapper", + "modules:i18n" ) project(":modules:cafeBot-api-wrapper").name = "cafeBot-api-wrapper" project(":modules:meme-api-wrapper").name = "meme-api-wrapper" +project(":modules:i18n").name = "i18n" diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java index cc96a19af..b008e7c03 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java @@ -5,6 +5,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -18,6 +19,7 @@ import java.util.Arrays; import java.util.Optional; +import java.util.ResourceBundle; import java.util.concurrent.CompletableFuture; public class BalanceCommand extends Command implements ICommand { @@ -27,35 +29,39 @@ public BalanceCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); CompletableFuture f1 = bot.getCafeAPI().getUserApi().getUser(user.getId()); CompletableFuture f2 = bot.getCafeAPI().getOrderApi().getOrders(user.getId()); + ResourceBundle bundle = ctx.getUserI18n(); + f1.thenAcceptBoth(f2, (discordUser, orders) -> { - event.getHook().sendMessageEmbeds(balanceEmbed(user, discordUser, orders)).queue(); + event.getHook().sendMessageEmbeds(balanceEmbed(user, discordUser, orders, bundle)).queue(); }).exceptionally((e) -> { event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Getting Balance", - "There was some sort of error getting your coin balance." + bundle.getString("command.balance.error.title"), + bundle.getString("command.balance.error.description") )).queue(); return null; }); } - public MessageEmbed balanceEmbed(User user, DiscordUser cafeUser, MenuOrder[] orders) { + public MessageEmbed balanceEmbed(User user, DiscordUser cafeUser, MenuOrder[] orders, ResourceBundle bundle) { long ordersBought = Arrays.stream(orders).filter((order) -> order.getFromId().equals(user.getId())).count(); long ordersReceived = Arrays.stream(orders).filter((order) -> order.getToId().equals(user.getId())).count(); + String description = bundle.getString("command.balance.embed.description").replace("{user}", user.getAsMention()).replace("{balance}", String.valueOf(Helper.roundFloat(cafeUser.getBalance()))); + return new EmbedBuilder() - .setTitle("cafeCoin Balance") + .setTitle(bundle.getString("command.balance.embed.title")) .setColor(Helper.getRandomColor()) - .addField("Orders Bought", String.valueOf(ordersBought), true) - .addField("Orders Received", String.valueOf(ordersReceived), true) - .setDescription(user.getAsMention() + " has a current balance of `$" + Helper.roundFloat(cafeUser.getBalance()) + "` cC (cafeCoins)!") - .setFooter("To learn how to make money do /help") + .addField(bundle.getString("command.balance.embed.orders.bought"), String.valueOf(ordersBought), true) + .addField(bundle.getString("command.balance.embed.orders.received"), String.valueOf(ordersReceived), true) + .setDescription(description) + .setFooter(bundle.getString("command.balance.embed.footer")) .build(); } @@ -65,8 +71,8 @@ public String getName() { } @Override - public String getDescription() { - return "Get your balance!"; + public String getDescriptionPath() { + return "command.balance.description"; } @Override @@ -77,7 +83,7 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The user you want to get the balance of.", false) + new OptionData(OptionType.USER, "user", "command.balance.arguments.user.description", false) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java index 7d4c514e3..ea5e2be6f 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java @@ -4,9 +4,11 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; @@ -18,6 +20,8 @@ import java.time.Instant; import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.CompletionException; public class DonateCommand extends Command implements ICommand { @@ -26,13 +30,16 @@ public DonateCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional receiverMapping = Optional.ofNullable(event.getOption("user")); User receiver = receiverMapping.map(OptionMapping::getAsUser).orElseThrow(); // Shouldn't be null. User sender = event.getUser(); if (receiver == sender) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed("Self Donation", "You can't donate to yourself!")).queue(); + event.getHook().sendMessageEmbeds(Helper.errorEmbed( + ctx.getUserI18n().getString("command.donate.embed.self.title"), + ctx.getUserI18n().getString("command.donate.embed.self.description") + )).queue(); return; } @@ -42,42 +49,47 @@ public void handle(SlashCommandInteractionEvent event) { bot.getCafeAPI().getDonationApi().createDonation(sender.getId(), receiver.getId(), amount) .thenAccept((result) -> { sendSuccessToSender(event, receiver); - sendDonationEmbed(event, sender, receiver, amount); + sendDonationEmbed(event, sender, receiver, amount, ctx.getGuildI18n()); }) .exceptionally((e) -> { - if (e.getCause() instanceof ApiRequestException requestException) { - JsonNode error = requestException.getBody(); - - if (error.get("from") != null && error.get("from").get(0).asString().equals("Insufficient balance")) { - sendNotEnoughCoinsEmbed(event, sender.getId(), amount); - return null; - } - - if (error.get("to") != null && error.get("to").get(0).asString().equals("That user can only be donated to once per hour")) { - sendNeedToWaitEmbed(event, receiver.getId()); - return null; - } - } - - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Donating", - "There was an error donating to the user: " + e.getMessage() - )).queue(); - - bot.getLogger().log(DonateCommand.class, LogLevel.WARN, "There was an error donating to a user: " + e.getMessage(), e.getCause()); - return null; + handleError(e, event, ctx, sender, receiver, amount); + + throw new CompletionException(e.getCause()); }); } - private void sendNotEnoughCoinsEmbed(SlashCommandInteractionEvent event, String userId, double amount) { + private void handleError(Throwable e, SlashCommandInteractionEvent event, CommandContext ctx, User sender, User receiver, double amount) { + if (e.getCause() instanceof ApiRequestException requestException) { + JsonNode error = requestException.getBody().get("error"); + + if (error.get("from") != null && error.get("from").get(0).asString().equals("Insufficient balance")) { + sendNotEnoughCoinsEmbed(event, sender.getId(), amount, ctx.getUserI18n()); + return; + } + + if (error.get("to") != null && error.get("to").get(0).asString().equals("That user can only be donated to once per hour")) { + sendNeedToWaitEmbed(event, receiver.getId(), ctx.getUserI18n()); + return; + } + } + + event.getHook().sendMessageEmbeds(Helper.uncaughtErrorEmbed( + ctx.getUserI18n(), + e.getMessage() + )).queue(); + + bot.getLogger().log(DonateCommand.class, LogLevel.WARN, "There was an error donating to a user: " + e.getMessage(), e.getCause()); + } + + private void sendNotEnoughCoinsEmbed(SlashCommandInteractionEvent event, String userId, double amount, ResourceBundle i18n) { bot.getCafeAPI().getUserApi().getUser(userId).thenAccept((user) -> { - event.getHook().sendMessageEmbeds(notEnoughCoinsEmbed(user.getBalance(), amount)).queue(); + event.getHook().sendMessageEmbeds(notEnoughCoinsEmbed(user.getBalance(), amount, i18n)).queue(); }); } - private void sendNeedToWaitEmbed(SlashCommandInteractionEvent event, String userId) { + private void sendNeedToWaitEmbed(SlashCommandInteractionEvent event, String userId, ResourceBundle i18n) { bot.getCafeAPI().getUserApi().getUser(userId).thenAccept((user) -> { - event.getHook().sendMessageEmbeds(needToWaitEmbed(user.getLastDonationTime().orElse(Instant.now()))).queue(); + event.getHook().sendMessageEmbeds(needToWaitEmbed(user.getLastDonationTime().orElse(Instant.now()), i18n)).queue(); }); } @@ -85,29 +97,35 @@ private void sendSuccessToSender(SlashCommandInteractionEvent event, User receiv event.getHook().sendMessageEmbeds(successEmbed(receiver)).queue(); } - private void sendDonationEmbed(SlashCommandInteractionEvent event, User sender, User receiver, double amount) { - event.getChannel().sendMessageEmbeds(donationEmbed(sender, receiver, amount)).mention(receiver).queue(); + private void sendDonationEmbed(SlashCommandInteractionEvent event, User sender, User receiver, double amount, ResourceBundle i18n) { + event.getChannel().sendMessageEmbeds(donationEmbed(sender, receiver, amount, i18n)).mention(receiver).queue(); } - private MessageEmbed notEnoughCoinsEmbed(double balance, double amount) { - return Helper.errorEmbed( - "Not Enough Coins", - String.format(""" - Sorry, you don't have enough bC to donate to that person. \ - You have **%.2f bC**, which is *not* enough to donate **%.2f bC** to that person. \ - Maybe try working for a bit? You can use `/serve` to work. - """, balance, amount) - ); + private MessageEmbed notEnoughCoinsEmbed(double balance, double amount, ResourceBundle i18n) { + String description = i18n.getString("command.donate.embed.balance.description") + .replace("{balance}", String.format("%.2f", balance)) + .replace("{payment}", String.format("%.2f", amount)); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle(i18n.getString("command.donate.embed.balance.title")); + embedBuilder.setDescription(description); + embedBuilder.setFooter(i18n.getString("command.donate.embed.balance.footer")); + embedBuilder.setColor(Helper.getRandomColor()); + + return embedBuilder.build(); } - private MessageEmbed needToWaitEmbed(Instant lastDonationTime) { - return Helper.errorEmbed( - "That User Can't Receive Donations", - String.format(""" - That user was last donated to at . You need to wait before they are able to \ - receive another donation. - """, lastDonationTime.getEpochSecond()) - ); + private MessageEmbed needToWaitEmbed(Instant lastDonationTime, ResourceBundle i18n) { + String description = i18n.getString("command.donate.embed.cooldown.description") + .replace("{timestamp}", String.valueOf(lastDonationTime.getEpochSecond())); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle(i18n.getString("command.donate.embed.cooldown.title")); + embedBuilder.setDescription(description); + embedBuilder.setFooter(i18n.getString("command.donate.embed.cooldown.footer")); + embedBuilder.setColor(Helper.getRandomColor()); + + return embedBuilder.build(); } private MessageEmbed successEmbed(User receiver) { @@ -117,15 +135,19 @@ private MessageEmbed successEmbed(User receiver) { ); } - private MessageEmbed donationEmbed(User sender, User receiver, double amount) { - return Helper.successEmbed( - "Donation!", - String.format(""" - Aww!~ - - %s just donated **%.2f bC** to %s! - """, sender.getAsMention(), amount, receiver.getAsMention()) - ); + private MessageEmbed donationEmbed(User sender, User receiver, double amount, ResourceBundle i18n) { + String description = i18n.getString("command.donate.embed.donation.description") + .replace("{donator}", sender.getAsMention()) + .replace("{donatee}", receiver.getAsMention()) + .replace("{amount}", String.format("%.2f", amount)); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle(i18n.getString("command.donate.embed.donation.title")); + embedBuilder.setDescription(description); + embedBuilder.setFooter(i18n.getString("command.donate.embed.donation.footer")); + embedBuilder.setColor(Helper.getRandomColor()); + + return embedBuilder.build(); } @Override @@ -134,8 +156,8 @@ public String getName() { } @Override - public String getDescription() { - return "Donate your beanCoins to another user!"; + public String getDescriptionPath() { + return "command.donate.description"; } @Override @@ -146,8 +168,8 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The user you want to donate to.", true), - new OptionData(OptionType.NUMBER, "amount", "The amount of beanCoins you want to donate.", true) + new OptionData(OptionType.USER, "user", "command.donate.arguments.user.description", true), + new OptionData(OptionType.NUMBER, "amount", "command.donate.arguments.amount.description", true) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/MenuCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/MenuCommand.java index 7c9f0059f..6693cb21b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/MenuCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/MenuCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.components.actionrow.ActionRow; @@ -15,7 +16,7 @@ public MenuCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook() .sendMessageEmbeds(bot.getMenuHandler().getAllMenuEmbed()) .addComponents(ActionRow.of(bot.getMenuHandler().getAllStringSelectMenu())) @@ -28,8 +29,8 @@ public String getName() { } @Override - public String getDescription() { - return "Hungry? Check the menu out!"; + public String getDescriptionPath() { + return "command.menu.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java index df339c5ef..2aadcfc50 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java @@ -4,9 +4,11 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; @@ -14,10 +16,13 @@ import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import org.jetbrains.annotations.Nullable; import tools.jackson.databind.JsonNode; import java.time.Instant; import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.CompletionException; public class ServeCommand extends Command implements ICommand { @@ -26,7 +31,7 @@ public ServeCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String word = event.getOption("word").getAsString(); // ! - Shouldn't be null. User user = event.getUser(); Optional optionalReceiver = Optional.ofNullable(event.getOption("customer")); @@ -35,66 +40,73 @@ public void handle(SlashCommandInteractionEvent event) { float reward = cafeUser.getReward(); float newBalance = cafeUser.getNewBalance(); - MessageEmbed embed = optionalReceiver - .map(OptionMapping::getAsUser) - .map((receiver) -> serveOtherEmbed(user, receiver, word, reward, newBalance)) - .orElse(serveSelfEmbed(user, word, reward, newBalance)); + User receiver = optionalReceiver.map(OptionMapping::getAsUser).orElse(null); + + MessageEmbed embed = serveEmbed(user, receiver, word, reward, newBalance, ctx.getGuildI18n()); event.getHook().sendMessageEmbeds(embed).queue(); }).exceptionallyAsync((e) -> { - if (e.getCause() instanceof ApiRequestException apiRequestException) { - JsonNode body = apiRequestException.getBody(); - - if (body.has("lastServeTime")) { - Instant lastServeTime = Instant.parse(body.get("lastServeTime").asString()); - event.getHook().sendMessageEmbeds(cannotServeEmbed(lastServeTime)).queue(); - return null; - } - - if (body.has("error") && body.get("error").has("word")) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "There was an error serving!", - String.format("Please make sure to use a real, single, **non-banned** english word: %s", body.get("error").get("word").get(0).asString()) - )).queue(); - return null; - } - return null; - } + handleError(e, event, ctx); + throw new CompletionException(e.getCause()); + }); - event.getHook().sendMessageEmbeds(Helper.defaultErrorEmbed()).queue(); - bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Serving Word: " + e.getMessage(), true, true); + } - return null; - }); + private void handleError(Throwable e, SlashCommandInteractionEvent event, CommandContext ctx) { + if (e.getCause() instanceof ApiRequestException apiRequestException) { + JsonNode body = apiRequestException.getBody(); + + if (body.has("lastServeTime")) { + Instant lastServeTime = Instant.parse(body.get("lastServeTime").asString()); + event.getHook().sendMessageEmbeds(cannotServeEmbed(lastServeTime, ctx.getUserI18n())).queue(); + return; + } + + if (body.has("error") && body.get("error").has("word")) { + event.getHook().sendMessageEmbeds(Helper.errorEmbed( + ctx.getUserI18n().getString("command.serve.embed.error.word.title"), + ctx.getUserI18n().getString("command.serve.embed.error.word.description") + )).queue(); + return; + } + } + event.getHook().sendMessageEmbeds(Helper.uncaughtErrorEmbed(ctx.getUserI18n(), e.getMessage())).queue(); + bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Serving Word: " + e.getMessage(), true, true); } - private MessageEmbed cannotServeEmbed(Instant lastServeTime) { + private MessageEmbed cannotServeEmbed(Instant lastServeTime, ResourceBundle i18n) { + String description = i18n.getString("command.serve.embed.error.time.description") + .replace("{time}", String.valueOf(lastServeTime.getEpochSecond())); + return Helper.errorEmbed( - "Cannot Serve", - String.format(""" - You served . - You need to wait an hour before you last served in order to serve again... - """, lastServeTime.getEpochSecond()) + i18n.getString("command.serve.embed.error.time.title"), + description ); } - private MessageEmbed serveSelfEmbed(User user, String word, float reward, float newBalance) { - return Helper.successEmbed( - "Order Up!", - String.format(""" - %s has served %s for **%.2f bC**! They now have **%.2f cC**! - """, user.getAsMention(), word, reward, newBalance) - ); - } + private MessageEmbed serveEmbed(User user, @Nullable User receiver, String word, float reward, float newBalance, ResourceBundle i18n) { + EmbedBuilder embedBuilder = new EmbedBuilder(); - private MessageEmbed serveOtherEmbed(User sender, User receiver, String word, double reward, float newBalance) { - return Helper.successEmbed( - "Order Up!", - String.format(""" - %s has served %s to %s for **%.2f cC**! They now have **%.2f cC**! - """, sender.getAsMention(), receiver.getAsMention(), word, reward, newBalance) - ); + embedBuilder.setTitle(i18n.getString("command.serve.embed.success.title")); + + String description = i18n.getString("command.serve.embed.success.description"); + if (receiver != null) { + description = i18n.getString("command.serve.embed.success.description_other") + .replace("{customer}", receiver.getAsMention()); + } + + description = description + .replace("{user}", user.getAsMention()) + .replace("{word}", word) + .replace("{balance}", String.valueOf(newBalance)) + .replace("{reward}", Float.toString(reward)); + + embedBuilder.setDescription(description); + embedBuilder.setFooter(i18n.getString("command.serve.embed.success.footer")); + embedBuilder.setColor(Helper.getRandomColor()); + + return embedBuilder.build(); } @Override @@ -103,8 +115,8 @@ public String getName() { } @Override - public String getDescription() { - return "Serve some words to customers to earn some bC (beanCoins)!"; + public String getDescriptionPath() { + return "command.serve.description"; } @Override @@ -115,8 +127,8 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "word", "Any english word!", true), - new OptionData(OptionType.MENTIONABLE, "customer", "The customer you want to serve the word to.", false) + new OptionData(OptionType.STRING, "word", "command.serve.arguments.word.description", true), + new OptionData(OptionType.MENTIONABLE, "customer", "command.serve.arguments.customer.description", false) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java index bfa0806cd..8223e08a0 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -19,7 +20,7 @@ public AiCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String guildId = event.getGuild().getId(); boolean status = event.getOption("status").getAsBoolean(); @@ -48,7 +49,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Want a sassy AI that will serve you some coffee?"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java index 05669eed2..0170a42ff 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -24,7 +25,7 @@ public AvatarCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userOption = Optional.ofNullable(event.getOption("user")); Optional serverOption = Optional.ofNullable(event.getOption("server")); @@ -73,7 +74,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get someone's avatar!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java index 29e1a44e6..14ff5c9b5 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -24,7 +25,7 @@ public BannerCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userOption = Optional.ofNullable(event.getOption("user")); User user = userOption.map(OptionMapping::getAsUser).orElse(event.getUser()); @@ -58,7 +59,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get someone's banner!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java index b0be18023..97e39bba1 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -19,7 +20,7 @@ public EightBallCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String question = event.getOption("question").getAsString(); event.getHook().sendMessageEmbeds(getAnswerEmbed(question, getAnswer())).queue(); } @@ -63,7 +64,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Ask me a question!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java index adf53773d..d2391602b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.types.PotentialSnipeMessage; @@ -11,8 +12,6 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import java.awt.*; - public class SnipeCommand extends Command implements ICommand { public SnipeCommand(CafeBot bot) { @@ -20,7 +19,7 @@ public SnipeCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getSnipeHandler().popLastMessage(event.getChannelId()).ifPresentOrElse( (snipedMessage) -> event.getHook().sendMessageEmbeds(this.getSnipedMessageEmbed(snipedMessage)).queue(), () -> event.getHook().sendMessageEmbeds(this.getNoSnipeMessageEmbed()).queue() @@ -55,7 +54,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Snipe a recently deleted message!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayCommand.java index 4395415ed..037f990be 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayCommand.java @@ -16,8 +16,8 @@ public String getName() { } @Override - public String getDescription() { - return "Get someone's birthday or change your own!"; + public String getDescriptionPath() { + return "command.birthday.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java index cdc93ecab..4534262fa 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.Birthday; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -20,6 +21,8 @@ import java.time.format.TextStyle; import java.util.Locale; import java.util.Optional; +import java.util.ResourceBundle; +import java.util.concurrent.CompletionException; public class BirthdayGetSubCommand extends Command implements ISubCommand { @@ -28,71 +31,74 @@ public BirthdayGetSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); boolean isSelf = userMapping.isEmpty(); bot.getCafeAPI().getBirthdayApi().getBirthday(user.getId()) - .thenAccept((birthday) -> { - sendBirthday(user, isSelf, birthday, event); - }) - .exceptionally((ex) -> { - if (ex.getCause() instanceof ApiRequestException apiRequestException) { + .thenAccept((birthday) -> { + sendBirthday(user, isSelf, birthday, event, ctx); + }) + .exceptionally((ex) -> { + handleError(ex, event, ctx); + throw new CompletionException(ex.getCause()); + }); + } - if (apiRequestException.getStatusCode() == HttpStatus.SC_NOT_FOUND) { - sendError(event); - } + private void handleError(Throwable ex, SlashCommandInteractionEvent event, CommandContext ctx) { + if (ex.getCause() instanceof ApiRequestException apiRequestException) { - return null; - } + if (apiRequestException.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + sendError(event, ctx.getUserI18n()); + return; + } - event.getHook().sendMessageEmbeds(Helper.defaultErrorEmbed()).queue(); - bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Getting Birthday: " + ex.getMessage()); + } - return null; - }); + event.getHook().sendMessageEmbeds(Helper.uncaughtErrorEmbed(ctx.getUserI18n(), ex.getMessage())).queue(); + bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Getting Birthday: " + ex.getMessage()); } - private void sendError(SlashCommandInteractionEvent event) { + private void sendError(SlashCommandInteractionEvent event, ResourceBundle i18n) { event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "🎂 Error Getting Birthday", - "<:cafeBot_sad:1171726165040447518> Sorry... there was an error getting their birthday. It might not be set, you should tell them to set it with `/birthday`!." + i18n.getString("command.birthday.subcommand.get.embed.error.title"), + i18n.getString("command.birthday.subcommand.get.embed.error.description") )).queue(); } - private void sendBirthday(User user, boolean isSelf, Birthday birthday, SlashCommandInteractionEvent event) { - MessageEmbed embed = (isSelf) ? selfBirthdayEmbed(birthday) : birthdayEmbed(user, birthday); + private void sendBirthday(User user, boolean isSelf, Birthday birthday, SlashCommandInteractionEvent event, CommandContext ctx) { + MessageEmbed embed = (isSelf) ? selfBirthdayEmbed(birthday, ctx.getUserI18n()) : birthdayEmbed(user, birthday, ctx.getUserI18n()); event.getHook().sendMessageEmbeds(embed).queue(); } - private MessageEmbed selfBirthdayEmbed(Birthday birthday) { + private MessageEmbed selfBirthdayEmbed(Birthday birthday, ResourceBundle i18n) { + String description = i18n.getString("command.birthday.subcommand.get.embed.self.description") + .replace("{month}", Month.of(birthday.getMonth()).getDisplayName(TextStyle.FULL, Locale.ENGLISH)) + .replace("{day}", String.valueOf(birthday.getDay())) + .replace("{timezone}", birthday.getTimeZone().getId()); + return new EmbedBuilder() .setColor(Helper.getRandomColor()) - .setTitle("🎂 Your Birthday") - .setDescription( - String.format( - "Your birthday is on **%s %d** (%s).", - Month.of(birthday.getMonth()).getDisplayName(TextStyle.FULL, Locale.ENGLISH), - birthday.getDay(), - birthday.getTimeZone().getId() - ) - ) + .setTitle(i18n.getString("command.birthday.subcommand.get.embed.self.title")) + .setDescription(description) .build(); } - private MessageEmbed birthdayEmbed(User user, Birthday birthday) { + private MessageEmbed birthdayEmbed(User user, Birthday birthday, ResourceBundle i18n) { + String title = i18n.getString("command.birthday.subcommand.get.embed.other.title") + .replace("{user}", user.getName()); + + String description = i18n.getString("command.birthday.subcommand.get.embed.other.description") + .replace("{month}", Month.of(birthday.getMonth()).getDisplayName(TextStyle.FULL, Locale.ENGLISH)) + .replace("{day}", String.valueOf(birthday.getDay())) + .replace("{timezone}", birthday.getTimeZone().getId()); + return new EmbedBuilder() .setColor(Helper.getRandomColor()) - .setTitle("🎂 " + user.getName() + "'s Birthday") - .setDescription( - String.format("Their birthday is on **%s %d** (%s).", - Month.of(birthday.getMonth()).getDisplayName(TextStyle.FULL, Locale.ENGLISH), - birthday.getDay(), - birthday.getTimeZone().getId() - ) - ) + .setTitle(title) + .setDescription(description) .build(); } @@ -102,14 +108,14 @@ public String getName() { } @Override - public String getDescription() { - return "Get someone's birthday!"; + public String getDescriptionPath() { + return "command.birthday.subcommand.get.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person you want to get the birthday of!", false) + new OptionData(OptionType.USER, "user", "command.birthday.subcommand.get.arguments.user.description", false) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayRemoveSubCommand.java index 6fcb7405c..5ba0b66a0 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayRemoveSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -14,14 +15,14 @@ public BirthdayRemoveSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { User user = event.getUser(); bot.getCafeAPI().getBirthdayApi().deleteBirthday(user.getId()) .thenRun(() -> { event.getHook().sendMessageEmbeds(Helper.successEmbed( - "Birthday Removed 🥺", - "<:cafeBot_sad:1171726165040447518> Your birthday has been removed... but I know it's sometimes better to keep things private..." + ctx.getUserI18n().getString("command.birthday.subcommand.remove.embed.title"), + ctx.getUserI18n().getString("command.birthday.subcommand.remove.embed.description") )).queue(); }); } @@ -32,8 +33,8 @@ public String getName() { } @Override - public String getDescription() { - return "Remove your birthday."; + public String getDescriptionPath() { + return "command.birthday.subcommand.remove.description"; } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdaySetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdaySetSubCommand.java index edaad339a..00b594c97 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdaySetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdaySetSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.Birthday; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -17,6 +18,7 @@ import java.time.format.TextStyle; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; public class BirthdaySetSubCommand extends Command implements ISubCommand { @@ -25,7 +27,7 @@ public BirthdaySetSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String userId = event.getUser().getId(); int month = event.getOption("month").getAsInt(); int day = event.getOption("day").getAsInt(); @@ -36,41 +38,44 @@ public void handle(SlashCommandInteractionEvent event) { bot.getCafeAPI().getBirthdayApi().setBirthday(userId, birthday) .thenAccept((newBirthday) -> { + String description = ctx.getUserI18n().getString("command.birthday.subcommand.set.embed.success.description") + .replace("{month}", Month.of(month).getDisplayName(TextStyle.FULL, Locale.ENGLISH)) + .replace("{day}", String.valueOf(day)) + .replace("{timezone}", zoneId.getId()); event.getHook().sendMessageEmbeds(Helper.successEmbed( - "🎂 Birthday Set", - String.format( - "You have successfully set your birthday to **%s %d** (%s).", - Month.of(month).getDisplayName(TextStyle.FULL, Locale.ENGLISH), - day, - zoneId.getId() - ) + ctx.getUserI18n().getString("command.birthday.subcommand.set.embed.success.title"), + description )).queue(); }) .exceptionally((ex) -> { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Setting Birthday", - "I... I don't know what happened... the computer's not letting me put your birthday in!" - )).queue(); - - bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Setting Birthday: " + ex.getMessage(), true, true); - return null; + handleError(ex, event, ctx); + throw new CompletionException(ex.getCause()); }); } + private void handleError(Throwable ex, SlashCommandInteractionEvent event, CommandContext ctx) { + event.getHook().sendMessageEmbeds(Helper.errorEmbed( + ctx.getUserI18n().getString("command.birthday.subcommand.set.embed.error.title"), + ctx.getUserI18n().getString("command.birthday.subcommand.set.embed.error.description") + )).queue(); + + bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Setting Birthday: " + ex.getMessage(), true, true); + } + @Override public String getName() { return "set"; } @Override - public String getDescription() { - return "Edit your birthday!"; + public String getDescriptionPath() { + return "command.birthday.subcommand.set.description"; } @Override public OptionData[] getOptions() { return new OptionData[]{ - new OptionData(OptionType.INTEGER, "month", "The month you were born!", true) + new OptionData(OptionType.INTEGER, "month", "command.birthday.subcommand.set.arguments.month.description", true) .addChoice("1 - January", 1) .addChoice("2 - February", 2) .addChoice("3 - March", 3) @@ -84,12 +89,12 @@ public OptionData[] getOptions() { .addChoice("11 - November", 11) .addChoice("12 - December", 12), - new OptionData(OptionType.INTEGER, "day", "The day you were born in the specified month!", true) + new OptionData(OptionType.INTEGER, "day", "command.birthday.subcommand.set.arguments.day.description", true) .setRequiredRange(1, 31), - new OptionData(OptionType.STRING, "timezone", "Start typing to see available options!", true, true), + new OptionData(OptionType.STRING, "timezone", "command.birthday.subcommand.set.arguments.timezone.description", true, true), - new OptionData(OptionType.INTEGER, "year", "The year you were born in!", false) + new OptionData(OptionType.INTEGER, "year", "command.birthday.subcommand.set.arguments.year.description", false) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/IMemeSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/IMemeSubCommand.java index 95169cf2a..da9443efc 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/IMemeSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/IMemeSubCommand.java @@ -1,5 +1,6 @@ package com.beanbeanjuice.cafebot.commands.fun.meme; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.meme.api.wrapper.MemeAPI; @@ -14,15 +15,15 @@ public interface IMemeSubCommand extends ISubCommand { String[] getSubreddits(); - default void handle(SlashCommandInteractionEvent event) { + default void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int subredditIndex = Helper.getRandomInteger(0, this.getSubreddits().length); MemeAPI.get(this.getSubreddits()[subredditIndex]) .thenAccept((redditMeme) -> event.getHook().sendMessageEmbeds(this.getMessageEmbed(redditMeme)).queue()) .exceptionally((ex) -> { event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Failed Getting Meme", - "I... I'm so sorry... I tripped and fell when getting your meme..." + ctx.getUserI18n().getString("command.meme.description.error.embed.title"), + ctx.getUserI18n().getString("command.meme.description.error.embed.description") )).queue(); throw new CompletionException(ex); }); diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCoffeeSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCoffeeSubCommand.java index c81d5f8c1..e6f2d2998 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCoffeeSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCoffeeSubCommand.java @@ -24,8 +24,8 @@ public String getName() { } @Override - public String getDescription() { - return "Get a coffee meme!"; + public String getDescriptionPath() { + return "command.meme.subcommand.coffee.description"; } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCommand.java index 01f9832c2..45a89f274 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeCommand.java @@ -19,8 +19,8 @@ public String getName() { } @Override - public String getDescription() { - return "Get a meme!"; + public String getDescriptionPath() { + return "command.meme.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeRandomSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeRandomSubCommand.java index 90a8b35e3..5a3ac9359 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeRandomSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeRandomSubCommand.java @@ -26,8 +26,8 @@ public String getName() { } @Override - public String getDescription() { - return "Get a random meme!"; + public String getDescriptionPath() { + return "command.meme.subcommand.random.description"; } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeTeaSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeTeaSubCommand.java index 1611f42fc..5291873b4 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeTeaSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/meme/MemeTeaSubCommand.java @@ -23,8 +23,8 @@ public String getName() { } @Override - public String getDescription() { - return "Get a tea meme!"; + public String getDescriptionPath() { + return "command.meme.subcommand.tea.description"; } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCaffeinatedSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCaffeinatedSubCommand.java index a5c288a2a..3c13b0663 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCaffeinatedSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCaffeinatedSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -19,15 +20,19 @@ public RateCaffeinatedSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); int percentage = Helper.getRandomInteger(0, 101); + String description = ctx.getGuildI18n().getString("command.rate.subcommand.caffeinated.embed.description") + .replace("{user}", user.getAsMention()) + .replace("{percent}", String.valueOf(percentage)); + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "☕ Caffeine Rating ☕", - String.format("%s is %d%% caffeinated! %s", user.getAsMention(), percentage, "") + ctx.getGuildI18n().getString("command.rate.subcommand.caffeinated.embed.title"), + description )).mention(user).queue(); } @@ -37,14 +42,14 @@ public String getName() { } @Override - public String getDescription() { - return "Rate how caffeinated you or someone is!"; + public String getDescriptionPath() { + return "command.rate.subcommand.caffeinated.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's caffeine levels you want to see.") + new OptionData(OptionType.USER, "user", "command.rate.subcommand.caffeinated.arguments.user.description") }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCommand.java index 525f50387..0353f7159 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateCommand.java @@ -19,8 +19,8 @@ public String getName() { } @Override - public String getDescription() { - return "Rate something!"; + public String getDescriptionPath() { + return "command.rate.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateGaySubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateGaySubCommand.java index 496f42f41..c19c259d5 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateGaySubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateGaySubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -19,15 +20,19 @@ public RateGaySubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); int percentage = Helper.getRandomInteger(0, 101); + String description = ctx.getGuildI18n().getString("command.rate.subcommand.gay.embed.description") + .replace("{user}", user.getAsMention()) + .replace("{percent}", String.valueOf(percentage)); + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "🏳️‍🌈 Gay Rating 🏳️‍🌈", - String.format("%s is %d%% gay! <:bloatedBlush:1154475611893547218>", user.getAsMention(), percentage) + ctx.getGuildI18n().getString("command.rate.subcommand.gay.embed.title"), + description )).mention(user).queue(); } @@ -37,14 +42,14 @@ public String getName() { } @Override - public String getDescription() { - return "See how colorful someone is!"; + public String getDescriptionPath() { + return "command.rate.subcommand.gay.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's gayness you want to see.") + new OptionData(OptionType.USER, "user", "command.rate.subcommand.gay.arguments.user.description") }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateInsaneSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateInsaneSubCommand.java index fd49113a7..8332e01c7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateInsaneSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateInsaneSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -19,15 +20,19 @@ public RateInsaneSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); int percentage = Helper.getRandomInteger(0, 101); + String description = ctx.getGuildI18n().getString("command.rate.subcommand.insane.embed.description") + .replace("{user}", user.getAsMention()) + .replace("{percent}", String.valueOf(percentage)); + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "‼️ Insane Rating ‼️", - String.format("%s is %d%% insane! %s", user.getAsMention(), percentage, "") + ctx.getGuildI18n().getString("command.rate.subcommand.insane.embed.title"), + description )).mention(user).queue(); } @@ -37,14 +42,14 @@ public String getName() { } @Override - public String getDescription() { - return "See how insane someone is!"; + public String getDescriptionPath() { + return "command.rate.subcommand.insane.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's insanity you want to see.") + new OptionData(OptionType.USER, "user", "command.rate.subcommand.insane.arguments.user.description") }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RatePoorSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RatePoorSubCommand.java index b284cffc1..cf54c7c47 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RatePoorSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RatePoorSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -19,15 +20,19 @@ public RatePoorSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); int percentage = Helper.getRandomInteger(0, 101); + String description = ctx.getGuildI18n().getString("command.rate.subcommand.poor.embed.description") + .replace("{user}", user.getAsMention()) + .replace("{percent}", String.valueOf(percentage)); + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "💸 Poor Rating 💸", - String.format("%s is %d%% poor! %s", user.getAsMention(), percentage, "🤢🤮") + ctx.getGuildI18n().getString("command.rate.subcommand.poor.embed.title"), + description )).mention(user).queue(); } @@ -37,14 +42,14 @@ public String getName() { } @Override - public String getDescription() { - return "Rate how poor someone is!"; + public String getDescriptionPath() { + return "command.rate.subcommand.poor.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's poor level you want to see.") + new OptionData(OptionType.USER, "user", "command.rate.subcommand.poor.arguments.user.description") }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSimpSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSimpSubCommand.java index f2f5ae670..852f1da59 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSimpSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSimpSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -19,15 +20,19 @@ public RateSimpSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); int percentage = Helper.getRandomInteger(0, 101); + String description = ctx.getGuildI18n().getString("command.rate.subcommand.simp.embed.description") + .replace("{user}", user.getAsMention()) + .replace("{percent}", String.valueOf(percentage)); + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "😍 Simp Rating 😍", - String.format("%s is %d%% simp! %s", user.getAsMention(), percentage, "<:flushed_nervous:841923862202548224>") + ctx.getGuildI18n().getString("command.rate.subcommand.simp.embed.title"), + description )).mention(user).queue(); } @@ -37,14 +42,14 @@ public String getName() { } @Override - public String getDescription() { - return "Rate how much of a simp someone is!"; + public String getDescriptionPath() { + return "command.rate.subcommand.simp.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's simp level you want to see.") + new OptionData(OptionType.USER, "user", "command.rate.subcommand.simp.arguments.user.description") }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSmartSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSmartSubCommand.java index 0193abcd0..b94501fcb 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSmartSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/rate/RateSmartSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.User; @@ -19,15 +20,19 @@ public RateSmartSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); int percentage = Helper.getRandomInteger(0, 101); + String description = ctx.getGuildI18n().getString("command.rate.subcommand.smart.embed.description") + .replace("{user}", user.getAsMention()) + .replace("{percent}", String.valueOf(percentage)); + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "<:smartPeepo:1000248538376196280> Smart Rating <:smartPeepo:1000248538376196280>", - String.format("%s is %d%% smart! %s", user.getAsMention(), percentage, "<:bloatedBlush:1154475611893547218>") + ctx.getGuildI18n().getString("command.rate.subcommand.smart.embed.title"), + description )).mention(user).queue(); } @@ -37,14 +42,14 @@ public String getName() { } @Override - public String getDescription() { - return "Rate how smart someone is!"; + public String getDescriptionPath() { + return "command.rate.subcommand.smart.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's smart levels you want to see.") + new OptionData(OptionType.USER, "user", "command.rate.subcommand.smart.arguments.user.description") }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java index abcbdfbb5..f264610dc 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -15,7 +16,7 @@ public CoinFlipCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { boolean isHeads = (Helper.getRandomInteger(0, 2) == 1); String coin = (isHeads) ? "HEADS" : "TAILS"; event.getHook().sendMessageEmbeds(Helper.descriptionEmbed(String.format("The coin is **%s**!", coin))).queue(); @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Flip a coin!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java index b65689359..1926d3294 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.CountingStatistics; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -14,10 +15,7 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletionException; import java.util.stream.Collectors; @@ -28,7 +26,7 @@ public CountingStatisticsCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Guild guild = event.getGuild(); String guildId = guild.getId(); @@ -117,7 +115,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get the server's global counting statistics!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java index df846dc48..c3ad12f60 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -20,7 +21,7 @@ public RollCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional sizeMapping = Optional.ofNullable(event.getOption("size")); int size = sizeMapping.map(OptionMapping::getAsInt).orElse(6); int roll = Helper.getRandomInteger(1, size + 1); @@ -33,7 +34,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Roll a pair of dice!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java index 467b151e3..ce633cfac 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.handlers.games.TicTacToeHandler; import com.beanbeanjuice.cafebot.utility.helper.Helper; @@ -26,7 +27,7 @@ public TicTacToeCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int wager = Optional.ofNullable(event.getOption("wager")).map(OptionMapping::getAsInt).orElse(0); User opponent = event.getOption("opponent").getAsUser(); @@ -59,7 +60,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Play tic-tac-toe with someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java index 31a8aa111..46c9e0933 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Something to do with games!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java index 2edd23f20..e8a116c59 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.GameType; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -25,7 +26,7 @@ public GameStatsSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); User user = userMapping.map(OptionMapping::getAsUser).orElse(event.getUser()); @@ -104,7 +105,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get someone's game stats!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java index f92a03666..5e3ee183b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -15,7 +16,7 @@ public BotDonateCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook().sendMessageEmbeds( Helper.successEmbed( "Donations!", @@ -34,7 +35,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Donate to keep the bot up!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java index b8b2a2fb2..d6376c018 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.components.actionrow.ActionRow; @@ -17,7 +18,7 @@ public BotInviteCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook().sendMessageComponents(ActionRow.of(getInviteButton())).queue(); } @@ -33,7 +34,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Want to invite this bot to a server? Use this command!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java index 3d05ca8e6..ab6a76b09 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -20,7 +21,7 @@ public BotUpvoteCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook().sendMessageEmbeds(Helper.successEmbed( "Voting List", """ @@ -49,7 +50,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Upvote the bot!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BugReportCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BugReportCommand.java index e8024bc44..928a65b2c 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BugReportCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BugReportCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.components.actionrow.ActionRow; @@ -17,7 +18,7 @@ public BugReportCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String BUG_REPORT_URL = "https://github.com/beanbeanjuice/cafeBot/issues/new/choose"; event.getHook().sendMessageComponents( ActionRow.of(Button.link(BUG_REPORT_URL, "Bug Report").withEmoji(Emoji.fromFormatted("<:bean_moment:841922879166742529>"))) @@ -30,7 +31,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Discovered a bug with me?"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/DefineCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/DefineCommand.java index b8dc45aaa..0404f5219 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/DefineCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/DefineCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.utility.api.dictionary.DictionaryAPIWrapper; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -21,7 +22,7 @@ public DefineCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String word = event.getOption("word").getAsString(); Optional languageCodeMapping = Optional.ofNullable(event.getOption("languageCode")); @@ -45,7 +46,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Define something!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/EmbedCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/EmbedCommand.java index 086052cb5..bf5b88a6e 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/EmbedCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/EmbedCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -27,7 +28,7 @@ public EmbedCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { GuildChannelUnion channel = event.getOption("channel").getAsChannel(); // Should not be null. Optional messageOptional = Optional.ofNullable(event.getOption("message")).map(OptionMapping::getAsString); Optional thumbnailOptional = Optional.ofNullable(event.getOption("thumbnail")).map(OptionMapping::getAsAttachment); @@ -116,7 +117,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Create a beautiful embed!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/FeatureCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/FeatureCommand.java index 87bf464f1..c4480be7a 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/FeatureCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/FeatureCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.components.actionrow.ActionRow; @@ -19,7 +20,7 @@ public FeatureCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook().sendMessageComponents( ActionRow.of(Button.link(FEATURE_REQUEST_URL, "Feature Request").withEmoji(Emoji.fromFormatted("<:ticket:851426785203322992>"))) ).queue(); @@ -31,7 +32,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Request a new feature for me!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/HelpCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/HelpCommand.java index aa9420184..a58b80d42 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/HelpCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/HelpCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.components.actionrow.ActionRow; @@ -15,7 +16,7 @@ public HelpCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook() .sendMessageEmbeds(bot.getHelpHandler().getCategoriesEmbed()) .addComponents(ActionRow.of(bot.getHelpHandler().getAllCategoriesSelectMenu(0))) @@ -28,7 +29,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get help with some commands!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/InfoCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/InfoCommand.java index 7c4cde015..cebede1c7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/InfoCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/InfoCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -17,7 +18,7 @@ public InfoCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook().sendMessageEmbeds(infoEmbed()).queue(); } @@ -43,7 +44,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get information about the bot!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java index 10c0ff2e6..588050cbe 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.scheduling.UpdateMessageScheduler; @@ -15,6 +16,7 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import java.util.Optional; +import java.util.ResourceBundle; public class PingCommand extends Command implements ICommand { @@ -23,10 +25,10 @@ public PingCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int shardId = event.isFromGuild() ? event.getJDA().getShardInfo().getShardId() : -1; - event.getHook().sendMessageEmbeds(messageEmbed(shardId)).queue(); + event.getHook().sendMessageEmbeds(messageEmbed(shardId, ctx.getUserI18n())).queue(); Optional wordOptionMapping = Optional.ofNullable(event.getOption("word")); Optional numberOptionMapping = Optional.ofNullable(event.getOption("number")); @@ -35,16 +37,23 @@ public void handle(SlashCommandInteractionEvent event) { numberOptionMapping.map(OptionMapping::getAsInt).ifPresent((number) -> event.getHook().sendMessage(String.valueOf(number)).queue()); } - private MessageEmbed messageEmbed(int shardId) { + private MessageEmbed messageEmbed(int shardId, ResourceBundle bundle) { EmbedBuilder embedBuilder = new EmbedBuilder(UpdateMessageScheduler.getUpdateEmbed(this.bot)); embedBuilder.setTitle("ping!", "https://www.beanbeanjuice.com/cafeBot.html"); - embedBuilder.appendDescription("\n\nHello!~ Would you like to order some coffee?"); + embedBuilder + .appendDescription("\n\n") + .appendDescription(bundle.getString("command.ping.embed.description")); - if (shardId > -1) embedBuilder.appendDescription(String.format("\n\nYour shard ID is %d!", shardId)); + if (shardId > -1) { + String shardIdString = bundle.getString("command.ping.embed.shard").replace("{SHARD_ID}", String.valueOf(shardId)); + embedBuilder + .appendDescription("\n\n") + .appendDescription(shardIdString); + } embedBuilder - .setFooter("Author: beanbeanjuice - " + "https://github.com/beanbeanjuice/cafeBot") + .setFooter(bundle.getString("command.ping.embed.author")) .setThumbnail(this.bot.getDiscordAvatarUrl()) .setColor(Helper.getRandomColor()); return embedBuilder.build(); @@ -56,8 +65,8 @@ public String getName() { } @Override - public String getDescription() { - return "Pong!"; + public String getDescriptionPath() { + return "commands.ping.description"; } @Override @@ -68,8 +77,8 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "word", "A word to repeat back to you.", false, false), - new OptionData(OptionType.INTEGER, "number", "An integer to repeat back to you.", false, false) + new OptionData(OptionType.STRING, "word", "command.ping.arguments.word.description", false, false), + new OptionData(OptionType.INTEGER, "number", "command.ping.arguments.number.description", false, false) .addChoice("One", 1) .addChoice("2", 2) }; diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/RemoveMyDataCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/RemoveMyDataCommand.java index 1f0df3222..968292c33 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/RemoveMyDataCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/RemoveMyDataCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -18,7 +19,7 @@ public RemoveMyDataCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String FORM_URL = "https://dashboard.cafebot.dev"; event.getHook().sendMessageEmbeds(Helper.successEmbed( "Data Removal", @@ -32,7 +33,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Remove your data from this bot."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/StatsCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/StatsCommand.java index b4e3d6d80..fe2382de4 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/StatsCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/StatsCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -17,7 +18,7 @@ public StatsCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getHook().sendMessageEmbeds(statsEmbed()).queue(); } @@ -50,7 +51,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get statistics about the bot!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/SupportCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/SupportCommand.java index ced123a6a..4ac657085 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/SupportCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/SupportCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -18,7 +19,7 @@ public SupportCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String SUPPORT_URL = "https://discord.gg/KrUFw3uHST"; event.getHook().sendMessageEmbeds(Helper.successEmbed( "Support", @@ -32,7 +33,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Something wrong with me? Get some support!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/VersionCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/VersionCommand.java index 230e0ab1a..1fa7e7786 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/VersionCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/VersionCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.utility.api.GitHubVersionEndpointWrapper; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -22,7 +23,7 @@ public VersionCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional versionMapping = Optional.ofNullable(event.getOption("version")); String version = versionMapping.map(OptionMapping::getAsString).orElse(bot.getBotVersion()); @@ -51,7 +52,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get release notes for the any of the previous versions!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/WhoCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/WhoCommand.java index 3360d9c71..87817f06b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/WhoCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/WhoCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; @@ -28,7 +29,7 @@ public WhoCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional userMapping = Optional.ofNullable(event.getOption("user")); if (event.isFromGuild()) handleFromGuild(event); @@ -86,7 +87,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "See stats about yourself or another user!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java index 89bb2f655..34fc5132a 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java @@ -5,8 +5,8 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.api.wrapper.type.calendar.PartialCalendar; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; -import com.beanbeanjuice.cafebot.utility.handlers.calendar.CalendarHandler; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -23,7 +23,7 @@ public CalendarAddSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { OwnerType type = OwnerType.valueOf(event.getOption("type").getAsString()); String name = event.getOption("name").getAsString(); String url = event.getOption("url").getAsString(); @@ -88,7 +88,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Add a calendar!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java index 36847e4e8..469fc91d8 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "All things to do with calendars!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java index e3cc58050..83ced5f15 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java @@ -5,6 +5,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.api.wrapper.type.calendar.Calendar; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -27,7 +28,7 @@ public CalendarDeleteSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String calendarId = event.getOption("id").getAsString().split("ID: ")[1]; bot.getCafeAPI().getCalendarApi().getCalendar(calendarId).thenAccept(calendar -> { @@ -69,7 +70,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Delete a calendar!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java index 41d756e80..db625f876 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.api.wrapper.type.calendar.Calendar; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.handlers.calendar.CalendarHandler; import com.beanbeanjuice.cafebot.utility.helper.Helper; @@ -13,10 +14,7 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.TimeZone; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -27,7 +25,7 @@ public CalendarGetSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String[] split = event.getOption("id").getAsString().split("ID: "); String calendarId = (split.length == 2) ? split[1] : split[0]; ZoneId zoneId = TimeZone.getTimeZone(event.getOption("timezone").getAsString()).toZoneId(); @@ -52,7 +50,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get your calendars!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java index 083d57676..bf632013a 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java @@ -2,10 +2,10 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.OptionMapping; @@ -23,7 +23,7 @@ public CalendarListSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { User user = Optional.ofNullable(event.getOption("user")).map(OptionMapping::getAsUser).orElse(event.getUser()); bot.getCafeAPI().getCalendarApi().getUserCalendars(user.getId()).thenAccept(calendars -> { @@ -48,7 +48,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "List all of the calendars for a specific user!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java index a047b550f..18e94c9ee 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -16,7 +17,7 @@ public TwitchAddSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String username = event.getOption("username").getAsString(); // Should not be null. bot.getCafeAPI().getTwitchChannelApi().addChannel(event.getGuild().getId(), username) @@ -44,7 +45,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Add a twitch channel to get live notifications for!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java index 3069e0825..2dc42dcb3 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java @@ -16,7 +16,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Add or remove channels/notification channels."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java index 03ad486d7..c8e9e6795 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -18,7 +19,7 @@ public TwitchListSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getCafeAPI().getTwitchChannelApi().getChannels(event.getGuild().getId()) .thenAccept((channels) -> event.getHook().sendMessageEmbeds(twitchChannelsEmbed(channels)).queue()) .exceptionally((ex) -> { @@ -57,7 +58,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get a list of all twitch channels for this server!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java index 9005ef87c..551aa3fbf 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -16,7 +17,7 @@ public TwitchRemoveSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String username = event.getOption("username").getAsString(); // Should not be null. bot.getCafeAPI().getTwitchChannelApi().deleteChannel(event.getGuild().getId(), username) @@ -43,7 +44,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Remove a twitch channel!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AmazedCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AmazedCommand.java index f8ee4efb9..4dc27b337 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AmazedCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AmazedCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public AmazedCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.AMAZED, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Be amazed at something or someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AskCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AskCommand.java index 5eb2087c0..d8aa167c7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AskCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/AskCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public AskCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.ASK, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Ask someone something!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BiteCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BiteCommand.java index 99172a4eb..d9ad8029b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BiteCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BiteCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public BiteCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.BITE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Bite someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BlushCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BlushCommand.java index 53df2d890..06f6a36e0 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BlushCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BlushCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public BlushCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.BLUSH, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Blush at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BonkCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BonkCommand.java index c87fec647..0aa9c91aa 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BonkCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BonkCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public BonkCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.BONK, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Bonk someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BoopCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BoopCommand.java index d8223d2ab..1903a209e 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BoopCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/BoopCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public BoopCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.BOOP, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Boop someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CryCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CryCommand.java index 9fab001bd..fe8533adb 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CryCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CryCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public CryCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.CRY, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Cry because of someone... :("; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CuddleCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CuddleCommand.java index de6bbe70d..1e778ebb7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CuddleCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/CuddleCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public CuddleCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.CUDDLE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Cuddle someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DabCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DabCommand.java index bc7824185..2d96ffef8 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DabCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DabCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public DabCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.DAB, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Dab! Why?"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DanceCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DanceCommand.java index e10e8472c..bd16adf4c 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DanceCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DanceCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public DanceCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.DANCE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Dance with someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DieCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DieCommand.java index 35dd7c116..7557649c7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DieCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/DieCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public DieCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.DIE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Die because of someone..."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/GreetCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/GreetCommand.java index 10e1c7542..e2968d96a 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/GreetCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/GreetCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public GreetCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.WELCOME, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Greet someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HeadPatCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HeadPatCommand.java index 087da04a5..ecf1d8a6e 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HeadPatCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HeadPatCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public HeadPatCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.HEADPAT, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Headpat someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HideCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HideCommand.java index e9cca2804..3addb0e18 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HideCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HideCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public HideCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.HIDE, event, this.bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Hide from someone... ;^;"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HmphCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HmphCommand.java index 711391d30..5b3520234 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HmphCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HmphCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public HmphCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.HMPH, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Hmph at someone~"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HugCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HugCommand.java index e797dface..8842cd297 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HugCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/HugCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public HugCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.HUG, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Hug someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/KissCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/KissCommand.java index ca10bb548..fda1f783f 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/KissCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/KissCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public KissCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.KISS, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Kiss someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LickCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LickCommand.java index 28d83d54c..2bb0bccf5 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LickCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LickCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public LickCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.LICK, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Lick someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LoveCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LoveCommand.java index 337d59f3a..9c5e60339 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LoveCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/LoveCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public LoveCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.LOVE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Love someone!~ <3"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NomCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NomCommand.java index 10f2657b7..ab066c06e 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NomCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NomCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public NomCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.NOM, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Nom on someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NoseBleedCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NoseBleedCommand.java index 20531af48..9ab860a41 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NoseBleedCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/NoseBleedCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public NoseBleedCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.NOSEBLEED, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Tell someone you caused them to have a nosebleed!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/OkCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/OkCommand.java index 2b32b8fc6..2170300ca 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/OkCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/OkCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public OkCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.OK, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Be \"ok\" with something."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PokeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PokeCommand.java index 48c6d1827..fb13dfbe1 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PokeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PokeCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public PokeCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.POKE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Poke someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PoutCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PoutCommand.java index 9c79dc756..2fc3bb880 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PoutCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PoutCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public PoutCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.POUT, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Pout at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PunchCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PunchCommand.java index 64604932a..8224a102b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PunchCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/PunchCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public PunchCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.PUNCH, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Punch someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/RageCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/RageCommand.java index 8c303f214..8170d5e76 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/RageCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/RageCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public RageCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.RAGE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Rage at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShootCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShootCommand.java index 3f0a03ee1..edc5d829b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShootCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShootCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public ShootCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.SHOOT, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Shoot someone! 🔫"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShushCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShushCommand.java index e308fdee4..2ad7da031 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShushCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ShushCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public ShushCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.SHUSH, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Shush someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SlapCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SlapCommand.java index 2ad5bcb37..6f4f70484 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SlapCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SlapCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public SlapCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.SLAP, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Slap someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SleepCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SleepCommand.java index ef7d7c3f5..bb102e1dd 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SleepCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SleepCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public SleepCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.SLEEP, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Sleep with someone~"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SmileCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SmileCommand.java index f65b539c8..ff22a4173 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SmileCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/SmileCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public SmileCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.SMILE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Smile at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StabCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StabCommand.java index 8dd234de7..42b96a616 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StabCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StabCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public StabCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.STAB, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Stab someone."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StareCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StareCommand.java index 8c1537f1d..957f75d5b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StareCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/StareCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public StareCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.STARE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Stare at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ThrowCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ThrowCommand.java index b48698878..4ab2ccc1c 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ThrowCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/ThrowCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public ThrowCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.THROW, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Throw someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/TickleCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/TickleCommand.java index f49efafc4..68c701039 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/TickleCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/TickleCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public TickleCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.TICKLE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Tickle someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/UWUCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/UWUCommand.java index 1bd3acb2e..5ce5fea74 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/UWUCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/UWUCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public UWUCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.UWU, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "UWU at someone!~"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WaveCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WaveCommand.java index 145761cc2..dd84b6e07 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WaveCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WaveCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public WaveCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.WAVE, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Wave at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WinkCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WinkCommand.java index 0a9c77135..8d8ff8268 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WinkCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/WinkCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public WinkCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.WINK, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Wink at someone! ;)"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/YellCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/YellCommand.java index 744e94f30..62e5b92d7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/YellCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/YellCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public YellCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { this.handleInteraction(InteractionType.YELL, event, bot); } @@ -27,7 +28,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Yell at someone!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionBlockSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionBlockSubCommand.java index 14259ddc0..e2a1b113d 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionBlockSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionBlockSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -19,7 +20,7 @@ public InteractionBlockSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { User userToBlock = event.getOption("user").getAsUser(); // Should not be null. bot.getCafeAPI().getInteractionsApi().blockUser(event.getUser().getId(), userToBlock.getId()).thenAccept((blockList) -> { @@ -44,7 +45,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Someone being annoying? Block them! You won't receive any interactions from them anymore."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionCommand.java index 8fa76768c..e72efc74c 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Block, unblock, and enable/disable interactions!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionStatusSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionStatusSubCommand.java index b2c35c871..422e7b994 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionStatusSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionStatusSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -17,7 +18,7 @@ public InteractionStatusSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { boolean status = event.getOption("status").getAsBoolean(); // Should not be null. bot.getCafeAPI().getInteractionsApi().setInteractionStatus(event.getUser().getId(), status).thenAccept(response -> { event.getHook().sendMessageEmbeds(Helper.successEmbed( @@ -40,7 +41,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Enable or disable the ability to send interactions to you!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionUnblockSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionUnblockSubCommand.java index 73c48cba4..6fbd5dcba 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionUnblockSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/interaction/generic/InteractionUnblockSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -25,7 +26,7 @@ public InteractionUnblockSubCommand(CafeBot bot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String unblockedUser = event.getOption("user-id").getAsString(); bot.getCafeAPI().getInteractionsApi().unBlockUser(event.getUser().getId(), unblockedUser).thenRun(() -> { @@ -59,7 +60,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Unblocks a user you have previously blocked."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/moderation/ClearChatCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/moderation/ClearChatCommand.java index ec7d0a50d..ca63169a4 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/moderation/ClearChatCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/moderation/ClearChatCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -20,7 +21,7 @@ public ClearChatCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int amount = event.getOption("amount").getAsInt(); MessageChannel channel = event.getChannel(); @@ -45,7 +46,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Clear the chat!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageCommand.java index 4f57491f2..831640987 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Set or remove an airport message!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageRemoveSubCommand.java index d9383c5b4..0f9d1c339 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageRemoveSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.api.wrapper.api.enums.AirportMessageType; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -19,7 +20,7 @@ public AirportMessageRemoveSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { AirportMessageType type = AirportMessageType.valueOf(event.getOption("type").getAsString()); String guildId = event.getGuild().getId(); @@ -44,7 +45,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Set the airport message back to the default!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageSetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageSetSubCommand.java index 0f2d27913..15772bbbd 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageSetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/airport/AirportMessageSetSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.AirportMessageType; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import net.dv8tion.jda.api.components.label.Label; import net.dv8tion.jda.api.components.textinput.TextInput; @@ -21,7 +22,7 @@ public AirportMessageSetSubCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { AirportMessageType type = AirportMessageType.valueOf(event.getOption("type").getAsString()); // TextDisplay instructions = TextDisplay.of(""" @@ -85,7 +86,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Set the airport message."; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindCommand.java index fbe882eed..b260f9fa0 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "All things to do with binding a role to a voice channel!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindListSubCommand.java index e219e9059..f7e09194b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindListSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.VoiceRole; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -24,7 +25,7 @@ public BindListSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getCafeAPI().getVoiceRoleApi().getVoiceRoles(event.getGuild().getId()).thenAccept((voiceRoles) -> { handleVoiceRolesEmbed(event, voiceRoles); }).exceptionally((ex) -> { @@ -68,7 +69,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "List all of the voice channel-role binds!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindRemoveSubCommand.java index 829c31a6a..c8cabe953 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindRemoveSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.Role; @@ -20,7 +21,7 @@ public BindRemoveSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { VoiceChannel channel = event.getOption("channel").getAsChannel().asVoiceChannel(); Role role = event.getOption("role").getAsRole(); String guildId = event.getGuild().getId(); @@ -45,7 +46,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Remove a bound role from a voice channel"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindSetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindSetSubCommand.java index ff3f2de42..b969d52f7 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindSetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/bind/BindSetSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.Role; @@ -20,7 +21,7 @@ public BindSetSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { VoiceChannel channel = event.getOption("channel").getAsChannel().asVoiceChannel(); Role role = event.getOption("role").getAsRole(); String guildId = event.getGuild().getId(); @@ -45,7 +46,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Bind a role to a voice channel!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelCommand.java index 1a6602ea4..3ff4d2700 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "All things to do with custom channels!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelListSubCommand.java index a747eb4d2..93767eb4e 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelListSubCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.CustomChannel; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -12,11 +13,9 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import java.awt.*; import java.util.Arrays; import java.util.Optional; import java.util.concurrent.CompletionException; -import java.util.stream.Collectors; public class ChannelListSubCommand extends Command implements ISubCommand { @@ -25,7 +24,7 @@ public ChannelListSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Guild guild = event.getGuild(); this.bot.getCafeAPI().getCustomChannelApi().getCustomChannels(guild.getId()) @@ -64,7 +63,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "List all custom channels for the server!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelRemoveSubCommand.java index 1a0c975ae..a38c0b3b2 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelRemoveSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.CustomChannelType; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -19,7 +20,7 @@ public ChannelRemoveSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String guildId = event.getGuild().getId(); CustomChannelType type = CustomChannelType.valueOf(event.getOption("type").getAsString()); @@ -47,7 +48,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Remove a custom channel!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelSetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelSetSubCommand.java index 777a8f2b7..46ae13b73 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelSetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/channels/ChannelSetSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.CustomChannelType; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -23,7 +24,7 @@ public ChannelSetSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { CustomChannelType type = CustomChannelType.valueOf(event.getOption("type").getAsString()); Optional channelMapping = Optional.ofNullable(event.getOption("channel")); GuildChannelUnion channel = channelMapping.map(OptionMapping::getAsChannel).orElse((GuildChannelUnion) event.getChannel()); @@ -62,7 +63,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Set a custom channel!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCommand.java index 2793ae56e..14fe8962a 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "All things to do with polls!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCreateSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCreateSubCommand.java index 20f6e9d56..cef806d9f 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCreateSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollCreateSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.listeners.modals.polls.PollModalListener; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -15,7 +16,7 @@ public PollCreateSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String userId = event.getUser().getId(); int duration = event.getOption("duration").getAsInt(); @@ -29,7 +30,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Create a poll!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollDeleteSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollDeleteSubCommand.java index ed67925f4..b1ffb9cda 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollDeleteSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/polls/PollDeleteSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.poll.Poll; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; @@ -22,7 +23,7 @@ public PollDeleteSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int pollId = Integer.valueOf(event.getOption("id").getAsString()); bot.getCafeAPI().getPollApi().deletePoll(pollId).thenRun(() -> { @@ -39,7 +40,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Delete a poll!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCommand.java index fc684b759..f0c8618e5 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "All things to do with raffles!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCreateSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCreateSubCommand.java index a96d5ff48..ab77c9853 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCreateSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleCreateSubCommand.java @@ -5,6 +5,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.Raffle; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -27,7 +28,7 @@ public RaffleCreateSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String guildId = event.getGuild().getId(); bot.getCafeAPI().getCustomChannelApi().getCustomChannel(guildId, CustomChannelType.RAFFLE) @@ -104,7 +105,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Create a raffle!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleDeleteSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleDeleteSubCommand.java index 151953212..946f7acf5 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleDeleteSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/raffles/RaffleDeleteSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.Raffle; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; @@ -22,7 +23,7 @@ public RaffleDeleteSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int raffleId = Integer.valueOf(event.getOption("id").getAsString()); bot.getCafeAPI().getRaffleApi().deleteRaffle(raffleId).thenRun(() -> { @@ -39,7 +40,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Delete a raffle!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleCommand.java index 91692f306..6df8dd7c0 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleCommand.java @@ -19,7 +19,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "All things to do with custom roles!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleListSubCommand.java index 2ae6af4a7..897b4c957 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleListSubCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.CustomRole; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.entities.Guild; @@ -21,7 +22,7 @@ public RoleListSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Guild guild = event.getGuild(); this.bot.getCafeAPI().getCustomRoleApi().getCustomRoles(guild.getId()) @@ -54,7 +55,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "List all custom roles for the server!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleRemoveSubCommand.java index ed776e637..4f9abcaed 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleRemoveSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.CustomRoleType; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -19,7 +20,7 @@ public RoleRemoveSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String guildId = event.getGuild().getId(); CustomRoleType type = CustomRoleType.valueOf(event.getOption("type").getAsString()); @@ -47,7 +48,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Remove a custom role!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleSetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleSetSubCommand.java index 09e50b569..17f8a0e34 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleSetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/settings/roles/RoleSetSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.CustomRoleType; import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; @@ -20,7 +21,7 @@ public RoleSetSubCommand(CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { CustomRoleType type = CustomRoleType.valueOf(event.getOption("type").getAsString()); Role role = event.getOption("role").getAsRole(); @@ -51,7 +52,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Set a custom role!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/social/ConfessCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/social/ConfessCommand.java index dfc1a5ab3..afac81a64 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/social/ConfessCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/social/ConfessCommand.java @@ -22,7 +22,7 @@ public ConfessCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String guildID = event.getGuild().getId(); String message = event.getOption("message").getAsString(); @@ -87,7 +87,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Confess something anonymously!~"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/social/MemberCountCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/social/MemberCountCommand.java index 9fe56a6da..0d637d3d2 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/social/MemberCountCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/social/MemberCountCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; +import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ICommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; import net.dv8tion.jda.api.Permission; @@ -15,7 +16,7 @@ public MemberCountCommand(final CafeBot cafeBot) { } @Override - public void handle(SlashCommandInteractionEvent event) { + public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { event.getGuild().retrieveMetaData().queue((metadata) -> { event.getHook().sendMessageEmbeds(Helper.successEmbed( "Member Count", @@ -30,7 +31,7 @@ public String getName() { } @Override - public String getDescription() { + public String getDescriptionPath() { return "Get the member count for your server!"; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java new file mode 100644 index 000000000..775867449 --- /dev/null +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java @@ -0,0 +1,15 @@ +package com.beanbeanjuice.cafebot.utility.commands; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.ResourceBundle; + +@Getter +@RequiredArgsConstructor +public class CommandContext { + + private final ResourceBundle guildI18n; + private final ResourceBundle userI18n; + +} diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java index ebbb75b4f..16d171144 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java @@ -1,17 +1,17 @@ package com.beanbeanjuice.cafebot.utility.commands; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; +import com.beanbeanjuice.cafebot.i18n.YamlControl; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; import lombok.Getter; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.interactions.DiscordLocale; import net.dv8tion.jda.api.interactions.InteractionContextType; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; -import net.dv8tion.jda.api.interactions.commands.build.Commands; -import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; -import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; -import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData; +import net.dv8tion.jda.api.interactions.commands.build.*; import net.dv8tion.jda.api.interactions.commands.Command.Choice; import java.util.*; @@ -33,13 +33,13 @@ public void addCommands(final ICommand... commands) { // Sub commands. SubcommandData[] subCommandDataArray = Arrays.stream(newCommand.getSubCommands()) - .map(this::getSubcommandData) + .map((subCommand) -> this.getSubcommandData(subCommand, newCommand)) .toArray(SubcommandData[]::new); commandData.addSubcommands(subCommandDataArray); // Groups. SubcommandGroupData[] groupDataArray = Arrays.stream(newCommand.getSubCommandGroups()) - .map(this::getSubcommandGroupData) + .map((subCommand) -> this.getSubcommandGroupData(subCommand, newCommand)) .toArray(SubcommandGroupData[]::new); commandData.addSubcommandGroups(groupDataArray); @@ -63,9 +63,43 @@ public void addCommands(final ICommand... commands) { ); } - private SlashCommandData getCommandData(final ICommand command) { - SlashCommandData commandData = Commands.slash(command.getName(), command.getDescription()); - commandData.addOptions(command.getOptions()); + private SlashCommandData getCommandData(ICommand command) { + String originalDescription = I18N.getBundle().getString(command.getDescriptionPath()); + SlashCommandData commandData = Commands.slash(command.getName(), originalDescription); + + Locale.availableLocales().forEach((locale) -> { + ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + DiscordLocale discordLocale = DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); + + String localizedDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + commandData.setDescriptionLocalization(discordLocale, localizedDescription); + }); + + OptionData[] commandOptions = command.getOptions(); + + commandOptions = Arrays.stream(commandOptions).peek((commandOption) -> { + // Set original (english) + String path = commandOption.getDescription(); // Description is originally the path. + String originalCommandOptionDescription = I18N.getBundle().getString(commandOption.getDescription()); + commandOption.setDescription(originalCommandOptionDescription); + + // Set localization + // Loop through all locales, map to discord locale, set localized description + Locale.availableLocales().map((locale) -> { + ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + + try { return DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); } + catch (MissingResourceException e) { return null; } + }).forEach((locale) -> { + if (locale == null) return; + + String localizedCommandOptionDescription = I18N.getBundle(locale.toLocale()).getString(path); + commandOption.setDescriptionLocalization(locale, localizedCommandOptionDescription); + }); + + }).toArray(OptionData[]::new); + + commandData.addOptions(commandOptions); ArrayList contexts = new ArrayList<>(); contexts.add(InteractionContextType.GUILD); @@ -77,15 +111,49 @@ private SlashCommandData getCommandData(final ICommand command) { return commandData; } - private SubcommandData getSubcommandData(final ISubCommand command) { - SubcommandData subcommandData = new SubcommandData(command.getName(), command.getDescription()); - subcommandData.addOptions(command.getOptions()); + private SubcommandData getSubcommandData(ISubCommand command, ICommand parent) { + String originalDescription = I18N.getBundle().getString(command.getDescriptionPath()); + SubcommandData subcommandData = new SubcommandData(command.getName(), originalDescription); + + Locale.availableLocales().forEach((locale) -> { + ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + DiscordLocale discordLocale = DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); + + String localizedDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + subcommandData.setDescriptionLocalization(discordLocale, localizedDescription); + }); + + OptionData[] commandOptions = command.getOptions(); + + commandOptions = Arrays.stream(commandOptions).peek((commandOption) -> { + // Set original (english) + String path = commandOption.getDescription(); // Description is originally the path. + String originalCommandOptionDescription = I18N.getBundle().getString(commandOption.getDescription()); + commandOption.setDescription(originalCommandOptionDescription); + + // Set localization + // Loop through all locales, map to discord locale, set localized description + Locale.availableLocales().map((locale) -> { + ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + + try { return DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); } + catch (MissingResourceException e) { return null; } + }).forEach((locale) -> { + if (locale == null) return; + + String localizedCommandOptionDescription = I18N.getBundle(locale.toLocale()).getString(path); + commandOption.setDescriptionLocalization(locale, localizedCommandOptionDescription); + }); + + }).toArray(OptionData[]::new); + + subcommandData.addOptions(commandOptions); return subcommandData; } - private SubcommandGroupData getSubcommandGroupData(final SubCommandGroup group) { + private SubcommandGroupData getSubcommandGroupData(SubCommandGroup group, ICommand parent) { SubcommandGroupData groupData = new SubcommandGroupData(group.getName(), group.getDescription()); - SubcommandData[] subcommandDataArray = Arrays.stream(group.getSubCommands()).map(this::getSubcommandData).toArray(SubcommandData[]::new); + SubcommandData[] subcommandDataArray = Arrays.stream(group.getSubCommands()).map((subCommand) -> this.getSubcommandData(subCommand, parent)).toArray(SubcommandData[]::new); groupData.addSubcommands(subcommandDataArray); return groupData; } @@ -125,17 +193,25 @@ private void handleSubCommandWithoutGroup(final String subCommandName, final ICo handleSubCommand(subCommand, command, event); } - private void handleSubCommand(final ISubCommand subCommand, final ICommand command, final SlashCommandInteractionEvent event) { + private void handleSubCommand(ISubCommand subCommand, ICommand command, SlashCommandInteractionEvent event) { if (!subCommand.isModal()) event.deferReply(command.isEphemeral()).queue(); - subCommand.handle(event); + Locale guildLocale = event.isFromGuild() ? event.getGuildLocale().toLocale() : event.getUserLocale().toLocale(); // Use user locale if not from guild + ResourceBundle guildBundle = ResourceBundle.getBundle("messages", guildLocale, YamlControl.INSTANCE); + ResourceBundle userBundle = ResourceBundle.getBundle("messages", event.getUserLocale().toLocale(), YamlControl.INSTANCE); + + subCommand.handle(event, new CommandContext(guildBundle, userBundle)); cafeBot.increaseCommandsRun(); } private void handleCommand(final ICommand command, final SlashCommandInteractionEvent event) { if (!command.isModal()) event.deferReply(command.isEphemeral()).queue(); - command.handle(event); + Locale guildLocale = event.isFromGuild() ? event.getGuildLocale().toLocale() : event.getUserLocale().toLocale(); // Use user locale if not from guild + ResourceBundle guildBundle = ResourceBundle.getBundle("messages", guildLocale, YamlControl.INSTANCE); + ResourceBundle userBundle = ResourceBundle.getBundle("messages", event.getUserLocale().toLocale(), YamlControl.INSTANCE); + + command.handle(event, new CommandContext(guildBundle, userBundle)); cafeBot.increaseCommandsRun(); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java index 4e3653cdd..904974399 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java @@ -1,8 +1,6 @@ package com.beanbeanjuice.cafebot.utility.commands; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.build.OptionData; @@ -13,11 +11,11 @@ public interface ICommand { - default void handle(SlashCommandInteractionEvent event) { } + default void handle(SlashCommandInteractionEvent event, CommandContext ctx) { } String getName(); - String getDescription(); + String getDescriptionPath(); CommandCategory getCategory(); diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ISubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ISubCommand.java index e06edc258..e08dabd67 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ISubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ISubCommand.java @@ -10,11 +10,11 @@ public interface ISubCommand { - void handle(SlashCommandInteractionEvent event); + void handle(SlashCommandInteractionEvent event, CommandContext ctx); String getName(); - String getDescription(); + String getDescriptionPath(); default OptionData[] getOptions() { return new OptionData[0]; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java b/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java index 5a8a40e87..38bdfff19 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java @@ -1,5 +1,7 @@ package com.beanbeanjuice.cafebot.utility.handlers; +import com.beanbeanjuice.cafebot.i18n.I18N; +import com.beanbeanjuice.cafebot.i18n.YamlControl; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandHandler; import com.beanbeanjuice.cafebot.utility.commands.ICommand; @@ -12,6 +14,7 @@ import net.dv8tion.jda.api.components.selections.StringSelectMenu.Builder; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.interactions.DiscordLocale; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import java.util.*; @@ -48,10 +51,12 @@ public MessageEmbed getCategoriesEmbed() { .build(); } - public MessageEmbed getCategoryEmbed(CommandCategory category, int index) { + public MessageEmbed getCategoryEmbed(CommandCategory category, DiscordLocale discordLocale, int index) { int skipAmount = 25 * index; String description = String.format("# Commands for %s", category.name()); + Locale locale = Locale.forLanguageTag(discordLocale.getLocale()); + EmbedBuilder embedBuilder = new EmbedBuilder(); commandHandler.getCommands().values() @@ -61,9 +66,11 @@ public MessageEmbed getCategoryEmbed(CommandCategory category, int index) { .skip(skipAmount) .limit(25) .forEach((command) -> { + String commandDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + embedBuilder.addField( String.format("**/%s**", command.getName()), - String.format("> *%s*", command.getDescription()), + String.format("> *%s*", commandDescription), true ); }); @@ -76,16 +83,21 @@ public MessageEmbed getCategoryEmbed(CommandCategory category, int index) { .build(); } - public MessageEmbed getCommandEmbed(String commandName) { + public MessageEmbed getCommandEmbed(String commandName, DiscordLocale discordLocale) { ICommand command = commandHandler.getCommands().get(commandName); + Locale locale = Locale.forLanguageTag(discordLocale.getLocale()); + ResourceBundle bundle = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + String subCommandsString = Arrays.stream(command.getSubCommands()).map((subCommand) -> { + String subCommandDescription = I18N.getBundle(locale).getString(subCommand.getDescriptionPath()); + return String.format( """ `%s %s` > %s - """, subCommand.getName(), this.getOptionsString(subCommand.getOptions()), subCommand.getDescription() + """, subCommand.getName(), this.getOptionsString(subCommand.getOptions()), subCommandDescription ); }).collect(Collectors.joining("\n")); subCommandsString = (subCommandsString.isBlank()) ? "*None*" : subCommandsString; @@ -96,6 +108,8 @@ public MessageEmbed getCommandEmbed(String commandName) { String permissionsString = this.getPermissionsString(command.getPermissions()); permissionsString = (permissionsString.isBlank()) ? "*None*" : permissionsString; + String commandDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + String commandString = String.format( """ # /%s @@ -109,7 +123,7 @@ public MessageEmbed getCommandEmbed(String commandName) { %s """, command.getName(), - command.getDescription(), + commandDescription, optionsString, subCommandsString, permissionsString diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java index d7fcda6fd..b864165f9 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java @@ -114,6 +114,18 @@ public static Integer stringToPositiveInteger(@NotNull String timeString) { } } + public static MessageEmbed uncaughtErrorEmbed(ResourceBundle i18n, String error) { + String title = i18n.getString("generic.error.uncaught.title"); + String description = i18n.getString("generic.error.uncaught.message") + .replace("{uncaught_error}", error); + + return new EmbedBuilder() + .setTitle(title) + .setDescription(description) + .setColor(Color.RED) + .build(); + } + public static MessageEmbed errorEmbed(@NotNull String title, @NotNull String description) { return new EmbedBuilder() .setTitle(title) diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HelpListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HelpListener.java index 4aea7acfb..e47358471 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HelpListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HelpListener.java @@ -38,7 +38,7 @@ public void onButtonInteraction(ButtonInteractionEvent event) { int index = Integer.parseInt(event.getComponentId().split(":")[5]); index = direction.equals("left") ? index - 1 : index + 1; - MessageEmbed categoryEmbed = helpHandler.getCategoryEmbed(category, index); + MessageEmbed categoryEmbed = helpHandler.getCategoryEmbed(category, event.getUserLocale(), index); List rows = new ArrayList<>(); rows.add(ActionRow.of(helpHandler.getAllCategoriesSelectMenu(0))); @@ -59,7 +59,7 @@ private void handleCategories(final StringSelectInteractionEvent event) { int index = Integer.parseInt(event.getComponentId().split(":")[2]); boolean isHome = value.equalsIgnoreCase("all"); - MessageEmbed categoryEmbed = isHome ? helpHandler.getCategoriesEmbed() : helpHandler.getCategoryEmbed(CommandCategory.valueOf(value), 0); + MessageEmbed categoryEmbed = isHome ? helpHandler.getCategoriesEmbed() : helpHandler.getCategoryEmbed(CommandCategory.valueOf(value), event.getUserLocale(), 0); List rows = new ArrayList<>(); rows.add(ActionRow.of(helpHandler.getAllCategoriesSelectMenu(0))); @@ -83,7 +83,7 @@ private void handleCommand(final StringSelectInteractionEvent event) { List rows = new ArrayList<>(); rows.add(ActionRow.of(helpHandler.getAllCategoriesSelectMenu(0))); - event.editMessageEmbeds(helpHandler.getCommandEmbed(commandName)) + event.editMessageEmbeds(helpHandler.getCommandEmbed(commandName, event.getUserLocale())) .setComponents(rows) .setReplace(true).queue(); } diff --git a/src/test/java/com/beanbeanjuice/utility/LocaleTest.java b/src/test/java/com/beanbeanjuice/utility/LocaleTest.java new file mode 100644 index 000000000..db9aa565b --- /dev/null +++ b/src/test/java/com/beanbeanjuice/utility/LocaleTest.java @@ -0,0 +1,17 @@ +package com.beanbeanjuice.utility; + +import net.dv8tion.jda.api.interactions.DiscordLocale; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LocaleTest { + + @Test + @DisplayName("can format discord locale") + public void canFormatDiscordLocaleFromLowerCase() { + String locale = "en-GB"; + Assertions.assertEquals(DiscordLocale.ENGLISH_UK, DiscordLocale.from(locale)); + } + +} From e4c6721028e711cb4d7202fe5d09cfa9bdb3bea0 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 2 Mar 2026 20:10:09 -0800 Subject: [PATCH 02/22] Updated Menu Command --- modules/i18n/src/main/resources/i18n/en/command/menu.yml | 2 ++ modules/i18n/src/main/resources/i18n/messages.yml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/menu.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/menu.yml b/modules/i18n/src/main/resources/i18n/en/command/menu.yml new file mode 100644 index 000000000..d5b2f8521 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/menu.yml @@ -0,0 +1,2 @@ +menu: + description: "Hungry? Check the menu out!" diff --git a/modules/i18n/src/main/resources/i18n/messages.yml b/modules/i18n/src/main/resources/i18n/messages.yml index c18c83cd6..75656d494 100644 --- a/modules/i18n/src/main/resources/i18n/messages.yml +++ b/modules/i18n/src/main/resources/i18n/messages.yml @@ -20,8 +20,6 @@ # #command: # -# menu: -# description: "Hungry? Check the menu out!" # # serve: # description: "Serve some words to customers to earn some Cafe Coins!" From 30b003b4100a254d62efafc54fbcfea9488edd71 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 2 Mar 2026 20:11:18 -0800 Subject: [PATCH 03/22] Updated Serve Command --- .../main/resources/i18n/en/command/serve.yml | 24 +++++++++++++++++ .../i18n/src/main/resources/i18n/messages.yml | 26 ------------------- 2 files changed, 24 insertions(+), 26 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/serve.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/serve.yml b/modules/i18n/src/main/resources/i18n/en/command/serve.yml new file mode 100644 index 000000000..0f0103d09 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/serve.yml @@ -0,0 +1,24 @@ +serve: + description: "Serve some words to customers to earn some Cafe Coins!" + embed: + success: + title: "Order up!" + description: "{user} has served {word} for **{reward}** CC! They now have **{balance}**!" + description_other: "{user} has served {word} to {customer} for **{reward}** CC! They now have **{balance}**!" + footer: "Type \"/serve\" to get some money!" + error: + word: + title: "Invalid Word" + description: "Umm... I don't think that's a real word... make sure you use a real, single, **non-banned** english word." + time: + title: "Cannot serve!" + description: | + You last served a word at . + + You need to wait an hour before you last served in order to serve again. + + arguments: + word: + description: "Any English word!" + customer: + description: "The user you want to serve the word to!" diff --git a/modules/i18n/src/main/resources/i18n/messages.yml b/modules/i18n/src/main/resources/i18n/messages.yml index 75656d494..0b533fcd7 100644 --- a/modules/i18n/src/main/resources/i18n/messages.yml +++ b/modules/i18n/src/main/resources/i18n/messages.yml @@ -20,32 +20,6 @@ # #command: # -# -# serve: -# description: "Serve some words to customers to earn some Cafe Coins!" -# embed: -# success: -# title: "Order up!" -# description: "{user} has served {word} for **{reward}** CC! They now have **{balance}**!" -# description_other: "{user} has served {word} to {customer} for **{reward}** CC! They now have **{balance}**!" -# footer: "Type \"/serve\" to get some money!" -# error: -# word: -# title: "Invalid Word" -# description: "Umm... I don't think that's a real word... make sure you use a real, single, **non-banned** english word." -# time: -# title: "Cannot serve!" -# description: | -# You last served a word at . -# -# You need to wait an hour before you last served in order to serve again. -# -# arguments: -# word: -# description: "Any english word!" -# customer: -# description: "The user you want to serve the word to!" -# # ping: # description: "Pong!" # embed: From ebaa3c38ad670b4f099156e81567675b5a23d8c8 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 2 Mar 2026 20:17:22 -0800 Subject: [PATCH 04/22] Updated Ping Command --- .../main/resources/i18n/en/command/menu.yml | 3 +- .../main/resources/i18n/en/command/ping.yml | 10 +++++ .../main/resources/i18n/en/command/serve.yml | 43 +++++++++---------- .../i18n/src/main/resources/i18n/messages.yml | 13 ------ .../cafebot/commands/generic/PingCommand.java | 4 +- 5 files changed, 34 insertions(+), 39 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/ping.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/menu.yml b/modules/i18n/src/main/resources/i18n/en/command/menu.yml index d5b2f8521..fd11fd55f 100644 --- a/modules/i18n/src/main/resources/i18n/en/command/menu.yml +++ b/modules/i18n/src/main/resources/i18n/en/command/menu.yml @@ -1,2 +1 @@ -menu: - description: "Hungry? Check the menu out!" +description: "Hungry? Check the menu out!" diff --git a/modules/i18n/src/main/resources/i18n/en/command/ping.yml b/modules/i18n/src/main/resources/i18n/en/command/ping.yml new file mode 100644 index 000000000..a13559330 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/ping.yml @@ -0,0 +1,10 @@ +description: "Pong!" +embed: + description: "Hello!~ Would you like to order some coffee?" + shard: "Your shard ID is {SHARD_ID}! Keep note of this in case you need some help..." + author: "Author: beanbeanjuice - https://github.com/beanbeanjuice/cafeBot" +arguments: + word: + description: "Any word you want repeated back to you." + number: + description: "An integer to repeat back to you." diff --git a/modules/i18n/src/main/resources/i18n/en/command/serve.yml b/modules/i18n/src/main/resources/i18n/en/command/serve.yml index 0f0103d09..3a8da9068 100644 --- a/modules/i18n/src/main/resources/i18n/en/command/serve.yml +++ b/modules/i18n/src/main/resources/i18n/en/command/serve.yml @@ -1,24 +1,23 @@ -serve: - description: "Serve some words to customers to earn some Cafe Coins!" - embed: - success: - title: "Order up!" - description: "{user} has served {word} for **{reward}** CC! They now have **{balance}**!" - description_other: "{user} has served {word} to {customer} for **{reward}** CC! They now have **{balance}**!" - footer: "Type \"/serve\" to get some money!" - error: - word: - title: "Invalid Word" - description: "Umm... I don't think that's a real word... make sure you use a real, single, **non-banned** english word." - time: - title: "Cannot serve!" - description: | - You last served a word at . +description: "Serve some words to customers to earn some Cafe Coins!" +embed: + success: + title: "Order up!" + description: "{user} has served {word} for **{reward}** CC! They now have **{balance}**!" + description_other: "{user} has served {word} to {customer} for **{reward}** CC! They now have **{balance}**!" + footer: "Type \"/serve\" to get some money!" + error: + word: + title: "Invalid Word" + description: "Umm... I don't think that's a real word... make sure you use a real, single, **non-banned** english word." + time: + title: "Cannot serve!" + description: | + You last served a word at . - You need to wait an hour before you last served in order to serve again. + You need to wait an hour before you last served in order to serve again. - arguments: - word: - description: "Any English word!" - customer: - description: "The user you want to serve the word to!" +arguments: + word: + description: "Any English word!" + customer: + description: "The user you want to serve the word to!" diff --git a/modules/i18n/src/main/resources/i18n/messages.yml b/modules/i18n/src/main/resources/i18n/messages.yml index 0b533fcd7..0e7042bbf 100644 --- a/modules/i18n/src/main/resources/i18n/messages.yml +++ b/modules/i18n/src/main/resources/i18n/messages.yml @@ -19,19 +19,6 @@ # message: "There was an error! I... I'll try to let me boss know... please try again later and make a report on the GitHub if it is not fixed soon with `/bug`!" # #command: -# -# ping: -# description: "Pong!" -# embed: -# description: "Hello!~ Would you like to order some coffee?" -# shard: "Your shard ID is {SHARD_ID}! Keep note of this in case you need some help..." -# author: "Author: beanbeanjuice - https://github.com/beanbeanjuice/cafeBot" -# arguments: -# word: -# description: "Any word you want repeated back to you." -# number: -# description: "An integer to repeat back to you." -# # birthday: # description: "Get someone's birthday or change your own!" # subcommand: diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java index 588050cbe..4fce0e132 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java @@ -40,7 +40,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { private MessageEmbed messageEmbed(int shardId, ResourceBundle bundle) { EmbedBuilder embedBuilder = new EmbedBuilder(UpdateMessageScheduler.getUpdateEmbed(this.bot)); - embedBuilder.setTitle("ping!", "https://www.beanbeanjuice.com/cafeBot.html"); + embedBuilder.setTitle("ping!", "https://www.cafebot.dev"); embedBuilder .appendDescription("\n\n") .appendDescription(bundle.getString("command.ping.embed.description")); @@ -66,7 +66,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "commands.ping.description"; + return "command.ping.description"; } @Override From 022b7a8cc797f5448ecdfa089e253ba0f2810d25 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 2 Mar 2026 20:19:32 -0800 Subject: [PATCH 05/22] Updated Birthday Commands --- .../resources/i18n/en/command/birthday.yml | 42 ++++++++++++++++++ .../i18n/src/main/resources/i18n/messages.yml | 43 ------------------- 2 files changed, 42 insertions(+), 43 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/birthday.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/birthday.yml b/modules/i18n/src/main/resources/i18n/en/command/birthday.yml new file mode 100644 index 000000000..994b20ce1 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/birthday.yml @@ -0,0 +1,42 @@ +description: "Get someone's birthday or change your own!" +subcommand: + get: + description: "Get someone's birthday!" + embed: + other: + title: "🎂 {user}'s Birthday" + description: "Their birthday is on **{month} {day}** ({timezone})." + self: + title: "🎂 Your Birthday" + description: "Your birthday is on **{month} {day}** ({timezone})." + error: + title: "Error Getting Birthday" + description: "<:cafeBot_sad:1171726165040447518> Sorry... there was an error getting their birthday. It might not be set, you should tell them to set it with `/birthday`!" + arguments: + user: + description: "The user who's birthday you want to see." + + set: + description: "Edit your birthday!" + embed: + error: + title: "Error Setting Birthday" + description: "I... I don't know what happened... the computer's not letting me put your birthday in!" + success: + title: "🎂 Birthday Set" + description: "You have successfully set your birthday to **{month} {day}** ({timezone})." + arguments: + month: + description: "The month you were born!" + day: + description: "The day you were born!" + timezone: + description: "Your current timezone! Start typing to see available options." + year: + description: "The year you were born in!" + + remove: + description: "Remove your birthday... :c" + embed: + title: "Birthday Removed 🥺" + description: "<:cafeBot_sad:1171726165040447518> Your birthday has been removed... but I know it's sometimes better to keep things private..." diff --git a/modules/i18n/src/main/resources/i18n/messages.yml b/modules/i18n/src/main/resources/i18n/messages.yml index 0e7042bbf..d40ec4ff1 100644 --- a/modules/i18n/src/main/resources/i18n/messages.yml +++ b/modules/i18n/src/main/resources/i18n/messages.yml @@ -19,49 +19,6 @@ # message: "There was an error! I... I'll try to let me boss know... please try again later and make a report on the GitHub if it is not fixed soon with `/bug`!" # #command: -# birthday: -# description: "Get someone's birthday or change your own!" -# subcommand: -# get: -# description: "Get someone's birthday!" -# embed: -# other: -# title: "🎂 {user}'s Birthday" -# description: "Their birthday is on **{month} {day}** ({timezone})." -# self: -# title: "🎂 Your Birthday" -# description: "Your birthday is on **{month} {day}** ({timezone})." -# error: -# title: "Error Getting Birthday" -# description: "<:cafeBot_sad:1171726165040447518> Sorry... there was an error getting their birthday. It might not be set, you should tell them to set it with `/birthday`!" -# arguments: -# user: -# description: "The user who's birthday you want to see." -# -# set: -# description: "Edit your birthday!" -# embed: -# error: -# title: "Error Setting Birthday" -# description: "I... I don't know what happened... the computer's not letting me put your birthday in!" -# success: -# title: "🎂 Birthday Set" -# description: "You have successfully set your birthday to **{month} {day}** ({timezone})." -# arguments: -# month: -# description: "The month you were born!" -# day: -# description: "The day you were born!" -# timezone: -# description: "Your current timezone! Start typing to see available options." -# year: -# description: "The year you were born in!" -# -# remove: -# description: "Remove your birthday... :c" -# embed: -# title: "Birthday Removed 🥺" -# description: "<:cafeBot_sad:1171726165040447518> Your birthday has been removed... but I know it's sometimes better to keep things private..." # # meme: # description: "Get a meme!" From a83718d947f78da90d318be71db82421057ca3ab Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 2 Mar 2026 20:24:33 -0800 Subject: [PATCH 06/22] Updated Meme Commands --- .../main/resources/i18n/en/command/meme.yml | 12 ++++ .../meme/api/wrapper/MemeAPI.java | 60 ++++++------------- 2 files changed, 29 insertions(+), 43 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/meme.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/meme.yml b/modules/i18n/src/main/resources/i18n/en/command/meme.yml new file mode 100644 index 000000000..205b033d4 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/meme.yml @@ -0,0 +1,12 @@ +description: "Get a meme!" +error: + embed: + title: "Failed Getting Meme" + description: "I... I'm so sorry... I tripped and fell when getting your meme..." +subcommand: + coffee: + description: "Get a coffee meme!" + random: + description: "Get a random meme!" + tea: + description: "Get a tea meme!" diff --git a/modules/meme-api-wrapper/src/main/java/com/beanbeanjuice/meme/api/wrapper/MemeAPI.java b/modules/meme-api-wrapper/src/main/java/com/beanbeanjuice/meme/api/wrapper/MemeAPI.java index 95083f605..6ecf62370 100644 --- a/modules/meme-api-wrapper/src/main/java/com/beanbeanjuice/meme/api/wrapper/MemeAPI.java +++ b/modules/meme-api-wrapper/src/main/java/com/beanbeanjuice/meme/api/wrapper/MemeAPI.java @@ -7,40 +7,32 @@ import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.concurrent.FutureCallback; -import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.Method; import tools.jackson.databind.JsonNode; import tools.jackson.databind.json.JsonMapper; -import java.net.URISyntaxException; import java.util.concurrent.CompletableFuture; public class MemeAPI { private MemeAPI() { } // Should not be able to instantiate. - public static CompletableFuture get(String subreddit) { - HttpHost host; - - try { - host = HttpHost.create("https://meme-api.com"); - } catch (URISyntaxException e) { - return CompletableFuture.failedFuture(e); - } + // Only one reusable client should exist. Good practice for ASYNC clients. + private static final CloseableHttpAsyncClient CLIENT = HttpAsyncClients.createDefault(); - SimpleHttpRequest request = SimpleRequestBuilder - .create(Method.GET) - .setHttpHost(host) - .setPath(String.format("/gimme/%s", subreddit)) - .build(); + static { + CLIENT.start(); + } - CloseableHttpAsyncClient client = HttpAsyncClients.custom().build(); - client.start(); + private static final JsonMapper MAPPER = new JsonMapper(); - JsonMapper mapper = new JsonMapper(); + public static CompletableFuture get(String subreddit) { CompletableFuture future = new CompletableFuture<>(); - client.execute(request, new FutureCallback<>() { + SimpleHttpRequest request = SimpleRequestBuilder + .get("https://meme-api.com/gimme/" + subreddit) + .build(); + + CLIENT.execute(request, new FutureCallback<>() { @Override public void completed(SimpleHttpResponse response) { @@ -48,21 +40,15 @@ public void completed(SimpleHttpResponse response) { int statusCode = response.getCode(); String body = response.getBodyText(); - JsonNode jsonNode; - - if (statusCode == 204 || body == null || body.isBlank()) { - // No content → represent as an empty object or null - jsonNode = mapper.createObjectNode(); // or null, depending on your design - } else { - jsonNode = mapper.readTree(body); - } - if (statusCode < 200 || statusCode >= 300) { future.completeExceptionally( - new IllegalStateException(String.format("Request failed with status %d.", statusCode)) + new IllegalStateException("Request failed: " + statusCode) ); + return; } + JsonNode jsonNode = MAPPER.readTree(body); + RedditMeme meme = new RedditMeme( jsonNode.get("title").asString(), jsonNode.get("subreddit").asString(), @@ -73,36 +59,24 @@ public void completed(SimpleHttpResponse response) { ); future.complete(meme); + } catch (Exception e) { future.completeExceptionally(e); - } finally { - closeClient(client); } } @Override public void failed(Exception ex) { future.completeExceptionally(ex); - closeClient(client); } @Override public void cancelled() { future.cancel(true); - closeClient(client); } - }); return future; } - private static void closeClient(CloseableHttpAsyncClient client) { - try { - client.close(); - } catch (Exception e) { - // Log or ignore - } - } - } From 9de400eb73900dbedaff2389b60b0e4188af0d15 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 2 Mar 2026 20:26:15 -0800 Subject: [PATCH 07/22] Updated Rate Commands --- .../main/resources/i18n/en/command/rate.yml | 55 +++++++++++ .../i18n/src/main/resources/i18n/messages.yml | 93 ------------------- 2 files changed, 55 insertions(+), 93 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/rate.yml delete mode 100644 modules/i18n/src/main/resources/i18n/messages.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/rate.yml b/modules/i18n/src/main/resources/i18n/en/command/rate.yml new file mode 100644 index 000000000..01cfadee6 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/rate.yml @@ -0,0 +1,55 @@ +description: "Rate something!" +subcommand: + caffeinated: + description: "Rate how caffeinated you, or someone else, is!" + embed: + title: "☕ Caffeine Rating ☕" + description: "{user} is {percent}% caffeinated! " + arguments: + user: + description: "The person who's caffeine levels you want to see." + + gay: + description: "Rate how ✨ gay ✨ someone is!" + embed: + title: "🏳️‍🌈 Gay Rating 🌈" + description: "{user} is {percent}% gay! <:bloatedBlush:1154475611893547218>" + arguments: + user: + description: "The person who's gayness you want to see." + + insane: + description: "See how insane someone is!" + embed: + title: "‼️ Insane Rating ‼️" + description: "{user} is {percent}% insane! " + arguments: + user: + description: "The person who's insanity you want to see." + + poor: + description: "Rate how poor someone is!" + embed: + title: "💸 Poor Rating 💸️" + description: "{user} is {percent}% poor! 🤢" + arguments: + user: + description: "The person who's poor level you want to see." + + simp: + description: "Rate how much of a simp someone is!" + embed: + title: "😍 Simp Rating 😍" + description: "{user} is {percent}% simp! <:flushed_nervous:841923862202548224>" + arguments: + user: + description: "The person who's simp level you want to see." + + smart: + description: "Rate how smart someone is!" + embed: + title: "<:smartPeepo:1000248538376196280> Smart Rating <:smartPeepo:1000248538376196280>" + description: "{user} is {percent}% smart! 👩‍🎓" + arguments: + user: + description: "The person who's smart levels you want to see." diff --git a/modules/i18n/src/main/resources/i18n/messages.yml b/modules/i18n/src/main/resources/i18n/messages.yml deleted file mode 100644 index d40ec4ff1..000000000 --- a/modules/i18n/src/main/resources/i18n/messages.yml +++ /dev/null @@ -1,93 +0,0 @@ -#bot: -# name: "cafeBot" -# greeting: "Hello, world!" -# discord-locale: ENGLISH_US -# -#error: -# uncaught: -# title: "Uncaught Error!" -# message: | -# I-... something went... seriously wrong... <:cafeBot_sad:1171726165040447518> -# -# I tried to do that and short-circuited... this is the error I got... -# -# ``` -# {uncaught_error} -# ``` -# generic: -# title: "Error" -# message: "There was an error! I... I'll try to let me boss know... please try again later and make a report on the GitHub if it is not fixed soon with `/bug`!" -# -#command: -# -# meme: -# description: "Get a meme!" -# error: -# embed: -# title: "Failed Getting Meme" -# description: "I... I'm so sorry... I tripped and fell when getting your meme..." -# subcommand: -# coffee: -# description: "Get a coffee meme!" -# random: -# description: "Get a random meme!" -# tea: -# description: "Get a tea meme!" -# -# rate: -# description: "Rate something!" -# subcommand: -# caffeinated: -# description: "Rate how caffeinated you, or someone else, is!" -# embed: -# title: "☕ Caffeine Rating ☕" -# description: "{user} is {percent}% caffeinated! " -# arguments: -# user: -# description: "The person who's caffeine levels you want to see." -# -# gay: -# description: "Rate how ✨ gay ✨ someone is!" -# embed: -# title: "🏳️‍🌈 Gay Rating 🌈" -# description: "{user} is {percent}% gay! <:bloatedBlush:1154475611893547218>" -# arguments: -# user: -# description: "The person who's gayness you want to see." -# -# insane: -# description: "See how insane someone is!" -# embed: -# title: "‼️ Insane Rating ‼️" -# description: "{user} is {percent}% insane! " -# arguments: -# user: -# description: "The person who's insanity you want to see." -# -# poor: -# description: "Rate how poor someone is!" -# embed: -# title: "💸 Poor Rating 💸️" -# description: "{user} is {percent}% poor! 🤢" -# arguments: -# user: -# description: "The person who's poor level you want to see." -# -# simp: -# description: "Rate how much of a simp someone is!" -# embed: -# title: "😍 Simp Rating 😍" -# description: "{user} is {percent}% simp! <:flushed_nervous:841923862202548224>" -# arguments: -# user: -# description: "The person who's simp level you want to see." -# -# smart: -# description: "Rate how smart someone is!" -# embed: -# title: "<:smartPeepo:1000248538376196280> Smart Rating <:smartPeepo:1000248538376196280>" -# description: "{user} is {percent}% smart! 👩‍🎓" -# arguments: -# user: -# description: "The person who's smart levels you want to see." -# From 6e43b320e014ccfc7f505e7f7694a872940fbb84 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Tue, 17 Mar 2026 20:28:27 -0700 Subject: [PATCH 08/22] Add AI I18N --- .../src/main/resources/i18n/en/command/ai.yml | 11 ++++++++++ .../cafebot/commands/fun/AiCommand.java | 22 ++++++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/ai.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/ai.yml b/modules/i18n/src/main/resources/i18n/en/command/ai.yml new file mode 100644 index 000000000..1bab2062f --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/ai.yml @@ -0,0 +1,11 @@ +description: "Want a sassy AI that will serve you some coffee?" +embed: + title: "AI Response Changed" + enabled: "Status: Enabled ✅" + disabled: "Status: Disabled ❌" +error: + title: "Error Changing AI Response" + description: "There was an error changing the AI response... I'm so sorry..." +arguments: + status: + description: "Whether to enable or disable the \"AI\"." diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java index 8223e08a0..e66a7e692 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AiCommand.java @@ -24,18 +24,24 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String guildId = event.getGuild().getId(); boolean status = event.getOption("status").getAsBoolean(); - String statusString = (status) ? "Enabled ✅" : "Disabled ❌"; - // TODO: Update syntax for this later bot.getCafeAPI().getGuildApi().updateDiscordServer(guildId, new DiscordServer(null, 0, status)).thenRun(() -> { + String title = ctx.getUserI18n().getString("command.ai.embed.title"); + String enabledString = ctx.getUserI18n().getString("command.ai.embed.enabled"); + String disabledString = ctx.getUserI18n().getString("command.ai.embed.disabled"); + String statusString = (status) ? enabledString : disabledString; + event.getHook().sendMessageEmbeds(Helper.successEmbed( - "AI Response Changed", - String.format("Status: %s", statusString) + title, + statusString )).queue(); }).exceptionally((ex) -> { + String title = ctx.getUserI18n().getString("command.ai.error.title"); + String description = ctx.getUserI18n().getString("command.ai.error.description"); + event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Changing AI Response", - "There was an error changing the AI response... I'm so sorry..." + title, + description )).queue(); bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Updating AI Response: " + ex.getMessage(), true, false); @@ -50,7 +56,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Want a sassy AI that will serve you some coffee?"; + return "command.ai.description"; } @Override @@ -61,7 +67,7 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.BOOLEAN, "status", "Whether to enable or disable the \"AI\".", true) + new OptionData(OptionType.BOOLEAN, "status", "command.ai.arguments.status.description", true) }; } From bd088e43aa245f4947a0658439e61d2d1d4209de Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Tue, 17 Mar 2026 21:09:02 -0700 Subject: [PATCH 09/22] Add Avatar Command I18N --- .../main/resources/i18n/en/command/avatar.yml | 15 ++++++ .../cafebot/commands/fun/AvatarCommand.java | 51 ++++++++++--------- 2 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/avatar.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/avatar.yml b/modules/i18n/src/main/resources/i18n/en/command/avatar.yml new file mode 100644 index 000000000..30913c6aa --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/avatar.yml @@ -0,0 +1,15 @@ +description: "Get someone's avatar!" +embed: + title: "{user}'s Avatar" +error: + missing: + title: "No User Avatar" + description: "The specified user does not have a Discord avatar." + server: + title: "Must Be In Server" + description: "You must be in a Discord server to get a server avatar!" +arguments: + user: + description: "Get their user avatar!" + server: + description: "Get their server avatar!" diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java index 0170a42ff..01e071077 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java @@ -17,6 +17,7 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import java.util.Optional; +import java.util.ResourceBundle; public class AvatarCommand extends Command implements ICommand { @@ -30,44 +31,46 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional serverOption = Optional.ofNullable(event.getOption("server")); User user = userOption.map(OptionMapping::getAsUser).orElse(event.getUser()); - Member member = userOption.map(OptionMapping::getAsMember).orElse(event.getMember()); + Member member = userOption.map(OptionMapping::getAsMember).orElseGet(event::getMember); boolean useServer = serverOption.map(OptionMapping::getAsBoolean).orElse(false); if (useServer && !event.isFromGuild()) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Must Be In Server", - "You must be in a Discord server to use the server avatar!" - )).queue(); + event.getHook().sendMessageEmbeds(serverErrorEmbed(ctx.getUserI18n())).queue(); return; } - Optional urlOptional = (useServer) ? getServerAvatarURL(member) : getUserAvatarURL(user); + Optional urlOptional = Optional.ofNullable(member) + .filter(m -> useServer) + .map(Member::getAvatarUrl) + .or(() -> Optional.ofNullable(user.getAvatarUrl())); urlOptional.ifPresentOrElse( - (url) -> event.getHook().sendMessageEmbeds(avatarEmbed(user.getName(), url)).queue(), - () -> event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "No User Avatar", - "The specified user does not have a Discord avatar." - )).queue() + (url) -> event.getHook().sendMessageEmbeds(avatarEmbed(user.getName(), url, ctx.getUserI18n())).queue(), + () -> event.getHook().sendMessageEmbeds(missingErrorEmbed(ctx.getUserI18n())).queue() ); } - private Optional getUserAvatarURL(final User user) { - return Optional.ofNullable(user.getAvatarUrl()); - } - - private Optional getServerAvatarURL(final Member member) { - return Optional.ofNullable(member.getAvatarUrl()); - } - - private MessageEmbed avatarEmbed(final String name, final String avatarURL) { + private MessageEmbed avatarEmbed(String name, String avatarURL, ResourceBundle i18n) { + String title = i18n.getString("command.avatar.embed.title").replace("{user}", name); return new EmbedBuilder() - .setTitle(name + "'s Avatar") + .setTitle(title) .setImage(avatarURL + "?size=512") .setColor(Helper.getRandomColor()) .build(); } + private MessageEmbed missingErrorEmbed(ResourceBundle i18n) { + String title = i18n.getString("command.avatar.error.missing.title"); + String description = i18n.getString("command.avatar.error.missing.description"); + return Helper.errorEmbed(title, description); + } + + private MessageEmbed serverErrorEmbed(ResourceBundle i18n) { + String title = i18n.getString("command.avatar.error.server.title"); + String description = i18n.getString("command.avatar.error.server.description"); + return Helper.errorEmbed(title, description); + } + @Override public String getName() { return "avatar"; @@ -75,7 +78,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Get someone's avatar!"; + return "command.avatar.description"; } @Override @@ -86,8 +89,8 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The user you want to get the avatar of.", false), - new OptionData(OptionType.BOOLEAN, "server", "Whether to get their server or user avatar.", false) + new OptionData(OptionType.USER, "user", "command.avatar.arguments.user.description", false), + new OptionData(OptionType.BOOLEAN, "server", "command.avatar.arguments.server.description", false) }; } From 698847909b7c9e7156e2e8d31fc739245588062a Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Tue, 17 Mar 2026 21:14:47 -0700 Subject: [PATCH 10/22] Add Banner Command I18N --- .../main/resources/i18n/en/command/banner.yml | 9 +++++++ .../cafebot/commands/fun/BannerCommand.java | 25 ++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/banner.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/banner.yml b/modules/i18n/src/main/resources/i18n/en/command/banner.yml new file mode 100644 index 000000000..fd8cd1941 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/banner.yml @@ -0,0 +1,9 @@ +description: "Get someone's banner!" +embed: + title: "{user}'s Banner" +error: + title: "No User Banner" + description: "The specified user does not have a Discord banner." +arguments: + user: + description: "Get their banner!" diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java index 14ff5c9b5..cd52cf4de 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java @@ -17,6 +17,7 @@ import java.awt.*; import java.util.Optional; +import java.util.ResourceBundle; public class BannerCommand extends Command implements ICommand { @@ -34,25 +35,31 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional urlOptional = Optional.ofNullable(profile.getBannerUrl()); urlOptional.ifPresentOrElse( - (url) -> event.getHook().sendMessageEmbeds(bannerEmbed(user.getName(), user.getAvatarUrl(), url, profile.getAccentColor())).queue(), - () -> event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "No User Banner", - "The specified user does not have a Discord banner." - )).queue() + (url) -> event.getHook().sendMessageEmbeds(bannerEmbed(user.getName(), user.getAvatarUrl(), url, profile.getAccentColor(), ctx.getUserI18n())).queue(), + () -> event.getHook().sendMessageEmbeds(noBannerError(ctx.getUserI18n())).queue() ); }); } - private MessageEmbed bannerEmbed(final String username, final String avatarURL, final String bannerURL, final Color accent) { + private MessageEmbed bannerEmbed(String username, String avatarURL, String bannerURL, Color accent, ResourceBundle i18n) { + String title = i18n.getString("command.banner.embed.title").replace("{user}", username); + EmbedBuilder embedBuilder = new EmbedBuilder() .setColor(accent) - .setAuthor(username + "'s Banner", null, avatarURL); + .setAuthor(title, null, avatarURL); embedBuilder.setImage(bannerURL + "?size=600"); return embedBuilder.build(); } + private MessageEmbed noBannerError(ResourceBundle i18n) { + String title = i18n.getString("command.banner.error.title"); + String description = i18n.getString("command.banner.error.description"); + + return Helper.errorEmbed(title, description); + } + @Override public String getName() { return "banner"; @@ -60,7 +67,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Get someone's banner!"; + return "command.banner.description"; } @Override @@ -71,7 +78,7 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person you want to get the banner of.", false), + new OptionData(OptionType.USER, "user", "command.banner.arguments.user.description", false), }; } From 4cb06f9687d7ffff7b5250c60cbcf38ba93b1bf2 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Sun, 29 Mar 2026 10:16:19 -0700 Subject: [PATCH 11/22] Rewrite I18N Parser --- .../com/beanbeanjuice/cafebot/i18n/I18N.java | 321 ++++++++---------- .../cafebot/i18n/YamlControl.java | 69 ---- .../resources/i18n/en/command/eightball.yml | 24 ++ .../main/resources/i18n/en/command/ping.yml | 2 +- .../resources/i18n/en_GB/command/ping.yml | 10 + .../src/main/resources/i18n/en_GB/info.yml | 2 +- .../cafebot/i18n/FolderPathTest.java | 110 ++++++ .../cafebot/i18n/FormattingTest.java | 224 ------------ .../i18n/I18NResourceVerificationTest.java | 120 +++++++ .../beanbeanjuice/cafebot/i18n/I18NTest.java | 97 ++++++ .../beanbeanjuice/cafebot/i18n/KeyTest.java | 58 ---- .../cafebot/i18n/ParseKeyTest.java | 39 +++ .../com/beanbeanjuice/cafebot/CafeBot.java | 6 +- .../cafebot/commands/cafe/BalanceCommand.java | 5 +- .../cafebot/commands/cafe/DonateCommand.java | 15 +- .../cafebot/commands/cafe/ServeCommand.java | 6 +- .../cafebot/commands/fun/AvatarCommand.java | 8 +- .../cafebot/commands/fun/BannerCommand.java | 6 +- .../commands/fun/EightBallCommand.java | 44 +-- .../fun/birthday/BirthdayGetSubCommand.java | 9 +- .../cafebot/commands/generic/PingCommand.java | 4 +- .../utility/commands/CommandContext.java | 12 +- .../utility/commands/CommandHandler.java | 54 +-- .../cafebot/utility/commands/ICommand.java | 1 + .../cafebot/utility/handlers/HelpHandler.java | 10 +- .../cafebot/utility/helper/Helper.java | 3 +- .../utility/listeners/AirportListener.java | 2 + .../utility/listeners/BotAddListener.java | 2 +- .../utility/listeners/BotRemoveListener.java | 1 + .../listeners/ConfessionBanListener.java | 1 + .../utility/listeners/CountingListener.java | 2 + .../utility/listeners/HoneypotListener.java | 2 + .../utility/listeners/RaffleListener.java | 4 + .../listeners/TwitchGoLiveEventListener.java | 3 +- .../listeners/VoiceRoleBindListener.java | 4 + 35 files changed, 657 insertions(+), 623 deletions(-) delete mode 100644 modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java create mode 100644 modules/i18n/src/main/resources/i18n/en/command/eightball.yml create mode 100644 modules/i18n/src/main/resources/i18n/en_GB/command/ping.yml create mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FolderPathTest.java delete mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java create mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java create mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NTest.java delete mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java create mode 100644 modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/ParseKeyTest.java diff --git a/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java index 0f8f1a2db..86858a9e5 100644 --- a/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java +++ b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/I18N.java @@ -1,228 +1,187 @@ package com.beanbeanjuice.cafebot.i18n; -import org.jetbrains.annotations.NotNull; import org.yaml.snakeyaml.Yaml; +import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; -public class I18N extends ResourceBundle { +public class I18N { - private final Map> loadedFiles = new HashMap<>(); private final Locale locale; - private final ClassLoader classLoader; - private final Yaml yaml = new Yaml(); - public I18N(Locale locale, ClassLoader classLoader) { + private static final Map> YAML_CACHE = new ConcurrentHashMap<>(); + private static final Map> FILE_PATH_CACHE = new ConcurrentHashMap<>(); + + public I18N() { + this.locale = Locale.ENGLISH; + } + + public I18N(Locale locale) { this.locale = locale; - this.classLoader = classLoader; } - @Override - protected Object handleGetObject(@NotNull String key) { - // Parse the key: "commands.help.description" - // -> file: "commands/help", nested key: "description" - - int lastDotIndex = key.lastIndexOf('.'); - if (lastDotIndex == -1) { - // Try to load from root level files - Object result = loadFromFile(key, ""); - if (result != null) { - return result; - } - } else { - String filePath = key.substring(0, lastDotIndex); - String nestedKey = key.substring(lastDotIndex + 1); - - // Try to resolve as nested path first - Object result = loadFromFile(filePath, nestedKey); - if (result != null) { - return result; - } + public String getString(String key) { + return getString(key, this.locale); + } - // If not found, try moving more parts to the file path - // e.g., if "commands.help" didn't work, try "commands" with "help.description" - int previousDotIndex = filePath.lastIndexOf('.'); - while (previousDotIndex != -1) { - filePath = filePath.substring(0, previousDotIndex); - nestedKey = key.substring(previousDotIndex + 1); - result = loadFromFile(filePath, nestedKey); - if (result != null) { - return result; - } - previousDotIndex = filePath.lastIndexOf('.'); - } + public List getStringArray(String key) { + return getStringArray(key, this.locale); + } - // Try root level with full key as nested path - result = loadFromFile("", key); - if (result != null) { - return result; - } - } + private static String getString(String key, Locale locale) { + // There are 2 paths we need to find. First, the path to the file. Next, the path within the file. + // For example, i18n/en/command/ai.yml, and command.ai.description + // Then we know command.ai is the file path, and description is the path within the file. + // Question is... how do we do this? There might be a dynamic number of folders. + Optional filePath = findFilePath(key, locale) + .or(() -> findFilePath(key, Locale.ENGLISH)); - // Not found in this locale - // If we have a parent (fallback), return null to let parent handle it - if (parent != null) { - return null; - } + if (filePath.isEmpty()) return key; - // We're the root bundle (English) and key not found - return the key itself - return key; + /* + Now we need to remove the overlap from the key and the filePath. + For example, the filePath might be i18n/en/command/ai.yml and + the key might be command.ai.description + + This means we need to remove "command.ai." + */ + + String pathInFile = parseKey(filePath.get(), key); + Map map = loadYaml(filePath.get()); + + if (map.isEmpty()) return key; + + Optional flattenedString = getFlattenedString(map, pathInFile); + + // If the string wasn't found, and the current locale is English, then it truly does not exist. + if (flattenedString.isEmpty() && locale.equals(Locale.ENGLISH)) return key; + + return flattenedString.orElseGet(() -> getString(key, Locale.ENGLISH)); } - private Object loadFromFile(String filePath, String nestedKey) { - // Convert dot notation to folder path - String folderPath = filePath.replace('.', '/'); + private static List getStringArray(String key, Locale locale) { + Optional filePath = findFilePath(key, locale) + .or(() -> findFilePath(key, Locale.ENGLISH)); - // Build locale string: "en_GB", "en_US", or just "en" - String localeString = locale.getLanguage(); - if (!locale.getCountry().isEmpty()) { - localeString += "_" + locale.getCountry(); - } + if (filePath.isEmpty()) return new ArrayList<>(); - // Include locale in cache key to avoid conflicts - String fileKey = localeString + ":" + (folderPath.isEmpty() ? "" : folderPath); - - // Check if file is already loaded - Map fileData = loadedFiles.get(fileKey); - - if (fileData == null) { - // Try to load the file: "i18n/en_GB/info.yml" - String resourcePath = "i18n/" + localeString + "/" + - (folderPath.isEmpty() ? "" : folderPath + ".yml"); - - fileData = loadYamlFile(resourcePath); - - if (fileData == null && !folderPath.isEmpty()) { - // Maybe it's in a parent directory file - // e.g., "commands.yml" contains "help" section - int lastSlash = folderPath.lastIndexOf('/'); - if (lastSlash != -1) { - String parentPath = folderPath.substring(0, lastSlash); - String section = folderPath.substring(lastSlash + 1); - resourcePath = "i18n/" + localeString + "/" + parentPath + ".yml"; - Map parentData = loadYamlFile(resourcePath); - if (parentData != null && parentData.containsKey(section)) { - Object sectionData = parentData.get(section); - if (sectionData instanceof Map) { - fileData = flatten((Map) sectionData); - } - } - } - } + /* + Now we need to remove the overlap from the key and the filePath. + For example, the filePath might be i18n/en/command/ai.yml and + the key might be command.ai.description - if (fileData != null) { - loadedFiles.put(fileKey, fileData); - } else { - loadedFiles.put(fileKey, Collections.emptyMap()); // Cache miss - } - } + This means we need to remove "command.ai." + */ - if (fileData == null || fileData.isEmpty()) { - return null; - } + String pathInFile = parseKey(filePath.get(), key); + Map map = loadYaml(filePath.get()); + if (map.isEmpty()) return new ArrayList<>(); - // Get nested key from flattened data - return fileData.get(nestedKey); + List flattenedStringArray = getFlattenedStringArray(map, pathInFile); + + // If the string wasn't found, and the current locale is English, then it truly does not exist. + if (flattenedStringArray.isEmpty() && locale.equals(Locale.ENGLISH)) return flattenedStringArray; + + if (!flattenedStringArray.isEmpty()) return flattenedStringArray; + return getStringArray(key, Locale.ENGLISH); } - private Map loadYamlFile(String resourcePath) { - try (InputStream stream = classLoader.getResourceAsStream(resourcePath)) { - if (stream == null) { - return null; - } - Map raw = yaml.load(stream); - return flatten(raw); - } catch (Exception e) { - return null; + @SuppressWarnings("unchecked") + private static Optional getFlattenedString(Map map, String key) { + String[] split = key.split("\\."); + + for (int i = 0; i < split.length - 1; i++) { + if (!map.containsKey(split[i])) return Optional.empty(); + + map = (Map) map.get(split[i]); } + + String finalKey = split[split.length - 1]; + return (map.containsKey(finalKey)) ? ((String) map.get(finalKey)).describeConstable() : Optional.empty(); } - @Override - public @NotNull Enumeration getKeys() { - // This is complex with lazy loading - for now return empty or loaded keys - Set keys = new HashSet<>(); - - for (Map.Entry> entry : loadedFiles.entrySet()) { - String prefix = entry.getKey().replace('/', '.'); - for (String key : entry.getValue().keySet()) { - if (prefix.isEmpty()) { - keys.add(key); - } else { - keys.add(prefix + "." + key); - } - } - } + @SuppressWarnings("unchecked") + private static List getFlattenedStringArray(Map map, String key) { + String[] split = key.split("\\."); + + for (int i = 0; i < split.length - 1; i++) { + if (!map.containsKey(split[i])) return new ArrayList<>(); - if (parent != null) { - parent.getKeys().asIterator().forEachRemaining(keys::add); + map = (Map) map.get(split[i]); } - return Collections.enumeration(keys); + String finalKey = split[split.length - 1]; + return (map.containsKey(finalKey)) ? ((List) map.get(finalKey)) : new ArrayList<>(); } - private Map flatten(Map source) { - Map result = new HashMap<>(); - flatten("", source, result); - return result; - } + private static String parseKey(String filePath, String key) { + String[] splitFilePath = filePath // i18n/en/command/ai.yml + .replace(".yml", "") // i18n/en/command/ai + .split("/"); // ["i18n", "en", "command", "ai"] + + String[] splitKey = key.split("\\."); // ["ai", "embed", "title"] - private void flatten(String prefix, Map source, Map result) { - for (var entry : source.entrySet()) { - String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey(); - Object value = entry.getValue(); + // Two pointer approach + int fileIndex = 0; + int keyIndex = 0; - if (value instanceof Map map) { - flatten(key, (Map) map, result); - } else { - result.put(key, value.toString()); + while (fileIndex < splitFilePath.length && keyIndex < splitKey.length) { + if (!splitFilePath[fileIndex].equals(splitKey[keyIndex])) { + fileIndex++; + continue; } + + fileIndex++; + keyIndex++; } - } - /** - * Gets the I18N bundle for the specified locale. - * @param locale The {@link Locale} to get the bundle for. - * @return The {@link I18N} bundle instance. - */ - public static I18N getBundle(Locale locale) { - return (I18N) ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + // Key starts at keyIndex + return String.join(".", Arrays.copyOfRange(splitKey, keyIndex, splitKey.length)); } - /** - * Gets the I18N bundle for English (default). - * @return The {@link I18N} bundle instance. - */ - public static I18N getBundle() { - return getBundle(Locale.ENGLISH); - } + private static Optional findFilePathUncached(String key, Locale locale) { + StringBuilder sb = new StringBuilder(); + sb.append("i18n/").append(locale.toString()).append("/"); // i18n/en/ - /** - * Gets the description based on the language file and path. - * @param path The {@link String path} to search for. - * @return The proper {@link String description} or the {@link String path} if not found. - */ - public static String getStringFromLanguageFile(String path) { - try { - return getStringFromLanguageFile(Locale.ENGLISH, path); - } catch (MissingResourceException e) { - return path; + String[] splitKey = key.split("\\."); + + for (String split : splitKey) { + sb.append(split); // i18n/en/command + + URL resource = I18N.class.getClassLoader().getResource(sb.toString()); + if (resource == null) { + // Directory with that name not found. Does a .yml exist in current directory? + resource = I18N.class.getClassLoader().getResource(sb + ".yml"); + if (resource == null && locale.equals(Locale.ENGLISH)) return Optional.empty(); // if english, and not found, it doesn't exist. + if (resource == null) return findFilePath(key, Locale.ENGLISH); // default to english. + return Optional.of(sb.append(".yml").toString()); + } + + sb.append("/"); } + + return Optional.empty(); } - /** - * Gets the description based on the language file and path. - * @param locale The {@link Locale} specifying which language file to use. If not found, defaults to {@link Locale english}. - * @param path The {@link String path} to search for. - * @return The proper {@link String description} or the {@link String path} if not found. - */ - public static String getStringFromLanguageFile(Locale locale, String path) { - try { - ResourceBundle bundle = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); - return bundle.getString(path); - } catch (MissingResourceException e) { - return path; - } + private static Optional findFilePath(String key, Locale locale) { + String cacheKey = locale + ":" + key; + return FILE_PATH_CACHE.computeIfAbsent(cacheKey, k -> findFilePathUncached(key, locale)); + } + + private static Map loadYaml(String filePath) { + return YAML_CACHE.computeIfAbsent(filePath, path -> { + try (InputStream is = I18N.class.getClassLoader().getResourceAsStream(path)) { + return new Yaml().load(is); + } catch (IOException e) { return Map.of(); } + }); } } diff --git a/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java b/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java deleted file mode 100644 index 1d8512445..000000000 --- a/modules/i18n/src/main/java/com/beanbeanjuice/cafebot/i18n/YamlControl.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.beanbeanjuice.cafebot.i18n; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.Locale; -import java.util.ResourceBundle; - -public class YamlControl extends ResourceBundle.Control { - - public static final YamlControl INSTANCE = new YamlControl(); - private YamlControl() {} - - @Override - public List getFormats(String baseName) { - return List.of("yaml"); - } - - @Override - public ResourceBundle newBundle( - String baseName, - Locale locale, - String format, - ClassLoader loader, - boolean reload - ) throws IOException { - // Build locale string: "en_GB", "en_US", or just "en" - String localeString = locale.getLanguage(); - if (!locale.getCountry().isEmpty()) { - localeString += "_" + locale.getCountry(); - } - - // Check if at least one YAML file exists for this locale - // Try a common base file to see if the locale directory exists - String testPath = "i18n/" + localeString + "/"; - - // We need to check if ANY file exists for this locale - // Try some common paths - boolean localeExists = false; - String[] commonFiles = {"info.yml", "generic.yml", "commands.yml"}; - - for (String file : commonFiles) { - try (InputStream stream = loader.getResourceAsStream(testPath + file)) { - if (stream != null) { - localeExists = true; - break; - } - } - } - - // If no files exist for this locale, return null so ResourceBundle tries fallback - if (!localeExists) { - return null; - } - - // Create and return the bundle - return new I18N(locale, loader); - } - - @Override - public Locale getFallbackLocale(String baseName, Locale locale) { - // Always fall back to English, unless we're already in English - if (locale.equals(Locale.ENGLISH)) { - return null; // No further fallback - } - return Locale.ENGLISH; - } - -} diff --git a/modules/i18n/src/main/resources/i18n/en/command/eightball.yml b/modules/i18n/src/main/resources/i18n/en/command/eightball.yml new file mode 100644 index 000000000..ba643d371 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/eightball.yml @@ -0,0 +1,24 @@ +description: "Have a question? Well I have an answer..." +embed: + field_title: "My Mystical Answer" + field_description: "*{answer}*" +answers: + positive: + - "It is likely..." + - "Probably." + - "Without a doubt." + - "Of course." + - "More than likely." + - "YES ABSOLUTELY." + - "Hmmm... I think so." + negative: + - "Of course not." + - "Probably not." + - "It is not likely..." + - "There is some doubt..." + - "Less than likely." + - "ABSOLUTELY NOT." + - "Are you kidding? No!" +arguments: + question: + description: "The question you want to ask me!" diff --git a/modules/i18n/src/main/resources/i18n/en/command/ping.yml b/modules/i18n/src/main/resources/i18n/en/command/ping.yml index a13559330..91d8370ed 100644 --- a/modules/i18n/src/main/resources/i18n/en/command/ping.yml +++ b/modules/i18n/src/main/resources/i18n/en/command/ping.yml @@ -1,4 +1,4 @@ -description: "Pong!" +description: "Pong! (US EDITION)" embed: description: "Hello!~ Would you like to order some coffee?" shard: "Your shard ID is {SHARD_ID}! Keep note of this in case you need some help..." diff --git a/modules/i18n/src/main/resources/i18n/en_GB/command/ping.yml b/modules/i18n/src/main/resources/i18n/en_GB/command/ping.yml new file mode 100644 index 000000000..6aad3729a --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en_GB/command/ping.yml @@ -0,0 +1,10 @@ +description: "Pong! (UK EDITION)" +embed: + description: "Hello!~ Would you like to order some coffee?" + shard: "Your shard ID is {SHARD_ID}! Keep note of this in case you need some help..." + author: "Author: beanbeanjuice - https://github.com/beanbeanjuice/cafeBot" +arguments: + word: + description: "Any word you want repeated back to you." + number: + description: "An integer to repeat back to you." diff --git a/modules/i18n/src/main/resources/i18n/en_GB/info.yml b/modules/i18n/src/main/resources/i18n/en_GB/info.yml index b10cdab23..f3bf5a606 100644 --- a/modules/i18n/src/main/resources/i18n/en_GB/info.yml +++ b/modules/i18n/src/main/resources/i18n/en_GB/info.yml @@ -1,3 +1,3 @@ bot: greeting: "Hello, eh?!" - discord-locale: ENGLISH_US + discord-locale: ENGLISH_UK diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FolderPathTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FolderPathTest.java new file mode 100644 index 000000000..43dd52408 --- /dev/null +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FolderPathTest.java @@ -0,0 +1,110 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Locale; +import java.util.Optional; + +public class FolderPathTest { + + @Test + @DisplayName("Test Single Folder") + public void testSingleFolder() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.ENGLISH); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "command.ai", Locale.ENGLISH); + + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals("i18n/en/command/ai.yml", result.get()); + } + + @Test + @DisplayName("Test No Folder") + public void testNoFolder() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.ENGLISH); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "generic", Locale.ENGLISH); + + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals("i18n/en/generic.yml", result.get()); + } + + @Test + @DisplayName("Test With Extra Key") + public void testWithExtraKey() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.ENGLISH); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "command.ai.description", Locale.ENGLISH); + + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals("i18n/en/command/ai.yml", result.get()); + } + + @Test + @DisplayName("Test File English Fallback") + public void testEnglishFallback() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.CHINA); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "command.ai.description", Locale.CHINA); + + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals("i18n/en/command/ai.yml", result.get()); + } + + @Test + @DisplayName("Test File with Valid Language") + public void testValidLanguage() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.UK); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "info.bot.greeting", Locale.UK); + + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals("i18n/en_GB/info.yml", result.get()); + } + + @Test + @DisplayName("Test Missing English File") + public void testMissingEnglishFile() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.ENGLISH); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "this.path.does.not.exist", Locale.ENGLISH); + + Assertions.assertFalse(result.isPresent()); + } + + @Test + @DisplayName("Test Missing Foreign and English Language File") + public void testMissingForeignLanguageFile() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.UK); + + Method method = I18N.class.getDeclaredMethod("findFilePath", String.class, Locale.class); + method.setAccessible(true); + + Optional result = (Optional) method.invoke(i18n, "this.path.does.not.exist", Locale.UK); + + Assertions.assertFalse(result.isPresent()); + } + +} diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java deleted file mode 100644 index ca2b89f62..000000000 --- a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/FormattingTest.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.beanbeanjuice.cafebot.i18n; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.yaml.snakeyaml.Yaml; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.*; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class FormattingTest { - - private static final String I18N_PATH = "i18n"; - private static final String ENGLISH_LOCALE = "en"; - - @Test - @DisplayName("all translations only contain keys present in English") - void allTranslationsOnlyContainEnglishKeys() throws IOException, URISyntaxException { - // Get all locale directories - Set locales = getLocaleDirectories(); - locales.remove(ENGLISH_LOCALE); // We'll compare against English - - // Get all English files and their keys - Map> englishKeysPerFile = loadAllKeysForLocale(ENGLISH_LOCALE); - - List errors = new ArrayList<>(); - - for (String locale : locales) { - Map> localeKeysPerFile = loadAllKeysForLocale(locale); - - for (Map.Entry> entry : localeKeysPerFile.entrySet()) { - String filePath = entry.getKey(); - Set localeKeys = entry.getValue(); - Set englishKeys = englishKeysPerFile.get(filePath); - - if (englishKeys == null) { - // This will be caught by the file structure test - continue; - } - - // Check for keys in locale that don't exist in English - Set extraKeys = new HashSet<>(localeKeys); - extraKeys.removeAll(englishKeys); - - if (!extraKeys.isEmpty()) { - errors.add(String.format("Locale '%s' file '%s' contains extra keys not in English: %s", - locale, filePath, extraKeys)); - } - } - } - - if (!errors.isEmpty()) { - Assertions.fail("Translation key validation failed:\n" + String.join("\n", errors)); - } - } - - @Test - @DisplayName("all translations only contain files present in English") - void allTranslationsOnlyContainEnglishFiles() throws IOException, URISyntaxException { - // Get all locale directories - Set locales = getLocaleDirectories(); - locales.remove(ENGLISH_LOCALE); - - // Get all English file paths - Set englishFiles = getAllFilesForLocale(ENGLISH_LOCALE); - - List errors = new ArrayList<>(); - - for (String locale : locales) { - Set localeFiles = getAllFilesForLocale(locale); - - // Check for files in locale that don't exist in English - Set extraFiles = new HashSet<>(localeFiles); - extraFiles.removeAll(englishFiles); - - if (!extraFiles.isEmpty()) { - errors.add(String.format("Locale '%s' contains extra files not in English: %s", - locale, extraFiles)); - } - } - - if (!errors.isEmpty()) { - Assertions.fail("File structure validation failed:\n" + String.join("\n", errors)); - } - } - - @Test - @DisplayName("all translations contain bot.discord-locale in info.yml") - void allTranslationsContainDiscordLocale() throws IOException, URISyntaxException { - // Get all locale directories - Set locales = getLocaleDirectories(); - - List errors = new ArrayList<>(); - - for (String locale : locales) { - String resourcePath = I18N_PATH + "/" + locale + "/info.yml"; - Set keys = loadKeysFromFile(resourcePath); - - if (keys.isEmpty()) { - errors.add(String.format("Locale '%s' is missing info.yml file", locale)); - } else if (!keys.contains("bot.discord-locale")) { - errors.add(String.format("Locale '%s' is missing 'bot.discord-locale' in info.yml", locale)); - } - } - - if (!errors.isEmpty()) { - Assertions.fail("Discord locale validation failed:\n" + String.join("\n", errors)); - } - } - - private Set getLocaleDirectories() throws IOException, URISyntaxException { - ClassLoader classLoader = getClass().getClassLoader(); - URI uri = classLoader.getResource(I18N_PATH).toURI(); - - if (uri.getScheme().equals("jar")) { - try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { - Path path = fileSystem.getPath("/" + I18N_PATH); - try (Stream paths = Files.list(path)) { - return paths - .filter(Files::isDirectory) - .map(p -> p.getFileName().toString()) - .collect(Collectors.toSet()); - } - } - } else { - Path path = Paths.get(uri); - try (Stream paths = Files.list(path)) { - return paths - .filter(Files::isDirectory) - .map(p -> p.getFileName().toString()) - .collect(Collectors.toSet()); - } - } - } - - private Set getAllFilesForLocale(String locale) throws IOException, URISyntaxException { - Set files = new HashSet<>(); - String localePath = I18N_PATH + "/" + locale; - - ClassLoader classLoader = getClass().getClassLoader(); - URI uri = classLoader.getResource(localePath).toURI(); - - if (uri.getScheme().equals("jar")) { - try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { - Path path = fileSystem.getPath("/" + localePath); - collectYamlFiles(path, "", files); - } - } else { - Path path = Paths.get(uri); - collectYamlFiles(path, "", files); - } - - return files; - } - - private void collectYamlFiles(Path dir, String prefix, Set files) throws IOException { - try (Stream paths = Files.list(dir)) { - List pathList = paths.collect(Collectors.toList()); - for (Path path : pathList) { - String fileName = path.getFileName().toString(); - String relativePath = prefix.isEmpty() ? fileName : prefix + "/" + fileName; - - if (Files.isDirectory(path)) { - collectYamlFiles(path, relativePath, files); - } else if (fileName.endsWith(".yml") || fileName.endsWith(".yaml")) { - files.add(relativePath); - } - } - } - } - - private Map> loadAllKeysForLocale(String locale) throws IOException, URISyntaxException { - Map> keysPerFile = new HashMap<>(); - Set files = getAllFilesForLocale(locale); - - for (String file : files) { - String resourcePath = I18N_PATH + "/" + locale + "/" + file; - Set keys = loadKeysFromFile(resourcePath); - keysPerFile.put(file, keys); - } - - return keysPerFile; - } - - private Set loadKeysFromFile(String resourcePath) throws IOException { - ClassLoader classLoader = getClass().getClassLoader(); - try (InputStream stream = classLoader.getResourceAsStream(resourcePath)) { - if (stream == null) { - return Collections.emptySet(); - } - - Yaml yaml = new Yaml(); - Map data = yaml.load(stream); - - if (data == null) { - return Collections.emptySet(); - } - - Set keys = new HashSet<>(); - flattenKeys("", data, keys); - return keys; - } - } - - private void flattenKeys(String prefix, Map source, Set keys) { - for (Map.Entry entry : source.entrySet()) { - String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey(); - Object value = entry.getValue(); - - if (value instanceof Map map) { - flattenKeys(key, (Map) map, keys); - } else { - keys.add(key); - } - } - } - -} diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java new file mode 100644 index 000000000..5bd0b9fa9 --- /dev/null +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java @@ -0,0 +1,120 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class I18NResourceVerificationTest { + + private Set getLanguageFilePaths(String initialPath) throws URISyntaxException { + URL resource = I18NResourceVerificationTest.class.getClassLoader().getResource(initialPath); + if (resource == null) Assertions.fail("Resource not found: " + initialPath); + + URL i18nResources = I18NResourceVerificationTest.class.getClassLoader().getResource("i18n"); + if (i18nResources == null) Assertions.fail("Failed to find i18n resources"); + Path relativePath = Path.of(i18nResources.toURI()); + + try (Stream walk = Files.walk(Paths.get(resource.toURI()))) { + return walk.filter(Files::isRegularFile) + .map(Path::toString) + .map((string) -> string.replace(relativePath.toString(), "")) + .map((string) -> string.replaceFirst("\\\\", "")) + .collect(Collectors.toSet()); + } catch (IOException | URISyntaxException e) { + Assertions.fail(e); + } + + Assertions.fail("Resource not found: " + initialPath); + return null; + } + + @Test + @DisplayName("Verify Lowercase Paths") + public void verifyLowercasePaths() throws URISyntaxException { + // Verify that all file paths in all languages are lowercase. + Set allPaths = getLanguageFilePaths("i18n"); + Assertions.assertNotNull(allPaths); + + for (String path : allPaths) { + String[] splitPathOrig = path.split("\\\\"); + String[] splitPath = Arrays.copyOfRange(splitPathOrig, 1, splitPathOrig.length); + + for (String split : splitPath) { + Assertions.assertEquals(split.toLowerCase(), split); + } + } + } + + private List getAllKeysInMap(Map map) { + // If value at key is a map, go deeper. + List keys = new ArrayList<>(); + + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + + keys.add(key); + if (value instanceof Map) keys.addAll(getAllKeysInMap((Map) value)); + } + + return keys; + } + + @Test + @DisplayName("Verify Lowercase Keys") + public void verifyLowercaseKeys() throws URISyntaxException { + // Verify that all key paths in all languages are lowercase. + Set paths = getLanguageFilePaths("i18n"); // en/info.yml + + for (String path : paths) { + String fullPath = "i18n\\" + path; + + URL resource = I18NResourceVerificationTest.class.getClassLoader().getResource(fullPath); + if (resource == null) Assertions.fail("Resource not found: " + fullPath); + + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(fullPath)) { + Yaml yaml = new Yaml(); + Map map = yaml.load(is); + + List keys = getAllKeysInMap(map); + + for (String key : keys) Assertions.assertEquals(key, key.toLowerCase(), String.format("key '%s' is incorrect in %s", key, fullPath)); + } catch (IOException e) { + Assertions.fail(e); + } + } + } + + @Test + @DisplayName("Verify Foreign Files are Subset of English Files") + @Disabled + public void testForeignFilesAreSubsetOfEnglishFiles() { + // Get a list of all file paths in the i18n/en directory. + Assertions.fail("Not yet implemented"); + } + + @Test + @DisplayName("Verify Foreign Keys are Subset of English Keys") + @Disabled + public void testForeignKeysAreSubsetOfEnglishKeys() { + Assertions.fail("Not yet implemented"); + } + +} diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NTest.java new file mode 100644 index 000000000..abdeb1e65 --- /dev/null +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NTest.java @@ -0,0 +1,97 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Locale; + +public class I18NTest { + + @Test + @DisplayName("Can Get I18N from Folder") + public void canGetI18N() { + I18N i18n = new I18N(Locale.ENGLISH); + + String description = i18n.getString("command.ai.description"); + Assertions.assertEquals("Want a sassy AI that will serve you some coffee?", description); + } + + @Test + @DisplayName("Can Get I18N String Array") + public void canGetI18NStringArray() { + I18N i18n = new I18N(Locale.ENGLISH); + + List answers = i18n.getStringArray("command.eightball.answers.positive"); + + Assertions.assertFalse(answers.isEmpty()); + Assertions.assertEquals("It is likely...", answers.getFirst()); + } + + @Test + @DisplayName("Can Get I18N Fallback String Array") + public void canGetI18NFallbackStringArray() { + I18N i18n = new I18N(Locale.CHINA); + + List answers = i18n.getStringArray("command.eightball.answers.positive"); + + Assertions.assertFalse(answers.isEmpty()); + Assertions.assertEquals("It is likely...", answers.getFirst()); + } + + @Test + @DisplayName("Can Get I18N without Folder") + public void canGetI18NWithoutFolder() { + I18N i18n = new I18N(Locale.ENGLISH); + + String description = i18n.getString("info.bot.name"); + Assertions.assertEquals("cafeBot", description); + } + + @Test + @DisplayName("Test With Invalid Key") + public void testWithInvalidKey() { + I18N i18n = new I18N(Locale.ENGLISH); + + String description = i18n.getString("this.key.does.not.exist"); + Assertions.assertEquals("this.key.does.not.exist", description); + } + + @Test + @DisplayName("Test Foreign Language") + public void testForeignLanguage() { + I18N i18n = new I18N(Locale.UK); + + String description = i18n.getString("info.bot.greeting"); + Assertions.assertEquals("Hello, eh?!", description); + } + + @Test + @DisplayName("Test Valid English Fallback - Missing Foreign, Valid English") + public void testValidEnglishFallback() { + I18N i18n = new I18N(Locale.CHINA); + + String description = i18n.getString("info.bot.name"); + Assertions.assertEquals("cafeBot", description); + } + + @Test + @DisplayName("Test Invalid English Fallback - Missing Foreign, Missing English") + public void testInvalidEnglishFallback() { + I18N i18n = new I18N(Locale.CHINA); + + String description = i18n.getString("this.key.does.not.exist"); + Assertions.assertEquals("this.key.does.not.exist", description); + } + + @Test + @DisplayName("Test Valid English Fallback - Valid Foreign, Missing Key, Valid English") + public void testValidEnglishFallbackWithKey() { + I18N i18n = new I18N(Locale.UK); + + String description = i18n.getString("info.bot.name"); + Assertions.assertEquals("cafeBot", description); + } + +} diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java deleted file mode 100644 index dd35af307..000000000 --- a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/KeyTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.beanbeanjuice.cafebot.i18n; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Locale; - -public class KeyTest { - - @Test - @DisplayName("can get default messages") - public void canGetDefaultMessages() { - I18N bundle = I18N.getBundle(); - Assertions.assertEquals("Hello, world!", bundle.getString("info.bot.greeting")); - } - - @Test - @DisplayName("different language works") - public void differentLanguageWorks() { - I18N bundle = I18N.getBundle(Locale.UK); - Assertions.assertEquals("Hello, eh?!", bundle.getString("info.bot.greeting")); - } - - @Test - @DisplayName("fallback works if language file exists but does not contain key") - public void fallbackWorksIfLanguageDoesNotContainKey() { - I18N bundle = I18N.getBundle(Locale.UK); - Assertions.assertEquals("cafeBot", bundle.getString("info.bot.name")); - } - - @Test - @DisplayName("fallback works if language file does not exist") - public void fallbackWorksIfLanguageDoesNotExist() { - I18N bundle = I18N.getBundle(Locale.TRADITIONAL_CHINESE); - - Assertions.assertEquals("Hello, world!", bundle.getString("info.bot.greeting")); - } - - @Test - @DisplayName("returns path if does not exist") - public void returnsPathIfDoesNotExist() { - I18N bundle = I18N.getBundle(); - String result = bundle.getString("path.that.does.not.exist"); - - Assertions.assertEquals("path.that.does.not.exist", result); - } - - @Test - @DisplayName("returns string if does exist") - public void returnsPathIfDoesExist() { - I18N bundle = I18N.getBundle(); - String result = bundle.getString("info.bot.name"); - - Assertions.assertEquals("cafeBot", result); - } - -} diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/ParseKeyTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/ParseKeyTest.java new file mode 100644 index 000000000..6bef061ef --- /dev/null +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/ParseKeyTest.java @@ -0,0 +1,39 @@ +package com.beanbeanjuice.cafebot.i18n; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Locale; + +public class ParseKeyTest { + + @Test + @DisplayName("Test Valid Key Parse") + public void testValidKeyParse() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.US); + + Method method = I18N.class.getDeclaredMethod("parseKey", String.class, String.class); + method.setAccessible(true); + + String result = (String) method.invoke(i18n, "i18n/en/command/ai.yml", "command.ai.embed.title"); + + Assertions.assertEquals("embed.title", result); + } + + @Test + @DisplayName("Test Invalid Key Parse") + public void testInvalidKeyParse() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + I18N i18n = new I18N(Locale.US); + + Method method = I18N.class.getDeclaredMethod("parseKey", String.class, String.class); + method.setAccessible(true); + + String result = (String) method.invoke(i18n, "i18n/en/command/ai.yml", "this.is.not.a.real.command"); + + Assertions.assertEquals("this.is.not.a.real.command", result); + } + +} diff --git a/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java b/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java index e1119d06b..d298db89d 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java +++ b/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java @@ -91,7 +91,7 @@ public class CafeBot { @Getter private SnipeHandler snipeHandler; // Additional Items - @Getter private final AtomicInteger commandsRun = new AtomicInteger(0); // Atomic since can run across multiple shards. + @Getter private static final AtomicInteger commandsRun = new AtomicInteger(0); // Atomic since can run across multiple shards. @Getter private final String discordAvatarUrl = "https://cdn.beanbeanjuice.com/images/cafeBot/cafeBot.gif"; public CafeBot() throws InterruptedException, ExecutionException { @@ -352,8 +352,8 @@ public void pmUser(User user, MessageEmbed embed) { user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embed)).queue(); } - public void increaseCommandsRun() { - this.commandsRun.incrementAndGet(); + public static void increaseCommandsRun() { + commandsRun.incrementAndGet(); } public SelfUser getSelfUser() { diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java index b008e7c03..ffe6a2c7b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/BalanceCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.type.DiscordUser; import com.beanbeanjuice.cafebot.api.wrapper.type.MenuOrder; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -36,7 +37,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { CompletableFuture f1 = bot.getCafeAPI().getUserApi().getUser(user.getId()); CompletableFuture f2 = bot.getCafeAPI().getOrderApi().getOrders(user.getId()); - ResourceBundle bundle = ctx.getUserI18n(); + I18N bundle = ctx.getUserI18n(); f1.thenAcceptBoth(f2, (discordUser, orders) -> { event.getHook().sendMessageEmbeds(balanceEmbed(user, discordUser, orders, bundle)).queue(); @@ -49,7 +50,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { }); } - public MessageEmbed balanceEmbed(User user, DiscordUser cafeUser, MenuOrder[] orders, ResourceBundle bundle) { + public MessageEmbed balanceEmbed(User user, DiscordUser cafeUser, MenuOrder[] orders, I18N bundle) { long ordersBought = Arrays.stream(orders).filter((order) -> order.getFromId().equals(user.getId())).count(); long ordersReceived = Arrays.stream(orders).filter((order) -> order.getToId().equals(user.getId())).count(); diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java index ea5e2be6f..fe42dfc44 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/DonateCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -13,6 +14,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.DiscordLocale; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; @@ -20,7 +22,6 @@ import java.time.Instant; import java.util.Optional; -import java.util.ResourceBundle; import java.util.concurrent.CompletionException; public class DonateCommand extends Command implements ICommand { @@ -81,13 +82,13 @@ private void handleError(Throwable e, SlashCommandInteractionEvent event, Comman bot.getLogger().log(DonateCommand.class, LogLevel.WARN, "There was an error donating to a user: " + e.getMessage(), e.getCause()); } - private void sendNotEnoughCoinsEmbed(SlashCommandInteractionEvent event, String userId, double amount, ResourceBundle i18n) { + private void sendNotEnoughCoinsEmbed(SlashCommandInteractionEvent event, String userId, double amount, I18N i18n) { bot.getCafeAPI().getUserApi().getUser(userId).thenAccept((user) -> { event.getHook().sendMessageEmbeds(notEnoughCoinsEmbed(user.getBalance(), amount, i18n)).queue(); }); } - private void sendNeedToWaitEmbed(SlashCommandInteractionEvent event, String userId, ResourceBundle i18n) { + private void sendNeedToWaitEmbed(SlashCommandInteractionEvent event, String userId, I18N i18n) { bot.getCafeAPI().getUserApi().getUser(userId).thenAccept((user) -> { event.getHook().sendMessageEmbeds(needToWaitEmbed(user.getLastDonationTime().orElse(Instant.now()), i18n)).queue(); }); @@ -97,11 +98,11 @@ private void sendSuccessToSender(SlashCommandInteractionEvent event, User receiv event.getHook().sendMessageEmbeds(successEmbed(receiver)).queue(); } - private void sendDonationEmbed(SlashCommandInteractionEvent event, User sender, User receiver, double amount, ResourceBundle i18n) { + private void sendDonationEmbed(SlashCommandInteractionEvent event, User sender, User receiver, double amount, I18N i18n) { event.getChannel().sendMessageEmbeds(donationEmbed(sender, receiver, amount, i18n)).mention(receiver).queue(); } - private MessageEmbed notEnoughCoinsEmbed(double balance, double amount, ResourceBundle i18n) { + private MessageEmbed notEnoughCoinsEmbed(double balance, double amount, I18N i18n) { String description = i18n.getString("command.donate.embed.balance.description") .replace("{balance}", String.format("%.2f", balance)) .replace("{payment}", String.format("%.2f", amount)); @@ -115,7 +116,7 @@ private MessageEmbed notEnoughCoinsEmbed(double balance, double amount, Resource return embedBuilder.build(); } - private MessageEmbed needToWaitEmbed(Instant lastDonationTime, ResourceBundle i18n) { + private MessageEmbed needToWaitEmbed(Instant lastDonationTime, I18N i18n) { String description = i18n.getString("command.donate.embed.cooldown.description") .replace("{timestamp}", String.valueOf(lastDonationTime.getEpochSecond())); @@ -135,7 +136,7 @@ private MessageEmbed successEmbed(User receiver) { ); } - private MessageEmbed donationEmbed(User sender, User receiver, double amount, ResourceBundle i18n) { + private MessageEmbed donationEmbed(User sender, User receiver, double amount, I18N i18n) { String description = i18n.getString("command.donate.embed.donation.description") .replace("{donator}", sender.getAsMention()) .replace("{donatee}", receiver.getAsMention()) diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java index 2aadcfc50..8d10b6624 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/cafe/ServeCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -21,7 +22,6 @@ import java.time.Instant; import java.util.Optional; -import java.util.ResourceBundle; import java.util.concurrent.CompletionException; public class ServeCommand extends Command implements ICommand { @@ -75,7 +75,7 @@ private void handleError(Throwable e, SlashCommandInteractionEvent event, Comman bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Serving Word: " + e.getMessage(), true, true); } - private MessageEmbed cannotServeEmbed(Instant lastServeTime, ResourceBundle i18n) { + private MessageEmbed cannotServeEmbed(Instant lastServeTime, I18N i18n) { String description = i18n.getString("command.serve.embed.error.time.description") .replace("{time}", String.valueOf(lastServeTime.getEpochSecond())); @@ -85,7 +85,7 @@ private MessageEmbed cannotServeEmbed(Instant lastServeTime, ResourceBundle i18n ); } - private MessageEmbed serveEmbed(User user, @Nullable User receiver, String word, float reward, float newBalance, ResourceBundle i18n) { + private MessageEmbed serveEmbed(User user, @Nullable User receiver, String word, float reward, float newBalance, I18N i18n) { EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setTitle(i18n.getString("command.serve.embed.success.title")); diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java index 01e071077..e4e75e8e3 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/AvatarCommand.java @@ -1,6 +1,7 @@ package com.beanbeanjuice.cafebot.commands.fun; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -17,7 +18,6 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import java.util.Optional; -import java.util.ResourceBundle; public class AvatarCommand extends Command implements ICommand { @@ -50,7 +50,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { ); } - private MessageEmbed avatarEmbed(String name, String avatarURL, ResourceBundle i18n) { + private MessageEmbed avatarEmbed(String name, String avatarURL, I18N i18n) { String title = i18n.getString("command.avatar.embed.title").replace("{user}", name); return new EmbedBuilder() .setTitle(title) @@ -59,13 +59,13 @@ private MessageEmbed avatarEmbed(String name, String avatarURL, ResourceBundle i .build(); } - private MessageEmbed missingErrorEmbed(ResourceBundle i18n) { + private MessageEmbed missingErrorEmbed(I18N i18n) { String title = i18n.getString("command.avatar.error.missing.title"); String description = i18n.getString("command.avatar.error.missing.description"); return Helper.errorEmbed(title, description); } - private MessageEmbed serverErrorEmbed(ResourceBundle i18n) { + private MessageEmbed serverErrorEmbed(I18N i18n) { String title = i18n.getString("command.avatar.error.server.title"); String description = i18n.getString("command.avatar.error.server.description"); return Helper.errorEmbed(title, description); diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java index cd52cf4de..f51d555b4 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/BannerCommand.java @@ -1,6 +1,7 @@ package com.beanbeanjuice.cafebot.commands.fun; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -17,7 +18,6 @@ import java.awt.*; import java.util.Optional; -import java.util.ResourceBundle; public class BannerCommand extends Command implements ICommand { @@ -41,7 +41,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { }); } - private MessageEmbed bannerEmbed(String username, String avatarURL, String bannerURL, Color accent, ResourceBundle i18n) { + private MessageEmbed bannerEmbed(String username, String avatarURL, String bannerURL, Color accent, I18N i18n) { String title = i18n.getString("command.banner.embed.title").replace("{user}", username); EmbedBuilder embedBuilder = new EmbedBuilder() @@ -53,7 +53,7 @@ private MessageEmbed bannerEmbed(String username, String avatarURL, String banne return embedBuilder.build(); } - private MessageEmbed noBannerError(ResourceBundle i18n) { + private MessageEmbed noBannerError(I18N i18n) { String title = i18n.getString("command.banner.error.title"); String description = i18n.getString("command.banner.error.description"); diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java index 97e39bba1..be6467aa6 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/EightBallCommand.java @@ -1,6 +1,7 @@ package com.beanbeanjuice.cafebot.commands.fun; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -13,6 +14,8 @@ import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; +import java.util.List; + public class EightBallCommand extends Command implements ICommand { public EightBallCommand(final CafeBot cafeBot) { @@ -22,40 +25,29 @@ public EightBallCommand(final CafeBot cafeBot) { @Override public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String question = event.getOption("question").getAsString(); - event.getHook().sendMessageEmbeds(getAnswerEmbed(question, getAnswer())).queue(); + String answer = getAnswer(ctx.getDefaultBundle()); + MessageEmbed answerEmbed = getAnswerEmbed(question, answer, ctx); + event.getHook().sendMessageEmbeds(answerEmbed).queue(); } - private MessageEmbed getAnswerEmbed(final String question, final String answer) { + private MessageEmbed getAnswerEmbed(String question, String answer, CommandContext ctx) { + String fieldTitle = ctx.getDefaultBundle().getString("command.eightball.embed.field_title"); + String fieldDescription = ctx.getDefaultBundle().getString("command.eightball.embed.field_description").replace("{answer}", answer); + return new EmbedBuilder() .setDescription("\"" + question + "\"") - .addField("My Mystical Answer", "\"" + answer + "\"", false) + .addField(fieldTitle, fieldDescription, false) .setColor(Helper.getRandomColor()) .build(); } - private String getAnswer() { - String[] yesAnswers = new String[] { - "It is likely...", - "Probably.", - "Without a doubt.", - "Of course.", - "More than likely.", - "YES ABSOLUTELY.", - "Hmmm... I think so." - }; + private String getAnswer(I18N i18n) { + boolean isYes = Helper.getRandomInteger(0, 2) == 1; - String[] noAnswers = new String[] { - "Of course not.", - "Probably not.", - "It is not likely...", - "There is some doubt...", - "Less than likely.", - "ABSOLUTELY NOT.", - "Are you kidding? No!" - }; + List answers = i18n.getStringArray("command.eightball.answers." + (isYes ? "positive" : "negative")); - if (Helper.getRandomInteger(0, 2) == 1) return yesAnswers[(Helper.getRandomInteger(0, yesAnswers.length))]; - else return noAnswers[(Helper.getRandomInteger(0, noAnswers.length))]; + int answerIndex = Helper.getRandomInteger(0, answers.size()); + return answers.get(answerIndex); } @Override @@ -65,7 +57,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Ask me a question!"; + return "command.eightball.description"; } @Override @@ -76,7 +68,7 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "question", "The question you want to ask me!", true) + new OptionData(OptionType.STRING, "question", "command.eightball.arguments.question.description", true) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java index 4534262fa..ce3ae89c9 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/birthday/BirthdayGetSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.api.wrapper.type.Birthday; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; @@ -12,6 +13,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.DiscordLocale; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; @@ -21,7 +23,6 @@ import java.time.format.TextStyle; import java.util.Locale; import java.util.Optional; -import java.util.ResourceBundle; import java.util.concurrent.CompletionException; public class BirthdayGetSubCommand extends Command implements ISubCommand { @@ -61,7 +62,7 @@ private void handleError(Throwable ex, SlashCommandInteractionEvent event, Comma bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Getting Birthday: " + ex.getMessage()); } - private void sendError(SlashCommandInteractionEvent event, ResourceBundle i18n) { + private void sendError(SlashCommandInteractionEvent event, I18N i18n) { event.getHook().sendMessageEmbeds(Helper.errorEmbed( i18n.getString("command.birthday.subcommand.get.embed.error.title"), i18n.getString("command.birthday.subcommand.get.embed.error.description") @@ -73,7 +74,7 @@ private void sendBirthday(User user, boolean isSelf, Birthday birthday, SlashCom event.getHook().sendMessageEmbeds(embed).queue(); } - private MessageEmbed selfBirthdayEmbed(Birthday birthday, ResourceBundle i18n) { + private MessageEmbed selfBirthdayEmbed(Birthday birthday, I18N i18n) { String description = i18n.getString("command.birthday.subcommand.get.embed.self.description") .replace("{month}", Month.of(birthday.getMonth()).getDisplayName(TextStyle.FULL, Locale.ENGLISH)) .replace("{day}", String.valueOf(birthday.getDay())) @@ -86,7 +87,7 @@ private MessageEmbed selfBirthdayEmbed(Birthday birthday, ResourceBundle i18n) { .build(); } - private MessageEmbed birthdayEmbed(User user, Birthday birthday, ResourceBundle i18n) { + private MessageEmbed birthdayEmbed(User user, Birthday birthday, I18N i18n) { String title = i18n.getString("command.birthday.subcommand.get.embed.other.title") .replace("{user}", user.getName()); diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java index 4fce0e132..ddca84e86 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/PingCommand.java @@ -1,6 +1,7 @@ package com.beanbeanjuice.cafebot.commands.generic; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -16,7 +17,6 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData; import java.util.Optional; -import java.util.ResourceBundle; public class PingCommand extends Command implements ICommand { @@ -37,7 +37,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { numberOptionMapping.map(OptionMapping::getAsInt).ifPresent((number) -> event.getHook().sendMessage(String.valueOf(number)).queue()); } - private MessageEmbed messageEmbed(int shardId, ResourceBundle bundle) { + private MessageEmbed messageEmbed(int shardId, I18N bundle) { EmbedBuilder embedBuilder = new EmbedBuilder(UpdateMessageScheduler.getUpdateEmbed(this.bot)); embedBuilder.setTitle("ping!", "https://www.cafebot.dev"); diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java index 775867449..0b43b564b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandContext.java @@ -1,15 +1,19 @@ package com.beanbeanjuice.cafebot.utility.commands; +import com.beanbeanjuice.cafebot.i18n.I18N; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.ResourceBundle; - @Getter @RequiredArgsConstructor public class CommandContext { - private final ResourceBundle guildI18n; - private final ResourceBundle userI18n; + private final I18N guildI18n; + private final I18N userI18n; + private final boolean isInServer; + + public I18N getDefaultBundle() { + return (isInServer) ? guildI18n : userI18n; + } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java index 16d171144..7259db6e1 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/CommandHandler.java @@ -2,7 +2,6 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.i18n.I18N; -import com.beanbeanjuice.cafebot.i18n.YamlControl; import com.beanbeanjuice.cafebot.utility.logging.LogLevel; import lombok.Getter; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; @@ -64,14 +63,17 @@ public void addCommands(final ICommand... commands) { } private SlashCommandData getCommandData(ICommand command) { - String originalDescription = I18N.getBundle().getString(command.getDescriptionPath()); + I18N defaultI18n = new I18N(Locale.ENGLISH); + + String originalDescription = defaultI18n.getString(command.getDescriptionPath()); SlashCommandData commandData = Commands.slash(command.getName(), originalDescription); Locale.availableLocales().forEach((locale) -> { - ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); - DiscordLocale discordLocale = DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); + I18N localeI18n = new I18N(locale); + + DiscordLocale discordLocale = DiscordLocale.valueOf(localeI18n.getString("info.bot.discord-locale")); - String localizedDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + String localizedDescription = localeI18n.getString(command.getDescriptionPath()); commandData.setDescriptionLocalization(discordLocale, localizedDescription); }); @@ -80,20 +82,21 @@ private SlashCommandData getCommandData(ICommand command) { commandOptions = Arrays.stream(commandOptions).peek((commandOption) -> { // Set original (english) String path = commandOption.getDescription(); // Description is originally the path. - String originalCommandOptionDescription = I18N.getBundle().getString(commandOption.getDescription()); + String originalCommandOptionDescription = defaultI18n.getString(commandOption.getDescription()); commandOption.setDescription(originalCommandOptionDescription); // Set localization // Loop through all locales, map to discord locale, set localized description Locale.availableLocales().map((locale) -> { - ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + I18N localeI18n = new I18N(locale); - try { return DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); } + try { return DiscordLocale.valueOf(localeI18n.getString("info.bot.discord-locale")); } catch (MissingResourceException e) { return null; } }).forEach((locale) -> { if (locale == null) return; - String localizedCommandOptionDescription = I18N.getBundle(locale.toLocale()).getString(path); + I18N localeI18n = new I18N(locale.toLocale()); + String localizedCommandOptionDescription = localeI18n.getString(path); commandOption.setDescriptionLocalization(locale, localizedCommandOptionDescription); }); @@ -112,14 +115,15 @@ private SlashCommandData getCommandData(ICommand command) { } private SubcommandData getSubcommandData(ISubCommand command, ICommand parent) { - String originalDescription = I18N.getBundle().getString(command.getDescriptionPath()); + I18N defaultI18n = new I18N(Locale.ENGLISH); + String originalDescription = defaultI18n.getString(command.getDescriptionPath()); SubcommandData subcommandData = new SubcommandData(command.getName(), originalDescription); Locale.availableLocales().forEach((locale) -> { - ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); - DiscordLocale discordLocale = DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); + I18N localeI18n = new I18N(locale); + DiscordLocale discordLocale = DiscordLocale.valueOf(localeI18n.getString("info.bot.discord-locale")); - String localizedDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + String localizedDescription = localeI18n.getString(command.getDescriptionPath()); subcommandData.setDescriptionLocalization(discordLocale, localizedDescription); }); @@ -128,20 +132,21 @@ private SubcommandData getSubcommandData(ISubCommand command, ICommand parent) { commandOptions = Arrays.stream(commandOptions).peek((commandOption) -> { // Set original (english) String path = commandOption.getDescription(); // Description is originally the path. - String originalCommandOptionDescription = I18N.getBundle().getString(commandOption.getDescription()); + String originalCommandOptionDescription = defaultI18n.getString(commandOption.getDescription()); commandOption.setDescription(originalCommandOptionDescription); // Set localization // Loop through all locales, map to discord locale, set localized description Locale.availableLocales().map((locale) -> { - ResourceBundle i18n = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + I18N localeI18n = new I18N(locale); - try { return DiscordLocale.valueOf(i18n.getString("info.bot.discord-locale")); } + try { return DiscordLocale.valueOf(localeI18n.getString("info.bot.discord-locale")); } catch (MissingResourceException e) { return null; } }).forEach((locale) -> { if (locale == null) return; - String localizedCommandOptionDescription = I18N.getBundle(locale.toLocale()).getString(path); + I18N localeI18n = new I18N(locale.toLocale()); + String localizedCommandOptionDescription = localeI18n.getString(path); commandOption.setDescriptionLocalization(locale, localizedCommandOptionDescription); }); @@ -197,10 +202,11 @@ private void handleSubCommand(ISubCommand subCommand, ICommand command, SlashCom if (!subCommand.isModal()) event.deferReply(command.isEphemeral()).queue(); Locale guildLocale = event.isFromGuild() ? event.getGuildLocale().toLocale() : event.getUserLocale().toLocale(); // Use user locale if not from guild - ResourceBundle guildBundle = ResourceBundle.getBundle("messages", guildLocale, YamlControl.INSTANCE); - ResourceBundle userBundle = ResourceBundle.getBundle("messages", event.getUserLocale().toLocale(), YamlControl.INSTANCE); - subCommand.handle(event, new CommandContext(guildBundle, userBundle)); + I18N guildBundle = new I18N(guildLocale); + I18N userBundle = new I18N(event.getUserLocale().toLocale()); + + subCommand.handle(event, new CommandContext(guildBundle, userBundle, event.isFromGuild())); cafeBot.increaseCommandsRun(); } @@ -208,10 +214,12 @@ private void handleCommand(final ICommand command, final SlashCommandInteraction if (!command.isModal()) event.deferReply(command.isEphemeral()).queue(); Locale guildLocale = event.isFromGuild() ? event.getGuildLocale().toLocale() : event.getUserLocale().toLocale(); // Use user locale if not from guild - ResourceBundle guildBundle = ResourceBundle.getBundle("messages", guildLocale, YamlControl.INSTANCE); - ResourceBundle userBundle = ResourceBundle.getBundle("messages", event.getUserLocale().toLocale(), YamlControl.INSTANCE); - command.handle(event, new CommandContext(guildBundle, userBundle)); + + I18N guildBundle = new I18N(guildLocale); + I18N userBundle = new I18N(event.getUserLocale().toLocale()); + + command.handle(event, new CommandContext(guildBundle, userBundle, event.isFromGuild())); cafeBot.increaseCommandsRun(); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java index 904974399..6ad0e3f82 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/commands/ICommand.java @@ -15,6 +15,7 @@ default void handle(SlashCommandInteractionEvent event, CommandContext ctx) { } String getName(); + // We want to get the path at runtime because it may be different depending on who sends it. String getDescriptionPath(); CommandCategory getCategory(); diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java b/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java index 38bdfff19..a5efc39b5 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/handlers/HelpHandler.java @@ -1,7 +1,6 @@ package com.beanbeanjuice.cafebot.utility.handlers; import com.beanbeanjuice.cafebot.i18n.I18N; -import com.beanbeanjuice.cafebot.i18n.YamlControl; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandHandler; import com.beanbeanjuice.cafebot.utility.commands.ICommand; @@ -56,6 +55,7 @@ public MessageEmbed getCategoryEmbed(CommandCategory category, DiscordLocale dis String description = String.format("# Commands for %s", category.name()); Locale locale = Locale.forLanguageTag(discordLocale.getLocale()); + I18N i18n = new I18N(locale); EmbedBuilder embedBuilder = new EmbedBuilder(); @@ -66,7 +66,7 @@ public MessageEmbed getCategoryEmbed(CommandCategory category, DiscordLocale dis .skip(skipAmount) .limit(25) .forEach((command) -> { - String commandDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + String commandDescription = i18n.getString(command.getDescriptionPath()); embedBuilder.addField( String.format("**/%s**", command.getName()), @@ -87,10 +87,10 @@ public MessageEmbed getCommandEmbed(String commandName, DiscordLocale discordLoc ICommand command = commandHandler.getCommands().get(commandName); Locale locale = Locale.forLanguageTag(discordLocale.getLocale()); - ResourceBundle bundle = ResourceBundle.getBundle("messages", locale, YamlControl.INSTANCE); + I18N i18n = new I18N(locale); String subCommandsString = Arrays.stream(command.getSubCommands()).map((subCommand) -> { - String subCommandDescription = I18N.getBundle(locale).getString(subCommand.getDescriptionPath()); + String subCommandDescription = i18n.getString(subCommand.getDescriptionPath()); return String.format( """ @@ -108,7 +108,7 @@ public MessageEmbed getCommandEmbed(String commandName, DiscordLocale discordLoc String permissionsString = this.getPermissionsString(command.getPermissions()); permissionsString = (permissionsString.isBlank()) ? "*None*" : permissionsString; - String commandDescription = I18N.getBundle(locale).getString(command.getDescriptionPath()); + String commandDescription = i18n.getString(command.getDescriptionPath()); String commandString = String.format( """ diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java index b864165f9..aecf40e23 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java @@ -1,5 +1,6 @@ package com.beanbeanjuice.cafebot.utility.helper; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import net.dv8tion.jda.api.EmbedBuilder; @@ -114,7 +115,7 @@ public static Integer stringToPositiveInteger(@NotNull String timeString) { } } - public static MessageEmbed uncaughtErrorEmbed(ResourceBundle i18n, String error) { + public static MessageEmbed uncaughtErrorEmbed(I18N i18n, String error) { String title = i18n.getString("generic.error.uncaught.title"); String description = i18n.getString("generic.error.uncaught.message") .replace("{uncaught_error}", error); diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/AirportListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/AirportListener.java index 5c34b597d..3b1e07b3a 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/AirportListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/AirportListener.java @@ -1,5 +1,6 @@ package com.beanbeanjuice.cafebot.utility.listeners; +import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.api.wrapper.CafeAPI; import com.beanbeanjuice.cafebot.api.wrapper.api.enums.AirportMessageType; import com.beanbeanjuice.cafebot.api.wrapper.api.enums.CustomChannelType; @@ -66,6 +67,7 @@ public void handleAirportEvent(GenericGuildEvent event, User user, CustomChannel () -> channel.sendMessageEmbeds(getAirportEmbed(airportMessage, user)).queue() ); + CafeBot.increaseCommandsRun(); return true; })); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotAddListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotAddListener.java index d15a474ab..19179f125 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotAddListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotAddListener.java @@ -36,11 +36,11 @@ public void onGuildJoin(final GuildJoinEvent event) { else if (channel.getType() == ChannelType.NEWS) channel.asNewsChannel().sendMessageEmbeds(guildJoinEmbed()).queue(); - cafeBot.increaseCommandsRun(); } catch (InsufficientPermissionException ignored) {} }); this.cafeBot.getLogger().log(BotAddListener.class, LogLevel.INFO, "**" + event.getGuild().getName() + "** has added me! :blush:", false, true); + CafeBot.increaseCommandsRun(); } private MessageEmbed guildJoinEmbed() { diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotRemoveListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotRemoveListener.java index d19bdd92f..dfebc0b63 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotRemoveListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/BotRemoveListener.java @@ -18,6 +18,7 @@ public BotRemoveListener(CafeBot cafeBot) { public void onGuildLeave(@NotNull GuildLeaveEvent event) { cafeBot.getCafeAPI().getGuildApi().deleteDiscordServer(event.getGuild().getId()); cafeBot.getLogger().log(BotRemoveListener.class, LogLevel.INFO, "**" + event.getGuild().getName() + "** has removed me... <:cafeBot_sad:1171726165040447518>", false, true); + CafeBot.increaseCommandsRun(); } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/ConfessionBanListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/ConfessionBanListener.java index 5d0e1cb8c..1576810f1 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/ConfessionBanListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/ConfessionBanListener.java @@ -37,6 +37,7 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) { member.ban(0, TimeUnit.MINUTES).reason( String.format("cafeBot: Banned by %s (%s) for confession content violation.", admin.getUser().getName(), admin.getId()) ).queue((success) -> event.retrieveMessage().queue((message) -> message.delete().queue())); + CafeBot.increaseCommandsRun(); }); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/CountingListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/CountingListener.java index 4c10cd6b5..8cf11df34 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/CountingListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/CountingListener.java @@ -39,6 +39,8 @@ private void handleCountingEvent(MessageReceivedEvent event) { String number = event.getMessage().getContentRaw().split(" ")[0]; if (!Helper.isNumber(number)) return; + CafeBot.increaseCommandsRun(); + checkNumber(event, Integer.parseInt(number)); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HoneypotListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HoneypotListener.java index 91774a97a..cca199d05 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HoneypotListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/HoneypotListener.java @@ -51,6 +51,8 @@ private void handleBan(Guild guild, Member member, String channelId) { try { guild.ban(member.getUser(), 1, TimeUnit.DAYS).reason("cafeBot: Message or reaction sent in honeypot channel.").queue(); bot.getLogger().logToGuild(guild, Helper.successEmbed("Honeypot", String.format("A pesky bot tried to grab some honey! %s - **%s** (%s) was banned.", member.getAsMention(), member.getUser().getName(), member.getId()))); + + CafeBot.increaseCommandsRun(); } catch (Exception e) { bot.getLogger().logToGuild(guild, Helper.errorEmbed("Honeypot Error", "Error Banning User in Honeypot Channel: " + e.getMessage())); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/RaffleListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/RaffleListener.java index 639fa50a8..2e2fb0917 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/RaffleListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/RaffleListener.java @@ -44,6 +44,8 @@ public void onGuildMemberRemove(@NonNull GuildMemberRemoveEvent event) { bot.getCafeAPI().getRaffleApi().getRaffles(event.getGuild().getId(), true, false).thenAccept((raffles) -> { for (Raffle raffle : raffles) { bot.getCafeAPI().getRaffleApi().setSubmission(raffle.getId(), event.getUser().getId(), false); + + CafeBot.increaseCommandsRun(); } }); } @@ -69,6 +71,8 @@ private void handleSubmission(GenericMessageReactionEvent event, boolean newStat bot.getCafeAPI().getRaffleApi().setSubmission(raffle.getId(), userId, newStatus).thenRun(() -> { Helper.pmUser(event.getUser(), getVoteMessage(raffle, newStatus)); }); + + CafeBot.increaseCommandsRun(); }); } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/TwitchGoLiveEventListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/TwitchGoLiveEventListener.java index 461862246..7f1858ecb 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/TwitchGoLiveEventListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/TwitchGoLiveEventListener.java @@ -97,7 +97,8 @@ private void handleMessageToServers(ChannelGoLiveEvent event, String profileImag channel.sendMessage(liveMessage).addEmbeds(liveEmbed(event, profileImageURL, boxArtURL, twitchName)).queue(); return null; }); - cafeBot.increaseCommandsRun(); + + CafeBot.increaseCommandsRun(); } public MessageEmbed liveEmbed(ChannelGoLiveEvent event, String profileImageURL, @Nullable String boxArtURL, String twitchName) { diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/VoiceRoleBindListener.java b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/VoiceRoleBindListener.java index 3349a8564..866a29e63 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/VoiceRoleBindListener.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/listeners/VoiceRoleBindListener.java @@ -48,6 +48,8 @@ private void handleVoiceJoin(GuildVoiceUpdateEvent event) { "There was an error setting a voice role bind... " + error.getMessage() )); }); + + CafeBot.increaseCommandsRun(); }); }); } @@ -69,6 +71,8 @@ private void handleVoiceLeave(GuildVoiceUpdateEvent event) { "There was an error removing a voice role bind... " + error.getMessage() )); }); + + CafeBot.increaseCommandsRun(); }); }); } From e3c0df8191bb3022939bd35e98c668377dcbb412 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Sun, 29 Mar 2026 10:30:10 -0700 Subject: [PATCH 12/22] Add Snipe Command --- .../main/resources/i18n/en/command/snipe.yml | 13 +++++++++ .../i18n/I18NResourceVerificationTest.java | 25 +++++++++++++++++ .../cafebot/commands/fun/SnipeCommand.java | 28 +++++++++++++------ 3 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/snipe.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/snipe.yml b/modules/i18n/src/main/resources/i18n/en/command/snipe.yml new file mode 100644 index 000000000..379a68089 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/snipe.yml @@ -0,0 +1,13 @@ +description: "Snipe a recently deleted message!" +embed: + none: + title: "No Snipe Found!" + description: "No one has deleted any messages in this channel recently..." + snipe: + title: "Snipe!" + description: "{user} said \"{message}\"" +arguments: + word: + description: "Any word you want repeated back to you." + number: + description: "An integer to repeat back to you." diff --git a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java index 5bd0b9fa9..3b226caae 100644 --- a/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java +++ b/modules/i18n/src/test/java/com/beanbeanjuice/cafebot/i18n/I18NResourceVerificationTest.java @@ -102,6 +102,31 @@ public void verifyLowercaseKeys() throws URISyntaxException { } } + @Test + @DisplayName("Verify Valid YAML Files") + public void verifyValidYamlFiles() throws URISyntaxException { + Set paths = getLanguageFilePaths("i18n"); // en/info.yml + + for (String path : paths) { + String fullPath = "i18n\\" + path; // i18n/en/info.yml + + URL resource = I18NResourceVerificationTest.class.getClassLoader().getResource(fullPath); + if (resource == null) Assertions.fail("Resource not found: " + fullPath); + + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(fullPath)) { + + try { + Yaml yaml = new Yaml(); + Map map = yaml.load(is); + } catch (Exception e) { + Assertions.fail(e); + } + } catch (IOException e) { + Assertions.fail(e); + } + } + } + @Test @DisplayName("Verify Foreign Files are Subset of English Files") @Disabled diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java index d2391602b..b4367ea8d 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/fun/SnipeCommand.java @@ -1,6 +1,7 @@ package com.beanbeanjuice.cafebot.commands.fun; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -21,29 +22,38 @@ public SnipeCommand(CafeBot bot) { @Override public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getSnipeHandler().popLastMessage(event.getChannelId()).ifPresentOrElse( - (snipedMessage) -> event.getHook().sendMessageEmbeds(this.getSnipedMessageEmbed(snipedMessage)).queue(), - () -> event.getHook().sendMessageEmbeds(this.getNoSnipeMessageEmbed()).queue() + (snipedMessage) -> event.getHook().sendMessageEmbeds(this.getSnipedMessageEmbed(snipedMessage, ctx.getGuildI18n())).queue(), + + () -> event.getHook().sendMessageEmbeds(this.getNoSnipeMessageEmbed(ctx.getGuildI18n())).queue() ); } - private MessageEmbed getSnipedMessageEmbed(PotentialSnipeMessage snipe) { + private MessageEmbed getSnipedMessageEmbed(PotentialSnipeMessage snipe, I18N guildBundle) { EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setTitle("Snipe!"); + String title = guildBundle.getString("command.snipe.embed.snipe.title"); + String description = guildBundle.getString("command.snipe.embed.snipe.description") + .replace("{user}", snipe.getUser().getAsMention()) + .replace("{message}", snipe.getMessage()); + + embedBuilder.setTitle(title); embedBuilder.setAuthor(snipe.getUser().getName(), null, snipe.getUser().getAvatarUrl()); embedBuilder.setColor(Helper.getRandomColor()); - embedBuilder.setDescription(String.format("%s said \"%s\"", snipe.getUser().getAsMention(), snipe.getMessage())); + embedBuilder.setDescription(description); embedBuilder.setTimestamp(snipe.getCreatedAt()); return embedBuilder.build(); } - private MessageEmbed getNoSnipeMessageEmbed() { + private MessageEmbed getNoSnipeMessageEmbed(I18N guildBundle) { EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setTitle("No Snipe Found!"); + String title = guildBundle.getString("command.snipe.embed.none.title"); + String description = guildBundle.getString("command.snipe.embed.none.description"); + + embedBuilder.setTitle(title); embedBuilder.setColor(Helper.getRandomColor()); - embedBuilder.setDescription("No one has deleted any messages in this channel recently..."); + embedBuilder.setDescription(description); return embedBuilder.build(); } @@ -55,7 +65,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Snipe a recently deleted message!"; + return "command.snipe.description"; } @Override From 193b0886ab4f4db822c639c5a90b3c4e61cd4e35 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Sun, 29 Mar 2026 11:49:34 -0700 Subject: [PATCH 13/22] Add Game Command I18N --- .../main/resources/i18n/en/command/game.yml | 15 +++++++++ .../main/resources/i18n/en/command/snipe.yml | 5 --- .../commands/games/game/GameCommand.java | 2 +- .../games/game/GameStatsSubCommand.java | 32 ++++++++++++++----- 4 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/game.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/game.yml b/modules/i18n/src/main/resources/i18n/en/command/game.yml new file mode 100644 index 000000000..b96c1b512 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/game.yml @@ -0,0 +1,15 @@ +description: "All the games you could ever need!" + +subcommands: + stats: + description: "Get someone's game stats!" + embed: + title: "{user}'s Game Statistics" + fields: + total: + name: "**Totals** 🎯" + stats: "**Wins**: {wins}\n**Losses**: {losses}\n**Played**: {played}" + footer: "Do you want to play a game?~" + arguments: + user: + description: "The person who's game data you want to see." diff --git a/modules/i18n/src/main/resources/i18n/en/command/snipe.yml b/modules/i18n/src/main/resources/i18n/en/command/snipe.yml index 379a68089..a72ac4491 100644 --- a/modules/i18n/src/main/resources/i18n/en/command/snipe.yml +++ b/modules/i18n/src/main/resources/i18n/en/command/snipe.yml @@ -6,8 +6,3 @@ embed: snipe: title: "Snipe!" description: "{user} said \"{message}\"" -arguments: - word: - description: "Any word you want repeated back to you." - number: - description: "An integer to repeat back to you." diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java index 46c9e0933..973d6f1ab 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameCommand.java @@ -20,7 +20,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Something to do with games!"; + return "command.game.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java index e8a116c59..f0c3c2883 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/game/GameStatsSubCommand.java @@ -3,6 +3,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.GameStatusType; import com.beanbeanjuice.cafebot.api.wrapper.api.enums.GameType; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; @@ -65,7 +66,7 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { gameLosses.put(game.getType(), currentLosses); }); - event.getHook().sendMessageEmbeds(gameStatisticsEmbed(user, gamesPlayed, gamesWon, gamesLost, gamesPlayedByType, gameWins, gameLosses)).queue(); + event.getHook().sendMessageEmbeds(gameStatisticsEmbed(user, gamesPlayed, gamesWon, gamesLost, gamesPlayedByType, gameWins, gameLosses, ctx.getGuildI18n())).queue(); }); @@ -78,23 +79,38 @@ private MessageEmbed gameStatisticsEmbed( int gamesLost, HashMap gameTotals, HashMap gameWins, - HashMap gameLosses + HashMap gameLosses, + I18N bundle ) { + String title = bundle.getString("command.game.subcommands.stats.embed.title").replace("{user}", user.getName()); + String description = bundle.getString("command.game.subcommands.stats.embed.stats"); + + String totalsTitle = bundle.getString("command.game.subcommands.stats.embed.fields.total.name"); + String totalsDescription = description + .replace("{wins}", String.valueOf(gamesWon)) + .replace("{losses}", String.valueOf(gamesLost)) + .replace("{played}", String.valueOf(gamesPlayed)); + EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setAuthor(String.format("%s's Game Statistics", user.getName()), null, user.getAvatarUrl()); + embedBuilder.setAuthor(title, null, user.getAvatarUrl()); - embedBuilder.addField("**Totals** 🎯", String.format("**Wins**: %s\n**Losses**: %s\n**Played**: %s", gamesWon, gamesLost, gamesPlayed), true); + embedBuilder.addField(totalsTitle, totalsDescription, true); Arrays.stream(GameType.values()).forEach((type) -> { + String gameDescription = description + .replace("{wins}", String.valueOf(gameWins.get(type))) + .replace("{losses}", String.valueOf(gameLosses.get(type))) + .replace("{played}", String.valueOf(gameTotals.get(type))); + embedBuilder.addField( String.format("**%s**", type.getFriendlyName()), - String.format("**Wins**: %s\n**Losses**: %s\n**Played**: %s", gameWins.get(type), gameLosses.get(type), gameTotals.get(type)), + gameDescription, true ); }); embedBuilder.setColor(Helper.getRandomColor()); - embedBuilder.setFooter("Do you want to play a game?~"); + embedBuilder.setFooter(bundle.getString("command.game.subcommands.stats.embed.footer")); return embedBuilder.build(); } @@ -106,13 +122,13 @@ public String getName() { @Override public String getDescriptionPath() { - return "Get someone's game stats!"; + return "command.game.subcommands.stats.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The person who's game data you want to see.", false) + new OptionData(OptionType.USER, "user", "command.game.subcommands.stats.arguments.user.description", false) }; } From fda2cdbbaedaeedd6759be97b208e8460c88b135 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Sun, 29 Mar 2026 12:36:11 -0700 Subject: [PATCH 14/22] Update to JDK 25 --- .github/workflows/codeql.yml | 4 +- .github/workflows/reusable-gradle-build.yml | 4 +- .github/workflows/reusable-gradle-test.yml | 6 +- Dockerfile | 2 +- build.gradle.kts | 4 +- .../endpoints/discord/server/PollApiTest.java | 64 ++++++++++--------- 6 files changed, 45 insertions(+), 39 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c69ebea27..5acc77883 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,10 +29,10 @@ jobs: with: languages: ${{ matrix.language }} - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v5 with: - java-version: '21' + java-version: '25' distribution: 'temurin' cache: gradle diff --git a/.github/workflows/reusable-gradle-build.yml b/.github/workflows/reusable-gradle-build.yml index ecd0e049a..d0e9e8cf8 100644 --- a/.github/workflows/reusable-gradle-build.yml +++ b/.github/workflows/reusable-gradle-build.yml @@ -6,11 +6,11 @@ on: gradle-version: required: false type: string - default: '8.14.3' + default: '9.4.0' java-version: required: false type: string - default: '21' + default: '25' jobs: gradle-build: diff --git a/.github/workflows/reusable-gradle-test.yml b/.github/workflows/reusable-gradle-test.yml index a850aadb3..83f4bfb49 100644 --- a/.github/workflows/reusable-gradle-test.yml +++ b/.github/workflows/reusable-gradle-test.yml @@ -6,11 +6,11 @@ on: java-version: required: false type: string - default: '21' + default: '25' gradle-version: required: false type: string - default: '8.14.3' + default: '9.4.0' secrets: FORGEJO_REGISTRY: required: true @@ -49,7 +49,7 @@ jobs: run: docker compose -f docker-compose.test.yml up -d --wait - name: Test with Gradle - run: gradle test + run: gradle clean test env: CAFEBOT_API_URL: 'http://localhost:5000' diff --git a/Dockerfile b/Dockerfile index 11c2c2341..cad6e6216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Use an official Gradle image with JDK 21 for building -FROM gradle:jdk21 AS build +FROM gradle:jdk25 AS build # Set the working directory WORKDIR /app diff --git a/build.gradle.kts b/build.gradle.kts index 76b071159..91fc4c12c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,8 +21,8 @@ allprojects { apply(plugin = "com.gradleup.shadow") java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 } repositories { diff --git a/modules/cafeBot-api-wrapper/src/test/java/com/beanbeanjuice/cafebot/endpoints/discord/server/PollApiTest.java b/modules/cafeBot-api-wrapper/src/test/java/com/beanbeanjuice/cafebot/endpoints/discord/server/PollApiTest.java index 976940603..410078a4d 100644 --- a/modules/cafeBot-api-wrapper/src/test/java/com/beanbeanjuice/cafebot/endpoints/discord/server/PollApiTest.java +++ b/modules/cafeBot-api-wrapper/src/test/java/com/beanbeanjuice/cafebot/endpoints/discord/server/PollApiTest.java @@ -17,11 +17,7 @@ public class PollApiTest extends ApiTest { - private Poll poll; - private String user1; - - @BeforeEach - public void setup() throws ExecutionException, InterruptedException { + private static Poll setupPoll() throws ExecutionException, InterruptedException { String guildId = generateSnowflake().toString(); String messageId = generateSnowflake().toString(); String user1 = generateSnowflake().toString(); @@ -35,32 +31,36 @@ public void setup() throws ExecutionException, InterruptedException { "Example Title", "Example Description", true, - Instant.now().plus(2, ChronoUnit.SECONDS).toString(), + Instant.now().plus(1, ChronoUnit.SECONDS).toString(), options.toArray(new PartialPollOption[0]) ); - this.poll = cafeAPI.getPollApi().createPoll(guildId, messageId, partialPoll).get(); - this.poll = cafeAPI.getPollApi().toggleVote(this.poll.getId(), this.poll.getOptions()[0].getId(), user1).get(); + Poll poll = cafeAPI.getPollApi().createPoll(guildId, messageId, partialPoll).get(); + poll = cafeAPI.getPollApi().toggleVote(poll.getId(), poll.getOptions()[0].getId(), user1).get(); + + Thread.sleep(Duration.of(1, ChronoUnit.SECONDS).toMillis()); + + return poll; } @Test @DisplayName("can get all polls") public void testCanGetAllPolls() throws ExecutionException, InterruptedException { - Thread.sleep(Duration.of(3, ChronoUnit.SECONDS).toMillis()); + Poll originalPoll = setupPoll(); Map> polls = cafeAPI.getPollApi().getPolls().get(); - Assertions.assertNotNull(polls.get(this.poll.getGuildId())); - Assertions.assertEquals(1, polls.get(this.poll.getGuildId()).size()); - Assertions.assertNotNull(polls.get(this.poll.getGuildId()).getFirst()); + Assertions.assertNotNull(polls.get(originalPoll.getGuildId())); + Assertions.assertEquals(1, polls.get(originalPoll.getGuildId()).size()); + Assertions.assertNotNull(polls.get(originalPoll.getGuildId()).getFirst()); } @Test @DisplayName("can get polls for guild") public void testCanGetSpecificPoll() throws ExecutionException, InterruptedException { - Thread.sleep(Duration.of(3, ChronoUnit.SECONDS).toMillis()); + Poll originalPoll = setupPoll(); - List polls = cafeAPI.getPollApi().getPolls(this.poll.getGuildId()).get(); + List polls = cafeAPI.getPollApi().getPolls(originalPoll.getGuildId()).get(); Assertions.assertEquals(1, polls.size()); Assertions.assertNotNull(polls.getFirst()); @@ -98,7 +98,7 @@ public void testCanCreatePoll() throws ExecutionException, InterruptedException Assertions.assertTrue(poll.getDescription().isPresent()); Assertions.assertEquals("Example Description", poll.getDescription().get()); - Assertions.assertEquals(2, poll.getOptions()[0].getEmoji().get().length()); + Assertions.assertEquals("🥺".length(), poll.getOptions()[0].getEmoji().get().length()); Assertions.assertEquals("🥺", poll.getOptions()[0].getEmoji().get()); Assertions.assertEquals("Poll Option #1", poll.getOptions()[0].getTitle()); Assertions.assertTrue(poll.getOptions()[0].getDescription().isPresent()); @@ -112,9 +112,10 @@ public void testCanCreatePoll() throws ExecutionException, InterruptedException @Test @DisplayName("can vote for poll") public void testCanVoteForPoll() throws ExecutionException, InterruptedException { + Poll originalPoll = setupPoll(); String user = generateSnowflake().toString(); - Poll poll = cafeAPI.getPollApi().toggleVote(this.poll.getId(), this.poll.getOptions()[1].getId(), user).get(); + Poll poll = cafeAPI.getPollApi().toggleVote(originalPoll.getId(), originalPoll.getOptions()[1].getId(), user).get(); Assertions.assertNotNull(poll); Assertions.assertTrue(Arrays.stream(poll.getOptions()[1].getVoters()).anyMatch((voters) -> voters.equalsIgnoreCase(user))); @@ -123,9 +124,9 @@ public void testCanVoteForPoll() throws ExecutionException, InterruptedException @Test @DisplayName("can close poll") public void testCanClosePoll() throws ExecutionException, InterruptedException { - Thread.sleep(Duration.of(3, ChronoUnit.SECONDS).toMillis()); + Poll originalPoll = setupPoll(); - Poll poll = cafeAPI.getPollApi().closePoll(this.poll.getId()).get(); + Poll poll = cafeAPI.getPollApi().closePoll(originalPoll.getId()).get(); Assertions.assertNotNull(poll); Assertions.assertFalse(poll.isActive()); @@ -135,33 +136,37 @@ public void testCanClosePoll() throws ExecutionException, InterruptedException { @Test @DisplayName("can delete poll") public void testCanDeletePoll() throws ExecutionException, InterruptedException { - List polls = cafeAPI.getPollApi().getPolls(this.poll.getGuildId(), true, false).get(); + Poll originalPoll = setupPoll(); + + List polls = cafeAPI.getPollApi().getPolls(originalPoll.getGuildId(), true, false).get(); Assertions.assertEquals(1, polls.size()); - cafeAPI.getPollApi().deletePoll(this.poll.getId()).join(); + cafeAPI.getPollApi().deletePoll(originalPoll.getId()).join(); - polls = cafeAPI.getPollApi().getPolls(this.poll.getGuildId()).get(); + polls = cafeAPI.getPollApi().getPolls(originalPoll.getGuildId()).get(); Assertions.assertEquals(0, polls.size()); } @Test @DisplayName("can get poll by message id") public void testCanGetPollById() throws ExecutionException, InterruptedException { - Poll poll = cafeAPI.getPollApi().getPoll(this.poll.getGuildId(), this.poll.getMessageId()).get(); + Poll originalPoll = setupPoll(); + Poll poll = cafeAPI.getPollApi().getPoll(originalPoll.getGuildId(), originalPoll.getMessageId()).get(); Assertions.assertNotNull(poll); - Assertions.assertEquals(poll.getId(), this.poll.getId()); - Assertions.assertEquals(poll.getTitle(), this.poll.getTitle()); - Assertions.assertEquals(poll.getDescription(), this.poll.getDescription()); + Assertions.assertEquals(poll.getId(), originalPoll.getId()); + Assertions.assertEquals(poll.getTitle(), originalPoll.getTitle()); + Assertions.assertEquals(poll.getDescription(), originalPoll.getDescription()); } @Test @DisplayName("can manually set poll submission to false") public void testCanSetPollSubmissionToFalse() throws ExecutionException, InterruptedException { - int originalVotes = poll.getOptions()[0].getVoters().length; + Poll originalPoll = setupPoll(); + int originalVotes = originalPoll.getOptions()[0].getVoters().length; String user = generateSnowflake().toString(); - Poll poll = cafeAPI.getPollApi().setVote(this.poll.getId(), this.poll.getOptions()[0].getId(), user, true).get(); + Poll poll = cafeAPI.getPollApi().setVote(originalPoll.getId(), originalPoll.getOptions()[0].getId(), user, true).get(); Assertions.assertEquals(originalVotes + 1, poll.getOptions()[0].getVoters().length); } @@ -169,10 +174,11 @@ public void testCanSetPollSubmissionToFalse() throws ExecutionException, Interru @Test @DisplayName("can manually set poll submission to true") public void testCanSetPollSubmissionToTrue() throws ExecutionException, InterruptedException { - int originalVotes = poll.getOptions()[0].getVoters().length; + Poll originalPoll = setupPoll(); + int originalVotes = originalPoll.getOptions()[0].getVoters().length; String user = generateSnowflake().toString(); - Poll poll = cafeAPI.getPollApi().setVote(this.poll.getId(), this.poll.getOptions()[0].getId(), user, false).get(); + Poll poll = cafeAPI.getPollApi().setVote(originalPoll.getId(), originalPoll.getOptions()[0].getId(), user, false).get(); Assertions.assertEquals(originalVotes, poll.getOptions()[0].getVoters().length); } From d033f82c8a8e0c09ec2e384fe264570f2d8be0b1 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Sun, 29 Mar 2026 14:49:19 -0700 Subject: [PATCH 15/22] Added I18N for CoinFlip and Counting Statistics --- .../resources/i18n/en/command/coinflip.yml | 6 +++ .../i18n/en/command/counting-statistics.yml | 17 +++++++ .../commands/games/CoinFlipCommand.java | 11 +++-- .../games/CountingStatisticsCommand.java | 48 +++++++++---------- 4 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/coinflip.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/command/counting-statistics.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/coinflip.yml b/modules/i18n/src/main/resources/i18n/en/command/coinflip.yml new file mode 100644 index 000000000..dde236e04 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/coinflip.yml @@ -0,0 +1,6 @@ +description: "Flip a coin!" +coin: + heads: "HEADS" + tails: "TAILS" +embed: + description: "The coin is **{side}**!" diff --git a/modules/i18n/src/main/resources/i18n/en/command/counting-statistics.yml b/modules/i18n/src/main/resources/i18n/en/command/counting-statistics.yml new file mode 100644 index 000000000..8e8a1edb1 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/counting-statistics.yml @@ -0,0 +1,17 @@ +description: "Get the server's global counting statistics!" +embed: + stats: + title: "Global Counting Statistics ➕" + description: | + ***Highest Ranking***: {highest}/{num_guilds} + *This is the ranking for your* ***highest*** *number.* + + ***Current Ranking***: {current}/{num_guilds} + *This is the ranking for your* ***current*** *number.* + extended-description: | + + Your highest number is **{highest}**! + Your current number is **{current}**! This means your next number should be **{next}**... + error: + title: "Counting Statistics Error" + description: "I... I'm really sorry... I'm having trouble getting the global counting statistics..." diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java index f264610dc..a592f7b1d 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CoinFlipCommand.java @@ -18,8 +18,13 @@ public CoinFlipCommand(final CafeBot cafeBot) { @Override public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { boolean isHeads = (Helper.getRandomInteger(0, 2) == 1); - String coin = (isHeads) ? "HEADS" : "TAILS"; - event.getHook().sendMessageEmbeds(Helper.descriptionEmbed(String.format("The coin is **%s**!", coin))).queue(); + + String path = "command.coinflip.coin." + (isHeads ? "heads" : "tails"); + String coin = ctx.getGuildI18n().getString(path); + + String description = ctx.getGuildI18n().getString("command.coinflip.embed.description").replace("{side}", coin); + + event.getHook().sendMessageEmbeds(Helper.descriptionEmbed(description)).queue(); } @Override @@ -29,7 +34,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Flip a coin!"; + return "command.coinflip.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java index 1926d3294..b40ecb400 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/CountingStatisticsCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.api.wrapper.type.CountingStatistics; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandCategory; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; @@ -35,12 +36,12 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { int currentRanking = getCurrentGlobalRanking(globalCountingMap, guildId); CountingStatistics guildStatistics = globalCountingMap.get(guild.getId()); - event.getHook().sendMessageEmbeds(getStatisticsEmbed(guildStatistics, highestRanking, currentRanking, globalCountingMap.size())).queue(); + event.getHook().sendMessageEmbeds(getStatisticsEmbed(guildStatistics, highestRanking, currentRanking, globalCountingMap.size(), ctx.getUserI18n())).queue(); }).exceptionally((ex) -> { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Counting Statistics Error", - "I'm really.. sorry... I'm having trouble getting the global counting statistics..." - )).queue(); + String title = ctx.getUserI18n().getString("command.counting-statistics.embed.error.title"); + String description = ctx.getUserI18n().getString("command.counting-statistics.embed.error.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); bot.getLogger().log(this.getClass(), LogLevel.WARN, String.format("Error Getting Counting Statistics: %s", ex.getMessage())); throw new CompletionException(ex); @@ -75,33 +76,28 @@ private int getHighestGlobalRanking(Map globalCounti return ranking; } - private MessageEmbed getStatisticsEmbed(@Nullable CountingStatistics statistics, int highestRanking, int currentRanking, int numGuildsInRanking) { + private MessageEmbed getStatisticsEmbed(@Nullable CountingStatistics statistics, int highestRanking, int currentRanking, int numGuildsInRanking, I18N userBundle) { EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setTitle("Global Counting Statistics ➕"); + String title = userBundle.getString("command.counting-statistics.embed.stats.title"); + String description = userBundle.getString("command.counting-statistics.embed.stats.description") + .replace("{num_guilds}", String.valueOf(numGuildsInRanking)) + .replace("{highest}", String.valueOf(highestRanking)) + .replace("{current}", String.valueOf(currentRanking)); + + embedBuilder.setTitle(title); embedBuilder.setColor(Helper.getRandomColor()); StringBuilder sb = new StringBuilder(); - sb.append(String.format(""" - ***Highest Ranking***: %d/%d - *This is the ranking for your* ***highest*** *number.* - - ***Current Ranking***: %d/%d - *This is the ranking for your* ***current*** *number.* - """, - highestRanking, numGuildsInRanking, - currentRanking, numGuildsInRanking) - ); + sb.append(description); if (statistics != null) { - sb.append(String.format(""" - - Your highest number is **%d**! - Your current number is **%d**! This means your next number should be **%d**... - """, - statistics.getHighestCount(), - statistics.getCurrentCount(), statistics.getCurrentCount() + 1) - ); + String extendedDescription = userBundle.getString("command.counting-statistics.embed.stats.extended-description") + .replace("{highest}", String.valueOf(statistics.getHighestCount())) + .replace("{current}", String.valueOf(statistics.getCurrentCount())) + .replace("{next}", String.valueOf(statistics.getCurrentCount() + 1)); + + sb.append(extendedDescription); } embedBuilder.setDescription(sb.toString()); @@ -116,7 +112,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Get the server's global counting statistics!"; + return "command.counting-statistics.description"; } @Override From 69d39164f69761965374dea4cf14d5c77c4d32aa Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Sun, 29 Mar 2026 14:57:06 -0700 Subject: [PATCH 16/22] Added I18N for Roll Command --- .../i18n/src/main/resources/i18n/en/command/roll.yml | 5 +++++ .../cafebot/commands/games/RollCommand.java | 11 +++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/roll.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/roll.yml b/modules/i18n/src/main/resources/i18n/en/command/roll.yml new file mode 100644 index 000000000..23dfc52ff --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/roll.yml @@ -0,0 +1,5 @@ +description: "Roll dice!" +roll: "You rolled a **{number}**!" +arguments: + size: + description: "The size of the dice." diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java index c3ad12f60..d433b7de4 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/RollCommand.java @@ -25,7 +25,10 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { Optional sizeMapping = Optional.ofNullable(event.getOption("size")); int size = sizeMapping.map(OptionMapping::getAsInt).orElse(6); int roll = Helper.getRandomInteger(1, size + 1); - event.getHook().sendMessageEmbeds(Helper.descriptionEmbed(String.format("You rolled a **%d**!", roll))).queue(); + + String description = ctx.getGuildI18n().getString("command.roll.roll").replace("{number}", String.valueOf(roll)); + + event.getHook().sendMessageEmbeds(Helper.descriptionEmbed(description)).queue(); } @Override @@ -35,7 +38,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Roll a pair of dice!"; + return "command.roll.description"; } @Override @@ -46,8 +49,8 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.INTEGER, "size", "The size of the die.", false) - .setRequiredRange(1, 10000) + new OptionData(OptionType.INTEGER, "size", "command.roll.arguments.size.description", false) + .setRequiredRange(1, Integer.MAX_VALUE) }; } From a3444bf4ab335a22ab4d506874435d33886836b3 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 30 Mar 2026 21:23:06 -0700 Subject: [PATCH 17/22] Added I18N for TicTacToe Command --- .../resources/i18n/en/command/tictactoe.yml | 13 +++++++++ .../commands/games/TicTacToeCommand.java | 28 ++++++++++--------- 2 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/tictactoe.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/tictactoe.yml b/modules/i18n/src/main/resources/i18n/en/command/tictactoe.yml new file mode 100644 index 000000000..2b1c500ce --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/tictactoe.yml @@ -0,0 +1,13 @@ +description: "Play tic-tac-toe with someone!" +embeds: + bot: + title: "Cannot Play Against Bot" + description: "What do you think you're even doing? <:cafeBot_angry:1171726164092518441> I refuse to play against you." + consent: + title: "Waiting for Consent" + description: "{player1}, do you accept the challenge from {player2} for **{wager} cC** (cafeCoins)? Once accepted, games will be automatically closed after 24 hours." +arguments: + opponent: + description: "The person you want to play against." + wager: + description: "The amount you want to wager for this game." diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java index ce633cfac..3edc1ecc6 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/games/TicTacToeCommand.java @@ -33,10 +33,10 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { User opponent = event.getOption("opponent").getAsUser(); if (opponent.isBot()) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Cannot Play Against Bot", - "What do you think you're even doing? <:cafeBot_angry:1171726164092518441>" - )).queue(); + String title = ctx.getUserI18n().getString("command.tictactoe.embeds.bot.title"); + String description = ctx.getUserI18n().getString("command.tictactoe.embeds.bot.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); return; } @@ -44,12 +44,14 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String playerId = player.getId(); String opponentId = opponent.getId(); - event.getHook().sendMessageEmbeds( - Helper.successEmbed( - "Waiting for Consent", - String.format("%s, do you accept the challenge from %s for **%d cC** (cafeCoins)? Once accepted, games will be automatically closed after 24 hours.", opponent.getAsMention(), player.getAsMention(), wager) - ) - ) + String title = ctx.getUserI18n().getString("command.tictactoe.embeds.consent.title"); + String description = ctx.getUserI18n().getString("command.tictactoe.embeds.consent.description") + .replace("{player1}", opponent.getAsMention()) + .replace("{player2}", player.getAsMention()) + .replace("{wager}", String.valueOf(wager)); + + event.getHook() + .sendMessageEmbeds(Helper.successEmbed(title, description)) .addComponents(ActionRow.of(Button.primary(String.format(TicTacToeHandler.CONSENT_BUTTON_ID_FULL, playerId, opponentId, wager), Emoji.fromUnicode("🤝")))) .queue(); } @@ -61,7 +63,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Play tic-tac-toe with someone!"; + return "command.tictactoe.description"; } @Override @@ -72,8 +74,8 @@ public CommandCategory getCategory() { @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "opponent", "The person you want to play against.", true), - new OptionData(OptionType.INTEGER, "wager", "The amount you want to wager for this game.", false) + new OptionData(OptionType.USER, "opponent", "command.tictactoe.arguments.opponent.description", true), + new OptionData(OptionType.INTEGER, "wager", "command.tictactoe.arguments.wager.description", false) .setMinValue(1) }; } From 27150b8fd3a3f70e04e4b6cb6533ea106eed367f Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 30 Mar 2026 22:06:44 -0700 Subject: [PATCH 18/22] Added I18N for Calendar Commands --- .../resources/i18n/en/command/calendar.yml | 62 +++++++++++++++++++ .../src/main/resources/i18n/en/generic.yml | 4 ++ .../calendar/CalendarAddSubCommand.java | 60 +++++++++--------- .../generic/calendar/CalendarCommand.java | 2 +- .../calendar/CalendarDeleteSubCommand.java | 27 +++++--- .../calendar/CalendarGetSubCommand.java | 16 +++-- .../calendar/CalendarListSubCommand.java | 16 +++-- .../cafebot/utility/helper/Helper.java | 19 ++++++ 8 files changed, 155 insertions(+), 51 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/calendar.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/calendar.yml b/modules/i18n/src/main/resources/i18n/en/command/calendar.yml new file mode 100644 index 000000000..d330e0ddf --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/calendar.yml @@ -0,0 +1,62 @@ +description: "All things to do with calendars!" + +subcommands: + add: + description: "Add a calendar!" + success: + title: "Calendar Added!" + description: "Successfully added your calendar! Use `/calender get` to preview your calendar!" + error: + count: + title: "Too Many Calendars!" + description: "You have added too many calendars... You can only have 3 active ones!" + url: + title: "Invalid URL" + description: "You must use a *valid* calendar URL!" + server: + title: "Invalid Command!" + description: "In order to set a server calendar, you need to use this command in a Discord server!" + generic: + title: "Error Adding Calendar" + description: "I'm sorry... I don't know *what* went wrong..." + arguments: + type: + description: "The type of calendar you want to add." + name: + description: "The calendar name!" + url: + description: "The calendar url! Make sure it ends in \".ics\"!" + + get: + description: "Get your calendars!" + error: + title: "Error Getting Calendar" + description: "I... couldn't find the calendar... is this an error??" + arguments: + id: + description: "The ID of the calendar you want to view!" + timezone: + description: "The timezone you want the calendar in." + + list: + description: "List all of the calendars for a specific user!" + success: + title: "Calendar List" + footer: "You can view guild calendars by typing /calendar get!" + empty: "No calendars found!" + arguments: + user: + description: "The user you want to see the calendar of!" + + delete: + description: "Delete a calendar!" + error: + exploit: + title: "You're joking right?" + description: "Oh well.. I thought you were smarter than that... since you tried to use an exploit I made sure to talk to my boss! <:cafeBot_angry:1171726164092518441>" + success: + title: "Calendar Deleted!" + description: "We won't see that pesky calendar any longer!" + arguments: + id: + description: "The ID of the calendar you want to delete!" diff --git a/modules/i18n/src/main/resources/i18n/en/generic.yml b/modules/i18n/src/main/resources/i18n/en/generic.yml index c1fe8f7f9..e5a3eff77 100644 --- a/modules/i18n/src/main/resources/i18n/en/generic.yml +++ b/modules/i18n/src/main/resources/i18n/en/generic.yml @@ -9,6 +9,10 @@ error: ``` {uncaught_error} ``` + permission: + title: "No Permission" + message: "What are you doing back here?? Get **out**!" + generic: title: "Error" message: "There was an error! I... I'll try to let me boss know... please try again later and make a report on the GitHub if it is not fixed soon with `/bug`!" diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java index 34fc5132a..3e8078130 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarAddSubCommand.java @@ -4,10 +4,12 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.OwnerType; import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.api.wrapper.type.calendar.PartialCalendar; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; import com.beanbeanjuice.cafebot.utility.helper.Helper; +import com.beanbeanjuice.cafebot.utility.logging.LogLevel; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -29,15 +31,15 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String url = event.getOption("url").getAsString(); if (type == OwnerType.GUILD && !event.isFromGuild()) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Invalid Command!", - "In order to set a server calendar, you need to use this command in a Discord server!" - )).queue(); + String title = ctx.getUserI18n().getString("command.calendar.subcommands.add.error.server.title"); + String description = ctx.getUserI18n().getString("command.calendar.subcommands.add.error.server.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); return; } if (type == OwnerType.GUILD && event.isFromGuild() && event.getMember() != null && !event.getMember().hasPermission(Permission.MANAGE_SERVER)) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed("No Permission", "What are you doing back here?? Get **out**!")).queue(); + event.getHook().sendMessageEmbeds(Helper.noPermissionEmbed(ctx.getUserI18n())).queue(); return; } @@ -45,41 +47,43 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { PartialCalendar partialCalendar = new PartialCalendar(type, ownerId, name, url); bot.getCafeAPI().getCalendarApi().createCalendar(partialCalendar).thenAccept((calendar) -> { - event.getHook().sendMessageEmbeds(Helper.successEmbed( - "Calendar Added!", - "Successfully added your calendar! Use `/calender get` to preview your calendar!" - )).queue(); + String title = ctx.getUserI18n().getString("command.calendar.subcommands.add.success.title"); + String description = ctx.getUserI18n().getString("command.calendar.subcommands.add.success.description"); + + event.getHook().sendMessageEmbeds(Helper.successEmbed(title, description)).queue(); }).exceptionally((ex) -> { - handleError(ex, event); + handleError(ex, event, ctx.getUserI18n()); throw new CompletionException(ex.getCause()); }); } - private void handleError(Throwable ex, SlashCommandInteractionEvent event) { + private void handleError(Throwable ex, SlashCommandInteractionEvent event, I18N bundle) { if (ex.getCause() instanceof ApiRequestException apiRequestException) { JsonNode errorBody = apiRequestException.getBody().get("error"); if (errorBody.has("calendars")) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Too Many Calendars!", - "You have added too many calendars... You can only have 3 active ones!" - )).queue(); + String title = bundle.getString("command.calendar.subcommands.add.error.count.title"); + String description = bundle.getString("command.calendar.subcommands.add.error.count.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); return; } if (errorBody.has("url")) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Invalid URL", - "You must use a *valid* calendar URL!" - )).queue(); + String title = bundle.getString("command.calendar.subcommands.add.error.count.title"); + String description = bundle.getString("command.calendar.subcommands.add.error.count.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); return; } } - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Adding Calendar", - "I'm sorry... I don't know *what* went wrong..." - )).queue(); + String title = bundle.getString("command.calendar.subcommands.add.error.generic.title"); + String description = bundle.getString("command.calendar.subcommands.add.error.generic.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); + + bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Adding Calendar", ex); } @Override @@ -89,17 +93,17 @@ public String getName() { @Override public String getDescriptionPath() { - return "Add a calendar!"; + return "command.calendar.subcommands.add.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "type", "The type of calendar you want to add.", true) - .addChoice("User (The calendar belongs to you)", "DISCORD_USER") + new OptionData(OptionType.STRING, "type", "command.calendar.subcommands.add.arguments.type.description", true) + .addChoice("User (The calendar belongs to you)", "DISCORD_USER") // TODO: Handle I18N for choices too. .addChoice("Server (The calendar belongs to the server)", "GUILD"), - new OptionData(OptionType.STRING, "name", "The calendar name!", true), - new OptionData(OptionType.STRING, "url", "The calendar url! Make sure it ends in \".ics\"!", true), + new OptionData(OptionType.STRING, "name", "command.calendar.subcommands.add.arguments.name.description", true), + new OptionData(OptionType.STRING, "url", "command.calendar.subcommands.add.arguments.url.description", true), }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java index 469fc91d8..81414601b 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarCommand.java @@ -20,7 +20,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "All things to do with calendars!"; + return "command.calendar.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java index 83ced5f15..6cdaa05aa 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarDeleteSubCommand.java @@ -4,6 +4,7 @@ import com.beanbeanjuice.cafebot.api.wrapper.api.enums.OwnerType; import com.beanbeanjuice.cafebot.api.wrapper.api.exception.ApiRequestException; import com.beanbeanjuice.cafebot.api.wrapper.type.calendar.Calendar; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; @@ -33,35 +34,41 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getCafeAPI().getCalendarApi().getCalendar(calendarId).thenAccept(calendar -> { if (calendar.getOwnerType() == OwnerType.GUILD && event.isFromGuild() && event.getMember() != null && !event.getMember().hasPermission(Permission.MANAGE_SERVER)) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed("No Permission", "What are you doing back here?? Get **out**!")).queue(); + event.getHook().sendMessageEmbeds(Helper.noPermissionEmbed(ctx.getUserI18n())).queue(); return; } String callerId = (calendar.getOwnerType() == OwnerType.GUILD && event.isFromGuild()) ? event.getGuild().getId() : event.getUser().getId(); - bot.getCafeAPI().getCalendarApi().deleteCalendar(calendarId, callerId).thenAccept(result -> { - event.getHook().sendMessageEmbeds(Helper.successEmbed("Calendar Deleted!", "We won't see that pesky calendar any longer!")).queue(); + bot.getCafeAPI().getCalendarApi().deleteCalendar(calendarId, callerId).thenAccept(_ -> { + String title = ctx.getUserI18n().getString("command.calendar.subcommands.delete.success.title"); + String description = ctx.getUserI18n().getString("command.calendar.subcommands.delete.success.description"); + + event.getHook().sendMessageEmbeds(Helper.successEmbed(title, description)).queue(); }).exceptionally((ex) -> { - handleError(ex, event); + handleError(ex, event, ctx.getUserI18n()); throw new CompletionException(ex.getCause()); }); }).exceptionally((ex) -> { - handleError(ex, event); + handleError(ex, event, ctx.getUserI18n()); throw new CompletionException(ex.getCause()); }); } - private void handleError(Throwable ex, SlashCommandInteractionEvent event) { + private void handleError(Throwable ex, SlashCommandInteractionEvent event, I18N userBundle) { if (ex.getCause() instanceof ApiRequestException apiRequestException) { JsonNode errorNode = apiRequestException.getBody().get("error"); if (errorNode.has("callerId")) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed("You're joking right?", "Oh well.. I thought you were smarter than that... since you tried to use an exploit I made sure to talk to my boss! <:cafeBot_angry:1171726164092518441>")).queue(); + String title = userBundle.getString("command.calendar.subcommands.delete.error.exploit.title"); + String description = userBundle.getString("command.calendar.subcommands.delete.error.exploit.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); return; } } - event.getHook().sendMessageEmbeds(Helper.errorEmbed("Error", "Could not delete the calendar!")).queue(); + event.getHook().sendMessageEmbeds(Helper.defaultErrorEmbed(userBundle)).queue(); } @Override @@ -71,13 +78,13 @@ public String getName() { @Override public String getDescriptionPath() { - return "Delete a calendar!"; + return "command.calendar.subcommands.delete.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "id", "The ID of the calendar you want to delete!", true, true), + new OptionData(OptionType.STRING, "id", "command.calendar.subcommands.delete.arguments.id.description", true, true), }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java index db625f876..d175756b9 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarGetSubCommand.java @@ -2,6 +2,7 @@ import com.beanbeanjuice.cafebot.CafeBot; import com.beanbeanjuice.cafebot.api.wrapper.type.calendar.Calendar; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; @@ -34,13 +35,16 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { String message = CalendarHandler.getCalendarMessage(calendar.getUrl(), zoneId); event.getHook().sendMessage(message).queue(); }).exceptionally((ex) -> { - handleError(ex, event); + handleError(ex, event, ctx.getUserI18n()); throw new CompletionException(ex.getCause()); }); } - private void handleError(Throwable ex, SlashCommandInteractionEvent event) { - event.getHook().sendMessageEmbeds(Helper.errorEmbed("Error Getting Calendar", "I... couldn't find the calendar... is this an error??")).queue(); + private void handleError(Throwable ex, SlashCommandInteractionEvent event, I18N userBundle) { + String title = userBundle.getString("command.calendar.subcommands.get.error.title"); + String description = userBundle.getString("command.calendar.subcommands.get.error.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); bot.getLogger().log(this.getClass(), LogLevel.WARN, ex.getMessage(), true, false, ex); } @@ -51,14 +55,14 @@ public String getName() { @Override public String getDescriptionPath() { - return "Get your calendars!"; + return "command.calendar.subcommands.get.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "id", "The ID of the calendar you want to view!", true, true), - new OptionData(OptionType.STRING, "timezone", "The timezone you want the calendar in.", true, true) + new OptionData(OptionType.STRING, "id", "command.calendar.subcommands.get.arguments.id.description", true, true), + new OptionData(OptionType.STRING, "timezone", "command.calendar.subcommands.get.arguments.timezone.description", true, true) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java index bf632013a..e10fabe1e 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/calendar/CalendarListSubCommand.java @@ -27,17 +27,21 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { User user = Optional.ofNullable(event.getOption("user")).map(OptionMapping::getAsUser).orElse(event.getUser()); bot.getCafeAPI().getCalendarApi().getUserCalendars(user.getId()).thenAccept(calendars -> { + String title = ctx.getUserI18n().getString("command.calendar.subcommands.list.success.title"); + String footer = ctx.getUserI18n().getString("command.calendar.subcommands.list.success.footer"); + String empty = ctx.getUserI18n().getString("command.calendar.subcommands.list.success.empty"); + EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("Calendar List"); - eb.setFooter("You can view guild calendars by typing /calendar get!"); + eb.setTitle(title); + eb.setFooter(footer); eb.setColor(Helper.getRandomColor()); eb.setDescription(calendars.stream().map((calendar) -> String.format("**%s** (ID: %s)", calendar.getName(), calendar.getId())).collect(Collectors.joining("\n"))); - if (calendars.isEmpty()) eb.appendDescription("No calendars found!"); + if (calendars.isEmpty()) eb.appendDescription(empty); event.getHook().sendMessageEmbeds(eb.build()).queue(); }).exceptionally((ex) -> { - event.getHook().sendMessageEmbeds(Helper.errorEmbed("Error Getting Calendars", "I.. don't know what went wrong...")).queue(); + event.getHook().sendMessageEmbeds(Helper.defaultErrorEmbed(ctx.getUserI18n())).queue(); throw new CompletionException(ex.getCause()); }); } @@ -49,13 +53,13 @@ public String getName() { @Override public String getDescriptionPath() { - return "List all of the calendars for a specific user!"; + return "command.calendar.subcommands.list.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.USER, "user", "The user you want to see the calendar of!", false) + new OptionData(OptionType.USER, "user", "command.calendar.subcommands.list.arguments.user.description", false) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java index aecf40e23..71ae936e6 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java +++ b/src/main/java/com/beanbeanjuice/cafebot/utility/helper/Helper.java @@ -115,6 +115,17 @@ public static Integer stringToPositiveInteger(@NotNull String timeString) { } } + public static MessageEmbed noPermissionEmbed(I18N i18n) { + String title = i18n.getString("generic.error.permission.title"); + String description = i18n.getString("generic.error.permission.message"); + + return new EmbedBuilder() + .setTitle(title) + .setDescription(description) + .setColor(Color.RED) + .build(); + } + public static MessageEmbed uncaughtErrorEmbed(I18N i18n, String error) { String title = i18n.getString("generic.error.uncaught.title"); String description = i18n.getString("generic.error.uncaught.message") @@ -135,10 +146,18 @@ public static MessageEmbed errorEmbed(@NotNull String title, @NotNull String des .build(); } + @Deprecated(since = "4.4.1") public static MessageEmbed defaultErrorEmbed() { return errorEmbed("Uncaught Error", "There was an error... and I don't know what's wrong... if this persists please submit a support ticket!"); } + public static MessageEmbed defaultErrorEmbed(I18N i18n) { + String title = i18n.getString("generic.error.generic.title"); + String description = i18n.getString("generic.error.generic.description"); + + return errorEmbed(title, description); + } + public static MessageEmbed successEmbed(final String title, final String description) { return new EmbedBuilder() .setTitle(title) From 5e0cbe970e019b04db5bc6dba3fd5f6cd411c43b Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Mon, 30 Mar 2026 22:26:46 -0700 Subject: [PATCH 19/22] Added I18N for Twitch Commands --- .../main/resources/i18n/en/command/twitch.yml | 35 +++++++++++++++++++ .../generic/twitch/TwitchAddSubCommand.java | 23 ++++++------ .../generic/twitch/TwitchCommand.java | 2 +- .../generic/twitch/TwitchListSubCommand.java | 27 ++++++-------- .../twitch/TwitchRemoveSubCommand.java | 20 +++++------ 5 files changed, 69 insertions(+), 38 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/twitch.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/twitch.yml b/modules/i18n/src/main/resources/i18n/en/command/twitch.yml new file mode 100644 index 000000000..a55738fc1 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/twitch.yml @@ -0,0 +1,35 @@ +description: "Add or remove channels/notification channels." + +subcommands: + add: + description: "Add a twitch channel to get live notifications for!" + success: + title: "Added Channel" + description: "Successfully added **{channel}**." + error: + title: "Error Adding Channel" + description: "There was an error adding **{channel}**." + arguments: + username: + description: "Their twitch username." + + list: + description: "Get a list of all twitch channels for this server!" + success: + title: "Twitch Channels" + description: | + These are the following channels that have been added for this server: + + {channels} + + remove: + description: "Remove a twitch channel!" + success: + title: "Twitch Channel Removed" + description: "They were successfully removed. You should no longer receive live notifications from them." + error: + title: "Error Removing Twitch Channel" + description: "There was an error removing them, you may still receive live notifications for their channel." + arguments: + username: + description: "The username you want to remove." diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java index 18e94c9ee..c32f1364f 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchAddSubCommand.java @@ -23,16 +23,19 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getCafeAPI().getTwitchChannelApi().addChannel(event.getGuild().getId(), username) .thenRun(() -> { bot.getTwitchHandler().addStream(username); - event.getHook().sendMessageEmbeds(Helper.successEmbed( - "Added Channel", - String.format("Successfully added **%s**.", username) - )).queue(); + + String title = ctx.getUserI18n().getString("command.twitch.subcommands.add.success.title"); + String description = ctx.getUserI18n().getString("command.twitch.subcommands.add.success.description") + .replace("{channel}", username); + + event.getHook().sendMessageEmbeds(Helper.successEmbed(title, description)).queue(); }) .exceptionally((ex) -> { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Adding Channel", - String.format("There was an error adding **%s**.", username) - )).queue(); + String title = ctx.getUserI18n().getString("command.twitch.subcommands.add.error.title"); + String description = ctx.getUserI18n().getString("command.twitch.subcommands.add.error.description") + .replace("{channel}", username); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Adding Twitch Channel: " + ex.getMessage()); return null; @@ -46,13 +49,13 @@ public String getName() { @Override public String getDescriptionPath() { - return "Add a twitch channel to get live notifications for!"; + return "command.twitch.subcommands.add.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "username", "Their twitch username.", true) + new OptionData(OptionType.STRING, "username", "command.twitch.subcommands.add.arguments.username.description", true) }; } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java index 2dc42dcb3..2df245895 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchCommand.java @@ -17,7 +17,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Add or remove channels/notification channels."; + return "command.twitch.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java index c8e9e6795..14c75c857 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchListSubCommand.java @@ -1,6 +1,7 @@ package com.beanbeanjuice.cafebot.commands.generic.twitch; import com.beanbeanjuice.cafebot.CafeBot; +import com.beanbeanjuice.cafebot.i18n.I18N; import com.beanbeanjuice.cafebot.utility.commands.Command; import com.beanbeanjuice.cafebot.utility.commands.CommandContext; import com.beanbeanjuice.cafebot.utility.commands.ISubCommand; @@ -21,32 +22,24 @@ public TwitchListSubCommand(CafeBot cafeBot) { @Override public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getCafeAPI().getTwitchChannelApi().getChannels(event.getGuild().getId()) - .thenAccept((channels) -> event.getHook().sendMessageEmbeds(twitchChannelsEmbed(channels)).queue()) + .thenAccept((channels) -> event.getHook().sendMessageEmbeds(twitchChannelsEmbed(channels, ctx.getUserI18n())).queue()) .exceptionally((ex) -> { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Getting Twitch Channels", - "There was an error getting twitch channels for this server." - )).queue(); + event.getHook().sendMessageEmbeds(Helper.defaultErrorEmbed(ctx.getUserI18n())).queue(); bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Getting Twitch Channels: " + ex.getMessage()); return null; }); } - private MessageEmbed twitchChannelsEmbed(List channels) { - EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setTitle("Twitch Channels"); + private MessageEmbed twitchChannelsEmbed(List channels, I18N userBundle) { + String title = userBundle.getString("command.twitch.subcommands.list.success.title"); String channelsString = String.join("\n", channels); + String description = userBundle.getString("command.twitch.subcommands.list.success.description") + .replace("{channels}", channelsString); - String description = String.format( - """ - These are the following channels that have been added for this server: - - %s - """, channelsString - ); - + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle(title); embedBuilder.setDescription(description); embedBuilder.setColor(Helper.getRandomColor()); return embedBuilder.build(); @@ -59,7 +52,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Get a list of all twitch channels for this server!"; + return "command.twitch.subcommands.list.description"; } } diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java index 551aa3fbf..b636ac676 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/twitch/TwitchRemoveSubCommand.java @@ -22,16 +22,16 @@ public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { bot.getCafeAPI().getTwitchChannelApi().deleteChannel(event.getGuild().getId(), username) .thenRun(() -> { - event.getHook().sendMessageEmbeds(Helper.successEmbed( - "Twitch Channel Removed", - "They were successfully removed. You should no longer receive live notifications from them." - )).queue(); + String title = ctx.getUserI18n().getString("command.twitch.subcommands.remove.success.title"); + String description = ctx.getUserI18n().getString("command.twitch.subcommands.remove.success.description"); + + event.getHook().sendMessageEmbeds(Helper.successEmbed(title, description)).queue(); }) .exceptionally((ex) -> { - event.getHook().sendMessageEmbeds(Helper.errorEmbed( - "Error Removing Twitch Channel", - "There was an error removing them, you may still receive live notifications for their channel." - )).queue(); + String title = ctx.getUserI18n().getString("command.twitch.subcommands.remove.error.title"); + String description = ctx.getUserI18n().getString("command.twitch.subcommands.remove.error.description"); + + event.getHook().sendMessageEmbeds(Helper.errorEmbed(title, description)).queue(); bot.getLogger().log(this.getClass(), LogLevel.WARN, "Error Deleting Twitch Channel: " + ex.getMessage()); return null; @@ -45,13 +45,13 @@ public String getName() { @Override public String getDescriptionPath() { - return "Remove a twitch channel!"; + return "command.twitch.subcommands.remove.description"; } @Override public OptionData[] getOptions() { return new OptionData[] { - new OptionData(OptionType.STRING, "username", "The username you want to remove.", true) + new OptionData(OptionType.STRING, "username", "command.twitch.subcommands.remove.arguments.username.description", true) }; } From b68c6234a3a8fb691b07d9487add1631fd4f8299 Mon Sep 17 00:00:00 2001 From: beanbeanjuice Date: Tue, 31 Mar 2026 22:35:42 -0700 Subject: [PATCH 20/22] Added I18N for Several Commands --- .../resources/i18n/en/command/bot-donate.yml | 8 ++++++++ .../resources/i18n/en/command/bot-invite.yml | 1 + .../resources/i18n/en/command/bot-upvote.yml | 8 ++++++++ .../src/main/resources/i18n/en/command/bug.yml | 3 +++ .../java/com/beanbeanjuice/cafebot/CafeBot.java | 14 ++++++++++---- .../commands/generic/BotDonateCommand.java | 16 +++++----------- .../commands/generic/BotInviteCommand.java | 2 +- .../commands/generic/BotUpvoteCommand.java | 14 +++++--------- .../commands/generic/BugReportCommand.java | 6 ++++-- 9 files changed, 45 insertions(+), 27 deletions(-) create mode 100644 modules/i18n/src/main/resources/i18n/en/command/bot-donate.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/command/bot-invite.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/command/bot-upvote.yml create mode 100644 modules/i18n/src/main/resources/i18n/en/command/bug.yml diff --git a/modules/i18n/src/main/resources/i18n/en/command/bot-donate.yml b/modules/i18n/src/main/resources/i18n/en/command/bot-donate.yml new file mode 100644 index 000000000..8aad1a588 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/bot-donate.yml @@ -0,0 +1,8 @@ +description: "Donate to keep the bot up!" + +success: + title: "Donations!" + description: | + Donations are absolutely optional, but they help keep me alive! + You can donate [here](https://buymeacoffee.com/beanbeanjuice)! + Thank you so much... diff --git a/modules/i18n/src/main/resources/i18n/en/command/bot-invite.yml b/modules/i18n/src/main/resources/i18n/en/command/bot-invite.yml new file mode 100644 index 000000000..f259bcdfd --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/bot-invite.yml @@ -0,0 +1 @@ +description: "Want to invite this bot to a server? Use this command!" diff --git a/modules/i18n/src/main/resources/i18n/en/command/bot-upvote.yml b/modules/i18n/src/main/resources/i18n/en/command/bot-upvote.yml new file mode 100644 index 000000000..bfced85c7 --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/bot-upvote.yml @@ -0,0 +1,8 @@ +description: "Upvote the bot!" + +embed: + title: "Voting List" + description: | + Upvoting helps me serve coffee to more people! + If you're enjoying my service, please consider doing so! + Any and all support is welcome! diff --git a/modules/i18n/src/main/resources/i18n/en/command/bug.yml b/modules/i18n/src/main/resources/i18n/en/command/bug.yml new file mode 100644 index 000000000..c0d3fd99d --- /dev/null +++ b/modules/i18n/src/main/resources/i18n/en/command/bug.yml @@ -0,0 +1,3 @@ +description: "A bug?!?! Where?!?!" +button: + label: "Bug Report" diff --git a/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java b/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java index d298db89d..de208d3ec 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java +++ b/src/main/java/com/beanbeanjuice/cafebot/CafeBot.java @@ -94,6 +94,15 @@ public class CafeBot { @Getter private static final AtomicInteger commandsRun = new AtomicInteger(0); // Atomic since can run across multiple shards. @Getter private final String discordAvatarUrl = "https://cdn.beanbeanjuice.com/images/cafeBot/cafeBot.gif"; + private void checkApiConnection() { + try { + Greeting greeting = cafeAPI.getGreetingApi().getAdminHello().get(); + if (greeting == null || greeting.getMessage() == null) throw new Exception("Greeting not found!"); + } catch (Exception e) { + this.logger.log(CafeBot.class, LogLevel.ERROR, "Connection to the API is invalid...", true, true); + } + } + public CafeBot() throws InterruptedException, ExecutionException { this.logger = new LogManager( this, @@ -110,10 +119,7 @@ public CafeBot() throws InterruptedException, ExecutionException { this.logger.addWebhookURL(EnvironmentVariable.CAFEBOT_GUILD_WEBHOOK_URL.getSystemVariable()); this.logger.log(CafeBot.class, LogLevel.OKAY, "Starting bot!", true, false); - Greeting greeting = cafeAPI.getGreetingApi().getAdminHello().get(); - if (greeting == null || greeting.getMessage() == null) { - this.logger.log(CafeBot.class, LogLevel.ERROR, "Connection to the API is invalid...", true, true); - } + this.checkApiConnection(); this.shardManager = DefaultShardManagerBuilder .createDefault(EnvironmentVariable.CAFEBOT_TOKEN.getSystemVariable()) diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java index 5e3ee183b..35a26f968 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotDonateCommand.java @@ -17,16 +17,10 @@ public BotDonateCommand(final CafeBot cafeBot) { @Override public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { - event.getHook().sendMessageEmbeds( - Helper.successEmbed( - "Donations!", - """ - Donations are absolutely optional, but they help keep me alive! \ - You can donate [here](https://buymeacoffee.com/beanbeanjuice)! \ - Thank you so much... - """ - ) - ).queue(); + String title = ctx.getDefaultBundle().getString("command.bot-donate.success.title"); + String description = ctx.getDefaultBundle().getString("command.bot-donate.success.description"); + + event.getHook().sendMessageEmbeds(Helper.successEmbed(title, description)).queue(); } @Override @@ -36,7 +30,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Donate to keep the bot up!"; + return "command.bot-donate.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java index d6376c018..d23f588ea 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotInviteCommand.java @@ -35,7 +35,7 @@ public String getName() { @Override public String getDescriptionPath() { - return "Want to invite this bot to a server? Use this command!"; + return "command.bot-invite.description"; } @Override diff --git a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java index ab6a76b09..b9c40f513 100644 --- a/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java +++ b/src/main/java/com/beanbeanjuice/cafebot/commands/generic/BotUpvoteCommand.java @@ -22,14 +22,10 @@ public BotUpvoteCommand(final CafeBot cafeBot) { @Override public void handle(SlashCommandInteractionEvent event, CommandContext ctx) { - event.getHook().sendMessageEmbeds(Helper.successEmbed( - "Voting List", - """ - Upvoting helps me serve coffee to more people! \ - If you're enjoying my service, please consider doing so! \ - Any and all support is welcome! - """ - )).setComponents(ActionRow.of(getButtons())).queue(); + String title = ctx.getUserI18n().getString("command.bot-upvote.embed.title"); + String description = ctx.getUserI18n().getString("command.bot-upvote.embed.description"); + + event.getHook().sendMessageEmbeds(Helper.successEmbed(title, description)).setComponents(ActionRow.of(getButtons())).queue(); } private ArrayList