mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-08-28 05:14:04 +00:00
Correctly configure package.json for MPS.
This has taken forever to get working, but now it is. index.ts is changed because that's how we know we've overriden the bot-sdk dep correctly.
This commit is contained in:
+7
-2
@@ -49,6 +49,7 @@
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.17.2",
|
||||
"@sentry/tracing": "^7.17.2",
|
||||
"@sinclair/typebox": "~0.31.15",
|
||||
"await-lock": "^2.2.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"config": "^3.3.9",
|
||||
@@ -59,14 +60,18 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"matrix-appservice-bridge": "^9.0.1",
|
||||
"matrix-protection-suite": "link:/home/user/experiments/matrix-protection-suite",
|
||||
"matrix-protection-suite-for-matrix-bot-sdk": "link:/home/user/experiments/matrix-protection-suite-for-matrix-bot-sdk",
|
||||
"matrix-protection-suite": "git+https://github.com/Gnuxie/matrix-protection-suite.git#0.8.0",
|
||||
"matrix-protection-suite-for-matrix-bot-sdk": "git+https://github.com/Gnuxie/matrix-protection-suite-for-matrix-bot-sdk.git#2df8b462442a42c975f7932d17a08c3aea23604b",
|
||||
"parse-duration": "^1.0.2",
|
||||
"pg": "^8.8.0",
|
||||
"shell-quote": "^1.7.3",
|
||||
"ulidx": "^2.2.1",
|
||||
"yaml": "^2.3.2"
|
||||
},
|
||||
"overrides": {
|
||||
"matrix-bot-sdk": "$@vector-im/matrix-bot-sdk",
|
||||
"@vector-im/matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@^0.6.6-element.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
|
||||
-501
@@ -1,501 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022 Gnuxie <Gnuxie@protonmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is modified and is NOT licensed under the Apache License.
|
||||
* This modified file incorperates work from mjolnir
|
||||
* https://github.com/matrix-org/mjolnir
|
||||
* which included the following license notice:
|
||||
|
||||
Copyright 2019-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.
|
||||
*
|
||||
* However, this file is modified and the modifications in this file
|
||||
* are NOT distributed, contributed, committed, or licensed under the Apache License.
|
||||
*/
|
||||
|
||||
import {
|
||||
LogLevel,
|
||||
LogService,
|
||||
MembershipEvent,
|
||||
} from "matrix-bot-sdk";
|
||||
|
||||
import { COMMAND_PREFIX, handleCommand } from "./commands/CommandHandler";
|
||||
import { UnlistedUserRedactionQueue } from "./queues/UnlistedUserRedactionQueue";
|
||||
import { htmlEscape } from "./utils";
|
||||
import { ReportManager } from "./report/ReportManager";
|
||||
import { ReportPoller } from "./report/ReportPoller";
|
||||
import { WebAPIs } from "./webapis/WebAPIs";
|
||||
import { ThrottlingQueue } from "./queues/ThrottlingQueue";
|
||||
import { getDefaultConfig, IConfig } from "./config";
|
||||
import ManagementRoomOutput from "./ManagementRoomOutput";
|
||||
import { ProtectionManager } from "./protections/ProtectionManager";
|
||||
import { findCommandTable } from "./commands/interface-manager/InterfaceCommand";
|
||||
import { MatrixReactionHandler } from "./commands/interface-manager/MatrixReactionHandler";
|
||||
import { ProtectedRoomsSet } from "matrix-protection-suite";
|
||||
|
||||
export const STATE_NOT_STARTED = "not_started";
|
||||
export const STATE_CHECKING_PERMISSIONS = "checking_permissions";
|
||||
export const STATE_SYNCING = "syncing";
|
||||
export const STATE_RUNNING = "running";
|
||||
|
||||
export class Mjolnir {
|
||||
private displayName: string;
|
||||
private localpart: string;
|
||||
private currentState: string = STATE_NOT_STARTED;
|
||||
/**
|
||||
* This is for users who are not listed on a watchlist,
|
||||
* but have been flagged by the automatic spam detection as suispicous
|
||||
*/
|
||||
private unlistedUserRedactionQueue = new UnlistedUserRedactionQueue();
|
||||
|
||||
private webapis: WebAPIs;
|
||||
public taskQueue: ThrottlingQueue;
|
||||
/**
|
||||
* Reporting back to the management room.
|
||||
*/
|
||||
public readonly managementRoomOutput: ManagementRoomOutput;
|
||||
/*
|
||||
* Config-enabled polling of reports in Synapse, so Mjolnir can react to reports
|
||||
*/
|
||||
private reportPoller?: ReportPoller;
|
||||
/**
|
||||
* Store the protections being used by Mjolnir.
|
||||
*/
|
||||
public readonly protectionManager: ProtectionManager;
|
||||
/**
|
||||
* Handle user reports from the homeserver.
|
||||
*/
|
||||
public readonly reportManager: ReportManager;
|
||||
|
||||
private readonly commandTable = findCommandTable("mjolnir");
|
||||
|
||||
public readonly reactionHandler: MatrixReactionHandler;
|
||||
|
||||
/**
|
||||
* Adds a listener to the client that will automatically accept invitations.
|
||||
* @param {MatrixSendClient} 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.acceptInvitesFromSpace A space of users to accept invites from, ignores invites form users not in this space.
|
||||
*/
|
||||
private static addJoinOnInviteListener(mjolnir: Mjolnir, client: MatrixSendClient, options: { [key: string]: any }) {
|
||||
mjolnir.matrixEmitter.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(mjolnir.managementRoomId, {
|
||||
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(mjolnir.managementRoomId);
|
||||
if (!managers.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
|
||||
} else {
|
||||
const spaceId = await client.resolveRoom(options.acceptInvitesFromSpace);
|
||||
const spaceUserIds = await client.getJoinedRoomMembers(spaceId)
|
||||
.catch(async e => {
|
||||
if (e.body?.errcode === "M_FORBIDDEN") {
|
||||
await mjolnir.managementRoomOutput.logMessage(LogLevel.ERROR, 'Mjolnir', `Mjolnir is not in the space configured for acceptInvitesFromSpace, did you invite it?`);
|
||||
await client.joinRoom(spaceId);
|
||||
return await client.getJoinedRoomMembers(spaceId);
|
||||
} else {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
if (!spaceUserIds.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 {MatrixSendClient} client The client for Mjolnir to use.
|
||||
* @returns A new Mjolnir instance that can be started without further setup.
|
||||
*/
|
||||
static async setupMjolnirFromConfig(client: MatrixSendClient, matrixEmitter: MatrixEmitter, config: IConfig): Promise<Mjolnir> {
|
||||
if (!config.autojoinOnlyIfManager && config.acceptInvitesFromSpace === getDefaultConfig().acceptInvitesFromSpace) {
|
||||
throw new TypeError("`autojoinOnlyIfManager` has been disabled, yet no space has been provided for `acceptInvitesFromSpace`.");
|
||||
}
|
||||
const joinedRooms = await client.getJoinedRooms();
|
||||
|
||||
// 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)) {
|
||||
await client.joinRoom(config.managementRoom);
|
||||
}
|
||||
|
||||
const mjolnir = new Mjolnir(client, await client.getUserId(), matrixEmitter, managementRoomId, config);
|
||||
await mjolnir.managementRoomOutput.logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
|
||||
Mjolnir.addJoinOnInviteListener(mjolnir, client, config);
|
||||
return mjolnir;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly client: MatrixSendClient,
|
||||
private readonly clientUserId: string,
|
||||
public readonly matrixEmitter: MatrixEmitter,
|
||||
public readonly managementRoomId: string,
|
||||
public readonly config: IConfig,
|
||||
private readonly protectedRoomsSet: ProtectedRoomsSet,
|
||||
) {
|
||||
this.protectedRoomsConfig = new ProtectedRoomsConfig(client);
|
||||
this.policyListManager = new PolicyListManager(this);
|
||||
this.reactionHandler = new MatrixReactionHandler(this.managementRoomId, client, clientUserId);
|
||||
|
||||
const mutedModules = (LogService as any).mutedModules;
|
||||
if (!Array.isArray(mutedModules)) {
|
||||
throw new TypeError("MatrixBotSdk has changed their hacky handling of muted modules, praise be");
|
||||
}
|
||||
for (const module of config.logMutedModules) {
|
||||
if (!mutedModules.includes(module)) {
|
||||
LogService.muteModule(module);
|
||||
}
|
||||
}
|
||||
// Setup bot.
|
||||
|
||||
matrixEmitter.on("room.event", this.handleEvent.bind(this));
|
||||
|
||||
matrixEmitter.on("room.message", async (roomId, event) => {
|
||||
if (roomId !== this.managementRoomId) return;
|
||||
if (!event['content']) return;
|
||||
|
||||
const content = event['content'];
|
||||
if (content['msgtype'] === "m.text" && content['body']) {
|
||||
const prefixes = [
|
||||
COMMAND_PREFIX,
|
||||
this.localpart + ":",
|
||||
this.displayName + ":",
|
||||
await client.getUserId() + ":",
|
||||
this.localpart + " ",
|
||||
this.displayName + " ",
|
||||
await client.getUserId() + " ",
|
||||
...config.commands.additionalPrefixes.map(p => `!${p}`),
|
||||
...config.commands.additionalPrefixes.map(p => `${p}:`),
|
||||
...config.commands.additionalPrefixes.map(p => `${p} `),
|
||||
...config.commands.additionalPrefixes,
|
||||
];
|
||||
if (config.commands.allowNoPrefix) prefixes.push("!");
|
||||
|
||||
const prefixUsed = prefixes.find(p => content['body'].toLowerCase().startsWith(p.toLowerCase()));
|
||||
if (!prefixUsed) return;
|
||||
|
||||
// rewrite the event body to make the prefix uniform (in case the bot has spaces in its display name)
|
||||
let restOfBody = content['body'].substring(prefixUsed.length);
|
||||
if (!restOfBody.startsWith(" ")) restOfBody = ` ${restOfBody}`;
|
||||
event['content']['body'] = COMMAND_PREFIX + restOfBody;
|
||||
LogService.info("Mjolnir", `Command being run by ${event['sender']}: ${event['content']['body']}`);
|
||||
|
||||
await client.sendReadReceipt(roomId, event['event_id']);
|
||||
|
||||
return handleCommand(roomId, event, this, this.commandTable);
|
||||
}
|
||||
});
|
||||
|
||||
matrixEmitter.on("room.join", (roomId: string, event: any) => {
|
||||
LogService.info("Mjolnir", `Joined ${roomId}`);
|
||||
return this.resyncJoinedRooms();
|
||||
});
|
||||
matrixEmitter.on("room.leave", (roomId: string, event: any) => {
|
||||
LogService.info("Mjolnir", `Left ${roomId}`);
|
||||
return this.resyncJoinedRooms();
|
||||
});
|
||||
|
||||
client.getUserId().then(userId => {
|
||||
this.localpart = userId.split(':')[0].substring(1);
|
||||
return client.getUserProfile(userId);
|
||||
}).then(profile => {
|
||||
if (profile['displayname']) {
|
||||
this.displayName = profile['displayname'];
|
||||
}
|
||||
});
|
||||
|
||||
// Setup Web APIs
|
||||
console.log("Creating Web APIs");
|
||||
this.reportManager = new ReportManager(this);
|
||||
this.webapis = new WebAPIs(this.reportManager, this.config);
|
||||
if (config.pollReports) {
|
||||
this.reportPoller = new ReportPoller(this, this.reportManager);
|
||||
}
|
||||
// Setup join/leave listener
|
||||
this.roomJoins = new RoomMemberManager(this.matrixEmitter);
|
||||
this.taskQueue = new ThrottlingQueue(this, config.backgroundDelayMS);
|
||||
|
||||
this.protectionManager = new ProtectionManager(this);
|
||||
|
||||
this.managementRoomOutput = new ManagementRoomOutput(managementRoomId, client, config);
|
||||
const protections = new ProtectionManager(this);
|
||||
this.protectedRoomsTracker = new ProtectedRoomsSet(client, clientUserId, managementRoomId, this.managementRoomOutput, protections, config);
|
||||
}
|
||||
|
||||
public get state(): string {
|
||||
return this.currentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handler to flag a user for redaction, removing any future messages that they send.
|
||||
* Typically this is used by the flooding or image protection on users that have not been banned from a list yet.
|
||||
* It cannot used to redact any previous messages the user has sent, in that cas you should use the `EventRedactionQueue`.
|
||||
*/
|
||||
public get unlistedUserRedactionHandler(): UnlistedUserRedactionQueue {
|
||||
return this.unlistedUserRedactionQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Mjölnir.
|
||||
*/
|
||||
public async start() {
|
||||
try {
|
||||
// Start the web server.
|
||||
console.log("Starting web server");
|
||||
await this.webapis.start();
|
||||
|
||||
if (this.reportPoller) {
|
||||
let reportPollSetting: { from: number } = { from: 0 };
|
||||
try {
|
||||
reportPollSetting = await this.client.getAccountData(REPORT_POLL_EVENT_TYPE);
|
||||
} catch (err) {
|
||||
if (err.body?.errcode !== "M_NOT_FOUND") {
|
||||
throw err;
|
||||
} else {
|
||||
this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "report poll setting does not exist yet");
|
||||
}
|
||||
}
|
||||
this.reportPoller.start(reportPollSetting.from);
|
||||
}
|
||||
|
||||
// Load the state.
|
||||
this.currentState = STATE_CHECKING_PERMISSIONS;
|
||||
|
||||
await this.managementRoomOutput.logMessage(LogLevel.DEBUG, "Mjolnir@startup", "Loading protected rooms...");
|
||||
await this.protectedRoomsConfig.loadProtectedRoomsFromConfig(this.config);
|
||||
await this.protectedRoomsConfig.loadProtectedRoomsFromAccountData();
|
||||
this.protectedRoomsConfig.getExplicitlyProtectedRooms().forEach(this.protectRoom, this);
|
||||
// We have to build the policy lists before calling `resyncJoinedRooms` otherwise mjolnir will try to protect
|
||||
// every policy list we are already joined to, as mjolnir will not be able to distinguish them from normal rooms.
|
||||
await this.policyListManager.start();
|
||||
await this.resyncJoinedRooms(false);
|
||||
await this.protectionManager.start();
|
||||
this.reactionHandler.start(this.matrixEmitter);
|
||||
|
||||
if (this.config.verifyPermissionsOnStartup) {
|
||||
await this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "Checking permissions...");
|
||||
await this.protectedRoomsTracker.verifyPermissions();
|
||||
}
|
||||
|
||||
// Start the bot.
|
||||
await this.matrixEmitter.start();
|
||||
|
||||
this.currentState = STATE_SYNCING;
|
||||
if (this.config.syncOnStartup) {
|
||||
await this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "Syncing lists...");
|
||||
await this.protectedRoomsTracker.syncLists();
|
||||
}
|
||||
|
||||
this.currentState = STATE_RUNNING;
|
||||
await this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "Startup complete. Now monitoring rooms.");
|
||||
if (this.config.verboseLogging) {
|
||||
await this.managementRoomOutput.logMessage(LogLevel.WARN, "Mjolnir@startup", "The use of verbose logging is deprecated and will be removed in a future version, check your config.");
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
LogService.error("Mjolnir", "Error during startup:", err);
|
||||
this.stop();
|
||||
await this.managementRoomOutput.logMessage(LogLevel.ERROR, "Mjolnir@startup", "Startup failed due to error - see console");
|
||||
} catch (e) {
|
||||
LogService.error("Mjolnir", `Failed to report startup error to the management room:`, e);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop Mjolnir from syncing and processing commands.
|
||||
*/
|
||||
public stop() {
|
||||
LogService.info("Mjolnir", "Stopping Mjolnir...");
|
||||
this.matrixEmitter.stop();
|
||||
this.reactionHandler.stop(this.matrixEmitter);
|
||||
this.webapis.stop();
|
||||
this.reportPoller?.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rooms that mjolnir is configured to explicitly protect.
|
||||
* Do not use to access all of the rooms that mjolnir protects.
|
||||
* FIXME: In future ProtectedRoomsSet on this mjolnir should not be public and should also be accessed via a delegator method.
|
||||
*/
|
||||
public get explicitlyProtectedRooms(): string[] {
|
||||
return this.protectedRoomsConfig.getExplicitlyProtectedRooms()
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly protect this room, adding it to the account data.
|
||||
* Should NOT be used to protect a room to implement e.g. `config.protectAllJoinedRooms`,
|
||||
* use `protectRoom` instead.
|
||||
* @param roomId The room to be explicitly protected by mjolnir and persisted in config.
|
||||
*/
|
||||
public async addProtectedRoom(roomId: string) {
|
||||
await this.protectedRoomsConfig.addProtectedRoom(roomId);
|
||||
this.protectRoom(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protect the room, but do not persist it to the account data.
|
||||
* @param roomId The room to protect.
|
||||
*/
|
||||
private protectRoom(roomId: string): void {
|
||||
this.protectedRoomsTracker.addProtectedRoom(roomId);
|
||||
this.roomJoins.addRoom(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a room from the explicitly protect set of rooms that is persisted to account data.
|
||||
* Should NOT be used to remove a room that we have left, e.g. when implementing `config.protectAllJoinedRooms`,
|
||||
* use `unprotectRoom` instead.
|
||||
* @param roomId The room to remove from account data and stop protecting.
|
||||
*/
|
||||
public async removeProtectedRoom(roomId: string) {
|
||||
await this.protectedRoomsConfig.removeProtectedRoom(roomId);
|
||||
this.unprotectRoom(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unprotect a room.
|
||||
* @param roomId The room to stop protecting.
|
||||
*/
|
||||
private unprotectRoom(roomId: string): void {
|
||||
this.roomJoins.removeRoom(roomId);
|
||||
this.protectedRoomsTracker.removeProtectedRoom(roomId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resynchronize the protected rooms with rooms that the mjolnir user is joined to.
|
||||
* This is to implement `config.protectAllJoinedRooms` functionality.
|
||||
* @param withSync Whether to synchronize all protected rooms with the watched policy lists afterwards.
|
||||
*/
|
||||
private async resyncJoinedRooms(withSync = true): Promise<void> {
|
||||
if (!this.config.protectAllJoinedRooms) return;
|
||||
|
||||
// We filter out all policy rooms so that we only protect ones that are
|
||||
// explicitly protected, so that we don't try to protect lists that we are just watching.
|
||||
const filterOutManagementAndPolicyRooms = (roomId: string) => {
|
||||
const policyListIds = this.policyListManager.lists.map(list => list.roomId);
|
||||
return roomId !== this.managementRoomId && !policyListIds.includes(roomId);
|
||||
};
|
||||
|
||||
const joinedRoomIdsToProtect = new Set([
|
||||
...(await this.client.getJoinedRooms()).filter(filterOutManagementAndPolicyRooms),
|
||||
// We do this specifically so policy lists that have been explicitly marked as protected
|
||||
// will be protected.
|
||||
...this.protectedRoomsConfig.getExplicitlyProtectedRooms(),
|
||||
]);
|
||||
const previousRoomIdsProtecting = new Set(this.protectedRoomsTracker.getProtectedRooms());
|
||||
// find every room that we have left (since last time)
|
||||
for (const roomId of previousRoomIdsProtecting.keys()) {
|
||||
if (!joinedRoomIdsToProtect.has(roomId)) {
|
||||
// Then we have left this room.
|
||||
this.unprotectRoom(roomId);
|
||||
}
|
||||
}
|
||||
// find every room that we have joined (since last time).
|
||||
for (const roomId of joinedRoomIdsToProtect.keys()) {
|
||||
if (!previousRoomIdsProtecting.has(roomId)) {
|
||||
// Then we have joined this room
|
||||
this.protectRoom(roomId);
|
||||
}
|
||||
}
|
||||
|
||||
if (withSync) {
|
||||
await this.protectedRoomsTracker.syncLists();
|
||||
}
|
||||
}
|
||||
|
||||
private async handleEvent(roomId: string, event: any) {
|
||||
// Check for UISI errors
|
||||
if (roomId === this.managementRoomId) {
|
||||
if (event['type'] === 'm.room.message' && event['content'] && event['content']['body']) {
|
||||
if (event['content']['body'] === "** Unable to decrypt: The sender's device has not sent us the keys for this message. **") {
|
||||
// UISI
|
||||
await this.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '⚠');
|
||||
await this.client.unstableApis.addReactionToEvent(roomId, event['event_id'], 'UISI');
|
||||
await this.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '🚨');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updated ban lists before checking protected rooms - the ban lists might be protected
|
||||
// themselves.
|
||||
const policyList = this.policyListManager.lists.find(list => list.roomId === roomId);
|
||||
if (policyList !== undefined) {
|
||||
if (ALL_BAN_LIST_RULE_TYPES.includes(event['type']) || event['type'] === 'm.room.redaction') {
|
||||
policyList.updateForEvent(event.event_id)
|
||||
}
|
||||
}
|
||||
|
||||
if (event.sender !== this.clientUserId) {
|
||||
this.protectedRoomsTracker.handleEvent(roomId, event);
|
||||
}
|
||||
}
|
||||
|
||||
public async isSynapseAdmin(): Promise<boolean> {
|
||||
try {
|
||||
const endpoint = `/_synapse/admin/v1/users/${await this.client.getUserId()}/admin`;
|
||||
const response = await this.client.doRequest("GET", endpoint);
|
||||
return response['admin'];
|
||||
} catch (e) {
|
||||
LogService.error("Mjolnir", "Error determining if Mjolnir is a server admin:", e);
|
||||
return false; // assume not
|
||||
}
|
||||
}
|
||||
|
||||
public async deactivateSynapseUser(userId: string): Promise<any> {
|
||||
const endpoint = `/_synapse/admin/v1/deactivate/${userId}`;
|
||||
return await this.client.doRequest("POST", endpoint);
|
||||
}
|
||||
|
||||
public async shutdownSynapseRoom(roomId: string, message?: string): Promise<any> {
|
||||
const endpoint = `/_synapse/admin/v1/rooms/${roomId}`;
|
||||
return await this.client.doRequest("DELETE", endpoint, null, {
|
||||
new_room_user_id: await this.client.getUserId(),
|
||||
block: true,
|
||||
message: message /* If `undefined`, we'll use Synapse's default message. */
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a user administrator via the Synapse Admin API
|
||||
* @param roomId the room where the user (or the bot) shall be made administrator.
|
||||
* @param userId optionally specify the user mxID to be made administrator.
|
||||
*/
|
||||
public async makeUserRoomAdmin(roomId: string, userId: string): Promise<void> {
|
||||
const endpoint = `/_synapse/admin/v1/rooms/${roomId}/make_room_admin`;
|
||||
return await this.client.doRequest("POST", endpoint, null, {
|
||||
user_id: userId
|
||||
});
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -40,8 +40,11 @@ import {
|
||||
} from "matrix-bot-sdk";
|
||||
import { StoreType } from "@matrix-org/matrix-sdk-crypto-nodejs";
|
||||
import { read as configRead } from "./config";
|
||||
import { Mjolnir } from "./Mjolnir";
|
||||
import { initializeSentry, patchMatrixClient } from "./utils";
|
||||
import { makeDraupnirBotModeFromConfig } from "./DraupnirBotMode";
|
||||
import { Draupnir } from "./Draupnir";
|
||||
import { SafeMatrixEmitterWrapper } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { DefaultEventDecoder } from "matrix-protection-suite";
|
||||
|
||||
|
||||
(async function () {
|
||||
@@ -64,7 +67,7 @@ import { initializeSentry, patchMatrixClient } from "./utils";
|
||||
healthz.listen();
|
||||
}
|
||||
|
||||
let bot: Mjolnir | null = null;
|
||||
let bot: Draupnir | null = null;
|
||||
try {
|
||||
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
|
||||
const storage = new SimpleFsStorageProvider(path.join(storagePath, "bot.json"));
|
||||
@@ -86,7 +89,7 @@ import { initializeSentry, patchMatrixClient } from "./utils";
|
||||
patchMatrixClient();
|
||||
config.RUNTIME.client = client;
|
||||
|
||||
bot = await Mjolnir.setupMjolnirFromConfig(client, client, config);
|
||||
bot = await makeDraupnirBotModeFromConfig(client, new SafeMatrixEmitterWrapper(client, DefaultEventDecoder), config);
|
||||
} catch (err) {
|
||||
console.error(`Failed to setup mjolnir from the config ${config.dataPath}: ${err}`);
|
||||
throw err;
|
||||
|
||||
@@ -211,10 +211,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@sinclair/typebox@^0.31.15":
|
||||
version "0.31.21"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.31.21.tgz#d52d8e35f71e5651042aa0237e918e4b21fbbbf8"
|
||||
integrity sha512-Wtq/K44EMkREaXytK+2c5DrygtYsH7ZxT0StQL8HMJz2BoOM7NZ/xfrUFBVuZxDrhJCoXf5Im282P2CCz5DHwQ==
|
||||
"@sinclair/typebox@~0.31.15":
|
||||
version "0.31.28"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.31.28.tgz#b68831e7bc7d09daac26968ea32f42bedc968ede"
|
||||
integrity sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.19.2"
|
||||
@@ -2319,13 +2319,19 @@ matrix-appservice@^2.0.0:
|
||||
request-promise "^4.2.6"
|
||||
sanitize-html "^2.8.0"
|
||||
|
||||
"matrix-protection-suite-for-matrix-bot-sdk@link:/home/user/experiments/matrix-protection-suite-for-matrix-bot-sdk":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
"matrix-protection-suite-for-matrix-bot-sdk@git+https://github.com/Gnuxie/matrix-protection-suite-for-matrix-bot-sdk.git#2df8b462442a42c975f7932d17a08c3aea23604b":
|
||||
version "0.8.0"
|
||||
resolved "git+https://github.com/Gnuxie/matrix-protection-suite-for-matrix-bot-sdk.git#2df8b462442a42c975f7932d17a08c3aea23604b"
|
||||
|
||||
"matrix-protection-suite@link:../../experiments/matrix-protection-suite":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
"matrix-protection-suite@git+https://github.com/Gnuxie/matrix-protection-suite.git#0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "git+https://github.com/Gnuxie/matrix-protection-suite.git#e67a5fcbba9565acad7da43fb84c4a4c0f321466"
|
||||
dependencies:
|
||||
await-lock "^2.2.2"
|
||||
crypto-js "^4.1.1"
|
||||
glob-to-regexp "^0.4.1"
|
||||
immutable "^5.0.0-beta.4"
|
||||
ulidx "^2.1.0"
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
|
||||
Reference in New Issue
Block a user