mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Avoid spamming the management room with errors
The intervals are arbitrarily defined. Fixes https://github.com/matrix-org/mjolnir/issues/10
This commit is contained in:
parent
30e186ca9c
commit
82214c6cd8
59
src/ErrorCache.ts
Normal file
59
src/ErrorCache.ts
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
export const ERROR_KIND_PERMISSION = "permission";
|
||||
export const ERROR_KIND_FATAL = "fatal";
|
||||
|
||||
const TRIGGER_INTERVALS = {
|
||||
[ERROR_KIND_PERMISSION]: 3 * 60 * 60 * 1000, // 3 hours
|
||||
[ERROR_KIND_FATAL]: 15 * 60 * 1000, // 15 minutes
|
||||
};
|
||||
|
||||
export default class ErrorCache {
|
||||
private static roomsToErrors: { [roomId: string]: { [kind: string]: number } } = {};
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static resetError(roomId: string, kind: string) {
|
||||
if (!ErrorCache.roomsToErrors[roomId]) {
|
||||
ErrorCache.roomsToErrors[roomId] = {};
|
||||
}
|
||||
ErrorCache.roomsToErrors[roomId][kind] = 0;
|
||||
}
|
||||
|
||||
public static triggerError(roomId: string, kind: string): boolean {
|
||||
if (!ErrorCache.roomsToErrors[roomId]) {
|
||||
ErrorCache.roomsToErrors[roomId] = {};
|
||||
}
|
||||
|
||||
const triggers = ErrorCache.roomsToErrors[roomId];
|
||||
if (!triggers[kind]) {
|
||||
triggers[kind] = 0;
|
||||
}
|
||||
|
||||
const lastTriggerTime = triggers[kind];
|
||||
const now = new Date().getTime();
|
||||
const interval = TRIGGER_INTERVALS[kind];
|
||||
|
||||
if ((now - lastTriggerTime) >= interval) {
|
||||
triggers[kind] = now;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import { COMMAND_PREFIX, handleCommand } from "./commands/CommandHandler";
|
||||
import { applyUserBans } from "./actions/ApplyBan";
|
||||
import config from "./config";
|
||||
import { logMessage } from "./LogProxy";
|
||||
import ErrorCache, { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "./ErrorCache";
|
||||
|
||||
export const STATE_NOT_STARTED = "not_started";
|
||||
export const STATE_CHECKING_PERMISSIONS = "checking_permissions";
|
||||
@ -166,13 +167,13 @@ export class Mjolnir {
|
||||
this.banLists = banLists;
|
||||
}
|
||||
|
||||
public async verifyPermissions(verbose = true) {
|
||||
public async verifyPermissions(verbose = true, printRegardless = false) {
|
||||
const errors: RoomUpdateError[] = [];
|
||||
for (const roomId of Object.keys(this.protectedRooms)) {
|
||||
errors.push(...(await this.verifyPermissionsIn(roomId)));
|
||||
}
|
||||
|
||||
const hadErrors = await this.printActionResult(errors, "Permission errors in protected rooms:");
|
||||
const hadErrors = await this.printActionResult(errors, "Permission errors in protected rooms:", printRegardless);
|
||||
if (!hadErrors && verbose) {
|
||||
const html = `<font color="#00cc00">All permissions look OK.</font>`;
|
||||
const text = "All permissions look OK.";
|
||||
@ -216,22 +217,42 @@ export class Mjolnir {
|
||||
// Wants: ban, kick, redact, m.room.server_acl
|
||||
|
||||
if (userLevel < ban) {
|
||||
errors.push({roomId, errorMessage: `Missing power level for bans: ${userLevel} < ${ban}`});
|
||||
errors.push({
|
||||
roomId,
|
||||
errorMessage: `Missing power level for bans: ${userLevel} < ${ban}`,
|
||||
errorKind: ERROR_KIND_PERMISSION,
|
||||
});
|
||||
}
|
||||
if (userLevel < kick) {
|
||||
errors.push({roomId, errorMessage: `Missing power level for kicks: ${userLevel} < ${kick}`});
|
||||
errors.push({
|
||||
roomId,
|
||||
errorMessage: `Missing power level for kicks: ${userLevel} < ${kick}`,
|
||||
errorKind: ERROR_KIND_PERMISSION,
|
||||
});
|
||||
}
|
||||
if (userLevel < redact) {
|
||||
errors.push({roomId, errorMessage: `Missing power level for redactions: ${userLevel} < ${redact}`});
|
||||
errors.push({
|
||||
roomId,
|
||||
errorMessage: `Missing power level for redactions: ${userLevel} < ${redact}`,
|
||||
errorKind: ERROR_KIND_PERMISSION,
|
||||
});
|
||||
}
|
||||
if (userLevel < aclLevel) {
|
||||
errors.push({roomId, errorMessage: `Missing power level for server ACLs: ${userLevel} < ${aclLevel}`});
|
||||
errors.push({
|
||||
roomId,
|
||||
errorMessage: `Missing power level for server ACLs: ${userLevel} < ${aclLevel}`,
|
||||
errorKind: ERROR_KIND_PERMISSION,
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise OK
|
||||
} catch (e) {
|
||||
LogService.error("Mjolnir", e);
|
||||
errors.push({roomId, errorMessage: e.message || (e.body ? e.body.error : '<no message>')});
|
||||
errors.push({
|
||||
roomId,
|
||||
errorMessage: e.message || (e.body ? e.body.error : '<no message>'),
|
||||
errorKind: ERROR_KIND_FATAL,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
@ -294,6 +315,7 @@ export class Mjolnir {
|
||||
if (event['sender'] === await this.client.getUserId()) return; // Ignore ourselves
|
||||
if (event['type'] === 'm.room.power_levels' && event['state_key'] === '') {
|
||||
// power levels were updated - recheck permissions
|
||||
ErrorCache.resetError(roomId, ERROR_KIND_PERMISSION);
|
||||
const url = this.protectedRooms[roomId];
|
||||
let html = `Power levels changed in <a href="${url}">${roomId}</a> - checking permissions...`;
|
||||
let text = `Power levels changed in ${url} - checking permissions...`;
|
||||
@ -329,9 +351,17 @@ export class Mjolnir {
|
||||
}
|
||||
}
|
||||
|
||||
private async printActionResult(errors: RoomUpdateError[], title: string = null) {
|
||||
private async printActionResult(errors: RoomUpdateError[], title: string = null, logAnyways = false) {
|
||||
if (errors.length <= 0) return false;
|
||||
|
||||
if (!logAnyways) {
|
||||
errors = errors.filter(e => ErrorCache.triggerError(e.roomId, e.errorKind));
|
||||
if (errors.length <= 0) {
|
||||
LogService.warn("Mjolnir", "Multiple errors are happening, however they are muted. Please check the management room.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let html = "";
|
||||
let text = "";
|
||||
|
||||
|
@ -21,6 +21,7 @@ import { Mjolnir } from "../Mjolnir";
|
||||
import config from "../config";
|
||||
import { LogLevel } from "matrix-bot-sdk";
|
||||
import { logMessage } from "../LogProxy";
|
||||
import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache";
|
||||
|
||||
/**
|
||||
* Applies the server ACLs represented by the ban lists to the provided rooms, returning the
|
||||
@ -69,7 +70,9 @@ export async function applyServerAcls(lists: BanList[], roomIds: string[], mjoln
|
||||
await logMessage(LogLevel.WARN, "ApplyAcl", `Tried to apply ACL in ${roomId} but Mjolnir is running in no-op mode`);
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push({roomId, errorMessage: e.message || (e.body ? e.body.error : '<no message>')});
|
||||
const message = e.message || (e.body ? e.body.error : '<no message>');
|
||||
const kind = message.includes("You don't have permission to post that to the room") ? ERROR_KIND_PERMISSION : ERROR_KIND_FATAL;
|
||||
errors.push({roomId, errorMessage: message, errorKind: kind});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import { Mjolnir } from "../Mjolnir";
|
||||
import config from "../config";
|
||||
import { logMessage } from "../LogProxy";
|
||||
import { LogLevel } from "matrix-bot-sdk";
|
||||
import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache";
|
||||
|
||||
/**
|
||||
* Applies the member bans represented by the ban lists to the provided rooms, returning the
|
||||
@ -80,7 +81,12 @@ export async function applyUserBans(lists: BanList[], roomIds: string[], mjolnir
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
errors.push({roomId, errorMessage: e.message || (e.body ? e.body.error : '<no message>')});
|
||||
const message = e.message || (e.body ? e.body.error : '<no message>');
|
||||
errors.push({
|
||||
roomId,
|
||||
errorMessage: message,
|
||||
errorKind: message.includes("You don't have permission to ban") ? ERROR_KIND_PERMISSION : ERROR_KIND_FATAL,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,5 +18,5 @@ import { Mjolnir } from "../Mjolnir";
|
||||
|
||||
// !mjolnir verify
|
||||
export async function execPermissionCheckCommand(roomId: string, event: any, mjolnir: Mjolnir) {
|
||||
return mjolnir.verifyPermissions();
|
||||
return mjolnir.verifyPermissions(true, true);
|
||||
}
|
||||
|
@ -17,4 +17,5 @@ limitations under the License.
|
||||
export interface RoomUpdateError {
|
||||
roomId: string;
|
||||
errorMessage: string;
|
||||
errorKind: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user