diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/Bot.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/Bot.java index cab08afa..0b27d5ef 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/Bot.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/Bot.java @@ -68,6 +68,7 @@ public class Bot { @Getter private ExploitsPlugin exploits; @Getter private FilterPlugin filter; @Getter private CommandSuggestionPlugin commandSuggestion; + @Getter private MailPlugin mail; public Bot (Configuration.BotOption botOption, List bots, Configuration config) { this.host = botOption.host; @@ -112,6 +113,7 @@ public class Bot { this.exploits = new ExploitsPlugin(this); this.filter = new FilterPlugin(this); this.commandSuggestion = new CommandSuggestionPlugin(this); + this.mail = new MailPlugin(this); reconnect(); } diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/CommandContext.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/CommandContext.java index a2e52dc6..32a5fb44 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/CommandContext.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/CommandContext.java @@ -15,12 +15,15 @@ public class CommandContext { @Getter private final String hash; @Getter private final String ownerHash; - public CommandContext(Bot bot, String prefix, MutablePlayerListEntry sender, String hash, String ownerHash) { + @Getter private final boolean inGame; + + public CommandContext(Bot bot, String prefix, MutablePlayerListEntry sender, String hash, String ownerHash, boolean inGame) { this.bot = bot; this.prefix = prefix; this.sender = sender; this.hash = hash; this.ownerHash = ownerHash; + this.inGame = inGame; } public Component displayName () { return Component.empty(); } diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/ConsoleCommandContext.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/ConsoleCommandContext.java index 249b778c..62ad8eff 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/ConsoleCommandContext.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/ConsoleCommandContext.java @@ -9,7 +9,7 @@ public class ConsoleCommandContext extends CommandContext { private final Bot bot; public ConsoleCommandContext (Bot bot, String prefix) { - super(bot, prefix, bot.players().getBotEntry() /* real */, null, null); + super(bot, prefix, bot.players().getBotEntry() /* real */, null, null, false); this.bot = bot; } diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/DiscordCommandContext.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/DiscordCommandContext.java index 0f95b9ce..8bc0f1c3 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/DiscordCommandContext.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/DiscordCommandContext.java @@ -37,7 +37,8 @@ public class DiscordCommandContext extends CommandContext { true ), hash, - ownerHash + ownerHash, + false ); this.bot = bot; this.event = event; diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/PlayerCommandContext.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/PlayerCommandContext.java index 98731f6a..80358eaf 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/command/PlayerCommandContext.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/command/PlayerCommandContext.java @@ -13,7 +13,7 @@ public class PlayerCommandContext extends CommandContext { private final Bot bot; public PlayerCommandContext (Bot bot, String playerName, String prefix, String selector, MutablePlayerListEntry sender, String hash, String ownerHash) { - super(bot, prefix, sender, hash, ownerHash); + super(bot, prefix, sender, hash, ownerHash, true); this.bot = bot; this.playerName = playerName; this.selector = selector; diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/commands/MailCommand.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/commands/MailCommand.java new file mode 100644 index 00000000..dab919f7 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/commands/MailCommand.java @@ -0,0 +1,183 @@ +package land.chipmunk.chayapak.chomens_bot.commands; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import land.chipmunk.chayapak.chomens_bot.Bot; +import land.chipmunk.chayapak.chomens_bot.command.Command; +import land.chipmunk.chayapak.chomens_bot.command.CommandContext; +import land.chipmunk.chayapak.chomens_bot.data.Mail; +import land.chipmunk.chayapak.chomens_bot.data.chat.MutablePlayerListEntry; +import land.chipmunk.chayapak.chomens_bot.util.ColorUtilities; +import land.chipmunk.chayapak.chomens_bot.util.ComponentUtilities; +import land.chipmunk.chayapak.chomens_bot.util.UUIDUtilities; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class MailCommand implements Command { + public String name() { return "mail"; } + + public String description() { + return "Sends a mail"; + } + + public List usage() { + final List usages = new ArrayList<>(); + usages.add("send <{message}>"); + usages.add("sendselecteditem "); + usages.add("read"); + + return usages; + } + + public List alias() { + final List aliases = new ArrayList<>(); + aliases.add(""); + + return aliases; + } + + public int trustLevel() { + return 0; + } + + public Component execute(CommandContext context, String[] args, String[] fullArgs) { + final Bot bot = context.bot(); + + final MutablePlayerListEntry sender = context.sender(); + + switch (args[0]) { + case "send" -> { + bot.mail().send( + new Mail( + sender.profile().getName(), + args[1], + DateTime.now(), + String.join(" ", Arrays.copyOfRange(args, 2, args.length)) + ) + ); + + return Component.text("Mail sent!").color(ColorUtilities.getColorByString(bot.config().colorPalette().defaultColor())); + } + case "sendselecteditem" -> { + final CompletableFuture future = bot.core().runTracked( + "minecraft:data get entity " + + UUIDUtilities.selector(sender.profile().getId()) + + " SelectedItem.tag.message" + ); + + if (future == null) return Component.text("There was an error while sending your mail").color(NamedTextColor.RED); + + future.thenApply(tags -> { + if (!tags.contains("LastOutput") || !(tags.get("LastOutput") instanceof StringTag)) return tags; + + final StringTag lastOutput = tags.get("LastOutput"); + + final Component output = GsonComponentSerializer.gson().deserialize(lastOutput.getValue()); + + final List children = output.children(); + + // too lazy to use translation,.,.,. + final String parsed = ComponentUtilities.stringify(children.get(0)); + + if (parsed.startsWith("Found no elements matching ")) { + context.sendOutput(Component.text("Player has no `message` NBT tag in the selected item").color(NamedTextColor.RED)); + return tags; + } + + final String value = ComponentUtilities.stringify(((TranslatableComponent) children.get(0)).args().get(1)); + + if (!value.startsWith("\"") && !value.endsWith("\"") && !value.startsWith("'") && !value.endsWith("'")) { + context.sendOutput(Component.text("`message` NBT is not a string").color(NamedTextColor.RED)); + return tags; + } + + bot.mail().send( + new Mail( + sender.profile().getName(), + args[1], + DateTime.now(), + value.substring(1).substring(0, value.length() - 2) + ) + ); + + context.sendOutput( + Component.text("Mail sent!").color(ColorUtilities.getColorByString(bot.config().colorPalette().defaultColor())) + ); + + return tags; + }); + + return null; + } + case "read" -> { + int senderMailSize = 0; + for (Mail ignored : bot.mail().mails()) senderMailSize++; + + if (senderMailSize == 0) return Component.text("You have no new mails").color(NamedTextColor.RED); + + final List mailsComponent = new ArrayList<>(); + + int i = 1; + for (Mail mail : bot.mail().mails()) { + if (!mail.sentTo().equals(sender.profile().getName())) continue; + + final DateTimeFormatter formatter = DateTimeFormat.forPattern("MMMM d, YYYY, hh:mm:ss a Z"); + final String formattedTime = formatter.print(mail.timeSent()); + + mailsComponent.add( + Component.translatable( + """ + %s %s Send by: %s At: %s + Contents: + %s""", + Component.text(i).color(ColorUtilities.getColorByString(bot.config().colorPalette().number())), + Component.text("-").color(NamedTextColor.DARK_GRAY), + + Component.text(mail.sentBy()).color(ColorUtilities.getColorByString(bot.config().colorPalette().username())), + Component.text(formattedTime).color(ColorUtilities.getColorByString(bot.config().colorPalette().string())), + Component.text(mail.contents()).color(NamedTextColor.WHITE) + ).color(NamedTextColor.GREEN) + ); + + i++; + } + + final Component component = Component.empty() + .append(Component.text("Mails ").color(NamedTextColor.GREEN)) + .append(Component.text("(").color(NamedTextColor.DARK_GRAY)) + .append(Component.text(senderMailSize).color(NamedTextColor.GRAY)) + .append(Component.text(")").color(NamedTextColor.DARK_GRAY)) + .append(Component.newline()) + .append(Component.join(JoinConfiguration.newlines(), mailsComponent)); + + if (context.inGame()) { + bot.chat().tellraw( + component, + context.sender().profile().getId() + ); + } else { + context.sendOutput(component); + } + + // thanks https://www.baeldung.com/java-concurrentmodificationexception#3-using-removeif:~:text=3.3.%20Using%20removeIf() + bot.mail().mails().removeIf(mail -> mail.sentTo().equals(sender.profile().getName())); + + return null; + } + default -> { + return Component.text("Invalid argument").color(NamedTextColor.RED); + } + } + } +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/data/Mail.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/data/Mail.java new file mode 100644 index 00000000..ea42263c --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/data/Mail.java @@ -0,0 +1,13 @@ +package land.chipmunk.chayapak.chomens_bot.data; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.joda.time.DateTime; + +@AllArgsConstructor +public class Mail { + @Getter private final String sentBy; + @Getter private final String sentTo; + @Getter private final DateTime timeSent; + @Getter private final String contents; +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/CommandHandlerPlugin.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/CommandHandlerPlugin.java index 7fad1697..a821062d 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/CommandHandlerPlugin.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/CommandHandlerPlugin.java @@ -60,6 +60,7 @@ public class CommandHandlerPlugin { registerCommand(new ClearChatQueueCommand()); registerCommand(new FilterCommand()); registerCommand(new UptimeCommand()); + registerCommand(new MailCommand()); } public void registerCommand (Command command) { @@ -97,13 +98,15 @@ public class CommandHandlerPlugin { final String[] fullArgs = Arrays.copyOfRange(splitInput, 1, splitInput.length); // TODO: improve these minimum args and maximum args stuff, the current one really sucks.,., + final int shortestUsageIndex = getShortestUsageIndex(command.usage()); final int longestUsageIndex = getLongestUsageIndex(command.usage()); - final String usage = command.usage().get(longestUsageIndex); + final String shortestUsage = command.usage().get(shortestUsageIndex); + final String longestUsage = command.usage().get(longestUsageIndex); - final int minimumArgs = getMinimumArgs(usage, inGame, command.trustLevel()); - final int maximumArgs = getMaximumArgs(usage, inGame, command.trustLevel()); + final int minimumArgs = getMinimumArgs(shortestUsage, inGame, command.trustLevel()); + final int maximumArgs = getMaximumArgs(longestUsage, inGame, command.trustLevel()); if (fullArgs.length < minimumArgs) return Component.text("Excepted minimum of " + minimumArgs + " argument(s), got " + fullArgs.length).color(NamedTextColor.RED); - if (fullArgs.length > maximumArgs && !usage.contains("{")) return Component.text("Too much arguments, expected " + maximumArgs + " max").color(NamedTextColor.RED); + if (fullArgs.length > maximumArgs && !longestUsage.contains("{")) return Component.text("Too much arguments, expected " + maximumArgs + " max").color(NamedTextColor.RED); if (trustLevel > 0 && splitInput.length < 2 && inGame) return Component.text("Please provide a hash").color(NamedTextColor.RED); @@ -194,6 +197,19 @@ public class CommandHandlerPlugin { return longestIndex; } + private int getShortestUsageIndex(List usages) { + int shortestIndex = 0; + int minLength = Integer.MAX_VALUE; + for (int i = 0; i < usages.size(); i++) { + String[] args = usages.get(i).split("\\s+"); + if (args.length < minLength) { + shortestIndex = i; + minLength = args.length; + } + } + return shortestIndex; + } + private int getMinimumArgs(String usage, boolean inGame, int trustLevel) { int count = 0; for (int i = 0; i < usage.length(); i++) { diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/MailPlugin.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/MailPlugin.java new file mode 100644 index 00000000..c21793e7 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/MailPlugin.java @@ -0,0 +1,59 @@ +package land.chipmunk.chayapak.chomens_bot.plugins; + +import land.chipmunk.chayapak.chomens_bot.Bot; +import land.chipmunk.chayapak.chomens_bot.data.Mail; +import land.chipmunk.chayapak.chomens_bot.data.chat.MutablePlayerListEntry; +import land.chipmunk.chayapak.chomens_bot.util.ColorUtilities; +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.ArrayList; +import java.util.List; + +public class MailPlugin extends PlayersPlugin.Listener { + private final Bot bot; + + // TODO: make this persistent + @Getter private final List mails = new ArrayList<>(); + + public MailPlugin (Bot bot) { + this.bot = bot; + + bot.players().addListener(this); + } + + @Override + public void playerJoined(MutablePlayerListEntry target) { + final String name = target.profile().getName(); + + final List sendTos = new ArrayList<>(); // confusing name,.,. + + for (Mail mail : mails) sendTos.add(mail.sentTo()); + + boolean shouldSend = false; + for (Mail mail : mails) { + if (mail.sentTo().equals(name)) { + shouldSend = true; + break; + } + } + + if (shouldSend) { + final Component component = Component.translatable( + "You have %s new mail%s!\n" + + "Do %s or %s to read", + Component.text(sendTos.size()).color(NamedTextColor.GREEN), + Component.text((sendTos.size() > 1) ? "s" : ""), + Component.text(bot.config().commandSpyPrefixes().get(0) + "mail read").color(ColorUtilities.getColorByString(bot.config().colorPalette().primary())), + Component.text(bot.config().prefixes().get(0) + "mail read").color(ColorUtilities.getColorByString(bot.config().colorPalette().primary())) + ).color(NamedTextColor.GOLD); + + bot.chat().tellraw(component, target.profile().getId()); + } + } + + public void send (Mail mail) { + mails.add(mail); + } +}