From 89bede86c30042dcadfdea2925e255b83da3bfc0 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 6 Dec 2023 17:05:17 +0000 Subject: [PATCH] Begin moving most commands to interface-manager and MPS. --- src/commands/AliasCommands.ts | 54 ++++++----- src/commands/Ban.tsx | 110 ++++++++-------------- src/commands/CreateBanListCommand.ts | 72 ++++++++++----- src/commands/DeactivateCommand.ts | 20 ++-- src/commands/HijackRoomCommand.ts | 30 +++--- src/commands/ImportCommand.ts | 91 +++++++++++------- src/commands/RedactCommand.ts | 127 ++++++++++++++++++-------- src/commands/Rooms.tsx | 8 +- src/commands/Rules.tsx | 6 +- src/commands/SetDisplayNameCommand.ts | 12 +-- src/commands/SetPowerLevelCommand.ts | 62 +++++++++---- src/commands/ShutdownRoomCommand.ts | 35 +++++-- src/commands/StatusCommand.tsx | 111 ++++++++++------------ src/commands/Unban.ts | 6 +- src/commands/WatchUnwatchCommand.ts | 6 +- 15 files changed, 434 insertions(+), 316 deletions(-) diff --git a/src/commands/AliasCommands.ts b/src/commands/AliasCommands.ts index ec58ae46..cb85c93e 100644 --- a/src/commands/AliasCommands.ts +++ b/src/commands/AliasCommands.ts @@ -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> { - 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> { + 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> { - 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> { + 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> { - 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> { + 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); }, }) diff --git a/src/commands/Ban.tsx b/src/commands/Ban.tsx index f3659a79..6bb06384 100644 --- a/src/commands/Ban.tsx +++ b/src/commands/Ban.tsx @@ -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

- {list.listShortcode} {list.roomId} -

-}) +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> { - 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> { - 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> { + 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> { - // 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> { + 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 { + prompt: async function (this: DraupnirContext, _parameter: ParameterDescription): Promise { return { - suggestions: this.mjolnir.policyListManager.lists + suggestions: this.draupnir.managerManager.policyRoomManager.getEditablePolicyRoomIDs( + this.draupnir.clientUserID, + PolicyRuleType.User + ) }; } }, ], - new RestDescription( + new RestDescription( "reason", findPresentationType("string"), async function(_parameter) { return { - suggestions: this.mjolnir.config.commands.ban.defaultReasons + suggestions: this.draupnir.config.commands.ban.defaultReasons } }), ), diff --git a/src/commands/CreateBanListCommand.ts b/src/commands/CreateBanListCommand.ts index 78723f30..15e5ce59 100644 --- a/src/commands/CreateBanListCommand.ts +++ b/src/commands/CreateBanListCommand.ts @@ -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 -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> { + 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 (${listRoomId}). 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 +}) diff --git a/src/commands/DeactivateCommand.ts b/src/commands/DeactivateCommand.ts index 1b8d8a93..b1d05dfd 100644 --- a/src/commands/DeactivateCommand.ts +++ b/src/commands/DeactivateCommand.ts @@ -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> { - 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> { + 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); }, }) diff --git a/src/commands/HijackRoomCommand.ts b/src/commands/HijackRoomCommand.ts index 6f67eeda..3752f0dd 100644 --- a/src/commands/HijackRoomCommand.ts +++ b/src/commands/HijackRoomCommand.ts @@ -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> { - 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> { + 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({ +defineInterfaceCommand({ designator: ["hijack", "room"], table: "synapse admin", parameters: parameters([ diff --git a/src/commands/ImportCommand.ts b/src/commands/ImportCommand.ts index a23e81a3..b03d6ae5 100644 --- a/src/commands/ImportCommand.ts +++ b/src/commands/ImportCommand.ts @@ -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 -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> { + 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'] || ''; - - 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 = ""; - - 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({ + 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 +}) diff --git a/src/commands/RedactCommand.ts b/src/commands/RedactCommand.ts index d1c185f5..31c286c7 100644 --- a/src/commands/RedactCommand.ts +++ b/src/commands/RedactCommand.ts @@ -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 [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> { + 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> { + const reason = reasonParts.join(' '); + if (reference instanceof MatrixEventViaAlias || reference instanceof MatrixEventViaRoomID) { + return await redactEvent(this.draupnir, reference, reason); + } + const rawLimit = keywords.getKeyword('limit', undefined); + const limit = rawLimit === undefined ? undefined : Number.parseInt(rawLimit, 10); + const restrictToRoomReference = keywords.getKeyword("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( + "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 +}) diff --git a/src/commands/Rooms.tsx b/src/commands/Rooms.tsx index eaf80613..170181f1 100644 --- a/src/commands/Rooms.tsx +++ b/src/commands/Rooms.tsx @@ -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> { + command: async function (this: DraupnirContext, _keywrods): Promise> { 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> { + command: async function (this: DraupnirContext, _keywords, roomRef: MatrixRoomReference): Promise> { 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> { + command: async function (this: DraupnirContext, _keywords, roomRef: MatrixRoomReference): Promise> { const roomID = await roomRef.resolve(this.mjolnir.client); await this.mjolnir.removeProtectedRoom(roomID.toRoomIdOrAlias()); try { diff --git a/src/commands/Rules.tsx b/src/commands/Rules.tsx index f1e3cb3d..8f2916b1 100644 --- a/src/commands/Rules.tsx +++ b/src/commands/Rules.tsx @@ -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> { return CommandResult.Ok( this.mjolnir.policyListManager.lists diff --git a/src/commands/SetDisplayNameCommand.ts b/src/commands/SetDisplayNameCommand.ts index ae6e905b..97355782 100644 --- a/src/commands/SetDisplayNameCommand.ts +++ b/src/commands/SetDisplayNameCommand.ts @@ -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( + new RestDescription( "displayname", findPresentationType("string"), ), @@ -21,16 +21,16 @@ defineInterfaceCommand({ }) // !draupnir displayname -export async function execSetDisplayNameCommand(this: MjolnirContext, _keywords: ParsedKeywords, ...displaynameParts: string[]): Promise> { +export async function execSetDisplayNameCommand(this: DraupnirContext, _keywords: ParsedKeywords, ...displaynameParts: string[]): Promise> { const displayname = displaynameParts.join(' '); try { await this.client.setDisplayName(displayname); } catch (e) { const message = e.message || (e.body ? e.body.error : ''); - 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({ diff --git a/src/commands/SetPowerLevelCommand.ts b/src/commands/SetPowerLevelCommand.ts index 759258e3..d5e6b87a 100644 --- a/src/commands/SetPowerLevelCommand.ts +++ b/src/commands/SetPowerLevelCommand.ts @@ -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 [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 : ''); - 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> { + 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" +}) diff --git a/src/commands/ShutdownRoomCommand.ts b/src/commands/ShutdownRoomCommand.ts index 54fcf3e6..2e67905d 100644 --- a/src/commands/ShutdownRoomCommand.ts +++ b/src/commands/ShutdownRoomCommand.ts @@ -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> { - 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> { + 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); }, }) diff --git a/src/commands/StatusCommand.tsx b/src/commands/StatusCommand.tsx index a8399db3..74fc9eb7 100644 --- a/src/commands/StatusCommand.tsx +++ b/src/commands/StatusCommand.tsx @@ -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 { + 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 { + 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, client: MatrixSendClient, commandRoomId: string, event: any, result: CommandResult): Promise { - const renderState = (state: StatusInfo['state']) => { - const notRunning = (text: string) => { - return Running: ❌ (${text})
- }; - 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 Running:
- default: - return notRunning('unknown state'); - } - }; + renderer: async function (this, client, commandRoomID, event, result: ActionResult): Promise { const renderPolicyLists = (header: string, lists: ListInfo[]) => { - const listInfo = lists.map(list => { + const renderedLists = lists.map(list => { return
  • - {list.shortcode} @ {list.roomId} - (rules: {list.serverRules} servers, {list.userRules} users, {list.roomRules} rooms) + {list.revision.room.toRoomIDOrAlias()} 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)
  • }); return {header}
      - {listInfo.length === 0 ?
    • None
    • : listInfo} + {renderedLists.length === 0 ?
    • None
    • : renderedLists}
    }; + if (isError(result)) { + await tickCrossRenderer.call(this, client, commandRoomID, event, result); + return; + } const info = result.ok; await renderMatrixAndSend( - {renderState(info.state)} Protected Rooms: {info.numberOfProtectedRooms}
    - {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)} Version: {info.version}
    Repository: {info.repository}
    , - commandRoomId, + commandRoomID, event, client); } @@ -151,12 +140,12 @@ defineInterfaceCommand({ acceptor: findPresentationType("string") }, ], - new RestDescription( + new RestDescription( "subcommand", findPresentationType("any") )), command: async function ( - this: MjolnirContext, _keywords, protectionName: string, ...subcommands: string[] + this: DraupnirContext, _keywords, protectionName: string, ...subcommands: string[] ): Promise>>> { const protection = this.mjolnir.protectionManager.getProtection(protectionName); if (!protection) { diff --git a/src/commands/Unban.ts b/src/commands/Unban.ts index e9f8fc21..fb7ce2fc 100644 --- a/src/commands/Unban.ts +++ b/src/commands/Unban.ts @@ -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 }; diff --git a/src/commands/WatchUnwatchCommand.ts b/src/commands/WatchUnwatchCommand.ts index 9d51b8b1..145f90ea 100644 --- a/src/commands/WatchUnwatchCommand.ts +++ b/src/commands/WatchUnwatchCommand.ts @@ -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> { + command: async function (this: DraupnirContext, _keywords: ParsedKeywords, list: MatrixRoomReference): Promise> { 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> { + command: async function (this: DraupnirContext, _keywords: ParsedKeywords, list: MatrixRoomReference): Promise> { await this.mjolnir.policyListManager.unwatchList(list); return CommandResult.Ok(undefined); },