mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-10-09 10:57:24 +00:00
Make rules and rules matching use interface-manager.
This commit is contained in:
@@ -27,7 +27,6 @@ limitations under the License.
|
||||
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { showJoinsStatus } from "./JoinsCommand";
|
||||
import { execDumpRulesCommand, execRulesMatchingCommand } from "./DumpRulesCommand";
|
||||
import { LogService, RichReply } from "matrix-bot-sdk";
|
||||
import { htmlEscape } from "../utils";
|
||||
import { execSyncCommand } from "./SyncCommand";
|
||||
@@ -71,6 +70,7 @@ import "./HijackRoomCommand";
|
||||
import "./Ban";
|
||||
import "./Unban";
|
||||
import "./StatusCommand";
|
||||
import "./Rules";
|
||||
|
||||
export const COMMAND_PREFIX = "!mjolnir";
|
||||
|
||||
@@ -85,10 +85,6 @@ export async function handleCommand(roomId: string, event: { content: { body: st
|
||||
try {
|
||||
if (parts.length === 1 || parts[1] === 'joins') {
|
||||
return await showJoinsStatus(roomId, event, mjolnir, parts.slice(/* ["joins"] */ 2));
|
||||
} else if (parts[1] === 'rules' && parts.length === 4 && parts[2] === 'matching') {
|
||||
return await execRulesMatchingCommand(roomId, event, mjolnir, parts[3])
|
||||
} else if (parts[1] === 'rules') {
|
||||
return await execDumpRulesCommand(roomId, event, mjolnir);
|
||||
} else if (parts[1] === 'sync') {
|
||||
return await execSyncCommand(roomId, event, mjolnir);
|
||||
} else if (parts[1] === 'verify') {
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022 Gnuxie <Gnuxie@protonmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is modified and is NOT licensed under the Apache License.
|
||||
* This modified file incorperates work from mjolnir
|
||||
* https://github.com/matrix-org/mjolnir
|
||||
* which included the following license notice:
|
||||
|
||||
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.
|
||||
*
|
||||
* However, this file is modified and the modifications in this file
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { RichReply } from "matrix-bot-sdk";
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { EntityType } from "../models/ListRule";
|
||||
import { htmlEscape } from "../utils";
|
||||
|
||||
/**
|
||||
* List all of the rules that match a given entity.
|
||||
* The reason why you want to test against all rules and not just e.g. user or server is because
|
||||
* there are situations where rules of different types can ban other entities e.g. server ACL can cause users to be banned.
|
||||
* @param roomId The room the command is from.
|
||||
* @param event The event containing the command.
|
||||
* @param mjolnir A mjolnir to fetch the watched lists from.
|
||||
* @param entity a user, room id or server.
|
||||
* @returns When a response has been sent to the command.
|
||||
*/
|
||||
export async function execRulesMatchingCommand(roomId: string, event: any, mjolnir: Mjolnir, entity: string) {
|
||||
let html = "";
|
||||
let text = "";
|
||||
for (const list of mjolnir.policyListManager.lists) {
|
||||
const matches = list.rulesMatchingEntity(entity)
|
||||
|
||||
if (matches.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchesInfo = `Found ${matches.length} ` + (matches.length === 1 ? 'match:' : 'matches:');
|
||||
const shortcodeInfo = list.listShortcode ? ` (shortcode: ${htmlEscape(list.listShortcode)})` : '';
|
||||
|
||||
html += `<a href="${htmlEscape(list.roomRef)}">${htmlEscape(list.roomId)}</a>${shortcodeInfo} ${matchesInfo}<br/><ul>`;
|
||||
text += `${list.roomRef}${shortcodeInfo} ${matchesInfo}:\n`;
|
||||
|
||||
for (const rule of matches) {
|
||||
// If we know the rule kind, we will give it a readable name, otherwise just use its name.
|
||||
let ruleKind: string = rule.kind;
|
||||
switch (ruleKind) {
|
||||
case EntityType.RULE_USER:
|
||||
ruleKind = 'user';
|
||||
break;
|
||||
case EntityType.RULE_SERVER:
|
||||
ruleKind = 'server';
|
||||
break;
|
||||
case EntityType.RULE_ROOM:
|
||||
ruleKind = 'room';
|
||||
break;
|
||||
}
|
||||
html += `<li>${htmlEscape(ruleKind)} (<code>${htmlEscape(rule.recommendation ?? "")}</code>): <code>${htmlEscape(rule.entity)}</code> (${htmlEscape(rule.reason)})</li>`;
|
||||
text += `* ${ruleKind} (${rule.recommendation}): ${rule.entity} (${rule.reason})\n`;
|
||||
}
|
||||
|
||||
html += "</ul>";
|
||||
}
|
||||
|
||||
if (text.length === 0) {
|
||||
html += `No results for ${htmlEscape(entity)}`;
|
||||
text += `No results for ${entity}`;
|
||||
}
|
||||
const reply = RichReply.createFor(roomId, event, text, html);
|
||||
reply["msgtype"] = "m.notice";
|
||||
return mjolnir.client.sendMessage(roomId, reply);
|
||||
}
|
||||
|
||||
// !mjolnir rules
|
||||
export async function execDumpRulesCommand(roomId: string, event: any, mjolnir: Mjolnir) {
|
||||
let html = "<b>Rules currently in use:</b><br/>";
|
||||
let text = "Rules currently in use:\n";
|
||||
|
||||
let hasLists = false;
|
||||
for (const list of mjolnir.policyListManager.lists) {
|
||||
hasLists = true;
|
||||
let hasRules = false;
|
||||
|
||||
const shortcodeInfo = list.listShortcode ? ` (shortcode: ${list.listShortcode})` : '';
|
||||
|
||||
html += `<a href="${list.roomRef}">${list.roomId}</a>${shortcodeInfo}:<br/><ul>`;
|
||||
text += `${list.roomRef}${shortcodeInfo}:\n`;
|
||||
|
||||
for (const rule of list.serverRules) {
|
||||
hasRules = true;
|
||||
html += `<li>server (<code>${rule.recommendation}</code>): <code>${htmlEscape(rule.entity)}</code> (${htmlEscape(rule.reason)})</li>`;
|
||||
text += `* server (${rule.recommendation}): ${rule.entity} (${rule.reason})\n`;
|
||||
}
|
||||
|
||||
for (const rule of list.userRules) {
|
||||
hasRules = true;
|
||||
html += `<li>user (<code>${rule.recommendation}</code>): <code>${htmlEscape(rule.entity)}</code> (${htmlEscape(rule.reason)})</li>`;
|
||||
text += `* user (${rule.recommendation}): ${rule.entity} (${rule.reason})\n`;
|
||||
}
|
||||
|
||||
for (const rule of list.roomRules) {
|
||||
hasRules = true;
|
||||
html += `<li>room (<code>${rule.recommendation}</code>): <code>${htmlEscape(rule.entity)}</code> (${htmlEscape(rule.reason)})</li>`;
|
||||
text += `* room (${rule.recommendation}): ${rule.entity} (${rule.reason})\n`;
|
||||
}
|
||||
|
||||
if (!hasRules) {
|
||||
html += "<li><i>No rules</i>";
|
||||
text += "* No rules\n";
|
||||
}
|
||||
|
||||
html += "</ul>";
|
||||
text += "\n";
|
||||
}
|
||||
|
||||
if (!hasLists) {
|
||||
html = "No ban lists configured";
|
||||
text = "No ban lists configured";
|
||||
}
|
||||
|
||||
const reply = RichReply.createFor(roomId, event, text, html);
|
||||
reply["msgtype"] = "m.notice";
|
||||
return mjolnir.client.sendMessage(roomId, reply);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (C) 2022 Gnuxie <Gnuxie@protonmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is modified and is NOT licensed under the Apache License.
|
||||
* This modified file incorperates work from mjolnir
|
||||
* https://github.com/matrix-org/mjolnir
|
||||
* which included the following license notice:
|
||||
|
||||
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.
|
||||
*
|
||||
* However, this file is modified and the modifications in this file
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { renderMatrixAndSend } from "./interface-manager/DeadDocumentMatrix";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { JSXFactory } from "./interface-manager/JSXFactory";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { findPresentationType, parameters, union } from "./interface-manager/ParameterParsing";
|
||||
import { CommandResult } from "./interface-manager/Validation";
|
||||
import { UserID } from "matrix-bot-sdk";
|
||||
import { MatrixSendClient } from "../MatrixEmitter";
|
||||
import { ListRule } from "../models/ListRule";
|
||||
|
||||
async function renderListMatches(
|
||||
client: MatrixSendClient, commandRoomId: string, event: any, result: CommandResult<ListMatches[]>
|
||||
) {
|
||||
if (result.isErr()) {
|
||||
return await tickCrossRenderer.call(this, ...arguments);
|
||||
}
|
||||
const lists = result.ok;
|
||||
if (lists.length === 0) {
|
||||
return await renderMatrixAndSend(
|
||||
<root>No policy lists configured</root>,
|
||||
commandRoomId, event, client
|
||||
)
|
||||
}
|
||||
return await renderMatrixAndSend(
|
||||
<root>
|
||||
<b>Rules currently in use:</b><br/>
|
||||
{lists.map(list => renderListRules(list))}
|
||||
</root>,
|
||||
commandRoomId, event, client
|
||||
)
|
||||
}
|
||||
|
||||
function renderListRules(list: ListMatches) {
|
||||
const renderRuleSummary = (rule: ListRule, entityDescription: string) => {
|
||||
return <li>
|
||||
{entityDescription} (<code>{rule.recommendation}</code>): <code>{rule.entity}</code> ({rule.reason})
|
||||
</li>
|
||||
};
|
||||
return <fragment>
|
||||
<a href={list.roomRef}>{list.roomId}</a>
|
||||
{list.shortcode ? `(shortcode: ${list.shortcode})` : ''}:<br/>
|
||||
<ul>
|
||||
{list.matches.length === 0
|
||||
? <li><i>No rules</i></li>
|
||||
: list.matches.map(rule => renderRuleSummary(rule, rule.kind))}
|
||||
</ul>
|
||||
</fragment>
|
||||
}
|
||||
|
||||
interface ListMatches {
|
||||
shortcode: string,
|
||||
roomRef: string,
|
||||
roomId: string,
|
||||
matches: ListRule[]
|
||||
}
|
||||
|
||||
defineInterfaceCommand({
|
||||
designator: ["rules"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([]),
|
||||
command: async function (this: MjolnirContext) {
|
||||
return CommandResult.Ok(
|
||||
this.mjolnir.policyListManager.lists
|
||||
.map(list => {
|
||||
return {
|
||||
shortcode: list.listShortcode,
|
||||
roomRef: list.roomRef,
|
||||
roomId: list.roomId,
|
||||
matches: list.allRules
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
summary: "Lists the rules currently in use by Mjolnir."
|
||||
})
|
||||
|
||||
defineMatrixInterfaceAdaptor({
|
||||
interfaceCommand: findTableCommand("mjolnir", "rules"),
|
||||
renderer: renderListMatches
|
||||
})
|
||||
|
||||
defineInterfaceCommand({
|
||||
designator: ["rules", "matching"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([
|
||||
{
|
||||
name: "entity",
|
||||
acceptor: union(
|
||||
findPresentationType("UserID"),
|
||||
findPresentationType("MatrixRoomReference"),
|
||||
findPresentationType("string"),
|
||||
)
|
||||
}
|
||||
]),
|
||||
command: async function (
|
||||
this: MjolnirContext, _keywords, entity: string|UserID|MatrixRoomReference
|
||||
): Promise<CommandResult<ListMatches[]>> {
|
||||
return CommandResult.Ok(
|
||||
this.mjolnir.policyListManager.lists
|
||||
.map(list => {
|
||||
return {
|
||||
shortcode: list.listShortcode,
|
||||
roomRef: list.roomRef,
|
||||
roomId: list.roomId,
|
||||
matches: list.rulesMatchingEntity(entity.toString())
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
summary: "Lists the rules in use that will match this entity e.g. `!rules matching @foo:example.com` will show all the user and server rules, including globs, that match this user"
|
||||
})
|
||||
|
||||
// I'm pretty sure that both commands could merge and use the same rendeer.
|
||||
defineMatrixInterfaceAdaptor({
|
||||
interfaceCommand: findTableCommand("mjolnir", "rules", "matching"),
|
||||
renderer: renderListMatches
|
||||
})
|
||||
Reference in New Issue
Block a user