2024-05-30 22:47:41 -04:00
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' )
2024-06-02 17:26:17 -04:00
const stream = require ( 'stream/promises' )
2024-05-30 22:47:41 -04:00
const LOG _QUERY _ALREADY _RUNNING _ERROR = new SimpleCommandExceptionType ( new TextMessage ( 'Another log query is already running!' ) )
module . exports = {
running : false ,
stream : null ,
2024-05-30 23:07:52 -04:00
create ( ) {
return Object . create ( this )
} ,
2024-05-30 22:47:41 -04:00
register ( dispatcher ) {
const node = dispatcher . register (
literal ( 'logquery' )
. then (
literal ( 'count' )
. then (
argument ( 'string' , greedyString ( ) )
2024-07-31 22:34:19 -04:00
. executes ( c => this . countCommand ( c ) )
2024-05-30 22:47:41 -04:00
)
)
. then (
literal ( 'abort' )
2024-07-31 22:34:19 -04:00
. executes ( c => this . abortCommand ( c ) )
2024-05-30 22:47:41 -04:00
)
)
node . description = 'Searches for text in log files'
node . permissionLevel = 0
} ,
2024-05-30 23:07:52 -04:00
cleanup ( ) {
this . abort ( )
} ,
2024-05-30 22:47:41 -04:00
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
2024-05-30 23:10:28 -04:00
const ran = await this . queryLogs ( source , line => line . includes ( string ) && count ++ )
2024-05-30 22:47:41 -04:00
2024-05-30 23:10:28 -04:00
if ( ran ) source . sendFeedback ( [ { text : 'Found ' , ... bot . styles . primary } , { text : count + '' , ... bot . styles . secondary } , ' instances of ' , { text : string , ... bot . styles . secondary } ] , true )
2024-05-30 22:47:41 -04:00
} ,
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
2024-06-02 17:26:17 -04:00
const filenames = await fs . promises . readdir ( logsDir )
for ( let i = 0 ; i < filenames . length ; i ++ ) {
const filename = filenames [ i ]
2024-05-30 22:47:41 -04:00
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 )
} )
2024-06-02 17:26:17 -04:00
try {
await stream . finished ( this . stream )
} catch {
}
2024-05-30 22:47:41 -04:00
2024-05-30 23:10:28 -04:00
if ( ! this . running ) return false // if this was aborted
2024-05-30 22:47:41 -04:00
}
this . running = false
this . stream = null
2024-05-30 23:10:28 -04:00
return true
2024-05-30 22:47:41 -04:00
}
}