mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
WIP
This commit is contained in:
parent
5395a66071
commit
7b094f3e28
@ -41,18 +41,200 @@ import { execMakeRoomAdminCommand } from "./MakeRoomAdminCommand";
|
||||
import { execSinceCommand } from "./SinceCommand";
|
||||
import { Lexer } from "./Lexer";
|
||||
|
||||
|
||||
export const COMMAND_PREFIX = "!mjolnir";
|
||||
|
||||
type Command = {
|
||||
cmd: string,
|
||||
help: string,
|
||||
code: (roomId: string, event: {content: {body: string}}, mjolnir: Mjolnir, parts: string[], lexer: Lexer) => Promise<any>,
|
||||
};
|
||||
const COMMANDS: Command[] = [{
|
||||
cmd: '',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execStatusCommand(roomId, event, mjolnir, parts.slice(2))
|
||||
},
|
||||
{
|
||||
cmd: 'ban',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execBanCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'unban',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execUnbanCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'rules',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execRulesMatchingCommand(roomId, event, mjolnir, parts[3]),
|
||||
},
|
||||
{
|
||||
cmd: 'rules',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execDumpRulesCommand(roomId, event, mjolnir),
|
||||
},
|
||||
{
|
||||
cmd: 'sync',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execSyncCommand(roomId, event, mjolnir),
|
||||
},
|
||||
{
|
||||
cmd: 'verify',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execPermissionCheckCommand(roomId, event, mjolnir),
|
||||
},
|
||||
{
|
||||
cmd: 'list',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execCreateListCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'watch',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execWatchCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'unwatch',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execUnwatchCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'redact',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execRedactCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'import',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execImportCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'default',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execSetDefaultListCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'deactivate',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execDeactivateCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'protections',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execListProtections(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'enable',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execEnableProtection(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'disable',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execDisableProtection(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'config',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execConfigSetProtection(roomId, event, mjolnir, parts.slice(3)),
|
||||
},
|
||||
{
|
||||
cmd: 'config',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execConfigAddProtection(roomId, event, mjolnir, parts.slice(3)),
|
||||
},
|
||||
{
|
||||
cmd: 'config',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execConfigRemoveProtection(roomId, event, mjolnir, parts.slice(3)),
|
||||
},
|
||||
{
|
||||
cmd: 'config',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execConfigGetProtection(roomId, event, mjolnir, parts.slice(3)),
|
||||
},
|
||||
{
|
||||
cmd: 'rooms',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execAddProtectedRoom(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'rooms',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execRemoveProtectedRoom(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'rooms',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execListProtectedRooms(roomId, event, mjolnir),
|
||||
},
|
||||
{
|
||||
cmd: 'move',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execMoveAliasCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'directory',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execAddRoomToDirectoryCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'directory',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execRemoveRoomFromDirectoryCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'alias',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execAddAliasCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'alias',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execRemoveAliasCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'resolve',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execResolveCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'powerlevel',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execSetPowerLevelCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'shutdown',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execShutdownRoomCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'since',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execSinceCommand(roomId, event, mjolnir, lexer),
|
||||
},
|
||||
{
|
||||
cmd: 'kick',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execKickCommand(roomId, event, mjolnir, parts),
|
||||
},
|
||||
{
|
||||
cmd: 'make',
|
||||
help: "FIXME",
|
||||
code: (roomId, event, mjolnir, parts, lexer) => execMakeRoomAdminCommand(roomId, event, mjolnir, parts),
|
||||
}];
|
||||
|
||||
export async function handleCommand(roomId: string, event: { content: { body: string } }, mjolnir: Mjolnir) {
|
||||
const line = event['content']['body'];
|
||||
const parts = line.trim().split(' ').filter(p => p.trim().length > 0);
|
||||
console.debug("YORIC", "line", line);
|
||||
const lexer = new Lexer(line);
|
||||
lexer.consume("command"); // Consume `!mjolnir`.
|
||||
let cmd = parts.length === 1 ? null : lexer.consume("id").text;
|
||||
lexer.token("command"); // Consume `!mjolnir`.
|
||||
const cmd = lexer.token("id").text;
|
||||
console.debug("YORIC", "cmd", cmd);
|
||||
|
||||
try {
|
||||
if (parts.length === 1 || cmd === 'status') {
|
||||
if (cmd === '' || cmd === 'status') {
|
||||
return await execStatusCommand(roomId, event, mjolnir, parts.slice(2));
|
||||
} else if (cmd === 'ban' && parts.length > 2) {
|
||||
return await execBanCommand(roomId, event, mjolnir, parts);
|
||||
|
@ -18,47 +18,47 @@ export class Lexer extends TokenizrClass {
|
||||
ctx.ignore()
|
||||
})
|
||||
|
||||
// Command rules, e.g. `!mjolnir`
|
||||
this.rule("command", /![a-zA-Z_]+/, (ctx) => {
|
||||
ctx.accept("command");
|
||||
});
|
||||
|
||||
// Identifier rules, used e.g. for subcommands `get`, `set` ...
|
||||
this.rule(/[a-zA-Z_]+/, (ctx) => {
|
||||
this.rule("id", /[a-zA-Z_]+/, (ctx) => {
|
||||
ctx.accept("id");
|
||||
});
|
||||
|
||||
// User IDs
|
||||
this.rule(/@[a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
// Users
|
||||
this.rule("userID", /@[a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
ctx.accept("userID");
|
||||
});
|
||||
this.rule(/@[a-zA-Z0-9_.=\-?*/]+:.+/, (ctx) => {
|
||||
this.rule("globUserID", /@[a-zA-Z0-9_.=\-?*/]+:.+/, (ctx) => {
|
||||
ctx.accept("globUserID");
|
||||
});
|
||||
|
||||
// User IDs
|
||||
this.rule(/![a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
// Rooms
|
||||
this.rule("roomID", /![a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
ctx.accept("roomID");
|
||||
});
|
||||
this.rule(/#[a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
this.rule("roomAlias", /#[a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
ctx.accept("roomAlias");
|
||||
});
|
||||
this.rule(/[#!][a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
this.rule("roomAliasOrID", /[#!][a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
ctx.accept("roomAliasOrID");
|
||||
});
|
||||
|
||||
// Numbers.
|
||||
this.rule(/[+-]?[0-9]+/, (ctx, match) => {
|
||||
this.rule("int", /[+-]?[0-9]+/, (ctx, match) => {
|
||||
ctx.accept("int", parseInt(match[0]))
|
||||
});
|
||||
|
||||
// Quoted strings.
|
||||
this.rule(/"((?:\\"|[^\r\n])*)"/, (ctx, match) => {
|
||||
this.rule("string", /"((?:\\"|[^\r\n])*)"/, (ctx, match) => {
|
||||
ctx.accept("string", match[1].replace(/\\"/g, "\""))
|
||||
});
|
||||
|
||||
// Arbitrary non-space content.
|
||||
this.rule(/\S+/, (ctx) => {
|
||||
ctx.accept("nospace");
|
||||
});
|
||||
|
||||
// Dates and durations.
|
||||
this.rule(/\S+/, (ctx, match) => {
|
||||
this.rule("dateOrDuration", /\S+/, (ctx, match) => {
|
||||
let date = new Date(match[0]);
|
||||
if (!date || Number.isNaN(date.getDate())) {
|
||||
let duration = parseDuration(match[0]);
|
||||
@ -73,13 +73,20 @@ export class Lexer extends TokenizrClass {
|
||||
});
|
||||
|
||||
// Jokers.
|
||||
this.rule(/\*/, (ctx) => {
|
||||
this.rule("STAR", /\*/, (ctx) => {
|
||||
ctx.accept("STAR");
|
||||
});
|
||||
this.rule(/.*/, ctx => {
|
||||
ctx.accept("EVERYTHING ELSE");
|
||||
this.rule(/./, (ctx) => {
|
||||
ctx.accept("ANYTHING ELSE")
|
||||
});
|
||||
|
||||
this.input(string);
|
||||
}
|
||||
|
||||
public token(state?: string): TokenizrModule.Token {
|
||||
if (typeof state === "string") {
|
||||
this.state(state);
|
||||
}
|
||||
return super.token();
|
||||
}
|
||||
}
|
||||
|
@ -69,15 +69,10 @@ function formatResult(action: string, targetRoomId: string, recentJoins: Join[],
|
||||
// - attempts to execute action;
|
||||
// - in case of success, returns `{ok: undefined}`, in case of error, returns `{error: string}`.
|
||||
async function execSinceCommandAux(destinationRoomId: string, event: any, mjolnir: Mjolnir, lexer: Lexer): Promise<Result<undefined>> {
|
||||
console.debug("YORIC", "lexer", lexer);
|
||||
|
||||
// Attempt to parse `<date/duration>` as a date or duration.
|
||||
let dateOrDurationToken: Date | number;
|
||||
try {
|
||||
dateOrDurationToken = lexer.alternatives(
|
||||
() => lexer.consume("date"),
|
||||
() => lexer.consume("duration")
|
||||
);
|
||||
dateOrDurationToken = lexer.token("dateOrDuration").value;
|
||||
} catch (ex) {
|
||||
return { error: "Invalid <date/duration>" };
|
||||
}
|
||||
@ -93,7 +88,7 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
}
|
||||
|
||||
// Attempt to parse `<action>` as Action.
|
||||
let actionToken = lexer.consume("id").text;
|
||||
let actionToken = lexer.token("id").text;
|
||||
let action: Action | null = null;
|
||||
for (let key in Action) {
|
||||
const maybeAction = Action[key as keyof typeof Action];
|
||||
@ -108,7 +103,7 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
console.debug("YORIC", "action", action);
|
||||
|
||||
// Attempt to parse `<limit>` as a number.
|
||||
const maxEntries = lexer.consume("int").value as number;
|
||||
const maxEntries = lexer.token("int").value as number;
|
||||
console.debug("YORIC", "maxEntries", maxEntries);
|
||||
|
||||
// Now list affected rooms.
|
||||
@ -116,8 +111,8 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
do {
|
||||
try {
|
||||
let token = lexer.alternatives(
|
||||
() => lexer.consume("STAR"),
|
||||
() => lexer.consume("roomAliasOrID"),
|
||||
() => lexer.token("STAR"),
|
||||
() => lexer.token("roomAliasOrID"),
|
||||
);
|
||||
if (token.type == "STAR") {
|
||||
for (let roomId of Object.keys(mjolnir.protectedRooms)) {
|
||||
@ -147,8 +142,8 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
|
||||
// Parse everything else as `<reason>`, stripping quotes if any have been added.
|
||||
const reason = lexer.alternatives(
|
||||
() => lexer.consume("string"),
|
||||
() => lexer.consume("EVERYTHING ELSE")
|
||||
() => lexer.token("string"),
|
||||
() => lexer.token("EVERYTHING ELSE")
|
||||
).text;
|
||||
console.debug("YORIC", "reason", reason);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user