mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Add a redact command
This commit is contained in:
parent
28f739a3a2
commit
78b73153b7
@ -15,7 +15,7 @@ Phase 1:
|
||||
Phase 2:
|
||||
* [x] Pantalaimon support
|
||||
* [x] No-op mode (for verifying behaviour)
|
||||
* [ ] Redact messages on ban (optionally)
|
||||
* [x] Redact messages on ban (optionally)
|
||||
* [x] More useful spam in management room
|
||||
* [ ] Command to import ACLs, etc from rooms
|
||||
* [x] Vet rooms on startup option
|
||||
|
@ -24,6 +24,7 @@ import { execSyncCommand } from "./SyncCommand";
|
||||
import { execPermissionCheckCommand } from "./PermissionCheckCommand";
|
||||
import { execCreateListCommand } from "./CreateBanListCommand";
|
||||
import { execUnwatchCommand, execWatchCommand } from "./WatchUnwatchCommand";
|
||||
import { execRedactCommand } from "./RedactCommand";
|
||||
|
||||
export const COMMAND_PREFIX = "!mjolnir";
|
||||
|
||||
@ -50,6 +51,8 @@ export async function handleCommand(roomId: string, event: any, mjolnir: Mjolnir
|
||||
return await execWatchCommand(roomId, event, mjolnir, parts);
|
||||
} else if (parts[1] === 'unwatch' && parts.length > 1) {
|
||||
return await execUnwatchCommand(roomId, event, mjolnir, parts);
|
||||
} else if (parts[1] === 'redact' && parts.length > 1) {
|
||||
return await execRedactCommand(roomId, event, mjolnir, parts);
|
||||
} else {
|
||||
// Help menu
|
||||
const menu = "" +
|
||||
@ -57,6 +60,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> - Removes an entity from the ban list\n" +
|
||||
"!mjolnir redact <user_id> [room alias/ID] - Redacts messages by the sender in the target room (or all rooms)\n" +
|
||||
"!mjolnir rules - Lists the rules currently in use by Mjolnir\n" +
|
||||
"!mjolnir sync - Force updates of all lists and re-apply rules\n" +
|
||||
"!mjolnir verify - Ensures Mjolnir can moderate all your rooms\n" +
|
||||
|
47
src/commands/RedactCommand.ts
Normal file
47
src/commands/RedactCommand.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { getMessagesByUserSinceLastJoin } from "../utils";
|
||||
import config from "../config";
|
||||
|
||||
// !mjolnir redact <user ID> [room alias]
|
||||
export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
|
||||
const userId = parts[2];
|
||||
let roomAlias = null;
|
||||
if (parts.length > 3) {
|
||||
roomAlias = await mjolnir.client.resolveRoom(parts[3]);
|
||||
}
|
||||
|
||||
const targetRoomIds = roomAlias ? [roomAlias] : Object.keys(mjolnir.protectedRooms);
|
||||
for (const targetRoomId of targetRoomIds) {
|
||||
if (config.verboseLogging) {
|
||||
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Fetching sent messages for ${userId} in ${targetRoomId} to redact...`);
|
||||
}
|
||||
|
||||
const eventsToRedact = await getMessagesByUserSinceLastJoin(mjolnir.client, userId, targetRoomId);
|
||||
for (const event of eventsToRedact) {
|
||||
if (config.verboseLogging) {
|
||||
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Redacting ${event['event_id']} in ${targetRoomId}`);
|
||||
}
|
||||
if (!config.noop) {
|
||||
await mjolnir.client.redactEvent(targetRoomId, event['event_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
|
||||
}
|
86
src/utils.ts
86
src/utils.ts
@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixClient } from "matrix-bot-sdk";
|
||||
|
||||
export function setToArray<T>(set: Set<T>): T[] {
|
||||
const arr: T[] = [];
|
||||
for (const v of set) {
|
||||
@ -21,3 +23,87 @@ export function setToArray<T>(set: Set<T>): T[] {
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
export async function getMessagesByUserSinceLastJoin(client: MatrixClient, sender: string, roomId: string): Promise<any[]> {
|
||||
const filter = {
|
||||
room: {
|
||||
rooms: [roomId],
|
||||
state: {
|
||||
types: ["m.room.member"],
|
||||
rooms: [roomId],
|
||||
},
|
||||
timeline: {
|
||||
senders: [sender],
|
||||
rooms: [roomId],
|
||||
},
|
||||
ephemeral: {
|
||||
limit: 0,
|
||||
types: [],
|
||||
},
|
||||
account_data: {
|
||||
limit: 0,
|
||||
types: [],
|
||||
},
|
||||
},
|
||||
presence: {
|
||||
limit: 0,
|
||||
types: [],
|
||||
},
|
||||
account_data: {
|
||||
limit: 0,
|
||||
types: [],
|
||||
},
|
||||
};
|
||||
|
||||
function initialSync() {
|
||||
const qs = {
|
||||
filter: JSON.stringify(filter),
|
||||
};
|
||||
return client.doRequest("GET", "/_matrix/client/r0/sync", qs);
|
||||
}
|
||||
|
||||
function backfill(from: string) {
|
||||
const qs = {
|
||||
filter: JSON.stringify(filter),
|
||||
from: from,
|
||||
dir: "b",
|
||||
};
|
||||
return client.doRequest("GET", `/_matrix/client/r0/rooms/${encodeURIComponent(roomId)}/messages`, qs);
|
||||
}
|
||||
|
||||
// Do an initial sync first to get the batch token
|
||||
const response = await initialSync();
|
||||
if (!response) return [];
|
||||
|
||||
let token = response['next_batch'];
|
||||
|
||||
const messages = [];
|
||||
|
||||
const timeline = (((response['rooms'] || {})['join'] || {})[roomId] || {})['timeline'] || {};
|
||||
const syncedMessages = timeline['events'] || [];
|
||||
token = timeline['prev_batch'] || token;
|
||||
for (const event of syncedMessages) {
|
||||
if (event['sender'] === sender) messages.push(event);
|
||||
if (event['type'] === 'm.room.member' && event['state_key'] === sender) {
|
||||
if (event['content'] && event['content']['membership'] === 'join') {
|
||||
return messages; // we're done!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (token) {
|
||||
const bfMessages = await backfill(token);
|
||||
token = bfMessages['end'];
|
||||
|
||||
for (const event of (bfMessages['chunk'] || [])) {
|
||||
if (event['sender'] === sender) messages.push(event);
|
||||
if (event['type'] === 'm.room.member' && event['state_key'] === sender) {
|
||||
if (event['content'] && event['content']['membership'] === 'join') {
|
||||
return messages; // we're done!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user