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
3cb4ffc3e6
commit
da4edb8854
@ -48,14 +48,14 @@ export const COMMAND_PREFIX = "!mjolnir";
|
||||
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.token("command"); // Consume `!mjolnir`.
|
||||
// Extract command.
|
||||
const cmd = lexer.alternatives(
|
||||
() => lexer.token("id").text,
|
||||
() => null
|
||||
);
|
||||
console.debug("YORIC", "cmd", cmd);
|
||||
|
||||
try {
|
||||
if (cmd === null || cmd === 'status') {
|
||||
|
@ -6,92 +6,98 @@ import * as TokenizrModule from "tokenizr";
|
||||
import { parseDuration } from "../utils";
|
||||
const TokenizrClass = Tokenizr || TokenizrModule;
|
||||
|
||||
const WHITESPACE = /\s+/;
|
||||
const COMMAND = /![a-zA-Z_]+/;
|
||||
const IDENTIFIER = /[a-zA-Z_]+/;
|
||||
const USER_ID = /@[a-zA-Z0-9_.=\-/]+:\S+/;
|
||||
const GLOB_USER_ID = /@[a-zA-Z0-9_.=\-?*/]+:\S+/;
|
||||
const ROOM_ID = /![a-zA-Z0-9_.=\-/]+:\S+/;
|
||||
const ROOM_ALIAS = /#[a-zA-Z0-9_.=\-/]+:\S+/;
|
||||
const ROOM_ALIAS_OR_ID = /[#!][a-zA-Z0-9_.=\-/]+:\S+/;
|
||||
const INT = /[+-]?[0-9]+/;
|
||||
const STRING = /"((?:\\"|[^\r\n])*)"/;
|
||||
const DATE_OR_DURATION = /(?:"([^"]+)")|([^"]\S+)/;
|
||||
const STAR = /\*/;
|
||||
const ETC = /.*$/;
|
||||
|
||||
/**
|
||||
* A lexer for common cases.
|
||||
* A lexer for command parsing.
|
||||
*
|
||||
* Recommended use is `lexer.token("state")`.
|
||||
*/
|
||||
export class Lexer extends TokenizrClass {
|
||||
constructor(string: string) {
|
||||
super();
|
||||
console.debug("YORIC", "Lexer", 0);
|
||||
// Ignore whitespace.
|
||||
this.rule(/\s+/, (ctx) => {
|
||||
this.rule(WHITESPACE, (ctx) => {
|
||||
ctx.ignore()
|
||||
})
|
||||
|
||||
// Command rules, e.g. `!mjolnir`
|
||||
this.rule("command", /![a-zA-Z_]+/, (ctx) => {
|
||||
this.rule("command", COMMAND, (ctx) => {
|
||||
ctx.accept("command");
|
||||
});
|
||||
|
||||
// Identifier rules, used e.g. for subcommands `get`, `set` ...
|
||||
this.rule("id", /[a-zA-Z_]+/, (ctx) => {
|
||||
this.rule("id", IDENTIFIER, (ctx) => {
|
||||
ctx.accept("id");
|
||||
});
|
||||
|
||||
// Users
|
||||
this.rule("userID", /@[a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
this.rule("userID", USER_ID, (ctx) => {
|
||||
ctx.accept("userID");
|
||||
});
|
||||
this.rule("globUserID", /@[a-zA-Z0-9_.=\-?*/]+:.+/, (ctx) => {
|
||||
this.rule("globUserID", GLOB_USER_ID, (ctx) => {
|
||||
ctx.accept("globUserID");
|
||||
});
|
||||
|
||||
// Rooms
|
||||
this.rule("roomID", /![a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
this.rule("roomID", ROOM_ID, (ctx) => {
|
||||
ctx.accept("roomID");
|
||||
});
|
||||
this.rule("roomAlias", /#[a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
this.rule("roomAlias", ROOM_ALIAS, (ctx) => {
|
||||
ctx.accept("roomAlias");
|
||||
});
|
||||
this.rule("roomAliasOrID", /[#!][a-zA-Z0-9_.=\-/]+:.+/, (ctx) => {
|
||||
this.rule("roomAliasOrID", ROOM_ALIAS_OR_ID, (ctx) => {
|
||||
ctx.accept("roomAliasOrID");
|
||||
});
|
||||
|
||||
// Numbers.
|
||||
this.rule("int", /[+-]?[0-9]+/, (ctx, match) => {
|
||||
this.rule("int", INT, (ctx, match) => {
|
||||
ctx.accept("int", parseInt(match[0]))
|
||||
});
|
||||
|
||||
// Quoted strings.
|
||||
this.rule("string", /"((?:\\"|[^\r\n])*)"/, (ctx, match) => {
|
||||
this.rule("string", STRING, (ctx, match) => {
|
||||
ctx.accept("string", match[1].replace(/\\"/g, "\""))
|
||||
});
|
||||
|
||||
// Dates and durations.
|
||||
console.debug("YORIC", "Lexer", 1);
|
||||
try {
|
||||
this.rule("dateOrDuration", /(?:"([^"]+)")|(\S+)/, (ctx, match) => {
|
||||
let content = match[1] || match[2];
|
||||
console.debug("YORIC", "Lexer", "dateOrDuration", content);
|
||||
let date = new Date(content);
|
||||
console.debug("YORIC", "Lexer", "dateOrDuration", "date", date);
|
||||
if (!date || Number.isNaN(date.getDate())) {
|
||||
let duration = parseDuration(content);
|
||||
console.debug("YORIC", "Lexer", "dateOrDuration", "duration", duration);
|
||||
if (!duration || Number.isNaN(duration)) {
|
||||
ctx.reject();
|
||||
} else {
|
||||
ctx.accept("duration", duration);
|
||||
}
|
||||
this.rule("dateOrDuration", DATE_OR_DURATION, (ctx, match) => {
|
||||
let content = match[1] || match[2];
|
||||
let date = new Date(content);
|
||||
if (!date || Number.isNaN(date.getDate())) {
|
||||
let duration = parseDuration(content);
|
||||
if (!duration || Number.isNaN(duration)) {
|
||||
ctx.reject();
|
||||
} else {
|
||||
ctx.accept("date", date);
|
||||
ctx.accept("duration", duration);
|
||||
}
|
||||
});
|
||||
} catch (ex) {
|
||||
console.error("YORIC", ex);
|
||||
}
|
||||
} else {
|
||||
ctx.accept("date", date);
|
||||
}
|
||||
});
|
||||
|
||||
// Jokers.
|
||||
this.rule("STAR", /\*/, (ctx) => {
|
||||
this.rule("STAR", STAR, (ctx) => {
|
||||
ctx.accept("STAR");
|
||||
});
|
||||
|
||||
// Everything left in the string.
|
||||
this.rule("ETC", /.*/, (ctx) => {
|
||||
ctx.accept("ETC")
|
||||
this.rule("ETC", ETC, (ctx, match) => {
|
||||
ctx.accept("ETC", match[0].trim());
|
||||
});
|
||||
|
||||
console.debug("YORIC", "Preparing lexer", string);
|
||||
this.input(string);
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,6 @@ function formatResult(action: string, targetRoomId: string, recentJoins: Join[],
|
||||
async function execSinceCommandAux(destinationRoomId: string, event: any, mjolnir: Mjolnir, lexer: Lexer): Promise<Result<undefined>> {
|
||||
// Attempt to parse `<date/duration>` as a date or duration.
|
||||
let dateOrDuration: Date |number = lexer.token("dateOrDuration").value;
|
||||
console.debug("YORIC", "dateOrDuration", dateOrDuration);
|
||||
let minDate;
|
||||
let maxAgeMS;
|
||||
if (dateOrDuration instanceof Date) {
|
||||
@ -101,25 +100,28 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
if (!action) {
|
||||
return {error: `Invalid <action>. Expected one of ${JSON.stringify(Action)}`};
|
||||
}
|
||||
console.debug("YORIC", "action", action);
|
||||
|
||||
// Attempt to parse `<limit>` as a number.
|
||||
const maxEntries = lexer.token("int").value as number;
|
||||
console.debug("YORIC", "maxEntries", maxEntries);
|
||||
|
||||
// Now list affected rooms.
|
||||
// Parse rooms.
|
||||
// Parse everything else as `<reason>`, stripping quotes if any have been added.
|
||||
const rooms: Set</* room id */string> = new Set();
|
||||
let reason = "";
|
||||
do {
|
||||
|
||||
let token = lexer.alternatives(
|
||||
// Room
|
||||
() => lexer.token("STAR"),
|
||||
() => lexer.token("roomAliasOrID"),
|
||||
// Reason
|
||||
() => lexer.token("string"),
|
||||
() => lexer.token("ETC")
|
||||
);
|
||||
console.debug("YORIC", "token", token);
|
||||
if (!token) {
|
||||
// We have reached the end of rooms.
|
||||
if (!token || token.type === "EOF") {
|
||||
// We have reached the end of rooms, no reason.
|
||||
break;
|
||||
}
|
||||
if (token.type === "STAR") {
|
||||
} else if (token.type === "STAR") {
|
||||
for (let roomId of Object.keys(mjolnir.protectedRooms)) {
|
||||
rooms.add(roomId);
|
||||
}
|
||||
@ -131,8 +133,9 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
}
|
||||
rooms.add(roomId);
|
||||
continue;
|
||||
}
|
||||
if (token.type == 'EOF') {
|
||||
} else if (token.type === "string" || token.type === "ETC") {
|
||||
// We have reached the end of rooms with a reason.
|
||||
reason = token.text;
|
||||
break;
|
||||
}
|
||||
} while(true);
|
||||
@ -141,14 +144,6 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
||||
error: "Missing rooms. Use `*` if you wish to apply to every protected room.",
|
||||
};
|
||||
}
|
||||
console.debug("YORIC", "rooms", rooms);
|
||||
|
||||
// Parse everything else as `<reason>`, stripping quotes if any have been added.
|
||||
const reason = lexer.alternatives(
|
||||
() => lexer.token("string"),
|
||||
() => lexer.token("ETC")
|
||||
)?.text || "";
|
||||
console.debug("YORIC", "reason", reason);
|
||||
|
||||
const progressEventId = await mjolnir.client.unstableApis.addReactionToEvent(destinationRoomId, event['event_id'], '⏳');
|
||||
|
||||
|
@ -12,7 +12,7 @@ export const mochaHooks = {
|
||||
console.error("---- entering test", JSON.stringify(this.currentTest.title)); // Makes MatrixClient error logs a bit easier to parse.
|
||||
console.log("mochaHooks.beforeEach");
|
||||
// Sometimes it takes a little longer to register users.
|
||||
this.timeout(10000)
|
||||
this.timeout(20000)
|
||||
this.managementRoomAlias = config.managementRoom;
|
||||
this.mjolnir = await makeMjolnir();
|
||||
config.RUNTIME.client = this.mjolnir.client;
|
||||
|
Loading…
Reference in New Issue
Block a user