Compare commits
2 commits
e56fc19ab3
...
41e57d04f2
Author | SHA1 | Date | |
---|---|---|---|
41e57d04f2 | |||
e8ef3adf3b |
6 changed files with 327 additions and 18 deletions
|
@ -69,6 +69,7 @@ public class Bot {
|
||||||
public TPSPlugin tps;
|
public TPSPlugin tps;
|
||||||
public EvalPlugin eval;
|
public EvalPlugin eval;
|
||||||
public TrustedPlugin trusted;
|
public TrustedPlugin trusted;
|
||||||
|
public GrepLogPlugin grepLog;
|
||||||
public BruhifyPlugin bruhify;
|
public BruhifyPlugin bruhify;
|
||||||
public CloopPlugin cloop;
|
public CloopPlugin cloop;
|
||||||
public ExploitsPlugin exploits;
|
public ExploitsPlugin exploits;
|
||||||
|
@ -123,6 +124,7 @@ public class Bot {
|
||||||
this.tps = new TPSPlugin(this);
|
this.tps = new TPSPlugin(this);
|
||||||
this.eval = new EvalPlugin(this);
|
this.eval = new EvalPlugin(this);
|
||||||
this.trusted = new TrustedPlugin(this);
|
this.trusted = new TrustedPlugin(this);
|
||||||
|
this.grepLog = new GrepLogPlugin(this);
|
||||||
this.bruhify = new BruhifyPlugin(this);
|
this.bruhify = new BruhifyPlugin(this);
|
||||||
this.cloop = new CloopPlugin(this);
|
this.cloop = new CloopPlugin(this);
|
||||||
this.exploits = new ExploitsPlugin(this);
|
this.exploits = new ExploitsPlugin(this);
|
||||||
|
|
|
@ -47,7 +47,8 @@ public class FilterCommand extends Command {
|
||||||
|
|
||||||
String action = context.getString(false, true);
|
String action = context.getString(false, true);
|
||||||
|
|
||||||
// this is a mess
|
// run 2 times. for example `*filter -ignorecase -regex add test` will be both accepted
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
if (action.equals("-ignorecase")) {
|
if (action.equals("-ignorecase")) {
|
||||||
ignoreCase = true;
|
ignoreCase = true;
|
||||||
action = context.getString(false, true);
|
action = context.getString(false, true);
|
||||||
|
@ -55,13 +56,6 @@ public class FilterCommand extends Command {
|
||||||
regex = true;
|
regex = true;
|
||||||
action = context.getString(false, true);
|
action = context.getString(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.equals("-ignorecase")) {
|
|
||||||
ignoreCase = true;
|
|
||||||
action = context.getString(false, true);
|
|
||||||
} else if (action.equals("-regex")) {
|
|
||||||
regex = true;
|
|
||||||
action = context.getString(false, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Gson gson = new Gson();
|
final Gson gson = new Gson();
|
||||||
|
@ -128,9 +122,7 @@ public class FilterCommand extends Command {
|
||||||
Component.join(JoinConfiguration.newlines(), filtersComponents)
|
Component.join(JoinConfiguration.newlines(), filtersComponents)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default -> {
|
default -> throw new CommandException(Component.text("Invalid action"));
|
||||||
throw new CommandException(Component.text("Invalid action"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package me.chayapak1.chomens_bot.commands;
|
||||||
|
|
||||||
|
import me.chayapak1.chomens_bot.Bot;
|
||||||
|
import me.chayapak1.chomens_bot.command.Command;
|
||||||
|
import me.chayapak1.chomens_bot.command.CommandContext;
|
||||||
|
import me.chayapak1.chomens_bot.command.CommandException;
|
||||||
|
import me.chayapak1.chomens_bot.command.TrustLevel;
|
||||||
|
import me.chayapak1.chomens_bot.util.ColorUtilities;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
||||||
|
public class GrepLogCommand extends Command {
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
public GrepLogCommand() {
|
||||||
|
super(
|
||||||
|
"greplog",
|
||||||
|
"Queries the bot's logs",
|
||||||
|
new String[] { "<input>", "...-ignorecase...", "...-regex...", "stop" },
|
||||||
|
new String[] { "logquery", "findlog" },
|
||||||
|
TrustLevel.PUBLIC,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component execute(CommandContext context) throws CommandException {
|
||||||
|
final Bot bot = context.bot;
|
||||||
|
|
||||||
|
if (bot.discord.jda == null) throw new CommandException(Component.text("The bot's Discord integration has to be enabled to use the command."));
|
||||||
|
|
||||||
|
boolean ignoreCase = false;
|
||||||
|
boolean regex = false;
|
||||||
|
|
||||||
|
String firstInput = context.getString(false, true);
|
||||||
|
|
||||||
|
// run 2 times. for example `*greplog -ignorecase -regex test` will be both accepted
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
if (firstInput.equals("-ignorecase")) {
|
||||||
|
ignoreCase = true;
|
||||||
|
firstInput = context.getString(false, true);
|
||||||
|
} else if (firstInput.equals("-regex")) {
|
||||||
|
regex = true;
|
||||||
|
firstInput = context.getString(false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// interesting code
|
||||||
|
final String input = (firstInput + " " + context.getString(true, false)).trim();
|
||||||
|
|
||||||
|
if (input.equals("stop")) {
|
||||||
|
if (thread == null) throw new CommandException(Component.text("There is no query process running"));
|
||||||
|
|
||||||
|
bot.grepLog.running = false;
|
||||||
|
|
||||||
|
thread.interrupt(); // ? should i interrupt it this way?
|
||||||
|
|
||||||
|
thread = null;
|
||||||
|
|
||||||
|
return Component.text("Stopped querying the logs").color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread != null) throw new CommandException(Component.text("Another query is already running"));
|
||||||
|
|
||||||
|
context.sendOutput(
|
||||||
|
Component
|
||||||
|
.translatable("Started querying the logs for %s")
|
||||||
|
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
|
||||||
|
.arguments(
|
||||||
|
Component
|
||||||
|
.text(input)
|
||||||
|
.color(ColorUtilities.getColorByString(bot.config.colorPalette.string))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
final boolean finalIgnoreCase = ignoreCase;
|
||||||
|
final boolean finalRegex = regex;
|
||||||
|
|
||||||
|
thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
bot.grepLog.search(context, input, finalIgnoreCase, finalRegex);
|
||||||
|
} catch (CommandException e) {
|
||||||
|
context.sendOutput(e.message.color(NamedTextColor.RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
thread = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,6 +57,7 @@ public class CommandHandlerPlugin {
|
||||||
registerCommand(new SeenCommand());
|
registerCommand(new SeenCommand());
|
||||||
registerCommand(new IPFilterCommand());
|
registerCommand(new IPFilterCommand());
|
||||||
registerCommand(new StopCommand());
|
registerCommand(new StopCommand());
|
||||||
|
registerCommand(new GrepLogCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean disabled = false;
|
public boolean disabled = false;
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
package me.chayapak1.chomens_bot.plugins;
|
||||||
|
|
||||||
|
import me.chayapak1.chomens_bot.Bot;
|
||||||
|
import me.chayapak1.chomens_bot.command.CommandContext;
|
||||||
|
import me.chayapak1.chomens_bot.command.CommandException;
|
||||||
|
import me.chayapak1.chomens_bot.util.ColorUtilities;
|
||||||
|
import me.chayapak1.chomens_bot.util.FileLoggerUtilities;
|
||||||
|
import me.chayapak1.chomens_bot.util.StringUtilities;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.utils.FileUpload;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.event.ClickEvent;
|
||||||
|
import net.kyori.adventure.text.event.HoverEvent;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NotDirectoryException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
public class GrepLogPlugin {
|
||||||
|
private final Bot bot;
|
||||||
|
|
||||||
|
private Pattern pattern;
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
|
||||||
|
public boolean running = false;
|
||||||
|
|
||||||
|
public GrepLogPlugin (Bot bot) {
|
||||||
|
this.bot = bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void search (CommandContext context, String input, boolean ignoreCase, boolean regex) throws CommandException {
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
try (final Stream<Path> files = Files.list(FileLoggerUtilities.logDirectory)) {
|
||||||
|
final Path[] fileList = files.toArray(Path[]::new);
|
||||||
|
|
||||||
|
Arrays.sort(fileList, Comparator.comparing(a -> a.getFileName().toString()));
|
||||||
|
|
||||||
|
final StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
for (Path filePath : fileList) {
|
||||||
|
if (!running) return;
|
||||||
|
|
||||||
|
if (count > 1_000_000) break;
|
||||||
|
|
||||||
|
final String fileName = filePath.getFileName().normalize().toString();
|
||||||
|
final String absolutePath = filePath.toAbsolutePath().normalize().toString();
|
||||||
|
|
||||||
|
if (fileName.endsWith(".txt.gz")) {
|
||||||
|
try (
|
||||||
|
final GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream(absolutePath));
|
||||||
|
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gzipInputStream, StandardCharsets.UTF_8))
|
||||||
|
) {
|
||||||
|
result.append(process(bufferedReader, input, ignoreCase, regex));
|
||||||
|
}
|
||||||
|
} else if (fileName.endsWith(".txt")) {
|
||||||
|
try (
|
||||||
|
final FileInputStream fileInputStream = new FileInputStream(absolutePath);
|
||||||
|
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream, StandardCharsets.UTF_8))
|
||||||
|
) {
|
||||||
|
result.append(process(bufferedReader, input, ignoreCase, regex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ? should this be here? (see `process` function below)
|
||||||
|
pattern = null;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
final String stringifiedResult = result.toString();
|
||||||
|
|
||||||
|
final long matches = stringifiedResult.lines().count();
|
||||||
|
|
||||||
|
if (matches == 0) throw new CommandException(Component.text("No matches has been found"));
|
||||||
|
|
||||||
|
final String channelId = bot.discord.servers.get(bot.host + ":" + bot.port);
|
||||||
|
final TextChannel logChannel = bot.discord.jda.getTextChannelById(channelId);
|
||||||
|
|
||||||
|
if (logChannel == null) return;
|
||||||
|
|
||||||
|
logChannel
|
||||||
|
.sendMessage("Greplog result:")
|
||||||
|
.addFiles(
|
||||||
|
FileUpload.fromData(
|
||||||
|
// as of the time writing this, discord has an 8 MB file size limit for bots
|
||||||
|
StringUtilities.truncateToFitUtf8ByteLength(stringifiedResult, 8 * 1000 * 1000).getBytes(),
|
||||||
|
String.format("result-%d.txt", System.currentTimeMillis() / 1000)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.queue(message -> {
|
||||||
|
final String url = message.getAttachments().get(0).getUrl();
|
||||||
|
|
||||||
|
final Component component = Component.translatable("Found %s matches for %s. You can see the results by clicking %s or in the Discord server.")
|
||||||
|
.color(ColorUtilities.getColorByString(bot.config.colorPalette.defaultColor))
|
||||||
|
.arguments(
|
||||||
|
Component.text(matches).color(ColorUtilities.getColorByString(bot.config.colorPalette.number)),
|
||||||
|
Component.text(input).color(ColorUtilities.getColorByString(bot.config.colorPalette.string)),
|
||||||
|
Component
|
||||||
|
.text("here")
|
||||||
|
.color(NamedTextColor.GREEN)
|
||||||
|
.hoverEvent(
|
||||||
|
HoverEvent.showText(
|
||||||
|
Component
|
||||||
|
.text("Click! :D")
|
||||||
|
.color(ColorUtilities.getColorByString(bot.config.colorPalette.secondary))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.clickEvent(
|
||||||
|
ClickEvent.openUrl(url)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
context.sendOutput(component);
|
||||||
|
});
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
running = false;
|
||||||
|
throw new CommandException(Component.text("File not found"));
|
||||||
|
} catch (NotDirectoryException e) {
|
||||||
|
running = false;
|
||||||
|
throw new CommandException(Component.text("Logger directory is not a directory"));
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
running = false;
|
||||||
|
throw new CommandException(Component.text("Pattern is invalid"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
running = false;
|
||||||
|
throw new CommandException(Component.text("An I/O error has occurred"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder process (BufferedReader bufferedReader, String input, boolean ignoreCase, boolean regex) throws IOException, PatternSyntaxException {
|
||||||
|
if (regex && pattern == null) {
|
||||||
|
if (ignoreCase) pattern = Pattern.compile(input, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CHARACTER_CLASS);
|
||||||
|
else pattern = Pattern.compile(input, Pattern.UNICODE_CHARACTER_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
if (
|
||||||
|
(regex && pattern.matcher(line).find()) || // *greplog -regex text OR *greplog -ignorecase -regex text
|
||||||
|
(!ignoreCase && !regex && line.contains(input)) || // *greplog text
|
||||||
|
(ignoreCase && StringUtilities.containsIgnoreCase(line, input)) // *greplog -ignorecase
|
||||||
|
) {
|
||||||
|
result.append(line).append("\n");
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package me.chayapak1.chomens_bot.util;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class StringUtilities {
|
||||||
|
// https://stackoverflow.com/a/35148974/18518424
|
||||||
|
public static String truncateToFitUtf8ByteLength(String s, int maxBytes) {
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Charset charset = StandardCharsets.UTF_8;
|
||||||
|
CharsetDecoder decoder = charset.newDecoder();
|
||||||
|
byte[] sba = s.getBytes(charset);
|
||||||
|
if (sba.length <= maxBytes) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
// Ensure truncation by having byte buffer = maxBytes
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(sba, 0, maxBytes);
|
||||||
|
CharBuffer cb = CharBuffer.allocate(maxBytes);
|
||||||
|
// Ignore an incomplete character
|
||||||
|
decoder.onMalformedInput(CodingErrorAction.IGNORE);
|
||||||
|
decoder.decode(bb, cb, true);
|
||||||
|
decoder.flush(cb);
|
||||||
|
return new String(cb.array(), 0, cb.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/25379180/18518424
|
||||||
|
public static boolean containsIgnoreCase(String src, String what) {
|
||||||
|
final int length = what.length();
|
||||||
|
if (length == 0)
|
||||||
|
return true; // Empty string is contained
|
||||||
|
|
||||||
|
final char firstLo = Character.toLowerCase(what.charAt(0));
|
||||||
|
final char firstUp = Character.toUpperCase(what.charAt(0));
|
||||||
|
|
||||||
|
for (int i = src.length() - length; i >= 0; i--) {
|
||||||
|
// Quick check before calling the more expensive regionMatches() method:
|
||||||
|
final char ch = src.charAt(i);
|
||||||
|
if (ch != firstLo && ch != firstUp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (src.regionMatches(true, i, what, 0, length))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue