From 3fa1a433eda37145b8312e155a6e65d1f0bf9013 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Wed, 23 Nov 2022 11:53:55 +0000 Subject: [PATCH] Use Ban/Unban command as an example. --- src/commands/UnbanBanCommand.ts | 102 +++++++++++++++++--------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/src/commands/UnbanBanCommand.ts b/src/commands/UnbanBanCommand.ts index 5a5b2b7c..cdd73eca 100644 --- a/src/commands/UnbanBanCommand.ts +++ b/src/commands/UnbanBanCommand.ts @@ -16,21 +16,17 @@ limitations under the License. import { Mjolnir } from "../Mjolnir"; import PolicyList from "../models/PolicyList"; -import { extractRequestError, LogLevel, LogService, MatrixGlob, RichReply } from "matrix-bot-sdk"; +import { extractRequestError, LogLevel, LogService, MatrixGlob } from "matrix-bot-sdk"; import { RULE_ROOM, RULE_SERVER, RULE_USER, USER_RULE_TYPES } from "../models/ListRule"; import { DEFAULT_LIST_EVENT_TYPE } from "./SetDefaultBanListCommand"; import { defineApplicationCommand } from "./ApplicationCommand"; import { defineMatrixInterfaceCommand } from "./MatrixInterfaceCommand"; +import { ValidationError, ValidationResult } from "./Validation"; -interface Arguments { - list: PolicyList | null; - entity: string; - ruleType: string | null; - reason: string; -} +type Arguments = Parameters<(mjolnir: Mjolnir, list: PolicyList, ruleType: string, entity: string, reason: string) => void>; // Exported for tests -export async function parseArguments(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]): Promise { +export async function parseArguments(mjolnir: Mjolnir, roomId: string, event: any, parts: string[]): Promise> { let defaultShortcode: string | null = null; try { const data: { shortcode: string } = await mjolnir.client.getAccountData(DEFAULT_LIST_EVENT_TYPE); @@ -42,10 +38,19 @@ export async function parseArguments(roomId: string, event: any, mjolnir: Mjolni // Assume no default. } + const findList = (mjolnir: Mjolnir, shortcode: string): ValidationResult => { + const foundList = mjolnir.lists.find(b => b.listShortcode.toLowerCase() === shortcode.toLowerCase()); + if (foundList !== undefined) { + return ValidationResult.Ok(foundList); + } else { + return ValidationResult.Err(ValidationError.makeValidationError('shortcode not found', `A list with the shourtcode ${shortcode} could not be found.`)); + } + } + let argumentIndex = 2; let ruleType: string | null = null; let entity: string | null = null; - let list: PolicyList | null = null; + let list: ValidationResult|null = null; let force = false; while (argumentIndex < 7 && argumentIndex < parts.length) { const arg = parts[argumentIndex++]; @@ -61,10 +66,7 @@ export async function parseArguments(roomId: string, event: any, mjolnir: Mjolni else if (arg.startsWith("!") && !ruleType) ruleType = RULE_ROOM; else if (!ruleType) ruleType = RULE_SERVER; } else if (!list) { - const foundList = mjolnir.lists.find(b => b.listShortcode.toLowerCase() === arg.toLowerCase()); - if (foundList !== undefined) { - list = foundList; - } + list = findList(mjolnir, arg.toLocaleLowerCase()); } if (entity) break; @@ -88,47 +90,55 @@ export async function parseArguments(roomId: string, event: any, mjolnir: Mjolni } if (!list) { - list = mjolnir.lists.find(b => b.listShortcode.toLowerCase() === defaultShortcode) || null; - } - - let replyMessage: string | null = null; - if (!list) replyMessage = "No ban list matching that shortcode was found"; - else if (!ruleType) replyMessage = "Please specify the type as either 'user', 'room', or 'server'"; - else if (!entity) replyMessage = "No entity found"; - - if (mjolnir.config.commands.confirmWildcardBan && /[*?]/.test(entity) && !force) { - replyMessage = "Wildcard bans require an additional `--force` argument to confirm"; + if (defaultShortcode) { + list = await findList(mjolnir, defaultShortcode); + if (list.isErr()) { + return ValidationResult.Err(ValidationError.makeValidationError( + "shortcode not found", + `A shortcode was not provided for this command, and a list couldn't be found with the default shortcode ${defaultShortcode}`)) + } + } else { + // FIXME: should be turned into a utility function to find the default list. + // and in general, why is there a default shortcode instead of a default list? + return ValidationResult.Err(ValidationError.makeValidationError( + "no default shortcode", + `A shortcode was not provided for this command, and a default shortcode was not set either.` + )) + } } - if (replyMessage) { - const reply = RichReply.createFor(roomId, event, replyMessage, replyMessage); - reply["msgtype"] = "m.notice"; - await mjolnir.client.sendMessage(roomId, reply); - return null; + if (list.isErr()) { + return ValidationResult.Err(list.err); + } else if (!ruleType) { + return ValidationResult.Err( + ValidationError.makeValidationError('uknown rule type', "Please specify the type as either 'user', 'room', or 'server'") + ); + } else if (!entity) { + return ValidationResult.Err( + ValidationError.makeValidationError('no entity', "No entity was able to be parsed from this command") + ); + } else if (mjolnir.config.commands.confirmWildcardBan && /[*?]/.test(entity) && !force) { + return ValidationResult.Err( + ValidationError.makeValidationError("wildcard required", "Wildcard bans require an additional `--force` argument to confirm") + ); } - return { - list, - entity, + return ValidationResult.Ok([ + mjolnir, + list.ok, ruleType, - reason: parts.splice(argumentIndex).join(" ").trim(), - }; + entity, + parts.splice(argumentIndex).join(" ").trim(), + ]); } -const BAN_COMMAND = defineApplicationCommand([], async (list: PolicyList, ruleType: string, entity: string, reason: string): Promise => { +const BAN_COMMAND = defineApplicationCommand([], async (mjonlir: Mjolnir, list: PolicyList, ruleType: string, entity: string, reason: string): Promise => { await list.banEntity(ruleType, entity, reason); }); // !mjolnir ban [reason] [--force] defineMatrixInterfaceCommand(["ban"], - async function (mjolnir: Mjolnir, roomId: string, event: any, parts: string[]): Promise<[PolicyList, string, string, string]> { - const bits = await parseArguments(roomId, event, mjolnir, parts); - if (bits === null) { - // FIXME - throw new Error("Couldn't parse arguments FIXME - parser needs to be rewritten to reject nulls"); - } - return [bits.list!, bits.ruleType!, bits.entity!, bits.reason!]; - }, + parseArguments, BAN_COMMAND, async function (mjolnir: Mjolnir, commandRoomId: string, event: any, result: void) { await mjolnir.client.unstableApis.addReactionToEvent(commandRoomId, event['event_id'], '✅'); @@ -180,13 +190,7 @@ const UNBAN_COMMAND = defineApplicationCommand([], async (mjolnir: Mjolnir, list // !mjolnir unban [apply:t/f] defineMatrixInterfaceCommand(["unban"], - async function (mjolnir: Mjolnir, roomId: string, event: any, parts: string[]): Promise<[Mjolnir, PolicyList, string, string, string]> { - const bits = await parseArguments(roomId, event, mjolnir, parts); - if (bits === null) { - throw new Error("Couldn't parse arguments FIXME"); - } - return [mjolnir, bits.list!, bits.ruleType!, bits.entity!, bits.reason!]; - }, + parseArguments, UNBAN_COMMAND, async function (mjolnir: Mjolnir, commandRoomId: string, event: any, result: void) { await mjolnir.client.unstableApis.addReactionToEvent(commandRoomId, event['event_id'], '✅');