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 { applyUserBans } from "./actions/ApplyBan";
|
||||||
import config from "./config";
|
import config from "./config";
|
||||||
import { logMessage } from "./LogProxy";
|
import { logMessage } from "./LogProxy";
|
||||||
|
import ErrorCache, { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "./ErrorCache";
|
||||||
|
|
||||||
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";
|
||||||
@ -166,13 +167,13 @@ export class Mjolnir {
|
|||||||
this.banLists = banLists;
|
this.banLists = banLists;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async verifyPermissions(verbose = true) {
|
public async verifyPermissions(verbose = true, printRegardless = false) {
|
||||||
const errors: RoomUpdateError[] = [];
|
const errors: RoomUpdateError[] = [];
|
||||||
for (const roomId of Object.keys(this.protectedRooms)) {
|
for (const roomId of Object.keys(this.protectedRooms)) {
|
||||||
errors.push(...(await this.verifyPermissionsIn(roomId)));
|
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) {
|
if (!hadErrors && verbose) {
|
||||||
const html = `<font color="#00cc00">All permissions look OK.</font>`;
|
const html = `<font color="#00cc00">All permissions look OK.</font>`;
|
||||||
const text = "All permissions look OK.";
|
const text = "All permissions look OK.";
|
||||||
@ -216,22 +217,42 @@ export class Mjolnir {
|
|||||||
// Wants: ban, kick, redact, m.room.server_acl
|
// Wants: ban, kick, redact, m.room.server_acl
|
||||||
|
|
||||||
if (userLevel < ban) {
|
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) {
|
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) {
|
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) {
|
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
|
// Otherwise OK
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
LogService.error("Mjolnir", 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;
|
return errors;
|
||||||
@ -294,6 +315,7 @@ export class Mjolnir {
|
|||||||
if (event['sender'] === await this.client.getUserId()) return; // Ignore ourselves
|
if (event['sender'] === await this.client.getUserId()) return; // Ignore ourselves
|
||||||
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);
|
||||||
const url = this.protectedRooms[roomId];
|
const url = this.protectedRooms[roomId];
|
||||||
let html = `Power levels changed in <a href="${url}">${roomId}</a> - checking permissions...`;
|
let html = `Power levels changed in <a href="${url}">${roomId}</a> - checking permissions...`;
|
||||||
let text = `Power levels changed in ${url} - 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 (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 html = "";
|
||||||
let text = "";
|
let text = "";
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import { Mjolnir } from "../Mjolnir";
|
|||||||
import config from "../config";
|
import config from "../config";
|
||||||
import { LogLevel } from "matrix-bot-sdk";
|
import { LogLevel } from "matrix-bot-sdk";
|
||||||
import { logMessage } from "../LogProxy";
|
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
|
* 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`);
|
await logMessage(LogLevel.WARN, "ApplyAcl", `Tried to apply ACL in ${roomId} but Mjolnir is running in no-op mode`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} 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 config from "../config";
|
||||||
import { logMessage } from "../LogProxy";
|
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";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the member bans represented by the ban lists to the provided rooms, returning the
|
* 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) {
|
} 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
|
// !mjolnir verify
|
||||||
export async function execPermissionCheckCommand(roomId: string, event: any, mjolnir: Mjolnir) {
|
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 {
|
export interface RoomUpdateError {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
|
errorKind: string;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user