Improve redaction handling

This commit is contained in:
Travis Ralston 2020-04-14 16:44:31 -06:00
parent 954a0e1561
commit 6f80a17558
3 changed files with 21 additions and 16 deletions

View File

@ -104,7 +104,7 @@ export async function handleCommand(roomId: string, event: any, mjolnir: Mjolnir
"!mjolnir status - Print status information\n" +
"!mjolnir ban <list shortcode> <user|room|server> <glob> [reason] - Adds an entity to the ban list\n" +
"!mjolnir unban <list shortcode> <user|room|server> <glob> [apply] - Removes an entity from the ban list. If apply is 'true', the users matching the glob will actually be unbanned\n" +
"!mjolnir redact <user ID> [room alias/ID] - Redacts messages by the sender in the target room (or all rooms)\n" +
"!mjolnir redact <user ID> [room alias/ID] [limit] - Redacts messages by the sender in the target room (or all rooms), up to a maximum number of events in the backlog (default 1000)\n" +
"!mjolnir redact <event permalink> - Redacts a message by permalink\n" +
"!mjolnir rules - Lists the rules currently in use by Mjolnir\n" +
"!mjolnir sync - Force updates of all lists and re-apply rules\n" +

View File

@ -18,25 +18,36 @@ import { Mjolnir } from "../Mjolnir";
import { redactUserMessagesIn } from "../utils";
import { Permalinks } from "matrix-bot-sdk";
// !mjolnir redact <user ID> [room alias]
// !mjolnir redact <user ID> [room alias] [limit]
export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
const userId = parts[2];
let roomAlias = null;
if (parts.length > 3) {
let limit = Number.parseInt(parts.length > 3 ? parts[3] : null); // default to NaN for later
if (parts.length > 3 && isNaN(limit)) {
roomAlias = await mjolnir.client.resolveRoom(parts[3]);
if (parts.length > 4) {
limit = Number.parseInt(parts[4]);
}
}
// Make sure we always have a limit set
if (isNaN(limit)) limit = 1000;
const processingReactionId = await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], 'In Progress');
if (userId[0] !== '@') {
// Assume it's a permalink
const parsed = Permalinks.parseUrl(parts[2]);
const targetRoomId = await mjolnir.client.resolveRoom(parsed.roomIdOrAlias);
await mjolnir.client.redactEvent(targetRoomId, parsed.eventId);
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing command');
return;
}
const targetRoomIds = roomAlias ? [roomAlias] : Object.keys(mjolnir.protectedRooms);
await redactUserMessagesIn(mjolnir.client, userId, targetRoomIds);
await redactUserMessagesIn(mjolnir.client, userId, targetRoomIds, limit);
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing');
}

View File

@ -37,11 +37,11 @@ export function isTrueJoinEvent(event: any): boolean {
return membership === 'join' && prevMembership !== "join";
}
export async function redactUserMessagesIn(client: MatrixClient, userIdOrGlob: string, targetRoomIds: string[]) {
export async function redactUserMessagesIn(client: MatrixClient, userIdOrGlob: string, targetRoomIds: string[], limit = 1000) {
for (const targetRoomId of targetRoomIds) {
await logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Fetching sent messages for ${userIdOrGlob} in ${targetRoomId} to redact...`);
const eventsToRedact = await getMessagesByUserSinceLastJoin(client, userIdOrGlob, targetRoomId);
const eventsToRedact = await getMessagesByUserIn(client, userIdOrGlob, targetRoomId, limit);
for (const victimEvent of eventsToRedact) {
await logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Redacting ${victimEvent['event_id']} in ${targetRoomId}`);
if (!config.noop) {
@ -59,20 +59,20 @@ export async function redactUserMessagesIn(client: MatrixClient, userIdOrGlob: s
* @param {MatrixClient} client The client to use.
* @param {string} sender The sender. Can include wildcards to match multiple people.
* @param {string} roomId The room ID to search in.
* @param {number} limit The maximum number of messages to search. Defaults to 1000.
* @returns {Promise<any>} Resolves to the events sent by the user(s) prior to join.
*/
export async function getMessagesByUserSinceLastJoin(client: MatrixClient, sender: string, roomId: string): Promise<any[]> {
const limit = 1000; // maximum number of events to process, regardless of outcome
export async function getMessagesByUserIn(client: MatrixClient, sender: string, roomId: string, limit: number): Promise<any[]> {
const filter = {
room: {
rooms: [roomId],
state: {
types: ["m.room.member"],
// types: ["m.room.member"], // We'll redact all types of events
rooms: [roomId],
},
timeline: {
rooms: [roomId],
types: ["m.room.message"],
// types: ["m.room.message"], // We'll redact all types of events
},
ephemeral: {
limit: 0,
@ -131,7 +131,6 @@ export async function getMessagesByUserSinceLastJoin(client: MatrixClient, sende
if (!response) return [];
const messages = [];
const stopProcessingMembers = [];
let processed = 0;
const timeline = (((response['rooms'] || {})['join'] || {})[roomId] || {})['timeline'] || {};
@ -143,12 +142,7 @@ export async function getMessagesByUserSinceLastJoin(client: MatrixClient, sende
if (processed >= limit) return messages; // we're done even if we don't want to be
processed++;
if (stopProcessingMembers.includes(event['sender'])) continue;
if (testUser(event['sender'])) messages.push(event);
if (event['type'] === 'm.room.member' && testUser(event['state_key']) && isTrueJoinEvent(event)) {
stopProcessingMembers.push(event['sender']);
if (!isGlob) return messages; // done!
}
}
if (token) {