mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 05:36:06 +00:00
WIP
This commit is contained in:
parent
7b094f3e28
commit
fb54799463
@ -28,8 +28,10 @@ import { execRedactCommand } from "./RedactCommand";
|
|||||||
import { execImportCommand } from "./ImportCommand";
|
import { execImportCommand } from "./ImportCommand";
|
||||||
import { execSetDefaultListCommand } from "./SetDefaultBanListCommand";
|
import { execSetDefaultListCommand } from "./SetDefaultBanListCommand";
|
||||||
import { execDeactivateCommand } from "./DeactivateCommand";
|
import { execDeactivateCommand } from "./DeactivateCommand";
|
||||||
import { execDisableProtection, execEnableProtection, execListProtections, execConfigGetProtection,
|
import {
|
||||||
execConfigSetProtection, execConfigAddProtection, execConfigRemoveProtection } from "./ProtectionsCommands";
|
execDisableProtection, execEnableProtection, execListProtections, execConfigGetProtection,
|
||||||
|
execConfigSetProtection, execConfigAddProtection, execConfigRemoveProtection
|
||||||
|
} from "./ProtectionsCommands";
|
||||||
import { execListProtectedRooms } from "./ListProtectedRoomsCommand";
|
import { execListProtectedRooms } from "./ListProtectedRoomsCommand";
|
||||||
import { execAddProtectedRoom, execRemoveProtectedRoom } from "./AddRemoveProtectedRoomsCommand";
|
import { execAddProtectedRoom, execRemoveProtectedRoom } from "./AddRemoveProtectedRoomsCommand";
|
||||||
import { execAddRoomToDirectoryCommand, execRemoveRoomFromDirectoryCommand } from "./AddRemoveRoomFromDirectoryCommand";
|
import { execAddRoomToDirectoryCommand, execRemoveRoomFromDirectoryCommand } from "./AddRemoveRoomFromDirectoryCommand";
|
||||||
@ -43,198 +45,20 @@ import { Lexer } from "./Lexer";
|
|||||||
|
|
||||||
export const COMMAND_PREFIX = "!mjolnir";
|
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) {
|
export async function handleCommand(roomId: string, event: { content: { body: string } }, mjolnir: Mjolnir) {
|
||||||
const line = event['content']['body'];
|
const line = event['content']['body'];
|
||||||
const parts = line.trim().split(' ').filter(p => p.trim().length > 0);
|
const parts = line.trim().split(' ').filter(p => p.trim().length > 0);
|
||||||
console.debug("YORIC", "line", line);
|
console.debug("YORIC", "line", line);
|
||||||
const lexer = new Lexer(line);
|
const lexer = new Lexer(line);
|
||||||
lexer.token("command"); // Consume `!mjolnir`.
|
lexer.token("command"); // Consume `!mjolnir`.
|
||||||
const cmd = lexer.token("id").text;
|
const cmd = lexer.alternatives(
|
||||||
|
() => lexer.token("id").text,
|
||||||
|
() => null
|
||||||
|
);
|
||||||
console.debug("YORIC", "cmd", cmd);
|
console.debug("YORIC", "cmd", cmd);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (cmd === '' || cmd === 'status') {
|
if (cmd === null || cmd === 'status') {
|
||||||
return await execStatusCommand(roomId, event, mjolnir, parts.slice(2));
|
return await execStatusCommand(roomId, event, mjolnir, parts.slice(2));
|
||||||
} else if (cmd === 'ban' && parts.length > 2) {
|
} else if (cmd === 'ban' && parts.length > 2) {
|
||||||
return await execBanCommand(roomId, event, mjolnir, parts);
|
return await execBanCommand(roomId, event, mjolnir, parts);
|
||||||
|
@ -58,10 +58,15 @@ export class Lexer extends TokenizrClass {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Dates and durations.
|
// Dates and durations.
|
||||||
this.rule("dateOrDuration", /\S+/, (ctx, match) => {
|
try {
|
||||||
let date = new Date(match[0]);
|
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())) {
|
if (!date || Number.isNaN(date.getDate())) {
|
||||||
let duration = parseDuration(match[0]);
|
let duration = parseDuration(content);
|
||||||
|
console.debug("YORIC", "Lexer", "dateOrDuration", "duration", duration);
|
||||||
if (!duration || Number.isNaN(duration)) {
|
if (!duration || Number.isNaN(duration)) {
|
||||||
ctx.reject();
|
ctx.reject();
|
||||||
} else {
|
} else {
|
||||||
@ -71,15 +76,21 @@ export class Lexer extends TokenizrClass {
|
|||||||
ctx.accept("date", date);
|
ctx.accept("date", date);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
console.error("YORIC", ex);
|
||||||
|
}
|
||||||
|
|
||||||
// Jokers.
|
// Jokers.
|
||||||
this.rule("STAR", /\*/, (ctx) => {
|
this.rule("STAR", /\*/, (ctx) => {
|
||||||
ctx.accept("STAR");
|
ctx.accept("STAR");
|
||||||
});
|
});
|
||||||
this.rule(/./, (ctx) => {
|
|
||||||
ctx.accept("ANYTHING ELSE")
|
// Everything left in the string.
|
||||||
|
this.rule("ETC", /.*/, (ctx) => {
|
||||||
|
ctx.accept("ETC")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.debug("YORIC", "Preparing lexer", string);
|
||||||
this.input(string);
|
this.input(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,13 @@ type Summary = { succeeded: userId[], failed: userId[] };
|
|||||||
|
|
||||||
// !mjolnir since <date>/<duration> <action> <number> [...rooms] [...reason]
|
// !mjolnir since <date>/<duration> <action> <number> [...rooms] [...reason]
|
||||||
export async function execSinceCommand(destinationRoomId: string, event: any, mjolnir: Mjolnir, lexer: Lexer) {
|
export async function execSinceCommand(destinationRoomId: string, event: any, mjolnir: Mjolnir, lexer: Lexer) {
|
||||||
let result = await execSinceCommandAux(destinationRoomId, event, mjolnir, lexer);
|
let result;
|
||||||
|
try {
|
||||||
|
result = await execSinceCommandAux(destinationRoomId, event, mjolnir, lexer);
|
||||||
|
} catch (ex) {
|
||||||
|
result = { error: ex.message };
|
||||||
|
console.error("Error executing `since` command", ex);
|
||||||
|
}
|
||||||
if ("error" in result) {
|
if ("error" in result) {
|
||||||
mjolnir.client.unstableApis.addReactionToEvent(destinationRoomId, event['event_id'], '❌');
|
mjolnir.client.unstableApis.addReactionToEvent(destinationRoomId, event['event_id'], '❌');
|
||||||
mjolnir.logMessage(LogLevel.WARN, "SinceCommand", result.error);
|
mjolnir.logMessage(LogLevel.WARN, "SinceCommand", result.error);
|
||||||
@ -70,21 +76,16 @@ function formatResult(action: string, targetRoomId: string, recentJoins: Join[],
|
|||||||
// - in case of success, returns `{ok: undefined}`, in case of error, returns `{error: string}`.
|
// - 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>> {
|
async function execSinceCommandAux(destinationRoomId: string, event: any, mjolnir: Mjolnir, lexer: Lexer): Promise<Result<undefined>> {
|
||||||
// Attempt to parse `<date/duration>` as a date or duration.
|
// Attempt to parse `<date/duration>` as a date or duration.
|
||||||
let dateOrDurationToken: Date | number;
|
let dateOrDuration: Date |number = lexer.token("dateOrDuration").value;
|
||||||
try {
|
console.debug("YORIC", "dateOrDuration", dateOrDuration);
|
||||||
dateOrDurationToken = lexer.token("dateOrDuration").value;
|
|
||||||
} catch (ex) {
|
|
||||||
return { error: "Invalid <date/duration>" };
|
|
||||||
}
|
|
||||||
console.debug("YORIC", "dateOrDurationToken", dateOrDurationToken);
|
|
||||||
let minDate;
|
let minDate;
|
||||||
let maxAgeMS;
|
let maxAgeMS;
|
||||||
if (dateOrDurationToken instanceof Date) {
|
if (dateOrDuration instanceof Date) {
|
||||||
minDate = dateOrDurationToken;
|
minDate = dateOrDuration;
|
||||||
maxAgeMS = Date.now() - dateOrDurationToken.getTime() as number;
|
maxAgeMS = Date.now() - dateOrDuration.getTime() as number;
|
||||||
} else {
|
} else {
|
||||||
minDate = new Date(Date.now() - dateOrDurationToken);
|
minDate = new Date(Date.now() - dateOrDuration);
|
||||||
maxAgeMS = dateOrDurationToken;
|
maxAgeMS = dateOrDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to parse `<action>` as Action.
|
// Attempt to parse `<action>` as Action.
|
||||||
@ -109,18 +110,21 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
|||||||
// Now list affected rooms.
|
// Now list affected rooms.
|
||||||
const rooms: Set</* room id */string> = new Set();
|
const rooms: Set</* room id */string> = new Set();
|
||||||
do {
|
do {
|
||||||
try {
|
|
||||||
let token = lexer.alternatives(
|
let token = lexer.alternatives(
|
||||||
() => lexer.token("STAR"),
|
() => lexer.token("STAR"),
|
||||||
() => lexer.token("roomAliasOrID"),
|
() => lexer.token("roomAliasOrID"),
|
||||||
);
|
);
|
||||||
if (token.type == "STAR") {
|
console.debug("YORIC", "token", token);
|
||||||
|
if (!token) {
|
||||||
|
// We have reached the end of rooms.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (token.type === "STAR") {
|
||||||
for (let roomId of Object.keys(mjolnir.protectedRooms)) {
|
for (let roomId of Object.keys(mjolnir.protectedRooms)) {
|
||||||
rooms.add(roomId);
|
rooms.add(roomId);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
} else if (token.type === "roomAliasOrID") {
|
||||||
if (token.type == "roomAliasOrID") {
|
|
||||||
const roomId = await mjolnir.client.resolveRoom(token.text);
|
const roomId = await mjolnir.client.resolveRoom(token.text);
|
||||||
if (!(roomId in mjolnir.protectedRooms)) {
|
if (!(roomId in mjolnir.protectedRooms)) {
|
||||||
return mjolnir.logMessage(LogLevel.WARN, "SinceCommand", `This room is not protected: ${htmlEscape(roomId)}.`);
|
return mjolnir.logMessage(LogLevel.WARN, "SinceCommand", `This room is not protected: ${htmlEscape(roomId)}.`);
|
||||||
@ -128,8 +132,7 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
|||||||
rooms.add(roomId);
|
rooms.add(roomId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
if (token.type == 'EOF') {
|
||||||
// If we're done with rooms, we have entered <reason>.
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while(true);
|
} while(true);
|
||||||
@ -143,8 +146,8 @@ async function execSinceCommandAux(destinationRoomId: string, event: any, mjolni
|
|||||||
// Parse everything else as `<reason>`, stripping quotes if any have been added.
|
// Parse everything else as `<reason>`, stripping quotes if any have been added.
|
||||||
const reason = lexer.alternatives(
|
const reason = lexer.alternatives(
|
||||||
() => lexer.token("string"),
|
() => lexer.token("string"),
|
||||||
() => lexer.token("EVERYTHING ELSE")
|
() => lexer.token("ETC")
|
||||||
).text;
|
)?.text || "";
|
||||||
console.debug("YORIC", "reason", reason);
|
console.debug("YORIC", "reason", reason);
|
||||||
|
|
||||||
const progressEventId = await mjolnir.client.unstableApis.addReactionToEvent(destinationRoomId, event['event_id'], '⏳');
|
const progressEventId = await mjolnir.client.unstableApis.addReactionToEvent(destinationRoomId, event['event_id'], '⏳');
|
||||||
|
Loading…
Reference in New Issue
Block a user