move LogProxy.logMessage to Mjolnir.logMessage (#194)

This commit is contained in:
Jess Porter 2022-02-15 15:44:41 +00:00 committed by GitHub
parent e9dff8fd5a
commit a58c7d3f1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 129 additions and 155 deletions

View File

@ -1,55 +0,0 @@
/*
Copyright 2019, 2020 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 { LogLevel, LogService, TextualMessageEventContent } from "matrix-bot-sdk";
import config from "./config";
import { htmlEscape, replaceRoomIdsWithPills } from "./utils";
const levelToFn = {
[LogLevel.DEBUG.toString()]: LogService.debug,
[LogLevel.INFO.toString()]: LogService.info,
[LogLevel.WARN.toString()]: LogService.warn,
[LogLevel.ERROR.toString()]: LogService.error,
};
export async function logMessage(level: LogLevel, module: string, message: string | any, additionalRoomIds: string[] | string | null = null, isRecursive = false) {
if (!additionalRoomIds) additionalRoomIds = [];
if (!Array.isArray(additionalRoomIds)) additionalRoomIds = [additionalRoomIds];
if (config.RUNTIME.client && (config.verboseLogging || LogLevel.INFO.includes(level))) {
let clientMessage = message;
if (level === LogLevel.WARN) clientMessage = `⚠ | ${message}`;
if (level === LogLevel.ERROR) clientMessage = `‼ | ${message}`;
const client = config.RUNTIME.client;
const managementRoomId = await client.resolveRoom(config.managementRoom);
const roomIds = new Set([managementRoomId, ...additionalRoomIds]);
let evContent: TextualMessageEventContent = {
body: message,
formatted_body: htmlEscape(message),
msgtype: "m.notice",
format: "org.matrix.custom.html",
};
if (!isRecursive) {
evContent = await replaceRoomIdsWithPills(client, clientMessage, roomIds, "m.notice");
}
await client.sendMessage(managementRoomId, evContent);
}
levelToFn[level.toString()](module, message);
}

View File

@ -23,7 +23,8 @@ import {
MatrixGlob, MatrixGlob,
MembershipEvent, MembershipEvent,
Permalinks, Permalinks,
UserID UserID,
TextualMessageEventContent
} from "matrix-bot-sdk"; } from "matrix-bot-sdk";
import BanList, { ALL_RULE_TYPES as ALL_BAN_LIST_RULE_TYPES, ListRuleChange, RULE_ROOM, RULE_SERVER, RULE_USER } from "./models/BanList"; import BanList, { ALL_RULE_TYPES as ALL_BAN_LIST_RULE_TYPES, ListRuleChange, RULE_ROOM, RULE_SERVER, RULE_USER } from "./models/BanList";
@ -32,7 +33,6 @@ import { RoomUpdateError } from "./models/RoomUpdateError";
import { COMMAND_PREFIX, handleCommand } from "./commands/CommandHandler"; import { COMMAND_PREFIX, handleCommand } from "./commands/CommandHandler";
import { applyUserBans } from "./actions/ApplyBan"; import { applyUserBans } from "./actions/ApplyBan";
import config from "./config"; import config from "./config";
import { logMessage } from "./LogProxy";
import ErrorCache, { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "./ErrorCache"; import ErrorCache, { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "./ErrorCache";
import { IProtection } from "./protections/IProtection"; import { IProtection } from "./protections/IProtection";
import { PROTECTIONS } from "./protections/protections"; import { PROTECTIONS } from "./protections/protections";
@ -43,8 +43,16 @@ import { EventRedactionQueue, RedactUserInRoom } from "./queues/EventRedactionQu
import { htmlEscape } from "./utils"; import { htmlEscape } from "./utils";
import { ReportManager } from "./report/ReportManager"; import { ReportManager } from "./report/ReportManager";
import { WebAPIs } from "./webapis/WebAPIs"; import { WebAPIs } from "./webapis/WebAPIs";
import { replaceRoomIdsWithPills } from "./utils";
import RuleServer from "./models/RuleServer"; import RuleServer from "./models/RuleServer";
const levelToFn = {
[LogLevel.DEBUG.toString()]: LogService.debug,
[LogLevel.INFO.toString()]: LogService.info,
[LogLevel.WARN.toString()]: LogService.warn,
[LogLevel.ERROR.toString()]: LogService.error,
};
export const STATE_NOT_STARTED = "not_started"; export const STATE_NOT_STARTED = "not_started";
export const STATE_CHECKING_PERMISSIONS = "checking_permissions"; export const STATE_CHECKING_PERMISSIONS = "checking_permissions";
export const STATE_SYNCING = "syncing"; export const STATE_SYNCING = "syncing";
@ -144,10 +152,10 @@ export class Mjolnir {
if (!joinedRooms.includes(managementRoomId)) { if (!joinedRooms.includes(managementRoomId)) {
await client.joinRoom(config.managementRoom); await client.joinRoom(config.managementRoom);
} }
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
const ruleServer = config.web.ruleServer ? new RuleServer() : null; const ruleServer = config.web.ruleServer ? new RuleServer() : null;
const mjolnir = new Mjolnir(client, managementRoomId, protectedRooms, banLists, ruleServer); const mjolnir = new Mjolnir(client, managementRoomId, protectedRooms, banLists, ruleServer);
await mjolnir.logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
Mjolnir.addJoinOnInviteListener(mjolnir, client, config); Mjolnir.addJoinOnInviteListener(mjolnir, client, config);
return mjolnir; return mjolnir;
} }
@ -270,7 +278,7 @@ export class Mjolnir {
// Load the state. // Load the state.
this.currentState = STATE_CHECKING_PERMISSIONS; this.currentState = STATE_CHECKING_PERMISSIONS;
await logMessage(LogLevel.DEBUG, "Mjolnir@startup", "Loading protected rooms..."); await this.logMessage(LogLevel.DEBUG, "Mjolnir@startup", "Loading protected rooms...");
await this.resyncJoinedRooms(false); await this.resyncJoinedRooms(false);
try { try {
const data: { rooms?: string[] } | null = await this.client.getAccountData(PROTECTED_ROOMS_EVENT_TYPE); const data: { rooms?: string[] } | null = await this.client.getAccountData(PROTECTED_ROOMS_EVENT_TYPE);
@ -287,25 +295,25 @@ export class Mjolnir {
this.applyUnprotectedRooms(); this.applyUnprotectedRooms();
if (config.verifyPermissionsOnStartup) { if (config.verifyPermissionsOnStartup) {
await logMessage(LogLevel.INFO, "Mjolnir@startup", "Checking permissions..."); await this.logMessage(LogLevel.INFO, "Mjolnir@startup", "Checking permissions...");
await this.verifyPermissions(config.verboseLogging); await this.verifyPermissions(config.verboseLogging);
} }
this.currentState = STATE_SYNCING; this.currentState = STATE_SYNCING;
if (config.syncOnStartup) { if (config.syncOnStartup) {
await logMessage(LogLevel.INFO, "Mjolnir@startup", "Syncing lists..."); await this.logMessage(LogLevel.INFO, "Mjolnir@startup", "Syncing lists...");
await this.syncLists(config.verboseLogging); await this.syncLists(config.verboseLogging);
await this.registerProtections(); await this.registerProtections();
} }
this.currentState = STATE_RUNNING; this.currentState = STATE_RUNNING;
Healthz.isHealthy = true; Healthz.isHealthy = true;
await logMessage(LogLevel.INFO, "Mjolnir@startup", "Startup complete. Now monitoring rooms."); await this.logMessage(LogLevel.INFO, "Mjolnir@startup", "Startup complete. Now monitoring rooms.");
} catch (err) { } catch (err) {
try { try {
LogService.error("Mjolnir", "Error during startup:"); LogService.error("Mjolnir", "Error during startup:");
LogService.error("Mjolnir", extractRequestError(err)); LogService.error("Mjolnir", extractRequestError(err));
await logMessage(LogLevel.ERROR, "Mjolnir@startup", "Startup failed due to error - see console"); await this.logMessage(LogLevel.ERROR, "Mjolnir@startup", "Startup failed due to error - see console");
} catch (e) { } catch (e) {
// If we failed to handle the error, just crash // If we failed to handle the error, just crash
console.error(e); console.error(e);
@ -323,6 +331,36 @@ export class Mjolnir {
this.webapis.stop(); this.webapis.stop();
} }
public async logMessage(level: LogLevel, module: string, message: string | any, additionalRoomIds: string[] | string | null = null, isRecursive = false): Promise<any> {
if (!additionalRoomIds) additionalRoomIds = [];
if (!Array.isArray(additionalRoomIds)) additionalRoomIds = [additionalRoomIds];
if (config.RUNTIME.client && (config.verboseLogging || LogLevel.INFO.includes(level))) {
let clientMessage = message;
if (level === LogLevel.WARN) clientMessage = `⚠ | ${message}`;
if (level === LogLevel.ERROR) clientMessage = `‼ | ${message}`;
const client = config.RUNTIME.client;
const managementRoomId = await client.resolveRoom(config.managementRoom);
const roomIds = [managementRoomId, ...additionalRoomIds];
let evContent: TextualMessageEventContent = {
body: message,
formatted_body: htmlEscape(message),
msgtype: "m.notice",
format: "org.matrix.custom.html",
};
if (!isRecursive) {
evContent = await replaceRoomIdsWithPills(this, clientMessage, new Set(roomIds), "m.notice");
}
await client.sendMessage(managementRoomId, evContent);
}
levelToFn[level.toString()](module, message);
}
public async addProtectedRoom(roomId: string) { public async addProtectedRoom(roomId: string) {
this.protectedRooms[roomId] = Permalinks.forRoom(roomId); this.protectedRooms[roomId] = Permalinks.forRoom(roomId);
@ -455,7 +493,7 @@ export class Mjolnir {
) { ) {
validatedSettings[key] = value; validatedSettings[key] = value;
} else { } else {
await logMessage( await this.logMessage(
LogLevel.WARN, LogLevel.WARN,
"getProtectionSetting", "getProtectionSetting",
`Tried to read ${protectionName}.${key} and got invalid value ${value}` `Tried to read ${protectionName}.${key} and got invalid value ${value}`
@ -606,7 +644,7 @@ export class Mjolnir {
// Ignore - probably haven't warned about it yet // Ignore - probably haven't warned about it yet
} }
await logMessage(LogLevel.WARN, "Mjolnir", `Not protecting ${roomId} - it is a ban list that this bot did not create. Add the room as protected if it is supposed to be protected. This warning will not appear again.`, roomId); await this.logMessage(LogLevel.WARN, "Mjolnir", `Not protecting ${roomId} - it is a ban list that this bot did not create. Add the room as protected if it is supposed to be protected. This warning will not appear again.`, roomId);
await this.client.setAccountData(WARN_UNPROTECTED_ROOM_EVENT_PREFIX + roomId, { warned: true }); await this.client.setAccountData(WARN_UNPROTECTED_ROOM_EVENT_PREFIX + roomId, { warned: true });
} }
@ -834,16 +872,16 @@ export class Mjolnir {
// Run the event handlers - we always run this after protections so that the protections // Run the event handlers - we always run this after protections so that the protections
// can flag the event for redaction. // can flag the event for redaction.
await this.unlistedUserRedactionHandler.handleEvent(roomId, event, this.client); await this.unlistedUserRedactionHandler.handleEvent(roomId, event, this);
if (event['type'] === 'm.room.power_levels' && event['state_key'] === '') { if (event['type'] === 'm.room.power_levels' && event['state_key'] === '') {
// power levels were updated - recheck permissions // power levels were updated - recheck permissions
ErrorCache.resetError(roomId, ERROR_KIND_PERMISSION); ErrorCache.resetError(roomId, ERROR_KIND_PERMISSION);
await logMessage(LogLevel.DEBUG, "Mjolnir", `Power levels changed in ${roomId} - checking permissions...`, roomId); await this.logMessage(LogLevel.DEBUG, "Mjolnir", `Power levels changed in ${roomId} - checking permissions...`, roomId);
const errors = await this.verifyPermissionsIn(roomId); const errors = await this.verifyPermissionsIn(roomId);
const hadErrors = await this.printActionResult(errors); const hadErrors = await this.printActionResult(errors);
if (!hadErrors) { if (!hadErrors) {
await logMessage(LogLevel.DEBUG, "Mjolnir", `All permissions look OK.`); await this.logMessage(LogLevel.DEBUG, "Mjolnir", `All permissions look OK.`);
} }
return; return;
} else if (event['type'] === "m.room.member") { } else if (event['type'] === "m.room.member") {
@ -982,7 +1020,7 @@ export class Mjolnir {
* @returns The list of errors encountered, for reporting to the management room. * @returns The list of errors encountered, for reporting to the management room.
*/ */
public async processRedactionQueue(roomId?: string): Promise<RoomUpdateError[]> { public async processRedactionQueue(roomId?: string): Promise<RoomUpdateError[]> {
return await this.eventRedactionQueue.process(this.client, roomId); return await this.eventRedactionQueue.process(this, roomId);
} }
private async handleReport(roomId: string, reporterId: string, event: any, reason?: string) { private async handleReport(roomId: string, reporterId: string, event: any, reason?: string) {

View File

@ -20,7 +20,6 @@ import { RoomUpdateError } from "../models/RoomUpdateError";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import config from "../config"; import config from "../config";
import { LogLevel, UserID } from "matrix-bot-sdk"; import { LogLevel, UserID } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache"; import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache";
/** /**
@ -45,7 +44,7 @@ export async function applyServerAcls(lists: BanList[], roomIds: string[], mjoln
const finalAcl = acl.safeAclContent(); const finalAcl = acl.safeAclContent();
if (finalAcl.deny.length !== acl.literalAclContent().deny.length) { if (finalAcl.deny.length !== acl.literalAclContent().deny.length) {
logMessage(LogLevel.WARN, "ApplyAcl", `Mjölnir has detected and removed an ACL that would exclude itself. Please check the ACL lists.`); mjolnir.logMessage(LogLevel.WARN, "ApplyAcl", `Mjölnir has detected and removed an ACL that would exclude itself. Please check the ACL lists.`);
} }
if (config.verboseLogging) { if (config.verboseLogging) {
@ -56,12 +55,12 @@ export async function applyServerAcls(lists: BanList[], roomIds: string[], mjoln
const errors: RoomUpdateError[] = []; const errors: RoomUpdateError[] = [];
for (const roomId of roomIds) { for (const roomId of roomIds) {
try { try {
await logMessage(LogLevel.DEBUG, "ApplyAcl", `Checking ACLs for ${roomId}`, roomId); await mjolnir.logMessage(LogLevel.DEBUG, "ApplyAcl", `Checking ACLs for ${roomId}`, roomId);
try { try {
const currentAcl = await mjolnir.client.getRoomStateEvent(roomId, "m.room.server_acl", ""); const currentAcl = await mjolnir.client.getRoomStateEvent(roomId, "m.room.server_acl", "");
if (acl.matches(currentAcl)) { if (acl.matches(currentAcl)) {
await logMessage(LogLevel.DEBUG, "ApplyAcl", `Skipping ACLs for ${roomId} because they are already the right ones`, roomId); await mjolnir.logMessage(LogLevel.DEBUG, "ApplyAcl", `Skipping ACLs for ${roomId} because they are already the right ones`, roomId);
continue; continue;
} }
} catch (e) { } catch (e) {
@ -69,12 +68,12 @@ export async function applyServerAcls(lists: BanList[], roomIds: string[], mjoln
} }
// We specifically use sendNotice to avoid having to escape HTML // We specifically use sendNotice to avoid having to escape HTML
await logMessage(LogLevel.DEBUG, "ApplyAcl", `Applying ACL in ${roomId}`, roomId); await mjolnir.logMessage(LogLevel.DEBUG, "ApplyAcl", `Applying ACL in ${roomId}`, roomId);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.sendStateEvent(roomId, "m.room.server_acl", "", finalAcl); await mjolnir.client.sendStateEvent(roomId, "m.room.server_acl", "", finalAcl);
} else { } else {
await logMessage(LogLevel.WARN, "ApplyAcl", `Tried to apply ACL in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "ApplyAcl", `Tried to apply ACL in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
} catch (e) { } catch (e) {
const message = e.message || (e.body ? e.body.error : '<no message>'); const message = e.message || (e.body ? e.body.error : '<no message>');

View File

@ -18,7 +18,6 @@ import BanList from "../models/BanList";
import { RoomUpdateError } from "../models/RoomUpdateError"; import { RoomUpdateError } from "../models/RoomUpdateError";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import config from "../config"; import config from "../config";
import { logMessage } from "../LogProxy";
import { LogLevel } from "matrix-bot-sdk"; import { LogLevel } from "matrix-bot-sdk";
import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache"; import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache";
@ -35,7 +34,7 @@ export async function applyUserBans(lists: BanList[], roomIds: string[], mjolnir
for (const roomId of roomIds) { for (const roomId of roomIds) {
try { try {
// We specifically use sendNotice to avoid having to escape HTML // We specifically use sendNotice to avoid having to escape HTML
await logMessage(LogLevel.DEBUG, "ApplyBan", `Updating member bans in ${roomId}`, roomId); await mjolnir.logMessage(LogLevel.DEBUG, "ApplyBan", `Updating member bans in ${roomId}`, roomId);
let members: { userId: string, membership: string }[]; let members: { userId: string, membership: string }[];
@ -63,7 +62,7 @@ export async function applyUserBans(lists: BanList[], roomIds: string[], mjolnir
// User needs to be banned // User needs to be banned
// We specifically use sendNotice to avoid having to escape HTML // We specifically use sendNotice to avoid having to escape HTML
await logMessage(LogLevel.INFO, "ApplyBan", `Banning ${member.userId} in ${roomId} for: ${userRule.reason}`, roomId); await mjolnir.logMessage(LogLevel.INFO, "ApplyBan", `Banning ${member.userId} in ${roomId} for: ${userRule.reason}`, roomId);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.banUser(member.userId, roomId, userRule.reason); await mjolnir.client.banUser(member.userId, roomId, userRule.reason);
@ -71,7 +70,7 @@ export async function applyUserBans(lists: BanList[], roomIds: string[], mjolnir
mjolnir.queueRedactUserMessagesIn(member.userId, roomId); mjolnir.queueRedactUserMessagesIn(member.userId, roomId);
} }
} else { } else {
await logMessage(LogLevel.WARN, "ApplyBan", `Tried to ban ${member.userId} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "ApplyBan", `Tried to ban ${member.userId} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
banned = true; banned = true;

View File

@ -16,7 +16,6 @@ limitations under the License.
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { extractRequestError, LogLevel, LogService } from "matrix-bot-sdk"; import { extractRequestError, LogLevel, LogService } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
// !mjolnir rooms add <room alias/ID> // !mjolnir rooms add <room alias/ID>
export async function execAddProtectedRoom(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) { export async function execAddProtectedRoom(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
@ -33,7 +32,7 @@ export async function execRemoveProtectedRoom(roomId: string, event: any, mjolni
await mjolnir.client.leaveRoom(protectedRoomId); await mjolnir.client.leaveRoom(protectedRoomId);
} catch (e) { } catch (e) {
LogService.warn("AddRemoveProtectedRoomsCommand", extractRequestError(e)); LogService.warn("AddRemoveProtectedRoomsCommand", extractRequestError(e));
await logMessage(LogLevel.WARN, "AddRemoveProtectedRoomsCommand", `Failed to leave ${protectedRoomId} - the room is no longer being protected, but the bot could not leave`, protectedRoomId); await mjolnir.logMessage(LogLevel.WARN, "AddRemoveProtectedRoomsCommand", `Failed to leave ${protectedRoomId} - the room is no longer being protected, but the bot could not leave`, protectedRoomId);
} }
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅'); await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { LogLevel } from "matrix-bot-sdk"; import { LogLevel } from "matrix-bot-sdk";
import config from "../config"; import config from "../config";
import { logMessage } from "../LogProxy";
// !mjolnir kick <user> [room] [reason] // !mjolnir kick <user> [room] [reason]
export async function execKickCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) { export async function execKickCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
@ -39,11 +38,11 @@ export async function execKickCommand(roomId: string, event: any, mjolnir: Mjoln
const joinedUsers = await mjolnir.client.getJoinedRoomMembers(targetRoomId); const joinedUsers = await mjolnir.client.getJoinedRoomMembers(targetRoomId);
if (!joinedUsers.includes(userId)) continue; // skip if (!joinedUsers.includes(userId)) continue; // skip
await logMessage(LogLevel.INFO, "KickCommand", `Kicking ${userId} in ${targetRoomId} for ${reason}`, targetRoomId); await mjolnir.logMessage(LogLevel.INFO, "KickCommand", `Kicking ${userId} in ${targetRoomId} for ${reason}`, targetRoomId);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.kickUser(userId, targetRoomId, reason); await mjolnir.client.kickUser(userId, targetRoomId, reason);
} else { } else {
await logMessage(LogLevel.WARN, "KickCommand", `Tried to kick ${userId} in ${targetRoomId} but the bot is running in no-op mode.`, targetRoomId); await mjolnir.logMessage(LogLevel.WARN, "KickCommand", `Tried to kick ${userId} in ${targetRoomId} but the bot is running in no-op mode.`, targetRoomId);
} }
} }

View File

@ -46,7 +46,7 @@ export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjo
} }
const targetRoomIds = roomAlias ? [roomAlias] : Object.keys(mjolnir.protectedRooms); const targetRoomIds = roomAlias ? [roomAlias] : Object.keys(mjolnir.protectedRooms);
await redactUserMessagesIn(mjolnir.client, userId, targetRoomIds, limit); await redactUserMessagesIn(mjolnir, userId, targetRoomIds, limit);
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅'); await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing'); await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing');

View File

@ -16,7 +16,6 @@ limitations under the License.
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { extractRequestError, LogLevel, LogService } from "matrix-bot-sdk"; import { extractRequestError, LogLevel, LogService } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
// !mjolnir powerlevel <user ID> <level> [room] // !mjolnir powerlevel <user ID> <level> [room]
export async function execSetPowerLevelCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) { export async function execSetPowerLevelCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
@ -31,7 +30,7 @@ export async function execSetPowerLevelCommand(roomId: string, event: any, mjoln
await mjolnir.client.setUserPowerLevel(victim, targetRoomId, level); await mjolnir.client.setUserPowerLevel(victim, targetRoomId, level);
} catch (e) { } catch (e) {
const message = e.message || (e.body ? e.body.error : '<no message>'); const message = e.message || (e.body ? e.body.error : '<no message>');
await logMessage(LogLevel.ERROR, "SetPowerLevelCommand", `Failed to set power level of ${victim} to ${level} in ${targetRoomId}: ${message}`, targetRoomId); await mjolnir.logMessage(LogLevel.ERROR, "SetPowerLevelCommand", `Failed to set power level of ${victim} to ${level} in ${targetRoomId}: ${message}`, targetRoomId);
LogService.error("SetPowerLevelCommand", extractRequestError(e)); LogService.error("SetPowerLevelCommand", extractRequestError(e));
} }
} }

View File

@ -19,7 +19,6 @@ import BanList, { RULE_ROOM, RULE_SERVER, RULE_USER, USER_RULE_TYPES } from "../
import { extractRequestError, LogLevel, LogService, MatrixGlob, RichReply } from "matrix-bot-sdk"; import { extractRequestError, LogLevel, LogService, MatrixGlob, RichReply } from "matrix-bot-sdk";
import { RECOMMENDATION_BAN, recommendationToStable } from "../models/ListRule"; import { RECOMMENDATION_BAN, recommendationToStable } from "../models/ListRule";
import config from "../config"; import config from "../config";
import { logMessage } from "../LogProxy";
import { DEFAULT_LIST_EVENT_TYPE } from "./SetDefaultBanListCommand"; import { DEFAULT_LIST_EVENT_TYPE } from "./SetDefaultBanListCommand";
interface Arguments { interface Arguments {
@ -144,21 +143,21 @@ export async function execUnbanCommand(roomId: string, event: any, mjolnir: Mjol
if (USER_RULE_TYPES.includes(bits.ruleType!) && bits.reason === 'true') { if (USER_RULE_TYPES.includes(bits.ruleType!) && bits.reason === 'true') {
const rule = new MatrixGlob(bits.entity); const rule = new MatrixGlob(bits.entity);
await logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.entity); await mjolnir.logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.entity);
let unbannedSomeone = false; let unbannedSomeone = false;
for (const protectedRoomId of Object.keys(mjolnir.protectedRooms)) { for (const protectedRoomId of Object.keys(mjolnir.protectedRooms)) {
const members = await mjolnir.client.getRoomMembers(protectedRoomId, undefined, ['ban'], undefined); const members = await mjolnir.client.getRoomMembers(protectedRoomId, undefined, ['ban'], undefined);
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Found ${members.length} banned user(s)`); await mjolnir.logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Found ${members.length} banned user(s)`);
for (const member of members) { for (const member of members) {
const victim = member.membershipFor; const victim = member.membershipFor;
if (member.membership !== 'ban') continue; if (member.membership !== 'ban') continue;
if (rule.test(victim)) { if (rule.test(victim)) {
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Unbanning ${victim} in ${protectedRoomId}`, protectedRoomId); await mjolnir.logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Unbanning ${victim} in ${protectedRoomId}`, protectedRoomId);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.unbanUser(victim, protectedRoomId); await mjolnir.client.unbanUser(victim, protectedRoomId);
} else { } else {
await logMessage(LogLevel.WARN, "UnbanBanCommand", `Attempted to unban ${victim} in ${protectedRoomId} but Mjolnir is running in no-op mode`, protectedRoomId); await mjolnir.logMessage(LogLevel.WARN, "UnbanBanCommand", `Attempted to unban ${victim} in ${protectedRoomId} but Mjolnir is running in no-op mode`, protectedRoomId);
} }
unbannedSomeone = true; unbannedSomeone = true;
@ -167,7 +166,7 @@ export async function execUnbanCommand(roomId: string, event: any, mjolnir: Mjol
} }
if (unbannedSomeone) { if (unbannedSomeone) {
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Syncing lists to ensure no users were accidentally unbanned`); await mjolnir.logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Syncing lists to ensure no users were accidentally unbanned`);
await mjolnir.syncLists(config.verboseLogging); await mjolnir.syncLists(config.verboseLogging);
} }
} }

View File

@ -24,7 +24,6 @@ import {
SimpleFsStorageProvider SimpleFsStorageProvider
} from "matrix-bot-sdk"; } from "matrix-bot-sdk";
import config from "./config"; import config from "./config";
import { logMessage } from "./LogProxy";
import { Healthz } from "./health/healthz"; import { Healthz } from "./health/healthz";
import { Mjolnir } from "./Mjolnir"; import { Mjolnir } from "./Mjolnir";
import { patchMatrixClient } from "./utils"; import { patchMatrixClient } from "./utils";
@ -42,22 +41,25 @@ if (config.health.healthz.enabled) {
} }
(async function () { (async function () {
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath); let bot: Mjolnir | null = null;
const storage = new SimpleFsStorageProvider(path.join(storagePath, "bot.json")); try {
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
const storage = new SimpleFsStorageProvider(path.join(storagePath, "bot.json"));
let client: MatrixClient; let client: MatrixClient;
if (config.pantalaimon.use) { if (config.pantalaimon.use) {
const pantalaimon = new PantalaimonClient(config.homeserverUrl, storage); const pantalaimon = new PantalaimonClient(config.homeserverUrl, storage);
client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password); client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
} else { } else {
client = new MatrixClient(config.homeserverUrl, config.accessToken, storage); client = new MatrixClient(config.homeserverUrl, config.accessToken, storage);
}
patchMatrixClient();
config.RUNTIME.client = client;
bot = await Mjolnir.setupMjolnirFromConfig(client);
await bot.start();
} catch (err) {
bot?.logMessage(LogLevel.ERROR, "index", err);
process.exit(1);
} }
patchMatrixClient(); })();
config.RUNTIME.client = client;
let bot = await Mjolnir.setupMjolnirFromConfig(client);
await bot.start();
})().catch(err => {
logMessage(LogLevel.ERROR, "index", err);
process.exit(1);
});

View File

@ -18,7 +18,6 @@ import { Protection } from "./IProtection";
import { NumberProtectionSetting } from "./ProtectionSettings"; import { NumberProtectionSetting } from "./ProtectionSettings";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { LogLevel, LogService } from "matrix-bot-sdk"; import { LogLevel, LogService } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config"; import config from "../config";
// if this is exceeded, we'll ban the user for spam and redact their messages // if this is exceeded, we'll ban the user for spam and redact their messages
@ -64,11 +63,11 @@ export class BasicFlooding extends Protection {
} }
if (messageCount >= this.settings.maxPerMinute.value) { if (messageCount >= this.settings.maxPerMinute.value) {
await logMessage(LogLevel.WARN, "BasicFlooding", `Banning ${event['sender']} in ${roomId} for flooding (${messageCount} messages in the last minute)`, roomId); await mjolnir.logMessage(LogLevel.WARN, "BasicFlooding", `Banning ${event['sender']} in ${roomId} for flooding (${messageCount} messages in the last minute)`, roomId);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.banUser(event['sender'], roomId, "spam"); await mjolnir.client.banUser(event['sender'], roomId, "spam");
} else { } else {
await logMessage(LogLevel.WARN, "BasicFlooding", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "BasicFlooding", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
if (this.recentlyBanned.includes(event['sender'])) return; // already handled (will be redacted) if (this.recentlyBanned.includes(event['sender'])) return; // already handled (will be redacted)
@ -81,7 +80,7 @@ export class BasicFlooding extends Protection {
await mjolnir.client.redactEvent(roomId, eventId, "spam"); await mjolnir.client.redactEvent(roomId, eventId, "spam");
} }
} else { } else {
await logMessage(LogLevel.WARN, "BasicFlooding", `Tried to redact messages for ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "BasicFlooding", `Tried to redact messages for ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
// Free up some memory now that we're ready to handle it elsewhere // Free up some memory now that we're ready to handle it elsewhere

View File

@ -17,7 +17,6 @@ limitations under the License.
import { Protection } from "./IProtection"; import { Protection } from "./IProtection";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { LogLevel, LogService } from "matrix-bot-sdk"; import { LogLevel, LogService } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config"; import config from "../config";
import { isTrueJoinEvent } from "../utils"; import { isTrueJoinEvent } from "../utils";
@ -58,11 +57,11 @@ export class FirstMessageIsImage extends Protection {
const formattedBody = content['formatted_body'] || ''; const formattedBody = content['formatted_body'] || '';
const isMedia = msgtype === 'm.image' || msgtype === 'm.video' || formattedBody.toLowerCase().includes('<img'); const isMedia = msgtype === 'm.image' || msgtype === 'm.video' || formattedBody.toLowerCase().includes('<img');
if (isMedia && this.justJoined[roomId].includes(event['sender'])) { if (isMedia && this.justJoined[roomId].includes(event['sender'])) {
await logMessage(LogLevel.WARN, "FirstMessageIsImage", `Banning ${event['sender']} for posting an image as the first thing after joining in ${roomId}.`); await mjolnir.logMessage(LogLevel.WARN, "FirstMessageIsImage", `Banning ${event['sender']} for posting an image as the first thing after joining in ${roomId}.`);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.banUser(event['sender'], roomId, "spam"); await mjolnir.client.banUser(event['sender'], roomId, "spam");
} else { } else {
await logMessage(LogLevel.WARN, "FirstMessageIsImage", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "FirstMessageIsImage", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
if (this.recentlyBanned.includes(event['sender'])) return; // already handled (will be redacted) if (this.recentlyBanned.includes(event['sender'])) return; // already handled (will be redacted)
@ -73,7 +72,7 @@ export class FirstMessageIsImage extends Protection {
if (!config.noop) { if (!config.noop) {
await mjolnir.client.redactEvent(roomId, event['event_id'], "spam"); await mjolnir.client.redactEvent(roomId, event['event_id'], "spam");
} else { } else {
await logMessage(LogLevel.WARN, "FirstMessageIsImage", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "FirstMessageIsImage", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
} }
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
import { Protection } from "./IProtection"; import { Protection } from "./IProtection";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { LogLevel, Permalinks, UserID } from "matrix-bot-sdk"; import { LogLevel, Permalinks, UserID } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config"; import config from "../config";
export class MessageIsMedia extends Protection { export class MessageIsMedia extends Protection {
@ -42,12 +41,12 @@ export class MessageIsMedia extends Protection {
const formattedBody = content['formatted_body'] || ''; const formattedBody = content['formatted_body'] || '';
const isMedia = msgtype === 'm.image' || msgtype === 'm.video' || formattedBody.toLowerCase().includes('<img'); const isMedia = msgtype === 'm.image' || msgtype === 'm.video' || formattedBody.toLowerCase().includes('<img');
if (isMedia) { if (isMedia) {
await logMessage(LogLevel.WARN, "MessageIsMedia", `Redacting event from ${event['sender']} for posting an image/video. ${Permalinks.forEvent(roomId, event['event_id'], [new UserID(await mjolnir.client.getUserId()).domain])}`); await mjolnir.logMessage(LogLevel.WARN, "MessageIsMedia", `Redacting event from ${event['sender']} for posting an image/video. ${Permalinks.forEvent(roomId, event['event_id'], [new UserID(await mjolnir.client.getUserId()).domain])}`);
// Redact the event // Redact the event
if (!config.noop) { if (!config.noop) {
await mjolnir.client.redactEvent(roomId, event['event_id'], "Images/videos are not permitted here"); await mjolnir.client.redactEvent(roomId, event['event_id'], "Images/videos are not permitted here");
} else { } else {
await logMessage(LogLevel.WARN, "MessageIsMedia", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "MessageIsMedia", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
} }
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
import { Protection } from "./IProtection"; import { Protection } from "./IProtection";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { LogLevel, Permalinks, UserID } from "matrix-bot-sdk"; import { LogLevel, Permalinks, UserID } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config"; import config from "../config";
export class MessageIsVoice extends Protection { export class MessageIsVoice extends Protection {
@ -39,12 +38,12 @@ export class MessageIsVoice extends Protection {
if (event['type'] === 'm.room.message' && event['content']) { if (event['type'] === 'm.room.message' && event['content']) {
if (event['content']['msgtype'] !== 'm.audio') return; if (event['content']['msgtype'] !== 'm.audio') return;
if (event['content']['org.matrix.msc3245.voice'] === undefined) return; if (event['content']['org.matrix.msc3245.voice'] === undefined) return;
await logMessage(LogLevel.INFO, "MessageIsVoice", `Redacting event from ${event['sender']} for posting a voice message. ${Permalinks.forEvent(roomId, event['event_id'], [new UserID(await mjolnir.client.getUserId()).domain])}`); await mjolnir.logMessage(LogLevel.INFO, "MessageIsVoice", `Redacting event from ${event['sender']} for posting a voice message. ${Permalinks.forEvent(roomId, event['event_id'], [new UserID(await mjolnir.client.getUserId()).domain])}`);
// Redact the event // Redact the event
if (!config.noop) { if (!config.noop) {
await mjolnir.client.redactEvent(roomId, event['event_id'], "Voice messages are not permitted here"); await mjolnir.client.redactEvent(roomId, event['event_id'], "Voice messages are not permitted here");
} else { } else {
await logMessage(LogLevel.WARN, "MessageIsVoice", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "MessageIsVoice", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
} }
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
import { Protection } from "./IProtection"; import { Protection } from "./IProtection";
import { Mjolnir } from "../Mjolnir"; import { Mjolnir } from "../Mjolnir";
import { LogLevel, LogService } from "matrix-bot-sdk"; import { LogLevel, LogService } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config"; import config from "../config";
import { isTrueJoinEvent } from "../utils"; import { isTrueJoinEvent } from "../utils";
@ -94,18 +93,18 @@ export class WordList extends Protection {
// Perform the test // Perform the test
if (message && this.badWords.test(message)) { if (message && this.badWords.test(message)) {
await logMessage(LogLevel.WARN, "WordList", `Banning ${event['sender']} for word list violation in ${roomId}.`); await mjolnir.logMessage(LogLevel.WARN, "WordList", `Banning ${event['sender']} for word list violation in ${roomId}.`);
if (!config.noop) { if (!config.noop) {
await mjolnir.client.banUser(event['sender'], roomId, "Word list violation"); await mjolnir.client.banUser(event['sender'], roomId, "Word list violation");
} else { } else {
await logMessage(LogLevel.WARN, "WordList", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "WordList", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
// Redact the event // Redact the event
if (!config.noop) { if (!config.noop) {
await mjolnir.client.redactEvent(roomId, event['event_id'], "spam"); await mjolnir.client.redactEvent(roomId, event['event_id'], "spam");
} else { } else {
await logMessage(LogLevel.WARN, "WordList", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId); await mjolnir.logMessage(LogLevel.WARN, "WordList", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
} }
} }
} }

View File

@ -13,11 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { LogLevel, MatrixClient } from "matrix-bot-sdk" import { LogLevel } from "matrix-bot-sdk"
import { ERROR_KIND_FATAL } from "../ErrorCache"; import { ERROR_KIND_FATAL } from "../ErrorCache";
import { logMessage } from "../LogProxy";
import { RoomUpdateError } from "../models/RoomUpdateError"; import { RoomUpdateError } from "../models/RoomUpdateError";
import { redactUserMessagesIn } from "../utils"; import { redactUserMessagesIn } from "../utils";
import { Mjolnir } from "../Mjolnir";
export interface QueuedRedaction { export interface QueuedRedaction {
/** The room which the redaction will take place in. */ /** The room which the redaction will take place in. */
@ -27,7 +27,7 @@ export interface QueuedRedaction {
* Called by the EventRedactionQueue. * Called by the EventRedactionQueue.
* @param client A MatrixClient to use to carry out the redaction. * @param client A MatrixClient to use to carry out the redaction.
*/ */
redact(client: MatrixClient): Promise<void> redact(mjolnir: Mjolnir): Promise<void>
/** /**
* Used to test whether the redaction is the equivalent to another redaction. * Used to test whether the redaction is the equivalent to another redaction.
* @param redaction Another QueuedRedaction to test if this redaction is an equivalent to. * @param redaction Another QueuedRedaction to test if this redaction is an equivalent to.
@ -47,9 +47,9 @@ export class RedactUserInRoom implements QueuedRedaction {
this.roomId = roomId; this.roomId = roomId;
} }
public async redact(client: MatrixClient) { public async redact(mjolnir: Mjolnir) {
await logMessage(LogLevel.DEBUG, "Mjolnir", `Redacting events from ${this.userId} in room ${this.roomId}.`); await mjolnir.logMessage(LogLevel.DEBUG, "Mjolnir", `Redacting events from ${this.userId} in room ${this.roomId}.`);
await redactUserMessagesIn(client, this.userId, [this.roomId]); await redactUserMessagesIn(mjolnir, this.userId, [this.roomId]);
} }
public redactionEqual(redaction: QueuedRedaction): boolean { public redactionEqual(redaction: QueuedRedaction): boolean {
@ -107,12 +107,12 @@ export class EventRedactionQueue {
* @param limitToRoomId If the roomId is provided, only redactions for that room will be processed. * @param limitToRoomId If the roomId is provided, only redactions for that room will be processed.
* @returns A description of any errors encountered by each QueuedRedaction that was processed. * @returns A description of any errors encountered by each QueuedRedaction that was processed.
*/ */
public async process(client: MatrixClient, limitToRoomId?: string): Promise<RoomUpdateError[]> { public async process(mjolnir: Mjolnir, limitToRoomId?: string): Promise<RoomUpdateError[]> {
const errors: RoomUpdateError[] = []; const errors: RoomUpdateError[] = [];
const redact = async (currentBatch: QueuedRedaction[]) => { const redact = async (currentBatch: QueuedRedaction[]) => {
for (const redaction of currentBatch) { for (const redaction of currentBatch) {
try { try {
await redaction.redact(client); await redaction.redact(mjolnir);
} catch (e) { } catch (e) {
let roomError: RoomUpdateError; let roomError: RoomUpdateError;
if (e.roomId && e.errorMessage && e.errorKind) { if (e.roomId && e.errorMessage && e.errorKind) {

View File

@ -13,9 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { extractRequestError, LogLevel, LogService, MatrixClient, Permalinks } from "matrix-bot-sdk"; import { extractRequestError, LogLevel, LogService, Permalinks } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config"; import config from "../config";
import { Mjolnir } from "../Mjolnir";
/** /**
* A queue of users who have been flagged for redaction typically by the flooding or image protection. * A queue of users who have been flagged for redaction typically by the flooding or image protection.
@ -38,18 +38,18 @@ export class UnlistedUserRedactionQueue {
return this.usersToRedact.has(userId); return this.usersToRedact.has(userId);
} }
public async handleEvent(roomId: string, event: any, mjolnirClient: MatrixClient) { public async handleEvent(roomId: string, event: any, mjolnir: Mjolnir) {
if (this.isUserQueued(event['sender'])) { if (this.isUserQueued(event['sender'])) {
const permalink = Permalinks.forEvent(roomId, event['event_id']); const permalink = Permalinks.forEvent(roomId, event['event_id']);
try { try {
LogService.info("AutomaticRedactionQueue", `Redacting event because the user is listed as bad: ${permalink}`) LogService.info("AutomaticRedactionQueue", `Redacting event because the user is listed as bad: ${permalink}`)
if (!config.noop) { if (!config.noop) {
await mjolnirClient.redactEvent(roomId, event['event_id']); await mjolnir.client.redactEvent(roomId, event['event_id']);
} else { } else {
await logMessage(LogLevel.WARN, "AutomaticRedactionQueue", `Tried to redact ${permalink} but Mjolnir is running in no-op mode`); await mjolnir.logMessage(LogLevel.WARN, "AutomaticRedactionQueue", `Tried to redact ${permalink} but Mjolnir is running in no-op mode`);
} }
} catch (e) { } catch (e) {
logMessage(LogLevel.WARN, "AutomaticRedactionQueue", `Unable to redact message: ${permalink}`); mjolnir.logMessage(LogLevel.WARN, "AutomaticRedactionQueue", `Unable to redact message: ${permalink}`);
LogService.warn("AutomaticRedactionQueue", extractRequestError(e)); LogService.warn("AutomaticRedactionQueue", extractRequestError(e));
} }
} }

View File

@ -27,7 +27,7 @@ import {
getRequestFn, getRequestFn,
setRequestFn, setRequestFn,
} from "matrix-bot-sdk"; } from "matrix-bot-sdk";
import { logMessage } from "./LogProxy"; import { Mjolnir } from "./Mjolnir";
import config from "./config"; import config from "./config";
import { ClientRequest, IncomingMessage } from "http"; import { ClientRequest, IncomingMessage } from "http";
@ -59,17 +59,17 @@ export function isTrueJoinEvent(event: any): boolean {
return membership === 'join' && prevMembership !== "join"; return membership === 'join' && prevMembership !== "join";
} }
export async function redactUserMessagesIn(client: MatrixClient, userIdOrGlob: string, targetRoomIds: string[], limit = 1000) { export async function redactUserMessagesIn(mjolnir: Mjolnir, userIdOrGlob: string, targetRoomIds: string[], limit = 1000) {
for (const targetRoomId of targetRoomIds) { for (const targetRoomId of targetRoomIds) {
await logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Fetching sent messages for ${userIdOrGlob} in ${targetRoomId} to redact...`, targetRoomId); await mjolnir.logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Fetching sent messages for ${userIdOrGlob} in ${targetRoomId} to redact...`, targetRoomId);
await getMessagesByUserIn(client, userIdOrGlob, targetRoomId, limit, async (eventsToRedact) => { await getMessagesByUserIn(mjolnir.client, userIdOrGlob, targetRoomId, limit, async (eventsToRedact) => {
for (const victimEvent of eventsToRedact) { for (const victimEvent of eventsToRedact) {
await logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Redacting ${victimEvent['event_id']} in ${targetRoomId}`, targetRoomId); await mjolnir.logMessage(LogLevel.DEBUG, "utils#redactUserMessagesIn", `Redacting ${victimEvent['event_id']} in ${targetRoomId}`, targetRoomId);
if (!config.noop) { if (!config.noop) {
await client.redactEvent(targetRoomId, victimEvent['event_id']); await mjolnir.client.redactEvent(targetRoomId, victimEvent['event_id']);
} else { } else {
await logMessage(LogLevel.WARN, "utils#redactUserMessagesIn", `Tried to redact ${victimEvent['event_id']} in ${targetRoomId} but Mjolnir is running in no-op mode`, targetRoomId); await mjolnir.logMessage(LogLevel.WARN, "utils#redactUserMessagesIn", `Tried to redact ${victimEvent['event_id']} in ${targetRoomId} but Mjolnir is running in no-op mode`, targetRoomId);
} }
} }
}); });
@ -191,7 +191,7 @@ export async function getMessagesByUserIn(client: MatrixClient, sender: string,
* @param msgtype The desired message type of the returned TextualMessageEventContent * @param msgtype The desired message type of the returned TextualMessageEventContent
* @returns A TextualMessageEventContent with replaced room IDs * @returns A TextualMessageEventContent with replaced room IDs
*/ */
export async function replaceRoomIdsWithPills(client: MatrixClient, text: string, roomIds: Set<string>, msgtype: MessageType = "m.text"): Promise<TextualMessageEventContent> { export async function replaceRoomIdsWithPills(mjolnir: Mjolnir, text: string, roomIds: Set<string>, msgtype: MessageType = "m.text"): Promise<TextualMessageEventContent> {
const content: TextualMessageEventContent = { const content: TextualMessageEventContent = {
body: text, body: text,
formatted_body: htmlEscape(text), formatted_body: htmlEscape(text),
@ -203,14 +203,14 @@ export async function replaceRoomIdsWithPills(client: MatrixClient, text: string
return v.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); return v.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}; };
const viaServers = [(new UserID(await client.getUserId())).domain]; const viaServers = [(new UserID(await mjolnir.client.getUserId())).domain];
for (const roomId of roomIds) { for (const roomId of roomIds) {
let alias = roomId; let alias = roomId;
try { try {
alias = (await client.getPublishedAlias(roomId)) || roomId; alias = (await mjolnir.client.getPublishedAlias(roomId)) || roomId;
} catch (e) { } catch (e) {
// This is a recursive call, so tell the function not to try and call us // This is a recursive call, so tell the function not to try and call us
await logMessage(LogLevel.WARN, "utils", `Failed to resolve room alias for ${roomId} - see console for details`, null, true); await mjolnir.logMessage(LogLevel.WARN, "utils", `Failed to resolve room alias for ${roomId} - see console for details`, null, true);
LogService.warn("utils", extractRequestError(e)); LogService.warn("utils", extractRequestError(e));
} }
const regexRoomId = new RegExp(escapeRegex(roomId), "g"); const regexRoomId = new RegExp(escapeRegex(roomId), "g");

View File

@ -16,7 +16,7 @@ describe("Test: utils", function() {
); );
const out = await replaceRoomIdsWithPills( const out = await replaceRoomIdsWithPills(
this.mjolnir.client, this.mjolnir,
`it's fun here in ${this.mjolnir.managementRoomId}`, `it's fun here in ${this.mjolnir.managementRoomId}`,
new Set([this.mjolnir.managementRoomId]) new Set([this.mjolnir.managementRoomId])
); );