From 0d5f6d382653128d604b4a1101443fa015fe0b49 Mon Sep 17 00:00:00 2001 From: "H. Shay" Date: Fri, 4 Oct 2024 20:08:53 -0700 Subject: [PATCH] refactor to handle queued redactions --- src/Mjolnir.ts | 2 ++ src/ProtectedRoomsSet.ts | 7 +++++- src/commands/RedactCommand.ts | 3 ++- src/queues/EventRedactionQueue.ts | 6 +++-- src/utils.ts | 37 ++++++++++++++++--------------- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/Mjolnir.ts b/src/Mjolnir.ts index 9fcf885d..0dc1e95c 100644 --- a/src/Mjolnir.ts +++ b/src/Mjolnir.ts @@ -320,6 +320,8 @@ export class Mjolnir { this.currentState = STATE_RUNNING; await this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "Startup complete. Now monitoring rooms."); + // update protected rooms set + this.protectedRoomsTracker.isAdmin = await this.isSynapseAdmin() } catch (err) { try { LogService.error("Mjolnir", "Error during startup:"); diff --git a/src/ProtectedRoomsSet.ts b/src/ProtectedRoomsSet.ts index 5f2908c9..aae9b27b 100644 --- a/src/ProtectedRoomsSet.ts +++ b/src/ProtectedRoomsSet.ts @@ -99,6 +99,11 @@ export class ProtectedRoomsSet { */ private readonly listRevisions = new Map(); + /** + * whether the mjolnir instance is server admin + */ + public isAdmin = false + constructor( private readonly client: MatrixSendClient, private readonly clientUserId: string, @@ -124,7 +129,7 @@ export class ProtectedRoomsSet { * @param roomId The room we want to redact them in. */ public redactUser(userId: string, roomId: string) { - this.eventRedactionQueue.add(new RedactUserInRoom(userId, roomId)); + this.eventRedactionQueue.add(new RedactUserInRoom(userId, roomId, this.isAdmin)); } /** diff --git a/src/commands/RedactCommand.ts b/src/commands/RedactCommand.ts index 72bd30b9..a9bd8342 100644 --- a/src/commands/RedactCommand.ts +++ b/src/commands/RedactCommand.ts @@ -46,7 +46,8 @@ export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjo } const targetRoomIds = targetRoom ? [targetRoom] : mjolnir.protectedRoomsTracker.getProtectedRooms(); - await redactUserMessagesIn(mjolnir, userId, targetRoomIds, limit); + const isAdmin = await mjolnir.isSynapseAdmin(); + await redactUserMessagesIn(mjolnir.client, mjolnir.managementRoomOutput, userId, targetRoomIds, isAdmin, limit); await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅'); await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing'); diff --git a/src/queues/EventRedactionQueue.ts b/src/queues/EventRedactionQueue.ts index effd00b2..8213e212 100644 --- a/src/queues/EventRedactionQueue.ts +++ b/src/queues/EventRedactionQueue.ts @@ -42,15 +42,17 @@ export interface QueuedRedaction { export class RedactUserInRoom implements QueuedRedaction { userId: string; roomId: string; + isAdmin: boolean; - constructor(userId: string, roomId: string) { + constructor(userId: string, roomId: string, isAdmin: boolean) { this.userId = userId; this.roomId = roomId; + this.isAdmin = isAdmin; } public async redact(client: MatrixClient, managementRoom: ManagementRoomOutput) { await managementRoom.logMessage(LogLevel.DEBUG, "Mjolnir", `Redacting events from ${this.userId} in room ${this.roomId}.`); - await redactUserMessagesIn(client, managementRoom, this.userId, [this.roomId]); + await redactUserMessagesIn(client, managementRoom, this.userId, [this.roomId], this.isAdmin); } public redactionEqual(redaction: QueuedRedaction): boolean { diff --git a/src/utils.ts b/src/utils.ts index 1147b04b..e97358ae 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -29,7 +29,7 @@ import { collectDefaultMetrics, Counter, Histogram, register } from "prom-client import { IHealthConfig } from "./config"; import { MatrixSendClient } from "./MatrixEmitter"; -import {Mjolnir} from "./Mjolnir"; +import ManagementRoomOutput from "./ManagementRoomOutput"; // Define a few aliases to simplify parsing durations. @@ -74,43 +74,44 @@ export function isTrueJoinEvent(event: any): boolean { * Redact a user's messages in a set of rooms. * See `getMessagesByUserIn`. * - * @param mjolnir Current mjolnir instance + * @param client Client to redact the messages with. + * @param managementRoom Management room to log messages back to. * @param userIdOrGlob A mxid or a glob which is applied to the whole sender field of events in the room, which will be redacted if they match. * See `MatrixGlob` in matrix-bot-sdk. * @param targetRoomIds Rooms to redact the messages from. + * @param isAdmin whether the bot is server admin * @param limit The number of messages to redact from most recent first. If the limit is reached then no further messages will be redacted. * @param noop Whether to operate in noop mode. */ -export async function redactUserMessagesIn(mjolnir: Mjolnir, userIdOrGlob: string, targetRoomIds: string[], limit = 1000, noop = false) { - const isAdmin = await mjolnir.isSynapseAdmin(); +export async function redactUserMessagesIn(client: MatrixSendClient, managementRoom: ManagementRoomOutput, userIdOrGlob: string, targetRoomIds: string[], isAdmin: boolean, limit = 1000, noop = false) { const usingGlob = userIdOrGlob.includes("*"); - async function adminRedaction(m: Mjolnir, userId: string, targetRooms: string[], lim = 1000) { + async function adminRedaction(c: MatrixSendClient, mr: ManagementRoomOutput, userId: string, targetRooms: string[], lim = 1000) { let redactID: string; const body = {"limit": lim, "rooms": targetRooms}; const redactEndpoint = `/_synapse/admin/v1/user/${userId}/redact`; - const response = await m.client.doRequest("GET", redactEndpoint, null, body); + const response = await c.doRequest("GET", redactEndpoint, null, body); redactID = response["redact_id"]; - await m.managementRoomOutput.logMessage(LogLevel.INFO, "utils#redactUserMessagesIn", `Successfully requested redaction, ID for task is ${redactID}`); + await mr.logMessage(LogLevel.INFO, "utils#redactUserMessagesIn", `Successfully requested redaction, ID for task is ${redactID}`); } - async function redaction(m: Mjolnir, uIdOrGlob: string, targetRooms: string [], lim = 1000) { + async function redaction(c: MatrixSendClient, mr: ManagementRoomOutput, uIdOrGlob: string, targetRooms: string [], lim = 1000) { for (const targetRoomId of targetRooms) { - await m.managementRoomOutput.logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Fetching sent messages for ${uIdOrGlob} in ${targetRoomId} to redact...`, targetRoomId); + await mr.logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Fetching sent messages for ${uIdOrGlob} in ${targetRoomId} to redact...`, targetRoomId); try { - await getMessagesByUserIn(m.client, uIdOrGlob, targetRoomId, lim, async (eventsToRedact) => { + await getMessagesByUserIn(c, uIdOrGlob, targetRoomId, lim, async (eventsToRedact) => { for (const targetEvent of eventsToRedact) { - await m.managementRoomOutput.logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Redacting ${targetEvent['event_id']} in ${targetRoomId}`, targetRoomId); + await mr.logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Redacting ${targetEvent['event_id']} in ${targetRoomId}`, targetRoomId); if (!noop) { - await m.client.redactEvent(targetRoomId, targetEvent['event_id']); + await c.redactEvent(targetRoomId, targetEvent['event_id']); } else { - await m.managementRoomOutput.logMessage(LogLevel.WARN, "utils#redactUserMessagesIn", `Tried to redact ${targetEvent['event_id']} in ${targetRoomId} but Mjolnir is running in no-op mode`, targetRoomId); + await mr.logMessage(LogLevel.WARN, "utils#redactUserMessagesIn", `Tried to redact ${targetEvent['event_id']} in ${targetRoomId} but Mjolnir is running in no-op mode`, targetRoomId); } } }); } catch (error) { - await m.managementRoomOutput.logMessage(LogLevel.ERROR, "utils#redactUserMessagesIn", `Caught an error while trying to redact messages for ${userIdOrGlob} in ${targetRoomId}: ${error}`, targetRoomId); + await mr.logMessage(LogLevel.ERROR, "utils#redactUserMessagesIn", `Caught an error while trying to redact messages for ${userIdOrGlob} in ${targetRoomId}: ${error}`, targetRoomId); } } } @@ -118,15 +119,15 @@ export async function redactUserMessagesIn(mjolnir: Mjolnir, userIdOrGlob: strin // if admin use the Admin API, but admin endpoint does not support globs if (isAdmin && !usingGlob) { try { - await adminRedaction(mjolnir, userIdOrGlob, targetRoomIds, limit) + await adminRedaction(client, managementRoom, userIdOrGlob, targetRoomIds, limit) } catch (e) { LogService.error("utils#redactUserMessagesIn", `Error using admin API to redact messages: ${extractRequestError(e)}`); - await mjolnir.managementRoomOutput.logMessage(LogLevel.ERROR, "utils#redactUserMessagesIn", `Error using admin API to redact messages for user ${userIdOrGlob}, please check logs for more info - falling + await managementRoom.logMessage(LogLevel.ERROR, "utils#redactUserMessagesIn", `Error using admin API to redact messages for user ${userIdOrGlob}, please check logs for more info - falling back to non-admin redaction process.`); - await redaction(mjolnir, userIdOrGlob, targetRoomIds, limit); + await redaction(client, managementRoom, userIdOrGlob, targetRoomIds, limit); } } else { - await redaction(mjolnir, userIdOrGlob, targetRoomIds, limit); + await redaction(client, managementRoom, userIdOrGlob, targetRoomIds, limit); } }