diff --git a/server/chat-plugins/helptickets.ts b/server/chat-plugins/helptickets.ts
index e477d41146..7c128c74e5 100644
--- a/server/chat-plugins/helptickets.ts
+++ b/server/chat-plugins/helptickets.ts
@@ -1,4 +1,4 @@
-import {FS, Utils} from '../../lib';
+import {FS, Utils, Net} from '../../lib';
import {getCommonBattles} from '../chat-commands/info';
import type {Punishment} from '../punishments';
import type {PartialModlogEntry, ModlogID} from '../modlog';
@@ -42,6 +42,12 @@ interface TextTicketInfo {
onSubmit?: (ticket: TicketState, text: [string, string], submitter: User, conn: Connection) => void;
}
+interface BattleInfo {
+ log: string[];
+ url: string;
+ title: string;
+}
+
type TicketResult = 'approved' | 'valid' | 'assisted' | 'denied' | 'invalid' | 'unassisted' | 'ticketban' | 'deleted';
export const tickets: {[k: string]: TicketState} = {};
@@ -401,23 +407,28 @@ export class HelpTicket extends Rooms.RoomGame {
void (room as GameRoom).uploadReplay(user, conn, "forpunishment");
}
}
- static visualizeBattleLogs(rooms: string[]) {
- const existingRooms = rooms.map(r => Rooms.get(r)).filter(r => r?.type !== 'chat');
+ static formatBattleLog(logs: string[], title: string, url: string) {
+ const log = logs.filter(l => l.startsWith('|c|'));
+ let buf = ``;
+ for (const line of log) {
+ const [,, username, message] = Utils.splitFirst(line, '|', 3);
+ buf += Utils.html`
${username}: ${message}
`;
+ }
+ if (buf) buf = ``;
+ return buf;
+ }
+ static async visualizeBattleLogs(rooms: string[]) {
+ const logs = [];
+ for (const room of rooms) {
+ const log = await getBattleLog(room);
+ if (log) logs.push(log);
+ }
+ const existingRooms = logs.filter(Boolean);
if (existingRooms.length) {
- const chatBuffer = existingRooms.map(room => {
- // there is no reason this should happen (room && room.type check above in .filter).
- // but typescript is stupid. so appeasement.
- if (!room) return '';
- const log = room.log.log.filter(l => l.startsWith('|c|'));
- if (!log?.length) return '';
- let innerBuf = `${room.title}
`;
- for (const line of log) {
- const [,, username, message] = Utils.splitFirst(line, '|', 3);
- innerBuf += Utils.html`
${username}: ${message}
`;
- }
- innerBuf += `
`;
- return innerBuf;
- }).filter(Boolean).join('');
+ const chatBuffer = existingRooms
+ .map(room => this.formatBattleLog(room.log, room.title, room.url))
+ .filter(Boolean)
+ .join('');
if (chatBuffer) {
return (
`Battle chat logs:
` +
@@ -658,6 +669,30 @@ export function getBattleLinks(text: string) {
return rooms;
}
+export async function getBattleLog(battle: string): Promise {
+ const battleRoom = Rooms.get(battle);
+ if (battleRoom && battleRoom.type !== 'chat') {
+ return {
+ log: battleRoom.log.log.filter(k => k.startsWith('|c|')),
+ title: battleRoom.title,
+ url: `/${Config.routes.client}/${battle}`,
+ };
+ }
+ battle = battle.replace(`battle-`, '').replace(/-[a-z0-9]pw/, '');
+ try {
+ const raw = await Net(`https://${Config.routes.replays}/${battle}.json`).get();
+ const data = JSON.parse(raw);
+ if (data.log?.length) {
+ return {
+ log: data.log.split('\n').filter((k: string) => k.startsWith('|c|')),
+ title: `${data.p1} vs ${data.p2}`,
+ url: `https://${Config.routes.replays}/${battle}.json`,
+ };
+ }
+ } catch (e) {}
+ return null;
+}
+
// Prevent a desynchronization issue when hotpatching
for (const room of Rooms.rooms.values()) {
if (!room.settings.isHelp || !room.game) continue;
@@ -760,7 +795,7 @@ export const textTickets: {[k: string]: TextTicketInfo} = {
}
return true;
},
- getReviewDisplay(ticket, staff, conn) {
+ async getReviewDisplay(ticket, staff, conn) {
let buf = '';
const reportUserid = toID(ticket.text[0]);
const sharedBattles = getCommonBattles(ticket.userid, null, reportUserid, null, conn);
@@ -773,7 +808,7 @@ export const textTickets: {[k: string]: TextTicketInfo} = {
buf += HelpTicket.displayPunishmentList(reportUserid, proofString);
if (sharedBattles.length) {
- const battleLogHTML = HelpTicket.visualizeBattleLogs(sharedBattles);
+ const battleLogHTML = await HelpTicket.visualizeBattleLogs(sharedBattles);
if (battleLogHTML) {
buf += `
`;
buf += battleLogHTML;
@@ -833,7 +868,7 @@ export const textTickets: {[k: string]: TextTicketInfo} = {
HelpTicket.uploadReplaysFrom(part, submitter, conn);
}
},
- getReviewDisplay(ticket, staff, connection) {
+ async getReviewDisplay(ticket, staff, connection) {
let buf = ``;
const [text, context] = ticket.text;
let rooms = getBattleLinks(text);
@@ -856,7 +891,7 @@ export const textTickets: {[k: string]: TextTicketInfo} = {
}
buf += `Battle links: ${rooms.map(url => Chat.formatText(`<<${url}>>`)).join(', ')}
`;
buf += `
`;
- const battleLogHTML = HelpTicket.visualizeBattleLogs(rooms);
+ const battleLogHTML = await HelpTicket.visualizeBattleLogs(rooms);
if (battleLogHTML) buf += battleLogHTML;
return buf;
},