Preserve conversation excerpts and deduplicate case evidence
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { db } from '../db/index.js';
|
||||
import { archivedEvidenceSnapshots } from './evidence.js';
|
||||
import { AuthError } from '../auth/index.js';
|
||||
import { moderatorMaximumRestrictionMs, type AuthUser } from '../../auth.js';
|
||||
import {
|
||||
@@ -7,7 +8,6 @@ import {
|
||||
isModerationCaseOpen,
|
||||
moderationCaseOutcomes,
|
||||
moderationCaseStatusLabels,
|
||||
moderationGuid,
|
||||
type ModerationCaseStatus,
|
||||
type ModerationCasePriority,
|
||||
type ModerationGame,
|
||||
@@ -404,42 +404,10 @@ function archivedEvidence(
|
||||
value: unknown,
|
||||
now: string
|
||||
): number {
|
||||
const requested = value as { matchId: unknown; message: unknown };
|
||||
if (!requested || !Number.isSafeInteger(requested.matchId))
|
||||
throw new AuthError('Choose a recorded chat message.');
|
||||
textField(requested.message, 'Chat message', 4096);
|
||||
// Evidence must match the archived bytes, including intentional whitespace.
|
||||
const message = requested.message as string;
|
||||
const match = db()
|
||||
.prepare('SELECT game,server,started_at FROM leaderboard_matches WHERE id=?')
|
||||
.get(requested.matchId) as { game: string; server: string; started_at: string } | undefined;
|
||||
if (!match || match.game !== target.game)
|
||||
throw new AuthError('The source match is unavailable.', 404);
|
||||
const entries = db()
|
||||
.prepare('SELECT entry FROM match_chat WHERE match_id=?')
|
||||
.all(requested.matchId) as { entry: string }[];
|
||||
const found = entries
|
||||
.filter((row) => {
|
||||
const entry = JSON.parse(row.entry);
|
||||
return (
|
||||
entry.type === 'message' &&
|
||||
moderationGuid(target.game, entry.message.guid) === target.guid &&
|
||||
entry.message.message === message
|
||||
);
|
||||
})
|
||||
.slice(0, 10);
|
||||
if (!found.length)
|
||||
throw new AuthError('The original chat message could not be verified in the archive.', 409);
|
||||
for (const row of found)
|
||||
evidence(
|
||||
actor,
|
||||
id,
|
||||
message,
|
||||
`Archived chat · ${match.server} · match ${requested.matchId}`,
|
||||
JSON.stringify({ matchId: requested.matchId, match, entry: JSON.parse(row.entry) }),
|
||||
now
|
||||
);
|
||||
return found.length;
|
||||
const snapshots = archivedEvidenceSnapshots(id, target, value);
|
||||
for (const snapshot of snapshots)
|
||||
evidence(actor, id, snapshot.text, snapshot.source, snapshot.snapshot, now);
|
||||
return snapshots.length;
|
||||
}
|
||||
function updateCase(actor: AuthUser, input: Record<string, unknown>, now: string) {
|
||||
const row = caseRow(input.id);
|
||||
@@ -548,6 +516,7 @@ export function mutateModeration(actorId: string, input: Record<string, unknown>
|
||||
const row = caseRow(input.id);
|
||||
checkCaseRevision(row, input.revision);
|
||||
const count = archivedEvidence(actor, row.id, row, input.evidence, now);
|
||||
if (!count) return { id: row.id };
|
||||
touchCase(row.id, now);
|
||||
moderationEvent(
|
||||
actor,
|
||||
|
||||
Reference in New Issue
Block a user