/**
* Modlog
* Pokemon Showdown - http://pokemonshowdown.com/
*
* Interface for viewing and searching modlog. These run in a
* subprocess.
*
* Also handles searching battle logs.
*
* Actually writing to modlog is handled in chat.js, rooms.js, and
* roomlogs.js
*
* @license MIT
*/
'use strict';
const FS = require('./../lib/fs');
const path = require('path');
const Dashycode = require('../lib/dashycode');
const execFileSync = require('child_process').execFileSync;
const MAX_PROCESSES = 1;
const MAX_QUERY_LENGTH = 2500;
const DEFAULT_RESULTS_LENGTH = 100;
const MORE_BUTTON_INCREMENTS = [200, 400, 800, 1600, 3200];
const LINES_SEPARATOR = 'lines=';
const MAX_RESULTS_LENGTH = MORE_BUTTON_INCREMENTS[MORE_BUTTON_INCREMENTS.length - 1];
const LOG_PATH = 'logs/modlog/';
class SortedLimitedLengthList {
constructor(maxSize) {
this.maxSize = maxSize;
this.list = [];
}
getListClone() {
return this.list.slice();
}
tryInsert(element) {
let insertedAt = -1;
for (let i = this.list.length - 1; i >= 0; i--) {
if (element.localeCompare(this.list[i]) < 0) {
insertedAt = i + 1;
if (i === this.list.length - 1) {
this.list.push(element);
break;
}
this.list.splice(i + 1, 0, element);
break;
}
}
if (insertedAt < 0) this.list.splice(0, 0, element);
if (this.list.length > this.maxSize) {
this.list.pop();
if (insertedAt === this.list.length) return false;
}
return true;
}
}
/*********************************************************
* Modlog Functions
*********************************************************/
function checkRipgrepAvailability() {
if (Config.ripgrepmodlog === undefined) {
try {
execFileSync('rg', ['--version'], {cwd: path.normalize(`${__dirname}/../`)});
Config.ripgrepmodlog = true;
} catch (error) {
Config.ripgrepmodlog = false;
}
}
return Config.ripgrepmodlog;
}
function getMoreButton(room, search, useExactSearch, lines, maxLines) {
let newLines = 0;
for (let increase of MORE_BUTTON_INCREMENTS) {
if (increase > lines) {
newLines = increase;
break;
}
}
if (!newLines || lines < maxLines) {
return ''; // don't show a button if no more pre-set increments are valid or if the amount of results is already below the max
} else {
if (useExactSearch) search = Chat.escapeHTML(`"${search}"`);
return `
[${date}]
`;
} else {
date = ``;
}
if (!line) {
return `${date}[${timestamp}] \u2190 current server time`;
}
let parenIndex = line.indexOf(')');
let thisRoomID = line.slice(bracketIndex + 3, parenIndex);
if (addModlogLinks) {
let url = Config.modloglink(time, thisRoomID);
if (url) timestamp = `${timestamp}`;
}
return `${date}[${timestamp}] (${thisRoomID})${Chat.escapeHTML(line.slice(parenIndex + 1))}`;
}).join(`
`);
let preamble;
const modlogid = room + (searchString ? '-' + Dashycode.encode(searchString) : '');
if (searchString) {
const searchStringDescription = (exactSearch ? `containing the string "${searchString}"` : `matching the username "${searchString}"`);
preamble = `>view-modlog-${modlogid}\n|init|html\n|title|[Modlog]${title}\n|pagehtml|
The last ${Chat.count(lines, "logged actions")} ${searchStringDescription} on ${roomName}.` + (exactSearch ? "" : " Add quotes to the search parameter to search for a phrase, rather than a user."); } else { preamble = `>view-modlog-${modlogid}\n|init|html\n|title|[Modlog]${title}\n|pagehtml|
The last ${Chat.count(lines, "lines")} of the Moderator Log of ${roomName}.`; } let moreButton = getMoreButton(room, searchString, exactSearch, lines, maxLines); return `${preamble}${resultString}${moreButton}
${tierid} battles on ${date} where ${userid} was a player and the battle lasted less than ${turnLimit} turn${Chat.plural(turnLimit)}:
`; buf += `${userid}'s ${tierid} Battles | |
|---|---|
| Category | Number |
| Total Battles | ${data.totalBattles} |
| Total Wins | ${data.totalWins} |
| Total Losses | ${data.totalLosses} |
| Total Ties | ${data.totalTies} |
| Opponent | Times Battled |
| ${foe} | ${data[foe]} |
Userid: ${userid}
Maximum Turns: ${turnLimit}
`; let months = FS('logs/').readdirSync(); months = months.filter(f => f.length === 7 && f.includes('-')).sort((aKey, bKey) => { const a = aKey.split('-').map(parseInt); const b = bKey.split('-').map(parseInt); if (a[0] === b[0]) { if (a[1] > b[1]) return -1; return 1; } if (a[0] > b[0]) return -1; return 1; }); let month = args.shift(); if (!month) { buf += `Please select a month:
Please select the tier to search:
`; } else { if (!tiers.includes(tierid)) return title + buf + `Invalid tier selected. `; title += `[${tierid}]`; buf += ``; } let date = args.shift(); const days = FS(`logs/${month}/${tierid}/`).readdirSync(); if (!date) { buf += `Please select the date to search:
`; } else { date = date += `-${args.shift()}-${args.shift()}`; if (!days.includes(date)) return title + buf + `Invalid date selected. `; title += `[${date}]`; buf += ``; } if (args[0] !== 'confirm') { buf += `Are you sure you want to run a battle search for for ${tierid} battles on ${date} where ${userid} was a player and the battle lasted less than ${turnLimit} turn${Chat.plural(turnLimit)}?
`; buf += ``; return title + buf + ``; } // Run search getBattleSearch(connection, userid, turnLimit, month, tierid, date); return title + `\n|pagehtml|Searching for ${tierid} battles on ${date} where ${userid} was a player and the battle lasted less than ${turnLimit} turn${Chat.plural(turnLimit)}.
Loading... (this will take a while)