mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Improve/change logging to management room
This commit is contained in:
parent
fff55abd67
commit
e69f0e6940
@ -15,7 +15,8 @@ Phase 2:
|
|||||||
* [x] Pantalaimon support
|
* [x] Pantalaimon support
|
||||||
* [ ] No-op mode (for verifying behaviour)
|
* [ ] No-op mode (for verifying behaviour)
|
||||||
* [ ] Redact messages on ban (optionally)
|
* [ ] Redact messages on ban (optionally)
|
||||||
* [ ] Less spam in management room (or more, by request)
|
* [x] More useful spam in management room
|
||||||
|
* [ ] Command to import ACLs, etc from rooms
|
||||||
* [ ] Vet rooms on startup option
|
* [ ] Vet rooms on startup option
|
||||||
* [ ] Room upgrade handling (both protected+list rooms)
|
* [ ] Room upgrade handling (both protected+list rooms)
|
||||||
* [ ] Command to actually unban users (instead of leaving them stuck)
|
* [ ] Command to actually unban users (instead of leaving them stuck)
|
||||||
|
@ -27,8 +27,12 @@ autojoin: true
|
|||||||
# The room ID where people can use the bot. The bot has no access controls, so
|
# The room ID where people can use the bot. The bot has no access controls, so
|
||||||
# anyone in this room can use the bot - secure your room!
|
# anyone in this room can use the bot - secure your room!
|
||||||
# This should be a room alias or room ID - not a matrix.to URL.
|
# This should be a room alias or room ID - not a matrix.to URL.
|
||||||
|
# Note: Mjolnir is fairly verbose - expect a lot of messages from it.
|
||||||
managementRoom: "#moderators:example.org"
|
managementRoom: "#moderators:example.org"
|
||||||
|
|
||||||
|
# Set to false to make the management room a bit quieter.
|
||||||
|
verboseLogging: true
|
||||||
|
|
||||||
# The room ID or alias where the bot's own personal ban list is kept. This is
|
# The room ID or alias where the bot's own personal ban list is kept. This is
|
||||||
# where the commands to manage a ban list end up being routed to. Note that
|
# where the commands to manage a ban list end up being routed to. Note that
|
||||||
# this room is NOT automatically added to the banLists list below - you will
|
# this room is NOT automatically added to the banLists list below - you will
|
||||||
|
@ -72,11 +72,23 @@ export class Mjolnir {
|
|||||||
await list.updateList();
|
await list.updateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
let errors = await applyServerAcls(this.banLists, Object.keys(this.protectedRooms), this.client);
|
let hadErrors = false;
|
||||||
await this.printActionResult(errors);
|
|
||||||
|
|
||||||
errors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this.client);
|
const aclErrors = await applyServerAcls(this.banLists, Object.keys(this.protectedRooms), this);
|
||||||
await this.printActionResult(errors);
|
const banErrors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this);
|
||||||
|
hadErrors = hadErrors || await this.printActionResult(aclErrors, "Errors updating server ACLs:");
|
||||||
|
hadErrors = hadErrors || await this.printActionResult(banErrors, "Errors updating member bans:");
|
||||||
|
|
||||||
|
if (!hadErrors) {
|
||||||
|
const html = `<font color="#00cc00">Done updating rooms - no errors</font>`;
|
||||||
|
const text = "Updated all protected rooms with new rules successfully";
|
||||||
|
await this.client.sendMessage(this.managementRoomId, {
|
||||||
|
msgtype: "m.notice",
|
||||||
|
body: text,
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: html,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async syncListForRoom(roomId: string) {
|
public async syncListForRoom(roomId: string) {
|
||||||
@ -88,11 +100,23 @@ export class Mjolnir {
|
|||||||
}
|
}
|
||||||
if (!updated) return;
|
if (!updated) return;
|
||||||
|
|
||||||
let errors = await applyServerAcls(this.banLists, Object.keys(this.protectedRooms), this.client);
|
let hadErrors = false;
|
||||||
await this.printActionResult(errors);
|
|
||||||
|
|
||||||
errors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this.client);
|
const aclErrors = await applyServerAcls(this.banLists, Object.keys(this.protectedRooms), this);
|
||||||
await this.printActionResult(errors);
|
const banErrors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this);
|
||||||
|
hadErrors = hadErrors || await this.printActionResult(aclErrors, "Errors updating server ACLs:");
|
||||||
|
hadErrors = hadErrors || await this.printActionResult(banErrors, "Errors updating member bans:");
|
||||||
|
|
||||||
|
if (!hadErrors) {
|
||||||
|
const html = `<font color="#00cc00"><b>Done updating rooms - no errors</b></font>`;
|
||||||
|
const text = "Done updating rooms - no errors";
|
||||||
|
await this.client.sendMessage(this.managementRoomId, {
|
||||||
|
msgtype: "m.notice",
|
||||||
|
body: text,
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: html,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(roomId: string, event: any) {
|
private async handleEvent(roomId: string, event: any) {
|
||||||
@ -101,13 +125,12 @@ export class Mjolnir {
|
|||||||
if (ALL_RULE_TYPES.includes(event['type'])) {
|
if (ALL_RULE_TYPES.includes(event['type'])) {
|
||||||
await this.syncListForRoom(roomId);
|
await this.syncListForRoom(roomId);
|
||||||
} else if (event['type'] === "m.room.member") {
|
} else if (event['type'] === "m.room.member") {
|
||||||
const errors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this.client);
|
const errors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this);
|
||||||
await this.printActionResult(errors);
|
const hadErrors = await this.printActionResult(errors);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!hadErrors) {
|
||||||
const html = `<font color="#00cc00"><b>Updated all protected rooms with new rules successfully.</b></font>`;
|
const html = `<font color="#00cc00"><b>Done updating rooms - no errors</b></font>`;
|
||||||
const text = "Updated all protected rooms with new rules successfully";
|
const text = "Done updating rooms - no errors";
|
||||||
await this.client.sendMessage(this.managementRoomId, {
|
await this.client.sendMessage(this.managementRoomId, {
|
||||||
msgtype: "m.notice",
|
msgtype: "m.notice",
|
||||||
body: text,
|
body: text,
|
||||||
@ -115,15 +138,20 @@ export class Mjolnir {
|
|||||||
formatted_body: html,
|
formatted_body: html,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else return; // Not processed
|
||||||
|
}
|
||||||
|
|
||||||
private async printActionResult(errors: RoomUpdateError[]) {
|
private async printActionResult(errors: RoomUpdateError[], title: string=null) {
|
||||||
if (errors.length <= 0) return;
|
if (errors.length <= 0) return false;
|
||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
let text = "";
|
let text = "";
|
||||||
|
|
||||||
html += `<font color="#ff0000"><b>${errors.length} errors updating protected rooms!</b></font><br /><ul>`;
|
const htmlTitle = title ? `${title}<br />` : '';
|
||||||
text += `${errors.length} errors updating protected rooms!\n`;
|
const textTitle = title ? `${title}\n` : '';
|
||||||
|
|
||||||
|
html += `<font color="#ff0000"><b>${htmlTitle}${errors.length} errors updating protected rooms!</b></font><br /><ul>`;
|
||||||
|
text += `${textTitle}${errors.length} errors updating protected rooms!\n`;
|
||||||
for (const error of errors) {
|
for (const error of errors) {
|
||||||
const url = this.protectedRooms[error.roomId] ? this.protectedRooms[error.roomId] : `https://matrix.to/#/${error.roomId}`;
|
const url = this.protectedRooms[error.roomId] ? this.protectedRooms[error.roomId] : `https://matrix.to/#/${error.roomId}`;
|
||||||
html += `<li><a href="${url}">${error.roomId}</a> - ${error.errorMessage}</li>`;
|
html += `<li><a href="${url}">${error.roomId}</a> - ${error.errorMessage}</li>`;
|
||||||
@ -137,6 +165,7 @@ export class Mjolnir {
|
|||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: html,
|
formatted_body: html,
|
||||||
};
|
};
|
||||||
return this.client.sendMessage(this.managementRoomId, message);
|
await this.client.sendMessage(this.managementRoomId, message);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,18 +15,19 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import BanList from "../models/BanList";
|
import BanList from "../models/BanList";
|
||||||
import { MatrixClient } from "matrix-bot-sdk";
|
|
||||||
import { ServerAcl } from "../models/ServerAcl";
|
import { ServerAcl } from "../models/ServerAcl";
|
||||||
import { RoomUpdateError } from "../models/RoomUpdateError";
|
import { RoomUpdateError } from "../models/RoomUpdateError";
|
||||||
|
import { Mjolnir } from "../Mjolnir";
|
||||||
|
import config from "../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* room IDs that could not be updated and their error.
|
* room IDs that could not be updated and their error.
|
||||||
* @param {BanList[]} lists The lists to construct ACLs from.
|
* @param {BanList[]} lists The lists to construct ACLs from.
|
||||||
* @param {string[]} roomIds The room IDs to apply the ACLs in.
|
* @param {string[]} roomIds The room IDs to apply the ACLs in.
|
||||||
* @param {MatrixClient} client The Matrix client to apply the ACLs with.
|
* @param {Mjolnir} mjolnir The Mjolnir client to apply the ACLs with.
|
||||||
*/
|
*/
|
||||||
export async function applyServerAcls(lists: BanList[], roomIds: string[], client: MatrixClient): Promise<RoomUpdateError[]> {
|
export async function applyServerAcls(lists: BanList[], roomIds: string[], mjolnir: Mjolnir): Promise<RoomUpdateError[]> {
|
||||||
// Construct a server ACL first
|
// Construct a server ACL first
|
||||||
const acl = new ServerAcl().denyIpAddresses().allowServer("*");
|
const acl = new ServerAcl().denyIpAddresses().allowServer("*");
|
||||||
for (const list of lists) {
|
for (const list of lists) {
|
||||||
@ -35,10 +36,22 @@ export async function applyServerAcls(lists: BanList[], roomIds: string[], clien
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const finalAcl = acl.safeAclContent();
|
||||||
|
|
||||||
|
if (config.verboseLogging) {
|
||||||
|
// We specifically use sendNotice to avoid having to escape HTML
|
||||||
|
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Constructed server ACL:\n${JSON.stringify(finalAcl, null, 2)}`);
|
||||||
|
}
|
||||||
|
|
||||||
const errors: RoomUpdateError[] = [];
|
const errors: RoomUpdateError[] = [];
|
||||||
for (const roomId of roomIds) {
|
for (const roomId of roomIds) {
|
||||||
try {
|
try {
|
||||||
await client.sendStateEvent(roomId, "m.room.server_acl", "", acl.safeAclContent());
|
if (config.verboseLogging) {
|
||||||
|
// We specifically use sendNotice to avoid having to escape HTML
|
||||||
|
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Applying ACL in ${roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await mjolnir.client.sendStateEvent(roomId, "m.room.server_acl", "", finalAcl);
|
||||||
} catch (e) {
|
} catch (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>')});
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,28 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import BanList from "../models/BanList";
|
import BanList from "../models/BanList";
|
||||||
import { MatrixClient } from "matrix-bot-sdk";
|
|
||||||
import { RoomUpdateError } from "../models/RoomUpdateError";
|
import { RoomUpdateError } from "../models/RoomUpdateError";
|
||||||
|
import { Mjolnir } from "../Mjolnir";
|
||||||
|
import config from "../config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* room IDs that could not be updated and their error.
|
* room IDs that could not be updated and their error.
|
||||||
* @param {BanList[]} lists The lists to determine bans from.
|
* @param {BanList[]} lists The lists to determine bans from.
|
||||||
* @param {string[]} roomIds The room IDs to apply the bans in.
|
* @param {string[]} roomIds The room IDs to apply the bans in.
|
||||||
* @param {MatrixClient} client The Matrix client to apply the bans with.
|
* @param {Mjolnir} mjolnir The Mjolnir client to apply the bans with.
|
||||||
*/
|
*/
|
||||||
export async function applyUserBans(lists: BanList[], roomIds: string[], client: MatrixClient): Promise<RoomUpdateError[]> {
|
export async function applyUserBans(lists: BanList[], roomIds: string[], mjolnir: Mjolnir): Promise<RoomUpdateError[]> {
|
||||||
// We can only ban people who are not already banned, and who match the rules.
|
// We can only ban people who are not already banned, and who match the rules.
|
||||||
const errors: RoomUpdateError[] = [];
|
const errors: RoomUpdateError[] = [];
|
||||||
for (const roomId of roomIds) {
|
for (const roomId of roomIds) {
|
||||||
try {
|
try {
|
||||||
const state = await client.getRoomState(roomId);
|
if (config.verboseLogging) {
|
||||||
|
// We specifically use sendNotice to avoid having to escape HTML
|
||||||
|
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Updating member bans in ${roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = await mjolnir.client.getRoomState(roomId);
|
||||||
const members = state.filter(s => s['type'] === 'm.room.member' && !!s['state_key']);
|
const members = state.filter(s => s['type'] === 'm.room.member' && !!s['state_key']);
|
||||||
|
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
@ -46,7 +52,13 @@ export async function applyUserBans(lists: BanList[], roomIds: string[], client:
|
|||||||
for (const userRule of list.userRules) {
|
for (const userRule of list.userRules) {
|
||||||
if (userRule.isMatch(member['state_key'])) {
|
if (userRule.isMatch(member['state_key'])) {
|
||||||
// User needs to be banned
|
// User needs to be banned
|
||||||
await client.banUser(member['state_key'], roomId, userRule.reason);
|
|
||||||
|
if (config.verboseLogging) {
|
||||||
|
// We specifically use sendNotice to avoid having to escape HTML
|
||||||
|
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Banning ${member['state_key']} in ${roomId} for: ${userRule.reason}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await mjolnir.client.banUser(member['state_key'], roomId, userRule.reason);
|
||||||
banned = true;
|
banned = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ interface IConfig {
|
|||||||
dataPath: string;
|
dataPath: string;
|
||||||
autojoin: boolean;
|
autojoin: boolean;
|
||||||
managementRoom: string;
|
managementRoom: string;
|
||||||
|
verboseLogging: boolean;
|
||||||
publishedBanListRoom: string;
|
publishedBanListRoom: string;
|
||||||
protectedRooms: string[]; // matrix.to urls
|
protectedRooms: string[]; // matrix.to urls
|
||||||
banLists: string[]; // matrix.to urls
|
banLists: string[]; // matrix.to urls
|
||||||
|
Loading…
Reference in New Issue
Block a user