diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/LogQueryCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/LogQueryCommand.java new file mode 100644 index 0000000..da4b9e0 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/LogQueryCommand.java @@ -0,0 +1,130 @@ +package land.chipmunk.chipmunkbot.commands; + +import land.chipmunk.chipmunkbot.util.Logging; +import land.chipmunk.chipmunkbot.command.*; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.literal; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.argument; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import lombok.AllArgsConstructor; +import java.io.File; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Scanner; +import java.util.function.Predicate; +import java.util.zip.GZIPInputStream; + +public class LogQueryCommand { + private Thread thread; + + private static SimpleCommandExceptionType ALREADY_QUERYING_LOGS_EXCEPTION = new SimpleCommandExceptionType(ComponentMessage.wrap(Component.translatable("Another log query is already running"))); + private static SimpleCommandExceptionType ALREADY_NOT_QUERYING_LOGS_EXCEPTION = new SimpleCommandExceptionType(ComponentMessage.wrap(Component.translatable("No log query is currently running"))); + + public static void register (CommandDispatcher dispatcher) { + final LogQueryCommand instance = new LogQueryCommand(); + + dispatcher.register( + literal("logquery") + .then( + literal("abort") + // .executes(this::abortCommand) + ) + .then( + literal("count") + .then( + argument("text", greedyString()) + .executes(instance::countCommand) + ) + ) + .then( + literal("report") + .then( + argument("text", greedyString()) + // .executes(instance::reportCommand) + ) + ) + ); + } + + public boolean querying () { + if (thread == null) return false; + if (thread.isAlive()) return true; + thread = null; + return false; + } + + public void alreadyQueryingCheck () throws CommandSyntaxException { + if (!querying()) return; + throw ALREADY_QUERYING_LOGS_EXCEPTION.create(); + } + + /* public int abortCommand (CommandContext context) throws CommandSyntaxException { + if (!querying()) throw ALREADY_NOT_QUERYING_LOGS_EXCEPTION.create(); + + } */ + + public int countCommand (CommandContext context) throws CommandSyntaxException { + alreadyQueryingCheck(); + + final CommandSource source = context.getSource(); + + final String text = getString(context, "text"); + + thread = new CountThread(line -> line.contains(text), Logging.LOGS_DIR, source); + thread.start(); + + source.sendOutput(Component.translatable("Searching for instances of %s in the logs", Component.text(text, NamedTextColor.GREEN))); + + return Command.SINGLE_SUCCESS; + } + + @AllArgsConstructor + public static class CountThread extends Thread { + public Predicate query; + public File directory; + public CommandSource source; + + public void run () { + int instances = 0; + final long startTime = System.currentTimeMillis(); + + final String[] filenames = directory.list(); + + if (filenames == null) { + source.sendOutput(Component.translatable("Unable to query the bot's logs, as the directory does not exist", NamedTextColor.RED)); + return; + } + + for (String filename : filenames) { + try { + InputStream is = new FileInputStream(new File(directory, filename)); + if (filename.endsWith(".log.gz")) is = new GZIPInputStream(is); + else if (!filename.endsWith(".log")) continue; + + final Scanner scanner = new Scanner(is, StandardCharsets.UTF_8); + + while (scanner.hasNextLine()) { + if (!query.test(scanner.nextLine())) continue; + instances++; + } + + scanner.close(); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + double seconds = (double) (System.currentTimeMillis() - startTime) / 1000d; + source.sendOutput(Component.translatable("Found %s instances of the specified query in %s seconds", Component.text(instances, NamedTextColor.GREEN), Component.text(seconds, NamedTextColor.GREEN))); + } + } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java index 40d9c5d..b9b8352 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java @@ -39,6 +39,7 @@ public class CommandManager { ReconnectCommand.register(dispatcher); NetMsgCommand.register(dispatcher); MusicCommand.register(dispatcher); + LogQueryCommand.register(dispatcher); } public static void sendException (CommandSource source, CommandSyntaxException exception) { diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/SongPlayer.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/SongPlayer.java index 5508c38..7412778 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/SongPlayer.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/SongPlayer.java @@ -63,7 +63,7 @@ public class SongPlayer extends SessionAdapter { } } -public void loadSong (URL location) { + public void loadSong (URL location) { if (loaderThread != null) { client.chat().tellraw(Component.translatable("Already loading a song, cannot load another", NamedTextColor.RED)); return; diff --git a/src/main/java/land/chipmunk/chipmunkbot/util/Logging.java b/src/main/java/land/chipmunk/chipmunkbot/util/Logging.java new file mode 100644 index 0000000..6fddcb9 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/util/Logging.java @@ -0,0 +1,25 @@ +package land.chipmunk.chipmunkbot.util; + +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.File; +import java.io.IOException; +import java.util.zip.GZIPInputStream; + +public interface Logging { + static File LOGS_DIR = new File("logs"); + static File LATEST_LOG_FILE = new File(LOGS_DIR, "latest.log"); + + static InputStream getInputStream (InputStream is) throws IOException { + try { + return new GZIPInputStream(is); + } catch (IOException ignored) { + is.reset(); + return is; + } + } + + static InputStream getInputStream (File file) throws IOException { + return getInputStream(new FileInputStream(file)); + } +}