Conversation
Yoric
left a comment
There was a problem hiding this comment.
Yeah, sounds like a sound plan.
| result: Awaited<ExecutorReturnType>) => Promise<void>; | ||
|
|
||
| /** | ||
| * Note, matrix interface command can be multi step ie ask for confirmation. |
There was a problem hiding this comment.
Nit: Generally speaking, documentation for a class should start with a description of what the class does, before any note. Here and for other classes.
| } else if (parts[1] === 'make' && parts[2] === 'admin' && parts.length > 3) { | ||
| return await execMakeRoomAdminCommand(roomId, event, mjolnir, parts); | ||
| } else { | ||
| const command = commandTable.findAMatchingCommand(parts.slice(1)); |
There was a problem hiding this comment.
So the idea is to apply it gradually, by progressively moving commands from handleCommand? Sounds like a good idea.
src/commands/CommandHandler.ts
Outdated
| return await mjolnir.client.sendMessage(roomId, reply); | ||
| } | ||
| } | ||
|
|
| */ | ||
|
|
||
| import { extractRequestError, LogService, MatrixClient, UserID } from "matrix-bot-sdk"; | ||
| import { extractRequestError, LogService, MatrixClient, RoomCreateOptions, UserID } from "matrix-bot-sdk"; |
| await client.sendReadReceipt(roomId, event['event_id']); | ||
| return handleCommand(roomId, event, this); | ||
|
|
||
| return handleCommand(roomId, event, this, this.matrixCommandTable); |
There was a problem hiding this comment.
Nit: Shouldn't handleCommand be a method of matrixCommandTable?
There was a problem hiding this comment.
Yep, but can't be until we have moved all the commands to the new table
| mjolnir: Mjolnir, | ||
| roomId: string, | ||
| event: any, | ||
| parts: string[]) => Promise<Parameters<ExecutorType>>; |
There was a problem hiding this comment.
I'm not entirely certain that parts is the right type for these. It makes it impossible to parse things that contains whitespaces. Or perhaps we'll want to add a special case for "any series of words between quotes" and turn them into a single part?
There was a problem hiding this comment.
Yep, this was mostly for compatibility. I'm not sure how to design the parsing properly yet but I do want this to be replaced.
What i do know though is that I am thinking of something where everything is read from a stream but we can just declare the accepting types for each argument + provide a parse function for specific arguments if they don't have common types (common meaning like a matrix room or mxid).
| * This is used by mjolnirs. | ||
| */ | ||
| export class MatrixCommandTable { | ||
| public readonly features: ApplicationFeature[]; |
There was a problem hiding this comment.
Is this the list of active features?
3e2e505 to
3fa1a43
Compare
Gnuxie
left a comment
There was a problem hiding this comment.
So, if you neglected to tell me if I was crazy before, how about now?
| /** | ||
| * Why do we need a Result Monad for the parser signiture. | ||
| * I (Gnuxie) don't like monadic error handling, simply because | ||
| * I'm a strong believer in failing early, yes i may be misinformed. | ||
| * The only reason we don't use an exception in this case is because | ||
| * these are NOT to be used nilly willy and thrown out of context | ||
| * from an unrelated place. The Monad ensures locality (in terms of call chain) | ||
| * to the user interface by being infuriating to deal with. | ||
| * It also does look different to an exception | ||
| * to a naive programmer. Ideally though, if the world had adopted | ||
| * condition based error handling i would simply create a condition | ||
| * type for validation errors that can be translated/overidden by | ||
| * the command handler, and it wouldn't have to look like this. | ||
| * It's important to remember the errors we are reporting are to do with user input, | ||
| * we're trying to tell the user they did something wrong and what that is. | ||
| * This is something completely different to a normal exception, | ||
| * where we are saying to ourselves that our assumptions in our code about | ||
| * the thing we're doing are completely wrong. The user never | ||
| * should see these as there is nothing they can do about it. | ||
| * | ||
| * OK, it would be too annoying even for me to have a real Monad. | ||
| * So this is dumb as hell, no worries though | ||
| * | ||
| * OK I'm beginning to regret my decision. | ||
| * | ||
| * TODO: Can we make ValidationResult include ValidationError | ||
| */ |
There was a problem hiding this comment.
Tell me what you think
| 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") | ||
| ); |
There was a problem hiding this comment.
And here as an example
| * Used by ApplicationCommands as required feature flags they depend on to function. | ||
| */ | ||
| export interface ApplicationFeature { | ||
| name: string, |
There was a problem hiding this comment.
Doc: Is this a human-readable name? A unique key? If the latter, possibly rename to key.
| */ | ||
| const APPLICATION_FEATURES = new Map<string/*feature name*/, ApplicationFeature>(); | ||
|
|
||
| export function defineApplicationFeature(feature: ApplicationFeature): void { |
| * type for validation errors that can be translated/overidden by | ||
| * the command handler, and it wouldn't have to look like this. | ||
| * It's important to remember the errors we are reporting are to do with user input, | ||
| * we're trying to tell the user they did something wrong and what that is. |
There was a problem hiding this comment.
Well, generally speaking, there are several kinds of errors and error-handling:
- Something in my code is broken. In JS, that's generally represented by a
TypeError(even if it's not a type error, sigh) orSyntaxError(exceptSyntaxErroris also used when parsing JSON, sigh). - Something in my environment is broken. In JS, that's generally other cases of
Error. - Something in my input is broken. There is nothing built-in in JS, but that could be handled by creating a new variant of
Error.
While I'm ok with using monads in the right setting and there are ways to use Monads in JS with some syntactic support, by encoding them as a generator and its caller. However, introducing a different paradigm in the middle of an application feels a bit cumbersome, so I'd like to avoid it if possible.
There was a problem hiding this comment.
On the other hand, I don't see anything that looks like monadic binding, so this isn't really a monad, more a data structure to represent good parse result/bad parse result, right? That feels ok to me.
What does it bring wrt a new variant of Error, though? Better type-checking?
There was a problem hiding this comment.
so this isn't really a monad, more a data structure to represent good parse result/bad parse result, right? That feels ok to me.
Yep
What does it bring wrt a new variant of Error, though? Better type-checking?
Because as I said, so it doesn't get misused. The validation errors are a description of something that the user has done wrong, for the user to see.
The idea is that commands can be used in other contexts than just from matrix, e.g. the mjolnir appservice web api that is given to the widget.
We also begin organizing commands into tables that can be automatically created from a set of features that are enabled in mjolnir's config (e.g. "synapse admin").
The only commands to have been changes so far is the ban/unban commands. Though the parser logic in that command is horrible.
This doesn't attempt to change the way we parse commands from Matrix, but I do plan to change that too, unsure of whether that will be in this PR though.
The PR is a draft because I want initial comments on the approach/design before i commit and rewrite more of the existing command handler.