Merge pull request #136 from matrix-org/gnuxie/mx-tester

An Integration test setup that can be used with mx-tester
This commit is contained in:
Gnuxie 2021-09-30 16:48:14 +01:00 committed by GitHub
commit 725d400650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1016 additions and 379 deletions

View File

@ -133,3 +133,55 @@ to restart Synapse to install the plugin.
## Development
TODO. It's a TypeScript project with a linter.
### Development and testing with mx-tester
WARNING: mx-tester is currently work in progress, but it can still save you some time and is better than struggling with nothing.
If you have docker installed you can quickly get setup with a development environment by using
[mx-tester](https://github.com/matrix-org/mx-tester).
To use mx-tester you will need to have rust installed. You can do that at [rustup](https://rustup.rs/) or [here](https://rust-lang.github.io/rustup/installation/other.html), you should probably also check your distro's documentation first to see if they have specific instructions for installing rust.
Once rust is installed you can install mx-tester like so.
```
$ cargo install mx-tester
```
Once you have mx-tester installed you we will want to build a synapse image with synapse_antispam from the mjolnir project root.
```
$ mx-tester build
```
Then we can start a container that uses that image and the config in `mx-tester.yml`.
```
$ mx-tester up
```
Once you have called `mx-tester up` you can run the integration tests.
```
$ yarn test:integration
```
After calling `mx-tester up`, if we want to play with mojlnir locally we can run the following and then point a matrix client to http://localhost:9999.
You should then be able to join the management room at `#moderators:localhost:9999`.
```
yarn test:manual
```
Once we are finished developing we can stop the synapse container.
```
mx-tester down
```
### Running integration tests
The integration tests can be run with `yarn test:integration`.
The config that the tests use is in `config/harness.yaml`
and by default this is configured to work with the server specified in `mx-tester.yml`,
but you can configure it however you like to run against your own setup.

161
config/harness.yaml Normal file
View File

@ -0,0 +1,161 @@
# This configuration file is for the integration tests run by yarn:integration.
# Editing this will do nothing and you shouldn't use it as a template.
# For a template use default.yaml
# Where the homeserver is located (client-server URL). This should point at
# pantalaimon if you're using that.
homeserverUrl: "http://localhost:9999"
# Pantalaimon options (https://github.com/matrix-org/pantalaimon)
pantalaimon:
# If true, accessToken above is ignored and the username/password below will be
# used instead. The access token of the bot will be stored in the dataPath.
use: true
# The username to login with.
username: mjolnir
# The password to login with. Can be removed after the bot has logged in once and
# stored the access token.
password: mjolnir
# The directory the bot should store various bits of information in
dataPath: "./test/harness/mjolnir-data/"
# If true (the default), only users in the `managementRoom` can invite the bot
# to new rooms.
autojoinOnlyIfManager: true
# If `autojoinOnlyIfManager` is false, only the members in this group can invite
# the bot to new rooms.
acceptInvitesFromGroup: '+example:example.org'
# If the bot is invited to a room and it won't accept the invite (due to the
# conditions above), report it to the management room. Defaults to disabled (no
# reporting).
recordIgnoredInvites: false
# The room ID where people can use the bot. The bot has no access controls, so
# anyone in this room can use the bot - secure your room!
# This should be a room alias or room ID - not a matrix.to URL.
# Note: Mjolnir is fairly verbose - expect a lot of messages from it.
managementRoom: "#moderators:localhost:9999"
# Set to false to make the management room a bit quieter.
verboseLogging: true
# The log level for the logs themselves. One of DEBUG, INFO, WARN, and ERROR.
# This should be at INFO or DEBUG in order to get support for Mjolnir problems.
logLevel: "DEBUG"
# Set to false to disable synchronizing the ban lists on startup. If true, this
# is the same as running !mjolnir sync immediately after startup.
syncOnStartup: true
# Set to false to prevent Mjolnir from checking its permissions on startup. This
# is recommended to be left as "true" to catch room permission problems (state
# resets, etc) before Mjolnir is needed.
verifyPermissionsOnStartup: true
# If true, Mjolnir won't actually ban users or apply server ACLs, but will
# think it has. This is useful to see what it does in a scenario where the
# bot might not be trusted fully, yet. Default false (do bans/ACLs).
noop: false
# Set to true to use /joined_members instead of /state to figure out who is
# in the room. Using /state is preferred because it means that users are
# banned when they are invited instead of just when they join, though if your
# server struggles with /state requests then set this to true.
fasterMembershipChecks: false
# A case-insensitive list of ban reasons to automatically redact a user's
# messages for. Typically this is useful to avoid having to type two commands
# to the bot. Use asterisks to represent globs (ie: "spam*testing" would match
# "spam for testing" as well as "spamtesting").
automaticallyRedactForReasons:
- "spam"
- "advertising"
# A list of rooms to protect (matrix.to URLs)
protectedRooms: []
# Set this option to true to protect every room the bot is joined to. Note that
# this effectively makes the protectedRooms and associated commands useless because
# the bot by nature must be joined to the room to protect it.
#
# Note: the management room is *excluded* from this condition. Add it to the
# protected rooms to protect it.
#
# Note: ban list rooms the bot is watching but didn't create will not be protected.
# Manually add these rooms to the protected rooms list if you want them protected.
protectAllJoinedRooms: false
# Misc options for command handling and commands
commands:
# If true, Mjolnir will respond to commands like !help and !ban instead of
# requiring a prefix. This is useful if Mjolnir is the only bot running in
# your management room.
#
# Note that Mjolnir can be pinged by display name instead of having to use
# the !mjolnir prefix. For example, "my_moderator_bot: ban @spammer:example.org"
# will ban a user.
allowNoPrefix: false
# In addition to the bot's display name, !mjolnir, and optionally no prefix
# above, the bot will respond to these names. The items here can be used either
# as display names or prefixed with exclamation points.
additionalPrefixes:
- "mjolnir_bot"
# If true, ban commands that use wildcard characters require confirmation with
# an extra `--force` argument
confirmWildcardBan: true
# Configuration specific to certain toggleable protections
protections:
# Configuration for the wordlist plugin, which can ban users based if they say certain
# blocked words shortly after joining.
wordlist:
# A list of words which should be monitored by the bot. These will match if any part
# of the word is present in the message in any case. e.g. "hello" also matches
# "HEllO". Additionally, regular expressions can be used.
words:
- "CaSe"
- "InSeNsAtIve"
- "WoRd"
- "LiSt"
# How long after a user joins the server should the bot monitor their messages. After
# this time, users can say words from the wordlist without being banned automatically.
# Set to zero to disable (users will always be banned if they say a bad word)
minutesBeforeTrusting: 20
# Options for monitoring the health of the bot
health:
# healthz options. These options are best for use in container environments
# like Kubernetes to detect how healthy the service is. The bot will report
# that it is unhealthy until it is able to process user requests. Typically
# this means that it'll flag itself as unhealthy for a number of minutes
# before saying "Now monitoring rooms" and flagging itself healthy.
#
# Health is flagged through HTTP status codes, defined below.
healthz:
# Whether the healthz integration should be enabled (default false)
enabled: false
# The port to expose the webserver on. Defaults to 8080.
port: 8080
# The address to listen for requests on. Defaults to all addresses.
address: "0.0.0.0"
# The path to expose the monitoring endpoint at. Defaults to `/healthz`
endpoint: "/healthz"
# The HTTP status code which reports that the bot is healthy/ready to
# process requests. Typically this should not be changed. Defaults to
# 200.
healthyStatus: 200
# The HTTP status code which reports that the bot is not healthy/ready.
# Defaults to 418.
unhealthyStatus: 418

63
mx-tester.yml Normal file
View File

@ -0,0 +1,63 @@
name: Mjolnir Testing
modules:
- name: synapse_antispam
build:
- cp -r synapse_antispam $MX_TEST_SYNAPSE_DIR
run:
- yarn test-integration
homeserver_config:
server_name: localhost:9999
pid_file: /data/homeserver.pid
public_baseurl: http://localhost:9999
listeners:
- port: 9999
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
database:
name: sqlite3
args:
database: /data/homeserver.db
media_store_path: "/data/media_store"
enable_registration: true
report_stats: false
registration_shared_secret: "REGISTRATION_SHARED_SECRET"
macaroon_secret_key: "MACROON_SECRET_KEY"
signing_key_path: "/data/localhost:9999.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
suppress_key_server_warning: true
rc_message:
per_second: 10000
burst_count: 10000
rc_registration:
per_second: 10000
burst_count: 10000
rc_login:
address:
per_second: 10000
burst_count: 10000
account:
per_second: 10000
burst_count: 10000
failed_attempts:
per_second: 10000
burst_count: 10000
rc_admin_redaction:
per_second: 10000
burst_count: 10000
rc_joins:
local:
per_second: 10000
burst_count: 10000
remote:
per_second: 10000
burst_count: 10000

View File

@ -11,14 +11,21 @@
"build": "tsc",
"lint": "tslint --project ./tsconfig.json -t stylish",
"start:dev": "yarn build && node lib/index.js",
"test": "ts-mocha --project ./tsconfig.json test/**/*.ts"
"test": "ts-mocha --project ./tsconfig.json test/commands/**/*.ts",
"test:integration": "NODE_ENV=harness ts-mocha --require test/integration/fixtures.ts --project ./tsconfig.json test/integration/**/*Test.ts",
"test:manual": "NODE_ENV=harness ts-node test/integration/manualLaunchScript.ts"
},
"devDependencies": {
"@types/axios": "^0.14.0",
"@types/crypto-js": "^4.0.2",
"@types/mocha": "^9.0.0",
"@types/node": "11",
"@types/node": "^16.7.10",
"axios": "^0.21.4",
"crypto-js": "^4.1.1",
"expect": "^27.0.6",
"mocha": "^9.0.1",
"ts-mocha": "^8.0.0",
"ts-node": "^10.2.1",
"tslint": "^6.1.3",
"typescript": "^4.3.5"
},

View File

@ -21,6 +21,7 @@ import {
LogService,
MatrixClient,
MatrixGlob,
MembershipEvent,
Permalinks,
UserID
} from "matrix-bot-sdk";
@ -37,6 +38,7 @@ import { PROTECTIONS } from "./protections/protections";
import { UnlistedUserRedactionQueue } from "./queues/UnlistedUserRedactionQueue";
import { Healthz } from "./health/healthz";
import { EventRedactionQueue, RedactUserInRoom } from "./queues/EventRedactionQueue";
import * as htmlEscape from "escape-html";
export const STATE_NOT_STARTED = "not_started";
export const STATE_CHECKING_PERMISSIONS = "checking_permissions";
@ -69,6 +71,84 @@ export class Mjolnir {
private explicitlyProtectedRoomIds: string[] = [];
private knownUnprotectedRooms: string[] = [];
/**
* Adds a listener to the client that will automatically accept invitations.
* @param {MatrixClient} client
* @param options By default accepts invites from anyone.
* @param {string} options.managementRoom The room to report ignored invitations to if `recordIgnoredInvites` is true.
* @param {boolean} options.recordIgnoredInvites Whether to report invites that will be ignored to the `managementRoom`.
* @param {boolean} options.autojoinOnlyIfManager Whether to only accept an invitation by a user present in the `managementRoom`.
* @param {string} options.acceptInvitesFromGroup A group of users to accept invites from, ignores invites form users not in this group.
*/
private static addJoinOnInviteListener(client: MatrixClient, options) {
client.on("room.invite", async (roomId: string, inviteEvent: any) => {
const membershipEvent = new MembershipEvent(inviteEvent);
const reportInvite = async () => {
if (!options.recordIgnoredInvites) return; // Nothing to do
await client.sendMessage(options.managementRoom, {
msgtype: "m.text",
body: `${membershipEvent.sender} has invited me to ${roomId} but the config prevents me from accepting the invitation. `
+ `If you would like this room protected, use "!mjolnir rooms add ${roomId}" so I can accept the invite.`,
format: "org.matrix.custom.html",
formatted_body: `${htmlEscape(membershipEvent.sender)} has invited me to ${htmlEscape(roomId)} but the config prevents me from `
+ `accepting the invitation. If you would like this room protected, use <code>!mjolnir rooms add ${htmlEscape(roomId)}</code> `
+ `so I can accept the invite.`,
});
};
if (options.autojoinOnlyIfManager) {
const managers = await client.getJoinedRoomMembers(options.managementRoom);
if (!managers.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
} else {
const groupMembers = await client.unstableApis.getGroupUsers(options.acceptInvitesFromGroup);
const userIds = groupMembers.map(m => m.user_id);
if (!userIds.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
}
return client.joinRoom(roomId);
});
}
/**
* Create a new Mjolnir instance from a client and the options in the configuration file, ready to be started.
* @param {MatrixClient} client The client for Mjolnir to use.
* @returns A new Mjolnir instance that can be started without further setup.
*/
static async setupMjolnirFromConfig(client: MatrixClient): Promise<Mjolnir> {
Mjolnir.addJoinOnInviteListener(client, config);
const banLists: BanList[] = [];
const protectedRooms: { [roomId: string]: string } = {};
const joinedRooms = await client.getJoinedRooms();
// Ensure we're also joined to the rooms we're protecting
LogService.info("index", "Resolving protected rooms...");
for (const roomRef of config.protectedRooms) {
const permalink = Permalinks.parseUrl(roomRef);
if (!permalink.roomIdOrAlias) continue;
let roomId = await client.resolveRoom(permalink.roomIdOrAlias);
if (!joinedRooms.includes(roomId)) {
roomId = await client.joinRoom(permalink.roomIdOrAlias, permalink.viaServers);
}
protectedRooms[roomId] = roomRef;
}
// Ensure we're also in the management room
LogService.info("index", "Resolving management room...");
const managementRoomId = await client.resolveRoom(config.managementRoom);
if (!joinedRooms.includes(managementRoomId)) {
config.managementRoom = await client.joinRoom(config.managementRoom);
} else {
config.managementRoom = managementRoomId;
}
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
return new Mjolnir(client, protectedRooms, banLists);
}
constructor(
public readonly client: MatrixClient,
public readonly protectedRooms: { [roomId: string]: string },
@ -209,6 +289,14 @@ export class Mjolnir {
});
}
/**
* Stop Mjolnir from syncing and processing commands.
*/
public stop() {
LogService.info("Mjolnir", "Stopping Mjolnir...");
this.client.stop();
}
public async addProtectedRoom(roomId: string) {
this.protectedRooms[roomId] = Permalinks.forRoom(roomId);

View File

@ -20,17 +20,13 @@ import {
LogService,
MatrixClient,
PantalaimonClient,
Permalinks,
RichConsoleLogger,
SimpleFsStorageProvider
} from "matrix-bot-sdk";
import config from "./config";
import BanList from "./models/BanList";
import { Mjolnir } from "./Mjolnir";
import { logMessage } from "./LogProxy";
import { MembershipEvent } from "matrix-bot-sdk/lib/models/events/MembershipEvent";
import * as htmlEscape from "escape-html";
import { Healthz } from "./health/healthz";
import { Mjolnir } from "./Mjolnir";
config.RUNTIME = {};
@ -45,7 +41,8 @@ if (config.health.healthz.enabled) {
}
(async function () {
const storage = new SimpleFsStorageProvider(path.join(config.dataPath, "bot.json"));
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
const storage = new SimpleFsStorageProvider(path.join(storagePath, "bot.json"));
let client: MatrixClient;
if (config.pantalaimon.use) {
@ -57,65 +54,7 @@ if (config.health.healthz.enabled) {
config.RUNTIME.client = client;
client.on("room.invite", async (roomId: string, inviteEvent: any) => {
const membershipEvent = new MembershipEvent(inviteEvent);
const reportInvite = async () => {
if (!config.recordIgnoredInvites) return; // Nothing to do
await client.sendMessage(config.managementRoom, {
msgtype: "m.text",
body: `${membershipEvent.sender} has invited me to ${roomId} but the config prevents me from accepting the invitation. `
+ `If you would like this room protected, use "!mjolnir rooms add ${roomId}" so I can accept the invite.`,
format: "org.matrix.custom.html",
formatted_body: `${htmlEscape(membershipEvent.sender)} has invited me to ${htmlEscape(roomId)} but the config prevents me from `
+ `accepting the invitation. If you would like this room protected, use <code>!mjolnir rooms add ${htmlEscape(roomId)}</code> `
+ `so I can accept the invite.`,
});
};
if (config.autojoinOnlyIfManager) {
const managers = await client.getJoinedRoomMembers(config.managementRoom);
if (!managers.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
} else {
const groupMembers = await client.unstableApis.getGroupUsers(config.acceptInvitesFromGroup);
const userIds = groupMembers.map(m => m.user_id);
if (!userIds.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
}
return client.joinRoom(roomId);
});
const banLists: BanList[] = [];
const protectedRooms: { [roomId: string]: string } = {};
const joinedRooms = await client.getJoinedRooms();
// Ensure we're also joined to the rooms we're protecting
LogService.info("index", "Resolving protected rooms...");
for (const roomRef of config.protectedRooms) {
const permalink = Permalinks.parseUrl(roomRef);
if (!permalink.roomIdOrAlias) continue;
let roomId = await client.resolveRoom(permalink.roomIdOrAlias);
if (!joinedRooms.includes(roomId)) {
roomId = await client.joinRoom(permalink.roomIdOrAlias, permalink.viaServers);
}
protectedRooms[roomId] = roomRef;
}
// Ensure we're also in the management room
LogService.info("index", "Resolving management room...");
const managementRoomId = await client.resolveRoom(config.managementRoom);
if (!joinedRooms.includes(managementRoomId)) {
config.managementRoom = await client.joinRoom(config.managementRoom);
} else {
config.managementRoom = managementRoomId;
}
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
const bot = new Mjolnir(client, protectedRooms, banLists);
let bot = await Mjolnir.setupMjolnirFromConfig(client);
await bot.start();
})().catch(err => {
logMessage(LogLevel.ERROR, "index", err);

View File

@ -0,0 +1,77 @@
import axios from "axios";
import { HmacSHA1 } from "crypto-js";
import { LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk";
import config from "../../src/config";
/**
* Register a user using the synapse admin api that requires the use of a registration secret rather than an admin user.
* This should only be used by test code and should not be included from any file in the source directory
* either by explicit imports or copy pasting.
* @param username The username to give the user.
* @param displayname The displayname to give the user.
* @param password The password to use.
* @param admin True to make the user an admin, false otherwise.
* @returns The response from synapse.
*/
export async function registerUser(username: string, displayname: string, password: string, admin: boolean) {
let registerUrl = `${config.homeserverUrl}/_synapse/admin/v1/register`
let { data } = await axios.get(registerUrl);
let nonce = data.nonce!;
let mac = HmacSHA1(`${nonce}\0${username}\0${password}\0${admin ? 'admin' : 'notadmin'}`, 'REGISTRATION_SHARED_SECRET');
return await axios.post(registerUrl, {
nonce,
username,
displayname,
password,
admin,
mac: mac.toString()
})
}
/**
* Register a new test user with a unique username.
* @param isAdmin Whether to make the new user an admin.
* @returns A string that is the username and password of a new user.
*/
export async function registerNewTestUser(isAdmin: boolean) {
let isUserValid = false;
let username;
do {
username = `mjolnir-test-user-${Math.floor(Math.random() * 100000)}`
await registerUser(username, username, username, isAdmin).then(_ => isUserValid = true).catch(e => {
if (e.isAxiosError && e?.response?.data?.errcode === 'M_USER_IN_USE') {
LogService.debug("test/clientHelper", `${username} already registered, trying another`);
false // continue and try again
} else {
console.error(`failed to register user ${e}`);
throw e;
}
})
} while (!isUserValid);
return username;
}
/**
* Registers a unique test user and returns a `MatrixClient` logged in and ready to use.
* @param isAdmin Whether to make the user an admin.
* @returns A new `MatrixClient` session for a unique test user.
*/
export async function newTestUser(isAdmin : boolean = false): Promise<MatrixClient> {
const username = await registerNewTestUser(isAdmin);
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
return await pantalaimon.createClientWithCredentials(username, username);
}
/**
* Utility to create an event listener for m.notice msgtype m.room.messages.
* @param targetRoomdId The roomId to listen into.
* @param cb The callback when a m.notice event is found in targetRoomId.
* @returns The callback to pass to `MatrixClient.on('room.message', cb)`
*/
export function noticeListener(targetRoomdId: string, cb) {
return (roomId, event) => {
if (roomId !== targetRoomdId) return;
if (event?.content?.msgtype !== "m.notice") return;
cb(event);
}
}

View File

@ -0,0 +1,28 @@
import config from "../../src/config";
import { makeMjolnir, teardownManagementRoom } from "./mjolnirSetupUtils";
// When Mjolnir starts (src/index.ts) it clobbers the config by resolving the management room
// alias specified in the config (config.managementRoom) and overwriting that with the room ID.
// Unfortunately every piece of code importing that config imports the same instance, including
// testing code, which is problematic when we want to create a fresh management room for each test.
// So there is some code in here to "undo" the mutation after we stop Mjolnir syncing.
export const mochaHooks = {
beforeEach: [
async function() {
this.managementRoomAlias = config.managementRoom
this.mjolnir = await makeMjolnir()
this.mjolnir.start()
}
],
afterEach: [
async function() {
await this.mjolnir.stop();
// Mjolnir resolves config.managementRoom and overwrites it, so we undo this here
// after stopping Mjolnir for the next time we setup a Mjolnir and a management room.
let managementRoomId = config.managementRoom;
config.managementRoom = this.managementRoomAlias;
// remove alias from management room and leave it.
await teardownManagementRoom(this.mjolnir.client, managementRoomId, this.managementRoomAlias);
}
]
};

View File

@ -0,0 +1,30 @@
import config from "../../src/config";
import { newTestUser, noticeListener } from "./clientHelper"
describe("help command", () => {
let client;
before(async function () {
client = await newTestUser(true);
await client.start();
})
it('Mjolnir responded to !mjolnir help', async function() {
this.timeout(30000);
console.log(`management room ${config.managementRoom}`);
// send a messgage
await client.joinRoom(config.managementRoom);
// listener for getting the event reply
let reply = new Promise((resolve, reject) => {
client.on('room.message', noticeListener(config.managementRoom, (event) => {
if (event.content.body.includes("Print status information")) {
resolve(event);
}
}))});
// check we get one back
console.log(config);
await client.sendMessage(config.managementRoom, {msgtype: "m.text", body: "!mjolnir help"})
await reply
})
after(async function () {
await client.stop();
})
})

View File

@ -0,0 +1,10 @@
/**
* This file is used to launch mjolnir for manual testing, creating a user and management room automatically if it doesn't already exist.
*/
import { makeMjolnir } from "./mjolnirSetupUtils";
(async () => {
let mjolnir = await makeMjolnir();
await mjolnir.start()
})();

View File

@ -0,0 +1,78 @@
/*
Copyright 2021 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 {
MatrixClient,
PantalaimonClient,
MemoryStorageProvider,
LogService,
LogLevel,
RichConsoleLogger
} from "matrix-bot-sdk";
import { Mjolnir} from '../../src/Mjolnir';
import config from "../../src/config";
import { registerUser } from "./clientHelper";
/**
* Ensures that a room exists with the alias, if it does not exist we create it.
* @param client The MatrixClient to use to resolve or create the aliased room.
* @param alias The alias of the room.
* @returns The room ID of the aliased room.
*/
export async function ensureAliasedRoomExists(client: MatrixClient, alias: string): Promise<string> {
return await client.resolveRoom(alias)
.catch(async e => {
if (e?.body?.errcode === 'M_NOT_FOUND') {
console.info(`${alias} hasn't been created yet, so we're making it now.`)
let roomId = await client.createRoom();
await client.createRoomAlias(config.managementRoom, roomId);
return roomId
}
throw e;
});
}
async function configureMjolnir() {
await registerUser('mjolnir', 'mjolnir', 'mjolnir', true).catch(e => {
if (e.isAxiosError && e.response.data.errcode === 'M_USER_IN_USE') {
console.log('mjolnir already registered, skipping');
} else {
throw e;
}
});
}
export async function makeMjolnir() {
await configureMjolnir();
LogService.setLogger(new RichConsoleLogger());
LogService.setLevel(LogLevel.fromString(config.logLevel, LogLevel.DEBUG));
LogService.info("test/mjolnirSetupUtils", "Starting bot...");
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
await ensureAliasedRoomExists(client, config.managementRoom);
return await Mjolnir.setupMjolnirFromConfig(client);
}
/**
* Remove the alias and leave the room, can't be implicitly provided from the config because Mjolnir currently mutates it.
* @param client The client to use to leave the room.
* @param roomId The roomId of the room to leave.
* @param alias The alias to remove from the room.
*/
export async function teardownManagementRoom(client: MatrixClient, roomId: string, alias: string) {
await client.deleteRoomAlias(alias);
await client.leaveRoom(roomId);
}

728
yarn.lock

File diff suppressed because it is too large Load Diff