chipmunkbot3/commands/logquery.js
2024-07-31 22:34:19 -04:00

114 lines
3 KiB
JavaScript

const { literal, argument, greedyString, SimpleCommandExceptionType } = require('brigadier-commands')
const TextMessage = require('../util/command/text_message')
const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const stream = require('stream/promises')
const LOG_QUERY_ALREADY_RUNNING_ERROR = new SimpleCommandExceptionType(new TextMessage('Another log query is already running!'))
module.exports = {
running: false,
stream: null,
create () {
return Object.create(this)
},
register (dispatcher) {
const node = dispatcher.register(
literal('logquery')
.then(
literal('count')
.then(
argument('string', greedyString())
.executes(c => this.countCommand(c))
)
)
.then(
literal('abort')
.executes(c => this.abortCommand(c))
)
)
node.description = 'Searches for text in log files'
node.permissionLevel = 0
},
cleanup () {
this.abort()
},
async countCommand (context) {
const source = context.source
const bot = source.bot
const string = context.getArgument('string')
source.sendFeedback([{ text: 'Searching for instances of ', ...bot.styles.primary }, { text: string, ...bot.styles.secondary }, ' in logs...'], false)
let count = 0
const ran = await this.queryLogs(source, line => line.includes(string) && count++)
if (ran) source.sendFeedback([{ text: 'Found ', ...bot.styles.primary }, { text: count + '', ...bot.styles.secondary }, ' instances of ', { text: string, ...bot.styles.secondary }], true)
},
abortCommand (context) {
const source = context.source
const bot = source.bot
this.abort()
source.sendFeedback({ text: 'Stopped the running log query', ...bot.styles.primary })
},
abort () {
this.running = false
this.stream?.close()
this.stream = null
},
async queryLogs (source, handler) {
if (this.running) throw LOG_QUERY_ALREADY_RUNNING_ERROR.create()
const bot = source.bot
const logsDir = bot.paths.logs
this.running = true
const filenames = await fs.promises.readdir(logsDir)
for (let i = 0; i < filenames.length; i++) {
const filename = filenames[i]
const readStream = fs.createReadStream(path.join(logsDir, filename))
this.stream = readStream
if (filename.endsWith('.gz')) {
const gunzip = zlib.createGunzip()
readStream.pipe(gunzip)
this.stream = gunzip
}
let queue = ''
this.stream.on('data', chunk => {
queue += chunk // * This should convert it to a string
const lastNewlineIdx = queue.lastIndexOf('\n')
const lines = queue.substring(0, lastNewlineIdx)
queue = queue.substring(lastNewlineIdx + 1)
for (const line of lines.split('\n')) handler(line)
})
try {
await stream.finished(this.stream)
} catch {
}
if (!this.running) return false // if this was aborted
}
this.running = false
this.stream = null
return true
}
}