mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-07-28 14:29:21 +00:00
Begin moving most commands to interface-manager and MPS.
This commit is contained in:
@@ -27,11 +27,11 @@ limitations under the License.
|
||||
|
||||
import { findPresentationType, parameters, ParsedKeywords } from "./interface-manager/ParameterParsing";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { MatrixRoomAlias, MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { CommandError, CommandResult } from "./interface-manager/Validation";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { ActionError, ActionResult, isError, MatrixRoomAlias, MatrixRoomReference, Ok } from "matrix-protection-suite";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
|
||||
// TODO: we should probably add an --admin keyword to these commands
|
||||
// since they don't actually need admin. Mjolnir had them as admin though.
|
||||
@@ -53,15 +53,18 @@ defineInterfaceCommand({
|
||||
description: 'The room to move the alias to.'
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, movingAlias: MatrixRoomAlias, room: MatrixRoomReference): Promise<CommandResult<void>> {
|
||||
const isAdmin = await this.mjolnir.isSynapseAdmin();
|
||||
if (!isAdmin) {
|
||||
return CommandError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, movingAlias: MatrixRoomAlias, room: MatrixRoomReference): Promise<ActionResult<void>> {
|
||||
const isAdminResult = await this.draupnir.synapseAdminClient?.isSynapseAdmin();
|
||||
if (isAdminResult === undefined || isError(isAdminResult) || !isAdminResult.ok) {
|
||||
return ActionError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
}
|
||||
const newRoomId = await room.resolve(this.mjolnir.client);
|
||||
await this.mjolnir.client.deleteRoomAlias(movingAlias.toRoomIdOrAlias());
|
||||
await this.mjolnir.client.createRoomAlias(movingAlias.toRoomIdOrAlias(), newRoomId.toRoomIdOrAlias());
|
||||
return CommandResult.Ok(undefined);
|
||||
const newRoomID = await resolveRoomReferenceSafe(this.client, room);
|
||||
if (isError(newRoomID)) {
|
||||
return newRoomID;
|
||||
}
|
||||
await this.draupnir.client.deleteRoomAlias(movingAlias.toRoomIDOrAlias());
|
||||
await this.draupnir.client.createRoomAlias(movingAlias.toRoomIDOrAlias(), newRoomID.ok.toRoomIDOrAlias());
|
||||
return Ok(undefined);
|
||||
},
|
||||
})
|
||||
|
||||
@@ -86,14 +89,17 @@ defineInterfaceCommand({
|
||||
description: 'The room to add the alias to.'
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, movingAlias: MatrixRoomAlias, room: MatrixRoomReference): Promise<CommandResult<void>> {
|
||||
const isAdmin = await this.mjolnir.isSynapseAdmin();
|
||||
if (!isAdmin) {
|
||||
return CommandError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, movingAlias: MatrixRoomAlias, room: MatrixRoomReference): Promise<ActionResult<void>> {
|
||||
const isAdmin = await this.draupnir.synapseAdminClient?.isSynapseAdmin();
|
||||
if (isAdmin === undefined || isError(isAdmin) || !isAdmin.ok) {
|
||||
return ActionError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
}
|
||||
const roomId = await room.resolve(this.mjolnir.client);
|
||||
await this.mjolnir.client.createRoomAlias(movingAlias.toRoomIdOrAlias(), roomId.toRoomIdOrAlias());
|
||||
return CommandResult.Ok(undefined);
|
||||
const roomID = await resolveRoomReferenceSafe(this.draupnir.client, room);
|
||||
if (isError(roomID)) {
|
||||
return roomID;
|
||||
}
|
||||
await this.draupnir.client.createRoomAlias(movingAlias.toRoomIDOrAlias(), roomID.ok.toRoomIDOrAlias());
|
||||
return Ok(undefined);
|
||||
},
|
||||
})
|
||||
|
||||
@@ -113,13 +119,13 @@ defineInterfaceCommand({
|
||||
description: 'The alias that should be deleted.'
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, alias: MatrixRoomAlias): Promise<CommandResult<void>> {
|
||||
const isAdmin = await this.mjolnir.isSynapseAdmin();
|
||||
if (!isAdmin) {
|
||||
return CommandError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, alias: MatrixRoomAlias): Promise<ActionResult<void>> {
|
||||
const isAdmin = await this.draupnir.synapseAdminClient?.isSynapseAdmin();
|
||||
if (isAdmin === undefined || isError(isAdmin) || !isAdmin.ok) {
|
||||
return ActionError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
}
|
||||
await this.mjolnir.client.deleteRoomAlias(alias.toRoomIdOrAlias());
|
||||
return CommandResult.Ok(undefined);
|
||||
await this.draupnir.client.deleteRoomAlias(alias.toRoomIDOrAlias());
|
||||
return Ok(undefined);
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
+40
-70
@@ -25,88 +25,57 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { UserID } from "matrix-bot-sdk";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { CommandError, CommandResult } from "./interface-manager/Validation";
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { RULE_ROOM, RULE_SERVER, RULE_USER } from "../models/ListRule";
|
||||
import PolicyList from "../models/PolicyList";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, makePresentationType, ParameterDescription, parameters, ParsedKeywords, RestDescription, simpleTypeValidator, union } from "./interface-manager/ParameterParsing";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { defineInterfaceCommand,findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, ParameterDescription, parameters, ParsedKeywords, RestDescription, union } from "./interface-manager/ParameterParsing";
|
||||
import "./interface-manager/MatrixPresentations";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { PromptOptions } from "./interface-manager/PromptForAccept";
|
||||
import { definePresentationRenderer } from "./interface-manager/DeadDocumentPresentation";
|
||||
import { DocumentNode } from "./interface-manager/DeadDocument";
|
||||
import { JSXFactory } from "./interface-manager/JSXFactory";
|
||||
|
||||
makePresentationType({
|
||||
name: "PolicyList",
|
||||
validator: simpleTypeValidator("PolicyList", (readItem: unknown) => readItem instanceof PolicyList)
|
||||
})
|
||||
|
||||
definePresentationRenderer(findPresentationType("PolicyList"), function(list: PolicyList): DocumentNode {
|
||||
return <p>
|
||||
{list.listShortcode} <a href={list.roomRef}>{list.roomId}</a>
|
||||
</p>
|
||||
})
|
||||
import { Draupnir } from "../Draupnir";
|
||||
import { ActionResult, MatrixRoomReference, PolicyRoomEditor, PolicyRuleType, isError } from "matrix-protection-suite";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
|
||||
|
||||
export async function findPolicyListFromRoomReference(mjolnir: Mjolnir, policyListReference: MatrixRoomReference): Promise<CommandResult<PolicyList, CommandError>> {
|
||||
const policyListRoomId = (await policyListReference.resolve(mjolnir.client)).toRoomIdOrAlias();
|
||||
const policyList = mjolnir.policyListManager.lists.find(list => list.roomId === policyListRoomId);
|
||||
if (policyList !== undefined) {
|
||||
return CommandResult.Ok(policyList);
|
||||
} else {
|
||||
// Would it be acceptable to create an anonymous policy list that is not being watched
|
||||
// by mjolnir for the purposes of banning / unbanning? unbanning requires loading the rules
|
||||
// but banning doesn't.. so this means you'd want two types of lists
|
||||
// one of which can only be made by a factory which watches them via sync
|
||||
// and makes sure mjolnir is joined.
|
||||
// This refactor would be important for previewing lists regardless.
|
||||
return CommandError.Result(`There is no policy list that Mjolnir is watching for ${policyListReference.toPermalink()}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function findPolicyListFromShortcode(mjolnir: Mjolnir, designator: string): Promise<CommandResult<PolicyList, CommandError>> {
|
||||
const list = mjolnir.policyListManager.resolveListShortcode(designator);
|
||||
if (list !== undefined) {
|
||||
return CommandResult.Ok(list);
|
||||
} else {
|
||||
return CommandError.Result(`There is no policy list with the shortcode ${designator} and a default list couldn't be found`);
|
||||
export async function findPolicyRoomEditorFromRoomReference(draupnir: Draupnir, policyRoomReference: MatrixRoomReference): Promise<ActionResult<PolicyRoomEditor>> {
|
||||
const policyRoomID = await resolveRoomReferenceSafe(draupnir.client, policyRoomReference);
|
||||
if (isError(policyRoomID)) {
|
||||
return policyRoomID;
|
||||
}
|
||||
return await draupnir.managerManager.policyRoomManager.getPolicyRoomEditor(policyRoomID.ok);
|
||||
}
|
||||
|
||||
async function ban(
|
||||
this: MjolnirContext,
|
||||
this: DraupnirContext,
|
||||
_keywords: ParsedKeywords,
|
||||
entity: UserID|MatrixRoomReference|string,
|
||||
policyListReference: MatrixRoomReference|string|PolicyList,
|
||||
policyRoomReference: MatrixRoomReference,
|
||||
...reasonParts: string[]
|
||||
): Promise<CommandResult<any, CommandError>> {
|
||||
// first step is to resolve the policy list
|
||||
const policyListResult = typeof policyListReference === 'string'
|
||||
? await findPolicyListFromShortcode(this.mjolnir, policyListReference)
|
||||
: policyListReference instanceof PolicyList
|
||||
? CommandResult.Ok(policyListReference)
|
||||
: await findPolicyListFromRoomReference(this.mjolnir, policyListReference);
|
||||
if (policyListResult.isErr()) {
|
||||
return policyListResult;
|
||||
): Promise<ActionResult<string>> {
|
||||
const policyListEditorResult = await findPolicyRoomEditorFromRoomReference(
|
||||
this.draupnir,
|
||||
policyRoomReference
|
||||
);
|
||||
if (isError(policyListEditorResult)) {
|
||||
return policyListEditorResult;
|
||||
}
|
||||
const policyList = policyListResult.ok;
|
||||
|
||||
const policyListEditor = policyListEditorResult.ok;
|
||||
const reason = reasonParts.join(' ');
|
||||
|
||||
if (entity instanceof UserID) {
|
||||
await policyList.banEntity(RULE_USER, entity.toString(), reason);
|
||||
} else if (entity instanceof MatrixRoomReference) {
|
||||
await policyList.banEntity(RULE_ROOM, entity.toRoomIdOrAlias(), reason);
|
||||
return await policyListEditor.banEntity(PolicyRuleType.User, entity.toString(), reason);
|
||||
} else if (typeof entity === 'string') {
|
||||
return await policyListEditor.banEntity(PolicyRuleType.Server,entity, reason);
|
||||
} else {
|
||||
await policyList.banEntity(RULE_SERVER, entity, reason);
|
||||
const resolvedRoomReference = await resolveRoomReferenceSafe(
|
||||
this.draupnir.client,
|
||||
entity
|
||||
);
|
||||
if (isError(resolvedRoomReference)) {
|
||||
return resolvedRoomReference;
|
||||
}
|
||||
return await policyListEditor.banEntity(PolicyRuleType.Server, resolvedRoomReference.ok.toRoomIDOrAlias(), reason);
|
||||
}
|
||||
return CommandResult.Ok(undefined);
|
||||
}
|
||||
|
||||
defineInterfaceCommand({
|
||||
@@ -124,23 +93,24 @@ defineInterfaceCommand({
|
||||
{
|
||||
name: "list",
|
||||
acceptor: union(
|
||||
findPresentationType("MatrixRoomReference"),
|
||||
findPresentationType("string"),
|
||||
findPresentationType("PolicyList"),
|
||||
findPresentationType("MatrixRoomReference")
|
||||
),
|
||||
prompt: async function (this: MjolnirContext, parameter: ParameterDescription): Promise<PromptOptions> {
|
||||
prompt: async function (this: DraupnirContext, _parameter: ParameterDescription): Promise<PromptOptions> {
|
||||
return {
|
||||
suggestions: this.mjolnir.policyListManager.lists
|
||||
suggestions: this.draupnir.managerManager.policyRoomManager.getEditablePolicyRoomIDs(
|
||||
this.draupnir.clientUserID,
|
||||
PolicyRuleType.User
|
||||
)
|
||||
};
|
||||
}
|
||||
},
|
||||
],
|
||||
new RestDescription<MjolnirContext>(
|
||||
new RestDescription<DraupnirContext>(
|
||||
"reason",
|
||||
findPresentationType("string"),
|
||||
async function(_parameter) {
|
||||
return {
|
||||
suggestions: this.mjolnir.config.commands.ban.defaultReasons
|
||||
suggestions: this.draupnir.config.commands.ban.defaultReasons
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -25,31 +25,55 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import PolicyList from "../models/PolicyList";
|
||||
import { RichReply } from "matrix-bot-sdk";
|
||||
import { Permalinks } from "./interface-manager/Permalinks";
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { ActionResult, MatrixRoomAlias, MatrixRoomID, PropagationType, isError } from "matrix-protection-suite";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { ParsedKeywords, findPresentationType, parameters } from "./interface-manager/ParameterParsing";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
|
||||
// !mjolnir list create <shortcode> <alias localpart>
|
||||
export async function execCreateListCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
|
||||
const shortcode = parts[3];
|
||||
const aliasLocalpart = parts[4];
|
||||
|
||||
const listRoomId = await PolicyList.createList(
|
||||
mjolnir.client,
|
||||
export async function createList(
|
||||
this: DraupnirContext,
|
||||
_keywords: ParsedKeywords,
|
||||
shortcode: string,
|
||||
alias: MatrixRoomAlias,
|
||||
...reasonParts: string[]
|
||||
): Promise<ActionResult<MatrixRoomID>> {
|
||||
const newList = await this.draupnir.managerManager.policyRoomManager.createPolicyRoom(
|
||||
shortcode,
|
||||
[event['sender']],
|
||||
{ room_alias_name: aliasLocalpart }
|
||||
[this.event.sender],
|
||||
{
|
||||
room_alias_name: alias.toRoomIDOrAlias()
|
||||
}
|
||||
);
|
||||
|
||||
const roomRef = MatrixRoomReference.fromPermalink(Permalinks.forRoom(listRoomId));
|
||||
await mjolnir.policyListManager.watchList(roomRef);
|
||||
await mjolnir.addProtectedRoom(listRoomId);
|
||||
|
||||
const html = `Created new list (<a href="${roomRef}">${listRoomId}</a>). This list is now being watched.`;
|
||||
const text = `Created new list (${roomRef}). This list is now being watched.`;
|
||||
const reply = RichReply.createFor(roomId, event, text, html);
|
||||
reply["msgtype"] = "m.notice";
|
||||
await mjolnir.client.sendMessage(roomId, reply);
|
||||
if (isError(newList)) {
|
||||
return newList;
|
||||
}
|
||||
const watchResult = await this.draupnir.protectedRoomsSet.issuerManager.watchList(PropagationType.Direct, newList.ok, {});
|
||||
if (isError(watchResult)) {
|
||||
return watchResult;
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
|
||||
defineInterfaceCommand({
|
||||
designator: ["list", "create"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([
|
||||
{
|
||||
name: "shortcode",
|
||||
acceptor: findPresentationType("string"),
|
||||
},
|
||||
{
|
||||
name: "alias",
|
||||
acceptor: findPresentationType("MatrixRoomAlias"),
|
||||
},
|
||||
]),
|
||||
command: createList,
|
||||
summary: "Create a new Policy Room which can be used to ban users, rooms and servers from your protected rooms"
|
||||
})
|
||||
|
||||
defineMatrixInterfaceAdaptor({
|
||||
interfaceCommand: findTableCommand("mjolnir", "list", "create"),
|
||||
renderer: tickCrossRenderer
|
||||
})
|
||||
|
||||
@@ -25,13 +25,12 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { UserID } from "matrix-bot-sdk";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters, ParsedKeywords } from "./interface-manager/ParameterParsing";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { CommandResult, CommandError } from "./interface-manager/Validation";
|
||||
import { ActionError, ActionResult, Ok, UserID, isError } from "matrix-protection-suite";
|
||||
|
||||
defineInterfaceCommand({
|
||||
table: "synapse admin",
|
||||
@@ -43,13 +42,16 @@ defineInterfaceCommand({
|
||||
acceptor: findPresentationType("UserID"),
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, targetUser: UserID): Promise<CommandResult<void, CommandError>> {
|
||||
const isAdmin = await this.mjolnir.isSynapseAdmin();
|
||||
if (!isAdmin) {
|
||||
return CommandError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, targetUser: UserID): Promise<ActionResult<void>> {
|
||||
const isAdmin = await this.draupnir.synapseAdminClient?.isSynapseAdmin();
|
||||
if (isAdmin === undefined || isError(isAdmin) || !isAdmin.ok) {
|
||||
return ActionError.Result('I am not a Synapse administrator, or the endpoint to deactivate a user is blocked');
|
||||
}
|
||||
await this.mjolnir.deactivateSynapseUser(targetUser.toString());
|
||||
return CommandResult.Ok(undefined);
|
||||
if (this.draupnir.synapseAdminClient === undefined) {
|
||||
throw new TypeError("Shouldn't be happening at this point");
|
||||
}
|
||||
await this.draupnir.synapseAdminClient.deactivateUser(targetUser.toString());
|
||||
return Ok(undefined);
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -27,25 +27,31 @@ limitations under the License.
|
||||
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters } from "./interface-manager/ParameterParsing";
|
||||
import { MjolnirBaseExecutor, MjolnirContext } from "./CommandHandler";
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { UserID } from "matrix-bot-sdk";
|
||||
import { CommandError, CommandResult } from "./interface-manager/Validation";
|
||||
import { DraupnirBaseExecutor, DraupnirContext } from "./CommandHandler";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { ActionError, ActionResult, MatrixRoomReference, Ok, UserID, isError } from "matrix-protection-suite";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
|
||||
async function hijackRoomCommand(
|
||||
this: MjolnirContext, _keywords: void, room: MatrixRoomReference, user: UserID
|
||||
): Promise<CommandResult<void, CommandError>> {
|
||||
const isAdmin = await this.mjolnir.isSynapseAdmin();
|
||||
if (!this.mjolnir.config.admin?.enableMakeRoomAdminCommand || !isAdmin) {
|
||||
return CommandError.Result("Either the command is disabled or Mjolnir is not running as homeserver administrator.")
|
||||
this: DraupnirContext, _keywords: void, room: MatrixRoomReference, user: UserID
|
||||
): Promise<ActionResult<void>> {
|
||||
const isAdmin = await this.draupnir.synapseAdminClient?.isSynapseAdmin();
|
||||
if (!this.draupnir.config.admin?.enableMakeRoomAdminCommand || isAdmin === undefined || isError(isAdmin) || !isAdmin.ok) {
|
||||
return ActionError.Result("Either the command is disabled or Mjolnir is not running as homeserver administrator.")
|
||||
}
|
||||
await this.mjolnir.makeUserRoomAdmin(room.toRoomIdOrAlias(), user.toString());
|
||||
return CommandResult.Ok(undefined);
|
||||
if (this.draupnir.synapseAdminClient === undefined) {
|
||||
throw new TypeError('Should be impossible at this point');
|
||||
}
|
||||
const resolvedRoom = await resolveRoomReferenceSafe(this.client, room);
|
||||
if (isError(resolvedRoom)) {
|
||||
return resolvedRoom;
|
||||
}
|
||||
await this.draupnir.synapseAdminClient?.makeUserRoomAdmin(resolvedRoom.ok.toRoomIDOrAlias(), user.toString());
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
defineInterfaceCommand<MjolnirBaseExecutor>({
|
||||
defineInterfaceCommand<DraupnirBaseExecutor>({
|
||||
designator: ["hijack", "room"],
|
||||
table: "synapse admin",
|
||||
parameters: parameters([
|
||||
|
||||
@@ -25,26 +25,36 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { RichReply } from "matrix-bot-sdk";
|
||||
import { EntityType } from "../models/ListRule";
|
||||
import PolicyList from "../models/PolicyList";
|
||||
import { DraupnirBaseExecutor, DraupnirContext } from "./CommandHandler";
|
||||
import { ActionResult, MatrixRoomReference, MultipleErrors, PolicyRuleType, RoomActionError, RoomUpdateError, isError } from "matrix-protection-suite";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters } from "./interface-manager/ParameterParsing";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
|
||||
// !mjolnir import <room ID> <shortcode>
|
||||
export async function execImportCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
|
||||
const importRoomId = await mjolnir.client.resolveRoom(parts[2]);
|
||||
const list = mjolnir.policyListManager.lists.find(b => b.listShortcode === parts[3]) as PolicyList;
|
||||
if (!list) {
|
||||
const errMessage = "Unable to find list - check your shortcode.";
|
||||
const errReply = RichReply.createFor(roomId, event, errMessage, errMessage);
|
||||
errReply["msgtype"] = "m.notice";
|
||||
mjolnir.client.sendMessage(roomId, errReply);
|
||||
return;
|
||||
export async function importCommand(
|
||||
this: DraupnirContext,
|
||||
_keywords: void,
|
||||
importFromRoomReference: MatrixRoomReference,
|
||||
policyRoomReference: MatrixRoomReference
|
||||
): Promise<ActionResult<void>> {
|
||||
const importFromRoom = await resolveRoomReferenceSafe(this.client, importFromRoomReference);
|
||||
if (isError(importFromRoom)) {
|
||||
return importFromRoom;
|
||||
}
|
||||
|
||||
let importedRules = 0;
|
||||
|
||||
const state = await mjolnir.client.getRoomState(importRoomId);
|
||||
const policyRoom = await resolveRoomReferenceSafe(this.client, policyRoomReference);
|
||||
if (isError(policyRoom)) {
|
||||
return policyRoom;
|
||||
}
|
||||
const policyRoomEditor = await this.draupnir.managerManager.policyRoomManager.getPolicyRoomEditor(
|
||||
policyRoom.ok
|
||||
);
|
||||
if (isError(policyRoomEditor)) {
|
||||
return policyRoomEditor;
|
||||
}
|
||||
const state = await this.client.getRoomState(importFromRoom.ok.toRoomIDOrAlias());
|
||||
const errors: RoomUpdateError[] = [];
|
||||
for (const stateEvent of state) {
|
||||
const content = stateEvent['content'] || {};
|
||||
if (!content || Object.keys(content).length === 0) continue;
|
||||
@@ -53,27 +63,44 @@ export async function execImportCommand(roomId: string, event: any, mjolnir: Mjo
|
||||
// Member event - check for ban
|
||||
if (content['membership'] === 'ban') {
|
||||
const reason = content['reason'] || '<no reason>';
|
||||
|
||||
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Adding user ${stateEvent['state_key']} to ban list`);
|
||||
await list.banEntity(EntityType.RULE_USER, stateEvent['state_key'], reason);
|
||||
importedRules++;
|
||||
const result = await policyRoomEditor.ok.banEntity(PolicyRuleType.User, stateEvent['state_key'], reason);
|
||||
if (isError(result)) {
|
||||
errors.push(RoomActionError.fromActionError(policyRoom.ok, result.error));
|
||||
}
|
||||
}
|
||||
} else if (stateEvent['type'] === 'm.room.server_acl' && stateEvent['state_key'] === '') {
|
||||
// ACL event - ban denied servers
|
||||
if (!content['deny']) continue;
|
||||
for (const server of content['deny']) {
|
||||
const reason = "<no reason>";
|
||||
|
||||
await mjolnir.client.sendNotice(mjolnir.managementRoomId, `Adding server ${server} to ban list`);
|
||||
|
||||
await list.banEntity(EntityType.RULE_SERVER, server, reason);
|
||||
importedRules++;
|
||||
const result = await policyRoomEditor.ok.banEntity(PolicyRuleType.Server, server, reason);
|
||||
if (isError(result)) {
|
||||
errors.push(RoomActionError.fromActionError(policyRoom.ok, result.error));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const message = `Imported ${importedRules} rules to ban list`;
|
||||
const reply = RichReply.createFor(roomId, event, message, message);
|
||||
reply['msgtype'] = "m.notice";
|
||||
await mjolnir.client.sendMessage(roomId, reply);
|
||||
return MultipleErrors.Result(`There were multiple errors when importing bans from the room ${importFromRoomReference.toPermalink()} to ${policyRoomReference.toPermalink()}`, { errors });
|
||||
}
|
||||
|
||||
defineInterfaceCommand<DraupnirBaseExecutor>({
|
||||
designator: ["import"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([
|
||||
{
|
||||
name: "import from room",
|
||||
acceptor: findPresentationType("MatrixRoomReference")
|
||||
},
|
||||
{
|
||||
name: "policy room",
|
||||
acceptor: findPresentationType("MatrixRoomReference")
|
||||
}
|
||||
]),
|
||||
command: importCommand,
|
||||
summary: "Import user and server bans from a Matrix room and add them to a policy room."
|
||||
})
|
||||
|
||||
defineMatrixInterfaceAdaptor({
|
||||
interfaceCommand: findTableCommand("mjolnir", "import"),
|
||||
renderer: tickCrossRenderer
|
||||
})
|
||||
|
||||
@@ -25,43 +25,98 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { ActionResult, MatrixEventReference, MatrixEventViaAlias, MatrixEventViaRoomID, MatrixRoomReference, Ok, UserID, isError } from "matrix-protection-suite";
|
||||
import { redactUserMessagesIn } from "../utils";
|
||||
import { Permalinks } from "./interface-manager/Permalinks";
|
||||
import { KeywordsDescription, ParsedKeywords, RestDescription, findPresentationType, parameters, union } from "./interface-manager/ParameterParsing";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { Draupnir } from "../Draupnir";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
|
||||
// !mjolnir redact <user ID> [room alias] [limit]
|
||||
export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
|
||||
const userId = parts[2];
|
||||
let roomAlias: string|null = null;
|
||||
let limit = Number.parseInt(parts.length > 3 ? parts[3] : "", 10); // default to NaN for later
|
||||
if (parts.length > 3 && isNaN(limit)) {
|
||||
roomAlias = await mjolnir.client.resolveRoom(parts[3]);
|
||||
if (parts.length > 4) {
|
||||
limit = Number.parseInt(parts[4], 10);
|
||||
}
|
||||
export async function redactEvent(
|
||||
draupnir: Draupnir,
|
||||
reference: MatrixEventReference,
|
||||
reason: string
|
||||
): Promise<ActionResult<void>> {
|
||||
const resolvedRoom = await resolveRoomReferenceSafe(draupnir.client, reference.reference);
|
||||
if (isError(resolvedRoom)) {
|
||||
return resolvedRoom;
|
||||
}
|
||||
|
||||
// Make sure we always have a limit set
|
||||
if (isNaN(limit)) limit = 1000;
|
||||
|
||||
const processingReactionId = await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], 'In Progress');
|
||||
|
||||
if (userId[0] !== '@') {
|
||||
// Assume it's a permalink
|
||||
const parsed = Permalinks.parseUrl(parts[2]);
|
||||
if (parsed.roomIdOrAlias === undefined || parsed.eventId === undefined) {
|
||||
throw new TypeError(`Got a malformed permalink ${parsed}`)
|
||||
}
|
||||
const targetRoomId = await mjolnir.client.resolveRoom(parsed.roomIdOrAlias);
|
||||
await mjolnir.client.redactEvent(targetRoomId, parsed.eventId);
|
||||
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
|
||||
await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing command');
|
||||
return;
|
||||
}
|
||||
|
||||
const targetRoomIds = roomAlias ? [roomAlias] : mjolnir.protectedRoomsTracker.getProtectedRooms();
|
||||
await redactUserMessagesIn(mjolnir.client, mjolnir.managementRoomOutput, userId, targetRoomIds, limit);
|
||||
|
||||
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
|
||||
await mjolnir.client.redactEvent(roomId, processingReactionId, 'done processing');
|
||||
await draupnir.client.redactEvent(resolvedRoom.ok.toRoomIDOrAlias(), reference.eventID, reason);
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
export async function redactCommand(
|
||||
this: DraupnirContext,
|
||||
keywords: ParsedKeywords,
|
||||
reference: UserID | MatrixEventReference,
|
||||
...reasonParts: string[]
|
||||
): Promise<ActionResult<void>> {
|
||||
const reason = reasonParts.join(' ');
|
||||
if (reference instanceof MatrixEventViaAlias || reference instanceof MatrixEventViaRoomID) {
|
||||
return await redactEvent(this.draupnir, reference, reason);
|
||||
}
|
||||
const rawLimit = keywords.getKeyword<string>('limit', undefined);
|
||||
const limit = rawLimit === undefined ? undefined : Number.parseInt(rawLimit, 10);
|
||||
const restrictToRoomReference = keywords.getKeyword<MatrixRoomReference>("room", undefined);
|
||||
const restrictToRoom = restrictToRoomReference ? await resolveRoomReferenceSafe(this.client, restrictToRoomReference) : undefined;
|
||||
if (restrictToRoom !== undefined && isError(restrictToRoom)) {
|
||||
return restrictToRoom;
|
||||
}
|
||||
const roomsToRedactWithin = restrictToRoom === undefined ? this.draupnir.protectedRoomsSet.protectedRoomsConfig.allRooms : [restrictToRoom.ok];
|
||||
await redactUserMessagesIn(
|
||||
this.client,
|
||||
this.draupnir.managementRoomOutput,
|
||||
reference.toString(),
|
||||
roomsToRedactWithin.map((room) => room.toRoomIDOrAlias()),
|
||||
limit,
|
||||
this.draupnir.config.noop
|
||||
);
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
defineInterfaceCommand({
|
||||
designator: ["redact"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([
|
||||
{
|
||||
name: "entity",
|
||||
acceptor: union(
|
||||
findPresentationType("UserID"),
|
||||
findPresentationType("MatrixEventReference")
|
||||
),
|
||||
}],
|
||||
new RestDescription<DraupnirContext>(
|
||||
"reason",
|
||||
findPresentationType("string"),
|
||||
async function(_parameter) {
|
||||
return {
|
||||
suggestions: this.draupnir.config.commands.ban.defaultReasons
|
||||
}
|
||||
}
|
||||
),
|
||||
new KeywordsDescription({
|
||||
limit: {
|
||||
name: "limit",
|
||||
isFlag: false,
|
||||
acceptor: findPresentationType("string"),
|
||||
description: 'Limit the number of messages to be redacted per room.'
|
||||
},
|
||||
room: {
|
||||
name: 'room',
|
||||
isFlag: false,
|
||||
acceptor: findPresentationType("MatrixRoomReference"),
|
||||
description: 'Allows the command to be scoped to just one protected room.'
|
||||
}
|
||||
}),
|
||||
),
|
||||
command: redactCommand,
|
||||
summary: "Redacts either a users's recent messagaes within protected rooms or a specific message shared with the bot."
|
||||
});
|
||||
|
||||
defineMatrixInterfaceAdaptor({
|
||||
interfaceCommand: findTableCommand("mjolnir", "redact"),
|
||||
renderer: tickCrossRenderer
|
||||
})
|
||||
|
||||
@@ -27,7 +27,7 @@ limitations under the License.
|
||||
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters } from "./interface-manager/ParameterParsing";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { MatrixRoomID, MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { CommandResult } from "./interface-manager/Validation";
|
||||
import { CommandException, CommandExceptionKind } from "./interface-manager/CommandException";
|
||||
@@ -43,7 +43,7 @@ defineInterfaceCommand({
|
||||
designator: ["rooms"],
|
||||
summary: "List all of the protected rooms.",
|
||||
parameters: parameters([]),
|
||||
command: async function (this: MjolnirContext, _keywrods): Promise<CommandResult<string[]>> {
|
||||
command: async function (this: DraupnirContext, _keywrods): Promise<CommandResult<string[]>> {
|
||||
return CommandResult.Ok(this.mjolnir.protectedRoomsTracker.getProtectedRooms());
|
||||
}
|
||||
})
|
||||
@@ -84,7 +84,7 @@ defineInterfaceCommand({
|
||||
description: 'The room to protect.'
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords, roomRef: MatrixRoomReference): Promise<CommandResult<void>> {
|
||||
command: async function (this: DraupnirContext, _keywords, roomRef: MatrixRoomReference): Promise<CommandResult<void>> {
|
||||
const roomIDOrError = await (async () => {
|
||||
try {
|
||||
return CommandResult.Ok(await roomRef.joinClient(this.mjolnir.client));
|
||||
@@ -115,7 +115,7 @@ defineInterfaceCommand({
|
||||
description: 'The room to stop protecting.'
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords, roomRef: MatrixRoomReference): Promise<CommandResult<void>> {
|
||||
command: async function (this: DraupnirContext, _keywords, roomRef: MatrixRoomReference): Promise<CommandResult<void>> {
|
||||
const roomID = await roomRef.resolve(this.mjolnir.client);
|
||||
await this.mjolnir.removeProtectedRoom(roomID.toRoomIdOrAlias());
|
||||
try {
|
||||
|
||||
@@ -25,7 +25,7 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { renderMatrixAndSend } from "./interface-manager/DeadDocumentMatrix";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { JSXFactory } from "./interface-manager/JSXFactory";
|
||||
@@ -87,7 +87,7 @@ defineInterfaceCommand({
|
||||
designator: ["rules"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([]),
|
||||
command: async function (this: MjolnirContext) {
|
||||
command: async function (this: DraupnirContext) {
|
||||
return CommandResult.Ok(
|
||||
this.mjolnir.policyListManager.lists
|
||||
.map(list => {
|
||||
@@ -122,7 +122,7 @@ defineInterfaceCommand({
|
||||
}
|
||||
]),
|
||||
command: async function (
|
||||
this: MjolnirContext, _keywords, entity: string|UserID|MatrixRoomReference
|
||||
this: DraupnirContext, _keywords, entity: string|UserID|MatrixRoomReference
|
||||
): Promise<CommandResult<ListMatches[]>> {
|
||||
return CommandResult.Ok(
|
||||
this.mjolnir.policyListManager.lists
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { ParsedKeywords, RestDescription, findPresentationType, parameters } from "./interface-manager/ParameterParsing";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { CommandError, CommandResult } from "./interface-manager/Validation";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { ActionError, ActionResult, Ok } from "matrix-protection-suite";
|
||||
|
||||
|
||||
defineInterfaceCommand({
|
||||
@@ -12,7 +12,7 @@ defineInterfaceCommand({
|
||||
summary: "Sets the displayname of the draupnir instance to the specified value in all rooms.",
|
||||
parameters: parameters(
|
||||
[],
|
||||
new RestDescription<MjolnirContext>(
|
||||
new RestDescription<DraupnirContext>(
|
||||
"displayname",
|
||||
findPresentationType("string"),
|
||||
),
|
||||
@@ -21,16 +21,16 @@ defineInterfaceCommand({
|
||||
})
|
||||
|
||||
// !draupnir displayname <displayname>
|
||||
export async function execSetDisplayNameCommand(this: MjolnirContext, _keywords: ParsedKeywords, ...displaynameParts: string[]): Promise<CommandResult<any>> {
|
||||
export async function execSetDisplayNameCommand(this: DraupnirContext, _keywords: ParsedKeywords, ...displaynameParts: string[]): Promise<ActionResult<void>> {
|
||||
const displayname = displaynameParts.join(' ');
|
||||
try {
|
||||
await this.client.setDisplayName(displayname);
|
||||
} catch (e) {
|
||||
const message = e.message || (e.body ? e.body.error : '<no message>');
|
||||
return CommandError.Result(`Failed to set displayname to ${displayname}: ${message}`)
|
||||
return ActionError.Result(`Failed to set displayname to ${displayname}: ${message}`)
|
||||
}
|
||||
|
||||
return CommandResult.Ok(undefined);
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
defineMatrixInterfaceAdaptor({
|
||||
|
||||
@@ -25,26 +25,50 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { Mjolnir } from "../Mjolnir";
|
||||
import { LogLevel, LogService } from "matrix-bot-sdk";
|
||||
import { ActionResult, MatrixRoomID, MatrixRoomReference, Ok, UserID, isError } from "matrix-protection-suite";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { ParsedKeywords, RestDescription, findPresentationType, parameters } from "./interface-manager/ParameterParsing";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { defineInterfaceCommand } from "./interface-manager/InterfaceCommand";
|
||||
|
||||
// !mjolnir powerlevel <user ID> <level> [room]
|
||||
export async function execSetPowerLevelCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
|
||||
const victim = parts[2];
|
||||
const level = Math.round(Number(parts[3]));
|
||||
const inRoom = parts[4];
|
||||
|
||||
let targetRooms = inRoom ? [await mjolnir.client.resolveRoom(inRoom)] : mjolnir.protectedRoomsTracker.getProtectedRooms();
|
||||
|
||||
for (const targetRoomId of targetRooms) {
|
||||
try {
|
||||
await mjolnir.client.setUserPowerLevel(victim, targetRoomId, level);
|
||||
} catch (e) {
|
||||
const message = e.message || (e.body ? e.body.error : '<no message>');
|
||||
LogService.error("SetPowerLevelCommand", e);
|
||||
await mjolnir.managementRoomOutput.logMessage(LogLevel.ERROR, "SetPowerLevelCommand", `Failed to set power level of ${victim} to ${level} in ${targetRoomId}: ${message}`, targetRoomId);
|
||||
async function setPowerLevelCommand(
|
||||
this: DraupnirContext,
|
||||
_keywords: ParsedKeywords,
|
||||
user: UserID,
|
||||
powerLevel: string,
|
||||
...givenRooms: MatrixRoomReference[]
|
||||
): Promise<ActionResult<void>> {
|
||||
const parsedLevel = Number.parseInt(powerLevel, 10);
|
||||
const resolvedGivenRooms: MatrixRoomID[] = [];
|
||||
for (const room of givenRooms) {
|
||||
const resolvedResult = await resolveRoomReferenceSafe(this.client, room);
|
||||
if (isError(resolvedResult)) {
|
||||
return resolvedResult;
|
||||
} else {
|
||||
resolvedGivenRooms.push(resolvedResult.ok);
|
||||
}
|
||||
}
|
||||
|
||||
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
|
||||
const rooms = givenRooms.length === 0 ? this.draupnir.protectedRoomsSet.protectedRoomsConfig.allRooms : resolvedGivenRooms;
|
||||
for (const room of rooms) {
|
||||
await this.draupnir.client.setUserPowerLevel(user.toString(), room.toRoomIDOrAlias(), parsedLevel);
|
||||
}
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
defineInterfaceCommand({
|
||||
table: "",
|
||||
designator: [],
|
||||
parameters: parameters([
|
||||
{
|
||||
name: "user",
|
||||
acceptor: findPresentationType("UserID")
|
||||
},
|
||||
{
|
||||
name: "power level",
|
||||
acceptor: findPresentationType("string")
|
||||
}
|
||||
],
|
||||
new RestDescription("rooms", findPresentationType("MatrixRoomReference"))),
|
||||
command: setPowerLevelCommand,
|
||||
summary: "Set the power level of a user across the protected rooms set, or within the provided rooms"
|
||||
})
|
||||
|
||||
@@ -27,11 +27,11 @@ limitations under the License.
|
||||
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters, ParsedKeywords, RestDescription } from "./interface-manager/ParameterParsing";
|
||||
import { CommandResult, CommandError } from "./interface-manager/Validation";
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { defineMatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { ActionError, ActionResult, MatrixRoomReference, Ok, isError } from "matrix-protection-suite";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
|
||||
defineInterfaceCommand({
|
||||
table: "synapse admin",
|
||||
@@ -41,17 +41,32 @@ defineInterfaceCommand({
|
||||
{
|
||||
name: 'room',
|
||||
acceptor: findPresentationType("MatrixRoomReference"),
|
||||
}
|
||||
},
|
||||
],
|
||||
new RestDescription("reason", findPresentationType("string"))),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, targetRoom: MatrixRoomReference, ...reasonParts: string[]): Promise<CommandResult<void, CommandError>> {
|
||||
const isAdmin = await this.mjolnir.isSynapseAdmin();
|
||||
if (!isAdmin) {
|
||||
return CommandError.Result('I am not a Synapse administrator, or the endpoint to shutdown a room is blocked');
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, targetRoom: MatrixRoomReference, ...reasonParts: string[]): Promise<ActionResult<void>> {
|
||||
const isAdmin = await this.draupnir.synapseAdminClient?.isSynapseAdmin();
|
||||
if (isAdmin === undefined || isError(isAdmin) || !isAdmin.ok) {
|
||||
return ActionError.Result('I am not a Synapse administrator, or the endpoint to shutdown a room is blocked');
|
||||
}
|
||||
if (this.draupnir.synapseAdminClient === undefined) {
|
||||
throw new TypeError(`Should be impossible at this point.`);
|
||||
}
|
||||
const resolvedRoom = await resolveRoomReferenceSafe(this.client, targetRoom);
|
||||
if (isError(resolvedRoom)) {
|
||||
return resolvedRoom;
|
||||
}
|
||||
const reason = reasonParts.join(" ");
|
||||
await this.mjolnir.shutdownSynapseRoom((await targetRoom.resolve(this.client)).toRoomIdOrAlias(), reason);
|
||||
return CommandResult.Ok(undefined);
|
||||
await this.draupnir.synapseAdminClient.deleteRoom(
|
||||
resolvedRoom.ok.toRoomIDOrAlias(),
|
||||
{
|
||||
message: reason,
|
||||
new_room_user_id: this.draupnir.clientUserID,
|
||||
block: true,
|
||||
}
|
||||
|
||||
);
|
||||
return Ok(undefined);
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -25,40 +25,34 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { STATE_CHECKING_PERMISSIONS, STATE_NOT_STARTED, STATE_RUNNING, STATE_SYNCING } from "../Mjolnir";
|
||||
import { RichReply } from "matrix-bot-sdk";
|
||||
import PolicyList from "../models/PolicyList";
|
||||
import { PACKAGE_JSON, SOFTWARE_VERSION } from "../config";
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters, RestDescription } from "./interface-manager/ParameterParsing";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { CommandError, CommandResult } from "./interface-manager/Validation";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { defineMatrixInterfaceAdaptor, MatrixContext, MatrixInterfaceAdaptor } from "./interface-manager/MatrixInterfaceAdaptor";
|
||||
import { MatrixSendClient } from "../MatrixEmitter";
|
||||
import { JSXFactory } from "./interface-manager/JSXFactory";
|
||||
import { Protection } from "../protections/Protection";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
import { renderMatrixAndSend } from "./interface-manager/DeadDocumentMatrix";
|
||||
import { ActionResult, Ok, PolicyRoomRevision, PolicyRoomWatchProfile, PolicyRuleType, isError } from "matrix-protection-suite";
|
||||
import { Draupnir } from "../Draupnir";
|
||||
|
||||
defineInterfaceCommand({
|
||||
designator: ["status"],
|
||||
table: "mjolnir",
|
||||
parameters: parameters([]),
|
||||
command: async function () { return CommandResult.Ok(mjolnirStatusInfo.call(this)) },
|
||||
command: async function (this: DraupnirContext) {
|
||||
return Ok(draupnirStatusInfo(this.draupnir))
|
||||
},
|
||||
summary: "Show the status of the bot."
|
||||
})
|
||||
|
||||
export interface ListInfo {
|
||||
shortcode: string,
|
||||
roomRef: string,
|
||||
roomId: string,
|
||||
serverRules: number,
|
||||
userRules: number,
|
||||
roomRules: number,
|
||||
watchedListProfile: PolicyRoomWatchProfile,
|
||||
revision: PolicyRoomRevision
|
||||
}
|
||||
|
||||
export interface StatusInfo {
|
||||
state: string, // a small description of the state of Mjolnir
|
||||
numberOfProtectedRooms: number,
|
||||
subscribedLists: ListInfo[],
|
||||
subscribedAndProtectedLists: ListInfo[],
|
||||
@@ -66,27 +60,36 @@ export interface StatusInfo {
|
||||
repository: string
|
||||
}
|
||||
|
||||
|
||||
function mjolnirStatusInfo(this: MjolnirContext): StatusInfo {
|
||||
const listInfo = (list: PolicyList): ListInfo => {
|
||||
return {
|
||||
shortcode: list.listShortcode,
|
||||
roomRef: list.roomRef,
|
||||
roomId: list.roomId,
|
||||
serverRules: list.serverRules.length,
|
||||
userRules: list.userRules.length,
|
||||
roomRules: list.roomRules.length,
|
||||
async function listInfo(draupnir: Draupnir): Promise<ListInfo[]> {
|
||||
const watchedListProfiles = draupnir.protectedRoomsSet.issuerManager.allWatchedLists;
|
||||
const issuerResults = await Promise.all(watchedListProfiles.map((profile) =>
|
||||
draupnir.managerManager.policyRoomManager.getPolicyRoomRevisionIssuer(profile.room)
|
||||
));
|
||||
return issuerResults.map((result) => {
|
||||
if (isError(result)) {
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
const revision = result.ok.currentRevision;
|
||||
const associatedProfile = watchedListProfiles.find((profile) => profile.room.toRoomIDOrAlias() === revision.room.toRoomIDOrAlias())
|
||||
if (associatedProfile === undefined) {
|
||||
throw new TypeError(`Shouldn't be possible to have got a result for a list profile we don't have`)
|
||||
}
|
||||
return {
|
||||
watchedListProfile: associatedProfile,
|
||||
revision: revision
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: need a shoutout to dependencies in here and NOTICE info.
|
||||
async function draupnirStatusInfo(draupnir: Draupnir): Promise<StatusInfo> {
|
||||
const watchedListInfo = await listInfo(draupnir);
|
||||
const protectedWatchedLists = watchedListInfo.filter((info) => draupnir.protectedRoomsSet.isProtectedRoom(info.revision.room.toRoomIDOrAlias()));
|
||||
const unprotectedListProfiles = watchedListInfo.filter((info) => !draupnir.protectedRoomsSet.isProtectedRoom(info.revision.room.toRoomIDOrAlias()));
|
||||
return {
|
||||
state: this.mjolnir.state,
|
||||
numberOfProtectedRooms: this.mjolnir.protectedRoomsTracker.getProtectedRooms().length,
|
||||
subscribedLists: this.mjolnir.policyListManager.lists
|
||||
.filter(list => !this.mjolnir.explicitlyProtectedRooms.includes(list.roomId))
|
||||
.map(listInfo),
|
||||
subscribedAndProtectedLists: this.mjolnir.policyListManager.lists
|
||||
.filter(list => this.mjolnir.explicitlyProtectedRooms.includes(list.roomId))
|
||||
.map(listInfo),
|
||||
numberOfProtectedRooms: draupnir.protectedRoomsSet.protectedRoomsConfig.allRooms.length,
|
||||
subscribedLists: unprotectedListProfiles,
|
||||
subscribedAndProtectedLists: protectedWatchedLists,
|
||||
version: SOFTWARE_VERSION,
|
||||
repository: PACKAGE_JSON['repository'] ?? 'Unknown'
|
||||
}
|
||||
@@ -94,49 +97,35 @@ function mjolnirStatusInfo(this: MjolnirContext): StatusInfo {
|
||||
|
||||
defineMatrixInterfaceAdaptor({
|
||||
interfaceCommand: findTableCommand("mjolnir", "status"),
|
||||
renderer: async function (this: MatrixInterfaceAdaptor<MatrixContext>, client: MatrixSendClient, commandRoomId: string, event: any, result: CommandResult<StatusInfo>): Promise<void> {
|
||||
const renderState = (state: StatusInfo['state']) => {
|
||||
const notRunning = (text: string) => {
|
||||
return <fragment><b>Running: </b>❌ (${text})<br/></fragment>
|
||||
};
|
||||
switch (state) {
|
||||
case STATE_NOT_STARTED:
|
||||
return notRunning('not started');
|
||||
case STATE_CHECKING_PERMISSIONS:
|
||||
return notRunning('checking own permission');
|
||||
case STATE_SYNCING:
|
||||
return notRunning('syncing lists');
|
||||
case STATE_RUNNING:
|
||||
return <fragment><b>Running: </b>✅<br/></fragment>
|
||||
default:
|
||||
return notRunning('unknown state');
|
||||
}
|
||||
};
|
||||
renderer: async function (this, client, commandRoomID, event, result: ActionResult<StatusInfo>): Promise<void> {
|
||||
const renderPolicyLists = (header: string, lists: ListInfo[]) => {
|
||||
const listInfo = lists.map(list => {
|
||||
const renderedLists = lists.map(list => {
|
||||
return <li>
|
||||
{list.shortcode} @ <a href={list.roomRef}>{list.roomId}</a>
|
||||
(rules: {list.serverRules} servers, {list.userRules} users, {list.roomRules} rooms)
|
||||
<a href={list.revision.room.toPermalink()}>{list.revision.room.toRoomIDOrAlias()}</a> propagation: {list.watchedListProfile.propagation}
|
||||
(rules: {list.revision.allRulesOfType(PolicyRuleType.Server).length} servers, {list.revision.allRulesOfType(PolicyRuleType.User)} users, {list.revision.allRulesOfType(PolicyRuleType.Room).length} rooms)
|
||||
</li>
|
||||
});
|
||||
return <fragment>
|
||||
<b>{header}</b><br/>
|
||||
<ul>
|
||||
{listInfo.length === 0 ? <li><i>None</i></li> : listInfo}
|
||||
{renderedLists.length === 0 ? <li><i>None</i></li> : renderedLists}
|
||||
</ul>
|
||||
</fragment>
|
||||
};
|
||||
if (isError(result)) {
|
||||
await tickCrossRenderer.call(this, client, commandRoomID, event, result);
|
||||
return;
|
||||
}
|
||||
const info = result.ok;
|
||||
|
||||
await renderMatrixAndSend(<root>
|
||||
{renderState(info.state)}
|
||||
<b>Protected Rooms: </b>{info.numberOfProtectedRooms}<br/>
|
||||
{renderPolicyLists('Subscribed policy lists', info.subscribedLists)}
|
||||
{renderPolicyLists('Subscribed and protected policy lists', info.subscribedAndProtectedLists)}
|
||||
{renderPolicyLists('Subscribed policy rooms', info.subscribedLists)}
|
||||
{renderPolicyLists('Subscribed and protected policy rooms', info.subscribedAndProtectedLists)}
|
||||
<b>Version: </b><code>{info.version}</code><br/>
|
||||
<b>Repository: </b><code>{info.repository}</code><br/>
|
||||
</root>,
|
||||
commandRoomId,
|
||||
commandRoomID,
|
||||
event,
|
||||
client);
|
||||
}
|
||||
@@ -151,12 +140,12 @@ defineInterfaceCommand({
|
||||
acceptor: findPresentationType("string")
|
||||
},
|
||||
],
|
||||
new RestDescription<MjolnirContext>(
|
||||
new RestDescription<DraupnirContext>(
|
||||
"subcommand",
|
||||
findPresentationType("any")
|
||||
)),
|
||||
command: async function (
|
||||
this: MjolnirContext, _keywords, protectionName: string, ...subcommands: string[]
|
||||
this: DraupnirContext, _keywords, protectionName: string, ...subcommands: string[]
|
||||
): Promise<CommandResult<Awaited<ReturnType<Protection['statusCommand']>>>> {
|
||||
const protection = this.mjolnir.protectionManager.getProtection(protectionName);
|
||||
if (!protection) {
|
||||
|
||||
@@ -25,7 +25,7 @@ limitations under the License.
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { findPresentationType, KeywordsDescription, parameters, ParsedKeywords, union } from "./interface-manager/ParameterParsing";
|
||||
import { UserID, MatrixGlob, LogLevel } from "matrix-bot-sdk";
|
||||
@@ -68,7 +68,7 @@ async function unbanUserFromRooms(mjolnir: Mjolnir, rule: MatrixGlob) {
|
||||
}
|
||||
|
||||
async function unban(
|
||||
this: MjolnirContext,
|
||||
this: DraupnirContext,
|
||||
keywords: ParsedKeywords,
|
||||
entity: UserID|MatrixRoomReference|string,
|
||||
policyListReference: MatrixRoomReference|string,
|
||||
@@ -126,7 +126,7 @@ defineInterfaceCommand({
|
||||
findPresentationType("string"),
|
||||
findPresentationType("PolicyList"),
|
||||
),
|
||||
prompt: async function (this: MjolnirContext) {
|
||||
prompt: async function (this: DraupnirContext) {
|
||||
return {
|
||||
suggestions: this.mjolnir.policyListManager.lists
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ limitations under the License.
|
||||
|
||||
import { defineInterfaceCommand, findTableCommand } from "./interface-manager/InterfaceCommand";
|
||||
import { findPresentationType, parameters, ParsedKeywords } from "./interface-manager/ParameterParsing";
|
||||
import { MjolnirContext } from "./CommandHandler";
|
||||
import { DraupnirContext } from "./CommandHandler";
|
||||
import { MatrixRoomReference } from "./interface-manager/MatrixRoomReference";
|
||||
import { CommandError, CommandResult } from "./interface-manager/Validation";
|
||||
import { tickCrossRenderer } from "./interface-manager/MatrixHelpRenderer";
|
||||
@@ -43,7 +43,7 @@ defineInterfaceCommand({
|
||||
acceptor: findPresentationType("MatrixRoomReference"),
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, list: MatrixRoomReference): Promise<CommandResult<void, CommandError>> {
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, list: MatrixRoomReference): Promise<CommandResult<void, CommandError>> {
|
||||
await this.mjolnir.policyListManager.watchList(list);
|
||||
return CommandResult.Ok(undefined);
|
||||
},
|
||||
@@ -64,7 +64,7 @@ defineInterfaceCommand({
|
||||
acceptor: findPresentationType("MatrixRoomReference"),
|
||||
}
|
||||
]),
|
||||
command: async function (this: MjolnirContext, _keywords: ParsedKeywords, list: MatrixRoomReference): Promise<CommandResult<void, CommandError>> {
|
||||
command: async function (this: DraupnirContext, _keywords: ParsedKeywords, list: MatrixRoomReference): Promise<CommandResult<void, CommandError>> {
|
||||
await this.mjolnir.policyListManager.unwatchList(list);
|
||||
return CommandResult.Ok(undefined);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user