Make the [un]ban command smarter

Fixes https://github.com/matrix-org/mjolnir/issues/11
This commit is contained in:
Travis Ralston 2019-11-13 21:38:19 -07:00
parent dd260091bb
commit 66a5775136
6 changed files with 1801 additions and 70 deletions

View File

@ -9,11 +9,17 @@
"private": true,
"scripts": {
"build": "tsc",
"lint": "tslint --project ./tsconfig.json --type-check -t stylish",
"start:dev": "yarn build && node lib/index.js"
"lint": "tslint --project ./tsconfig.json -t stylish",
"start:dev": "yarn build && node lib/index.js",
"test": "ts-mocha --project ./tsconfig.json test/**/*.ts"
},
"devDependencies": {
"@types/expect": "^24.3.0",
"@types/mocha": "^5.2.7",
"@types/node": "11",
"expect": "^24.9.0",
"mocha": "^6.2.2",
"ts-mocha": "^6.0.0",
"tslint": "^5.20.0",
"typescript": "^3.6.3"
},

View File

@ -37,9 +37,9 @@ export async function handleCommand(roomId: string, event: any, mjolnir: Mjolnir
try {
if (parts.length === 1 || parts[1] === 'status') {
return await execStatusCommand(roomId, event, mjolnir);
} else if (parts[1] === 'ban' && parts.length > 4) {
} else if (parts[1] === 'ban' && parts.length > 2) {
return await execBanCommand(roomId, event, mjolnir, parts);
} else if (parts[1] === 'unban' && parts.length > 4) {
} else if (parts[1] === 'unban' && parts.length > 2) {
return await execUnbanCommand(roomId, event, mjolnir, parts);
} else if (parts[1] === 'rules') {
return await execDumpRulesCommand(roomId, event, mjolnir);

View File

@ -15,91 +15,121 @@ limitations under the License.
*/
import { Mjolnir } from "../Mjolnir";
import { RULE_ROOM, RULE_SERVER, RULE_USER, ruleTypeToStable, USER_RULE_TYPES } from "../models/BanList";
import { LogLevel, RichReply } from "matrix-bot-sdk";
import BanList, { RULE_ROOM, RULE_SERVER, RULE_USER, USER_RULE_TYPES } from "../models/BanList";
import { LogLevel, LogService, RichReply } from "matrix-bot-sdk";
import { RECOMMENDATION_BAN, recommendationToStable } from "../models/ListRule";
import { MatrixGlob } from "matrix-bot-sdk/lib/MatrixGlob";
import config from "../config";
import { logMessage } from "../LogProxy";
import { DEFAULT_LIST_EVENT_TYPE } from "./SetDefaultBanListCommand";
function parseBits(parts: string[]): { listShortcode: string, entityType: string, ruleType: string, glob: string, reason: string } {
const shortcode = parts[2].toLowerCase();
const entityType = parts[3].toLowerCase();
const glob = parts[4];
const reason = parts.slice(5).join(' ') || "<no reason>";
let rule = null;
if (entityType === "user") {
rule = RULE_USER;
} else if (entityType === "room") {
rule = RULE_ROOM;
} else if (entityType === "server") {
rule = RULE_SERVER;
interface Arguments {
list: BanList;
entity: string;
ruleType: string;
reason: string;
}
if (!rule) {
return {listShortcode: shortcode, entityType, ruleType: null, glob, reason};
}
rule = ruleTypeToStable(rule);
return {listShortcode: shortcode, entityType, ruleType: rule, glob, reason};
// Exported for tests
export async function parseArguments(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]): Promise<Arguments> {
let defaultShortcode = null;
try {
const data = await mjolnir.client.getAccountData(DEFAULT_LIST_EVENT_TYPE);
defaultShortcode = data['shortcode'];
} catch (e) {
LogService.warn("UnbanBanCommand", "Non-fatal error getting default ban list");
LogService.warn("UnbanBanCommand", e);
// Assume no default.
}
let argumentIndex = 2;
let ruleType = null;
let entity = null;
let list = null;
while (argumentIndex < 6 && argumentIndex < parts.length) {
const arg = parts[argumentIndex++];
if (!arg) break;
if (["user", "room", "server"].includes(arg.toLowerCase())) {
if (arg.toLowerCase() === 'user') ruleType = RULE_USER;
if (arg.toLowerCase() === 'room') ruleType = RULE_ROOM;
if (arg.toLowerCase() === 'server') ruleType = RULE_SERVER;
} else if (!entity && (arg[0] === '@' || arg[0] === '!' || arg[0] === '#' || arg.includes("*"))) {
entity = arg;
if (arg.startsWith("@") && !ruleType) ruleType = RULE_USER;
else if (arg.startsWith("#") && !ruleType) ruleType = RULE_ROOM;
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) {
list = foundList;
}
}
if (entity) break;
}
if (!list) {
list = mjolnir.lists.find(b => b.listShortcode.toLowerCase() === defaultShortcode);
}
if (!entity) {
entity = parts[argumentIndex - 1];
if (!ruleType) ruleType = RULE_SERVER; // due to the conditions above, it can't be anything else
console.log(parts);
}
let replyMessage = 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 to ban";
if (replyMessage) {
const reply = RichReply.createFor(roomId, event, replyMessage, replyMessage);
reply["msgtype"] = "m.notice";
await mjolnir.client.sendMessage(roomId, reply);
return null;
}
return {
list,
entity,
ruleType,
reason: parts.splice(argumentIndex).join(" ").trim(),
};
}
// !mjolnir ban <shortcode> <user|server|room> <glob> [reason]
export async function execBanCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
const bits = parseBits(parts);
if (!bits.ruleType) {
const replyText = "Unknown entity type '" + bits.entityType + "' - try one of user, room, or server";
const reply = RichReply.createFor(roomId, event, replyText, replyText);
reply["msgtype"] = "m.notice";
return mjolnir.client.sendMessage(roomId, reply);
}
const bits = await parseArguments(roomId, event, mjolnir, parts);
if (!bits) return; // error already handled
const recommendation = recommendationToStable(RECOMMENDATION_BAN);
const ruleContent = {
entity: bits.glob,
entity: bits.entity,
recommendation,
reason: bits.reason,
};
const stateKey = `rule:${bits.glob}`;
const stateKey = `rule:${bits.entity}`;
const list = mjolnir.lists.find(b => b.listShortcode === bits.listShortcode);
if (!list) {
const replyText = "No ban list with that shortcode was found.";
const reply = RichReply.createFor(roomId, event, replyText, replyText);
reply["msgtype"] = "m.notice";
return mjolnir.client.sendMessage(roomId, reply);
}
await mjolnir.client.sendStateEvent(list.roomId, bits.ruleType, stateKey, ruleContent);
await mjolnir.client.sendStateEvent(bits.list.roomId, bits.ruleType, stateKey, ruleContent);
await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅');
}
// !mjolnir unban <shortcode> <user|server|room> <glob> [apply:t/f]
export async function execUnbanCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
const bits = parseBits(parts);
if (!bits.ruleType) {
const replyText = "Unknown entity type '" + bits.entityType + "' - try one of user, room, or server";
const reply = RichReply.createFor(roomId, event, replyText, replyText);
reply["msgtype"] = "m.notice";
return mjolnir.client.sendMessage(roomId, reply);
}
const bits = await parseArguments(roomId, event, mjolnir, parts);
if (!bits) return; // error already handled
const ruleContent = {}; // empty == clear/unban
const stateKey = `rule:${bits.glob}`;
const stateKey = `rule:${bits.entity}`;
const list = mjolnir.lists.find(b => b.listShortcode === bits.listShortcode);
if (!list) {
const replyText = "No ban list with that shortcode was found.";
const reply = RichReply.createFor(roomId, event, replyText, replyText);
reply["msgtype"] = "m.notice";
return mjolnir.client.sendMessage(roomId, reply);
}
await mjolnir.client.sendStateEvent(list.roomId, bits.ruleType, stateKey, ruleContent);
await mjolnir.client.sendStateEvent(bits.list.roomId, bits.ruleType, stateKey, ruleContent);
if (USER_RULE_TYPES.includes(bits.ruleType) && parts.length > 5 && parts[5] === 'true') {
const rule = new MatrixGlob(bits.glob);
await logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.glob);
const rule = new MatrixGlob(bits.entity);
await logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.entity);
let unbannedSomeone = false;
for (const protectedRoomId of Object.keys(mjolnir.protectedRooms)) {
const members = await mjolnir.client.getMembers(protectedRoomId, null, ['ban'], null);

View File

@ -0,0 +1,403 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as expect from "expect";
import { Mjolnir } from "../../src/Mjolnir";
import { DEFAULT_LIST_EVENT_TYPE } from "../../src/commands/SetDefaultBanListCommand";
import { parseArguments } from "../../src/commands/UnbanBanCommand";
import { RULE_ROOM, RULE_SERVER, RULE_USER } from "../../src/models/BanList";
function createTestMjolnir(defaultShortcode: string = null): Mjolnir {
const client = {
getAccountData: (eventType: string): Promise<any> => {
if (eventType === DEFAULT_LIST_EVENT_TYPE && defaultShortcode) {
return Promise.resolve({shortcode: defaultShortcode});
}
throw new Error("Unknown event type");
},
};
return <Mjolnir>{client};
}
function createFakeEvent(command: string): any {
return {
sender: "@alice:example.org",
event_id: "$example",
content: {
body: command,
msgtype: "m.text",
},
};
}
describe("UnbanBanCommand", () => {
describe("parseArguments", () => {
it("should be able to detect servers", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_SERVER);
expect(bits.entity).toBe("example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect servers with globs", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test *.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_SERVER);
expect(bits.entity).toBe("*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect servers with the type specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test server @*.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_SERVER);
expect(bits.entity).toBe("@*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect room IDs", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test !example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_ROOM);
expect(bits.entity).toBe("!example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect room IDs with globs", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test !*.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_ROOM);
expect(bits.entity).toBe("!*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect room aliases", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test #example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_ROOM);
expect(bits.entity).toBe("#example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect room aliases with globs", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test #*.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_ROOM);
expect(bits.entity).toBe("#*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect rooms with the type specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test room @*.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_ROOM);
expect(bits.entity).toBe("@*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect user IDs", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test @example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect user IDs with globs", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test @*.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should be able to detect user IDs with the type specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test user #*.example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("#*.example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
describe("[without default list]", () => {
it("should error if no list (with type) is specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
expect(content).toBeDefined();
expect(content['body']).toContain("No ban list matching that shortcode was found");
return Promise.resolve("$fake");
};
const command = "!mjolnir ban user @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeFalsy();
});
it("should error if no list (without type) is specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
expect(content).toBeDefined();
expect(content['body']).toContain("No ban list matching that shortcode was found");
return Promise.resolve("$fake");
};
const command = "!mjolnir ban @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeFalsy();
});
it("should not error if a list (with type) is specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban user test @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should not error if a list (without type) is specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should not error if a list (with type reversed) is specified", async () => {
const mjolnir = createTestMjolnir();
(<any>mjolnir).lists = [{listShortcode: "test"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban test user @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
});
describe("[with default list]", () => {
it("should use the default list if no list (with type) is specified", async () => {
const mjolnir = createTestMjolnir("test");
(<any>mjolnir).lists = [{listShortcode: "test"}, {listShortcode: "other"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban user @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should use the default list if no list (without type) is specified", async () => {
const mjolnir = createTestMjolnir("test");
(<any>mjolnir).lists = [{listShortcode: "test"}, {listShortcode: "other"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("test");
});
it("should use the specified list if a list (with type) is specified", async () => {
const mjolnir = createTestMjolnir("test");
(<any>mjolnir).lists = [{listShortcode: "test"}, {listShortcode: "other"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban user other @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("other");
});
it("should use the specified list if a list (without type) is specified", async () => {
const mjolnir = createTestMjolnir("test");
(<any>mjolnir).lists = [{listShortcode: "test"}, {listShortcode: "other"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban other @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("other");
});
it("should not error if a list (with type reversed) is specified", async () => {
const mjolnir = createTestMjolnir("test");
(<any>mjolnir).lists = [{listShortcode: "test"}, {listShortcode: "other"}];
mjolnir.client.sendMessage = (roomId: string, content: any): Promise<string> => {
throw new Error("sendMessage should not have been called: " + JSON.stringify(content));
};
const command = "!mjolnir ban other user @example:example.org";
const bits = await parseArguments("!a", createFakeEvent(command), mjolnir, command.split(' '));
expect(bits).toBeTruthy();
expect(bits.reason).toBeFalsy();
expect(bits.ruleType).toBe(RULE_USER);
expect(bits.entity).toBe("@example:example.org");
expect(bits.list).toBeDefined();
expect(bits.list.listShortcode).toBe("other");
});
});
});
});

View File

@ -9,7 +9,8 @@
"sourceMap": true,
"outDir": "./lib",
"types": [
"node"
"node",
"mocha"
]
},
"include": [

1309
yarn.lock

File diff suppressed because it is too large Load Diff