mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-08-29 07:08:50 +00:00
Add functionality for config.protectAllJoinedRooms via a protection + adjacent changes. (#711)
* Initial ProtectAllJoinedRoomsProtection. We need something like this to implement `config.protectAllJoinedRooms`, we also need something to go alongside that removes rooms as they are added or removed. it will probably be a behaviour of the same protection that we will split out. * Move it cos i cba and we need to merge with a protection to unprotect on leave and ban. * IDK i keep getting distracted i need my notebook back aaaa. * Create RoomsSetBehaviourProtection out of redundant protections. * Add toggle for RoomsSetBehaviour. * Enable the RoomsSetBehaviour protection by default. * Add the behaviour protection to protections index. * Whoopsie unprotected parted rooms should actually call itself. * Fix bugs meow. * handleExternalInvite -> handleExternalMembership * Make sure leave events get propagated in bot mode. * Add batcher to ProtectJoinedRooms component. * Hook into handleExternalMembership for RoomsSetBehaviour. * Remove crap from fixtures.ts * leave all rooms when starting integration tests. * Change how protections are informed of membership. * Add test for Joinig and protecting rooms on invite. * Tidy up UnprotectPartedRooms rendering. * Update for MPS v2.7.0 Added - Generic item batching is now available for protections to use by using the `StandardBatcher`. Changed - `Task` has been improved to be more liberal in the closures it accepts. And `Task` now has more options for logging how tasks have failed. - The `Protection` callback `handleExternalInvite` has been renamed to `handleExternalMembership`. Fixed - An issue where adding rooms to the protected rooms set more than once could sometimes cause duplicate events to be propagated. * Fix typo mare. * Stop protecting rooms automatically when config.protectAllJoinedRooms is false. * Update CHANGELOG.md
This commit is contained in:
@@ -14,6 +14,22 @@ and this project adheres to
|
||||
|
||||
## [Unreleased] - None
|
||||
|
||||
### Fixed
|
||||
|
||||
- `config.protectAllJoinedRooms` was unimplemented in versions `v2.0.2` and
|
||||
below. This went under the radar in the beta programme because it would have
|
||||
only been detectable for first time testers migrating over. Reported by
|
||||
@cremesk.
|
||||
|
||||
- Draupnir will now automatically unprotect rooms when the bot is kicked, and
|
||||
send an alert to the management room.
|
||||
|
||||
### Added
|
||||
|
||||
- `RoomSetBehaviourProtection` to add the
|
||||
`config.protectAllJoinedRoomsFunctionality`. This is also responsible for
|
||||
unprotecting rooms as the bot is removed from them.
|
||||
|
||||
## [v2.0.2] 2025-01-24
|
||||
|
||||
### Added
|
||||
|
||||
+2
-2
@@ -63,8 +63,8 @@
|
||||
"jsdom": "^24.0.0",
|
||||
"matrix-appservice-bridge": "^10.3.1",
|
||||
"matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@^0.7.1-element.6",
|
||||
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@2.6.0",
|
||||
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@2.6.0",
|
||||
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@2.7.0",
|
||||
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@2.7.0",
|
||||
"pg": "^8.8.0",
|
||||
"yaml": "^2.3.2"
|
||||
},
|
||||
|
||||
+7
-3
@@ -16,6 +16,7 @@ import {
|
||||
EventReport,
|
||||
LoggableConfigTracker,
|
||||
Logger,
|
||||
Membership,
|
||||
MembershipEvent,
|
||||
Ok,
|
||||
PolicyRoomManager,
|
||||
@@ -50,7 +51,6 @@ import { RendererMessageCollector } from "./capabilities/RendererMessageCollecto
|
||||
import { DraupnirRendererMessageCollector } from "./capabilities/DraupnirRendererMessageCollector";
|
||||
import { renderProtectionFailedToStart } from "./protections/ProtectedRoomsSetRenderers";
|
||||
import { draupnirStatusInfo, renderStatusInfo } from "./commands/StatusCommand";
|
||||
import { isInvitationForUser } from "./protections/invitation/inviteCore";
|
||||
import {
|
||||
StringRoomID,
|
||||
StringUserID,
|
||||
@@ -319,9 +319,13 @@ export class Draupnir implements Client, MatrixAdaptorContext {
|
||||
public handleTimelineEvent(roomID: StringRoomID, event: RoomEvent): void {
|
||||
if (
|
||||
Value.Check(MembershipEvent, event) &&
|
||||
isInvitationForUser(event, this.clientUserID)
|
||||
event.state_key === this.clientUserID &&
|
||||
// if the membership is join, make sure that we filter out protected rooms.
|
||||
(event.content.membership === Membership.Join
|
||||
? !this.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
: true)
|
||||
) {
|
||||
this.protectedRoomsSet.handleExternalInvite(roomID, event);
|
||||
this.protectedRoomsSet.handleExternalMembership(roomID, event);
|
||||
}
|
||||
this.managementRoomMessageListener(roomID, event);
|
||||
void Task(
|
||||
|
||||
@@ -96,6 +96,10 @@ export class DraupnirBotModeToggle implements BotModeTogle {
|
||||
this.roomStateManagerFactory.handleTimelineEvent(roomID, event);
|
||||
this.clientsInRoomMap.handleTimelineEvent(roomID, event);
|
||||
});
|
||||
this.matrixEmitter.on("room.leave", (roomID, event) => {
|
||||
this.roomStateManagerFactory.handleTimelineEvent(roomID, event);
|
||||
this.clientsInRoomMap.handleTimelineEvent(roomID, event);
|
||||
});
|
||||
}
|
||||
public static async create(
|
||||
client: MatrixSendClient,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { RedactionSynchronisationProtection } from "./RedactionSynchronisation";
|
||||
import { PolicyChangeNotification } from "./PolicyChangeNotification";
|
||||
import { JoinRoomsOnInviteProtection } from "./invitation/JoinRoomsOnInviteProtection";
|
||||
import { RoomsSetBehaviour } from "./ProtectedRooms/RoomsSetBehaviourProtection";
|
||||
|
||||
export const DefaultEnabledProtectionsMigration =
|
||||
new SchemedDataManager<MjolnirEnabledProtectionsEvent>([
|
||||
@@ -143,4 +144,25 @@ export const DefaultEnabledProtectionsMigration =
|
||||
[DRAUPNIR_SCHEMA_VERSION_KEY]: 5,
|
||||
});
|
||||
},
|
||||
async function enableRoomsSetBehaviourProtection(input, toVersion) {
|
||||
if (!Value.Check(MjolnirEnabledProtectionsEvent, input)) {
|
||||
return ActionError.Result(
|
||||
`The data for ${MjolnirEnabledProtectionsEventType} is corrupted.`
|
||||
);
|
||||
}
|
||||
const enabledProtections = new Set(input.enabled);
|
||||
const protection = findProtection(RoomsSetBehaviour.name);
|
||||
if (protection === undefined) {
|
||||
const message = `Cannot find the ${RoomsSetBehaviour.name} protection`;
|
||||
return ActionException.Result(message, {
|
||||
exception: new TypeError(message),
|
||||
exceptionKind: ActionExceptionKind.Unknown,
|
||||
});
|
||||
}
|
||||
enabledProtections.add(protection.name);
|
||||
return Ok({
|
||||
enabled: [...enabledProtections],
|
||||
[DRAUPNIR_SCHEMA_VERSION_KEY]: toVersion,
|
||||
});
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -19,6 +19,7 @@ import "./MessageIsMedia";
|
||||
import "./MessageIsVoice";
|
||||
import "./NewJoinerProtection";
|
||||
import "./PolicyChangeNotification";
|
||||
import "./ProtectedRooms/RoomsSetBehaviourProtection";
|
||||
import "./TrustedReporters";
|
||||
import "./WordList";
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import {
|
||||
MatrixRoomReference,
|
||||
StringRoomID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
ClientRooms,
|
||||
ConstantPeriodItemBatch,
|
||||
isError,
|
||||
Logger,
|
||||
MembershipChange,
|
||||
MembershipChangeType,
|
||||
MembershipEvent,
|
||||
ProtectedRoomsSet,
|
||||
RoomMessageSender,
|
||||
RoomSetResultBuilder,
|
||||
StandardBatcher,
|
||||
Task,
|
||||
} from "matrix-protection-suite";
|
||||
import { sendMatrixEventsFromDeadDocument } from "../../commands/interface-manager/MPSMatrixInterfaceAdaptor";
|
||||
import { renderRoomSetResult } from "../../capabilities/CommonRenderers";
|
||||
import { DeadDocumentJSX } from "@the-draupnir-project/interface-manager";
|
||||
|
||||
const log = new Logger("ProtectAllJoinedRooms");
|
||||
|
||||
export class ProtectedJoinedRooms {
|
||||
private readonly batcher = new StandardBatcher(
|
||||
() =>
|
||||
new ConstantPeriodItemBatch<StringRoomID, void>(
|
||||
this.syncProtectedRooms.bind(this),
|
||||
{ waitPeriodMS: 1000 }
|
||||
)
|
||||
);
|
||||
public constructor(
|
||||
private readonly clientUserID: StringUserID,
|
||||
private readonly managementRoomID: StringRoomID,
|
||||
private readonly protectedRoomsSet: ProtectedRoomsSet,
|
||||
private readonly clientRooms: ClientRooms,
|
||||
private readonly roomMessageSender: RoomMessageSender
|
||||
) {
|
||||
// nothing to do.
|
||||
}
|
||||
|
||||
handleMembershipChange(changes: MembershipChange[]): void {
|
||||
for (const change of changes) {
|
||||
if (change.userID === this.clientUserID) {
|
||||
switch (change.membershipChangeType) {
|
||||
case MembershipChangeType.NoChange: {
|
||||
continue;
|
||||
}
|
||||
default: {
|
||||
this.batcher.add(change.roomID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleExternalMembership(
|
||||
roomID: StringRoomID,
|
||||
_event: MembershipEvent
|
||||
): void {
|
||||
this.batcher.add(roomID);
|
||||
}
|
||||
|
||||
public async syncProtectedRooms() {
|
||||
const policyRooms =
|
||||
this.protectedRoomsSet.issuerManager.allWatchedLists.map((profile) =>
|
||||
profile.room.toRoomIDOrAlias()
|
||||
);
|
||||
const roomsToProtect =
|
||||
this.clientRooms.currentRevision.allJoinedRooms.filter((roomID) => {
|
||||
return (
|
||||
!policyRooms.includes(roomID) &&
|
||||
!this.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
);
|
||||
});
|
||||
const setResult = new RoomSetResultBuilder();
|
||||
for (const roomID of roomsToProtect) {
|
||||
const protectResult =
|
||||
await this.protectedRoomsSet.protectedRoomsManager.addRoom(
|
||||
MatrixRoomReference.fromRoomID(roomID)
|
||||
);
|
||||
if (isError(protectResult)) {
|
||||
log.error("Unable to protect the room", roomID, protectResult.error);
|
||||
}
|
||||
setResult.addResult(roomID, protectResult);
|
||||
}
|
||||
if (setResult.getResult().map.size === 0) {
|
||||
return;
|
||||
} else {
|
||||
void Task(
|
||||
sendMatrixEventsFromDeadDocument(
|
||||
this.roomMessageSender,
|
||||
this.managementRoomID,
|
||||
<root>
|
||||
{renderRoomSetResult(setResult.getResult(), {
|
||||
summary: <p>Protecting new rooms.</p>,
|
||||
})}
|
||||
</root>,
|
||||
{}
|
||||
),
|
||||
{
|
||||
description: "Report newly protected rooms to the managmeent room.",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import {
|
||||
AbstractProtection,
|
||||
describeProtection,
|
||||
MembershipChange,
|
||||
MembershipEvent,
|
||||
ProtectedRoomsSet,
|
||||
ProtectionDescription,
|
||||
RoomMembershipRevision,
|
||||
UnknownConfig,
|
||||
} from "matrix-protection-suite";
|
||||
import { DraupnirProtection } from "../Protection";
|
||||
import { Draupnir } from "../../Draupnir";
|
||||
import { Ok, Result } from "@gnuxie/typescript-result";
|
||||
import { ProtectedJoinedRooms } from "./ProtectJoinedRooms";
|
||||
import { UnprotectPartedRooms } from "./UnprotectPartedRooms";
|
||||
import { StringRoomID } from "@the-draupnir-project/matrix-basic-types";
|
||||
|
||||
export type RoomsSetBehaviourCapabailities = Record<string, never>;
|
||||
export type RoomsSetBehaviourSettings = UnknownConfig;
|
||||
|
||||
export type RoomsSetBehaviourDescription = ProtectionDescription<
|
||||
Draupnir,
|
||||
RoomsSetBehaviourSettings,
|
||||
RoomsSetBehaviourCapabailities
|
||||
>;
|
||||
|
||||
export class RoomsSetBehaviour
|
||||
extends AbstractProtection<RoomsSetBehaviourDescription>
|
||||
implements DraupnirProtection<RoomsSetBehaviourDescription>
|
||||
{
|
||||
private readonly protectJoinedRooms = new ProtectedJoinedRooms(
|
||||
this.draupnir.clientUserID,
|
||||
this.draupnir.managementRoomID,
|
||||
this.protectedRoomsSet,
|
||||
this.draupnir.clientRooms,
|
||||
this.draupnir.clientPlatform.toRoomMessageSender()
|
||||
);
|
||||
private readonly unprotectedPartedRooms = new UnprotectPartedRooms(
|
||||
this.draupnir.clientUserID,
|
||||
this.draupnir.managementRoomID,
|
||||
this.protectedRoomsSet.protectedRoomsManager,
|
||||
this.draupnir.clientPlatform.toRoomMessageSender()
|
||||
);
|
||||
public constructor(
|
||||
description: RoomsSetBehaviourDescription,
|
||||
capabilities: RoomsSetBehaviourCapabailities,
|
||||
protectedRoomsSet: ProtectedRoomsSet,
|
||||
private readonly draupnir: Draupnir
|
||||
) {
|
||||
super(description, capabilities, protectedRoomsSet, {});
|
||||
if (this.draupnir.config.protectAllJoinedRooms) {
|
||||
void this.protectJoinedRooms.syncProtectedRooms();
|
||||
}
|
||||
}
|
||||
|
||||
public handleMembershipChange(
|
||||
_revision: RoomMembershipRevision,
|
||||
changes: MembershipChange[]
|
||||
): Promise<Result<void>> {
|
||||
if (this.draupnir.config.protectAllJoinedRooms) {
|
||||
this.protectJoinedRooms.handleMembershipChange(changes);
|
||||
}
|
||||
for (const change of changes) {
|
||||
this.unprotectedPartedRooms.handleMembershipChange(change);
|
||||
}
|
||||
return Promise.resolve(Ok(undefined));
|
||||
}
|
||||
|
||||
public handleExternalMembership(
|
||||
roomID: StringRoomID,
|
||||
event: MembershipEvent
|
||||
): void {
|
||||
if (this.draupnir.config.protectAllJoinedRooms) {
|
||||
this.protectJoinedRooms.handleExternalMembership(roomID, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describeProtection<RoomsSetBehaviourCapabailities, Draupnir>({
|
||||
name: RoomsSetBehaviour.name,
|
||||
description:
|
||||
"Unprotects parted rooms and update the list of protected rooms.",
|
||||
capabilityInterfaces: {},
|
||||
defaultCapabilities: {},
|
||||
factory(description, protectedRoomsSet, draupnir, capabilities, _settings) {
|
||||
return Ok(
|
||||
new RoomsSetBehaviour(
|
||||
description,
|
||||
capabilities,
|
||||
protectedRoomsSet,
|
||||
draupnir
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import {
|
||||
MatrixRoomReference,
|
||||
StringRoomID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
isOk,
|
||||
MembershipChange,
|
||||
MembershipChangeType,
|
||||
ProtectedRoomsManager,
|
||||
RoomMessageSender,
|
||||
Task,
|
||||
} from "matrix-protection-suite";
|
||||
import { sendMatrixEventsFromDeadDocument } from "../../commands/interface-manager/MPSMatrixInterfaceAdaptor";
|
||||
import {
|
||||
renderMentionPill,
|
||||
renderRoomPill,
|
||||
} from "../../commands/interface-manager/MatrixHelpRenderer";
|
||||
import { DeadDocumentJSX } from "@the-draupnir-project/interface-manager";
|
||||
|
||||
export class UnprotectPartedRooms {
|
||||
constructor(
|
||||
private readonly clientUserID: StringUserID,
|
||||
private readonly managementRoomID: StringRoomID,
|
||||
private readonly protectedRoomsManager: ProtectedRoomsManager,
|
||||
private readonly messageSender: RoomMessageSender
|
||||
) {
|
||||
// nothing to do.
|
||||
}
|
||||
|
||||
public async handlePartedRoom(change: MembershipChange): Promise<void> {
|
||||
const room = MatrixRoomReference.fromRoomID(change.roomID);
|
||||
const unprotectResult = await this.protectedRoomsManager.removeRoom(room);
|
||||
const removalDescription = (
|
||||
<fragment>
|
||||
Draupnir has been removed from {renderRoomPill(room)} by{" "}
|
||||
{renderMentionPill(change.sender, change.sender)}
|
||||
{change.content.reason ? (
|
||||
<fragment>
|
||||
{" "}
|
||||
for reason: <code>{change.content.reason}</code>
|
||||
</fragment>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
.
|
||||
</fragment>
|
||||
);
|
||||
if (isOk(unprotectResult)) {
|
||||
void Task(
|
||||
sendMatrixEventsFromDeadDocument(
|
||||
this.messageSender,
|
||||
this.managementRoomID,
|
||||
<root>{removalDescription} The room is now unprotected.</root>,
|
||||
{}
|
||||
),
|
||||
{
|
||||
description:
|
||||
"Report recently unprotected rooms to the management room.",
|
||||
}
|
||||
);
|
||||
} else {
|
||||
void Task(
|
||||
sendMatrixEventsFromDeadDocument(
|
||||
this.messageSender,
|
||||
this.managementRoomID,
|
||||
<root>
|
||||
{removalDescription} Draupnir could not unprotect the room. Please
|
||||
use <code>!draupnir rooms remove {room.toRoomIDOrAlias()}</code> if
|
||||
the room is still marked as protected.
|
||||
</root>,
|
||||
{}
|
||||
),
|
||||
{
|
||||
description:
|
||||
"Report recently unprotected rooms to the management room.",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public handleMembershipChange(change: MembershipChange): void {
|
||||
if (change.userID === this.clientUserID) {
|
||||
if (!this.protectedRoomsManager.isProtectedRoom(change.roomID)) {
|
||||
return;
|
||||
}
|
||||
switch (change.membershipChangeType) {
|
||||
case MembershipChangeType.Banned:
|
||||
case MembershipChangeType.Kicked:
|
||||
case MembershipChangeType.Left:
|
||||
void this.handlePartedRoom(change);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export class JoinRoomsOnInviteProtection
|
||||
this.watchRoomsOnInvite.handleProtectionDisable();
|
||||
}
|
||||
|
||||
handleExternalInvite(roomID: StringRoomID, event: MembershipEvent): void {
|
||||
handleExternalMembership(roomID: StringRoomID, event: MembershipEvent): void {
|
||||
if (!isInvitationForUser(event, this.protectedRoomsSet.userID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
makeBotModeToggle,
|
||||
teardownManagementRoom,
|
||||
} from "./mjolnirSetupUtils";
|
||||
import { MatrixRoomReference } from "@the-draupnir-project/matrix-basic-types";
|
||||
|
||||
patchMatrixClient();
|
||||
|
||||
@@ -38,10 +37,6 @@ export const mochaHooks = {
|
||||
JSON.stringify(this.currentTest?.title)
|
||||
); // Makes MatrixClient error logs a bit easier to parse.
|
||||
console.log("mochaHooks.beforeEach");
|
||||
const test = MatrixRoomReference.fromPermalink(
|
||||
"https://matrix.to/#/!JzRjamSLPHAikHkPab%3Alocalhost%3A9999?via=localhost:9999"
|
||||
);
|
||||
console.log(test);
|
||||
// Sometimes it takes a little longer to register users.
|
||||
this.timeout(30000);
|
||||
const config = (this.config = configRead());
|
||||
|
||||
@@ -21,6 +21,7 @@ import { IConfig } from "../../src/config";
|
||||
import { Draupnir } from "../../src/Draupnir";
|
||||
import { DraupnirBotModeToggle } from "../../src/DraupnirBotMode";
|
||||
import {
|
||||
MatrixSendClient,
|
||||
SafeMatrixEmitter,
|
||||
SafeMatrixEmitterWrapper,
|
||||
} from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
@@ -152,6 +153,7 @@ export async function makeBotModeToggle(
|
||||
client.setAccountData(MJOLNIR_WATCHED_POLICY_ROOMS_EVENT_TYPE, {
|
||||
references: [],
|
||||
}),
|
||||
leaveAllRooms(client),
|
||||
]);
|
||||
}
|
||||
await overrideRatelimitForUser(
|
||||
@@ -197,3 +199,10 @@ export async function teardownManagementRoom(
|
||||
await client.deleteRoomAlias(alias);
|
||||
await client.leaveRoom(roomId);
|
||||
}
|
||||
|
||||
export async function leaveAllRooms(client: MatrixSendClient): Promise<void> {
|
||||
const joinedRooms = await client.getJoinedRooms();
|
||||
await Promise.allSettled(
|
||||
joinedRooms.map((roomID) => client.leaveRoom(roomID))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import {
|
||||
MatrixRoomReference,
|
||||
StringRoomID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import { newTestUser } from "../clientHelper";
|
||||
import { DraupnirTestContext } from "../mjolnirSetupUtils";
|
||||
import expect from "expect";
|
||||
import { MatrixSendClient } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { Draupnir } from "../../../src/Draupnir";
|
||||
|
||||
async function setupProtectedRooms(
|
||||
draupnir: Draupnir,
|
||||
moderator: MatrixSendClient,
|
||||
{ numberOfRooms }: { numberOfRooms: number }
|
||||
): Promise<StringRoomID[]> {
|
||||
await moderator.joinRoom(draupnir.managementRoomID);
|
||||
return await Promise.all(
|
||||
[...Array(numberOfRooms)].map(async (_) => {
|
||||
const room = await moderator.createRoom({
|
||||
invite: [draupnir.clientUserID],
|
||||
});
|
||||
await moderator.setUserPowerLevel(draupnir.clientUserID, room, 90);
|
||||
return room as StringRoomID;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
describe("JoinRoomsOnInvite", function () {
|
||||
it(
|
||||
"Should automatically protect and unrpotect rooms when joining and leaving.\
|
||||
The principle is that we add a bunch of rooms, and then kick the bot.\
|
||||
You can go to the management room in a client and see what the output looks like for this flow.",
|
||||
async function (this: DraupnirTestContext) {
|
||||
const draupnir = this.draupnir;
|
||||
if (draupnir === undefined) {
|
||||
throw new TypeError(`setup didn't run properly`);
|
||||
}
|
||||
const moderator = await newTestUser(this.config.homeserverUrl, {
|
||||
name: { contains: "moderator" },
|
||||
});
|
||||
// Mutate the config which is a little naughty, but protections
|
||||
// currently access it dynamically.
|
||||
draupnir.config.protectAllJoinedRooms = true;
|
||||
const protectedRooms = await setupProtectedRooms(draupnir, moderator, {
|
||||
numberOfRooms: 5,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
expect(
|
||||
protectedRooms.every((roomID: StringRoomID) =>
|
||||
draupnir.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
)
|
||||
).toBe(true);
|
||||
// now test that kicking them works
|
||||
await Promise.all(
|
||||
protectedRooms.map((roomID, i) =>
|
||||
moderator.kickUser(
|
||||
draupnir.clientUserID,
|
||||
roomID,
|
||||
i === 0 ? "don't want this bot" : undefined
|
||||
)
|
||||
)
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
expect(
|
||||
protectedRooms.every(
|
||||
(roomID: StringRoomID) =>
|
||||
!draupnir.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
)
|
||||
).toBe(true);
|
||||
// allow for messages to send to the mangement room.
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
} as unknown as Mocha.AsyncFunc
|
||||
);
|
||||
it(
|
||||
"That rooms will automatically be unprotected when protectAllJoinedRooms is false",
|
||||
async function (this: DraupnirTestContext) {
|
||||
const draupnir = this.draupnir;
|
||||
if (draupnir === undefined) {
|
||||
throw new TypeError(`setup didn't run properly`);
|
||||
}
|
||||
const moderator = await newTestUser(this.config.homeserverUrl, {
|
||||
name: { contains: "moderator" },
|
||||
});
|
||||
// Mutate the config which is a little naughty, but protections
|
||||
// currently access it dynamically.
|
||||
draupnir.config.protectAllJoinedRooms = false;
|
||||
const protectedRooms = await setupProtectedRooms(draupnir, moderator, {
|
||||
numberOfRooms: 5,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
// we shouldn't be protecting rooms automatically
|
||||
expect(
|
||||
protectedRooms.every(
|
||||
(roomID: StringRoomID) =>
|
||||
!draupnir.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
)
|
||||
).toBe(true);
|
||||
// protect the rooms manually
|
||||
await Promise.all(
|
||||
protectedRooms.map((roomID) =>
|
||||
draupnir.protectedRoomsSet.protectedRoomsManager.addRoom(
|
||||
MatrixRoomReference.fromRoomID(roomID)
|
||||
)
|
||||
)
|
||||
);
|
||||
expect(
|
||||
protectedRooms.every((roomID: StringRoomID) =>
|
||||
draupnir.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
)
|
||||
).toBe(true);
|
||||
// now test that banning them works
|
||||
await Promise.all(
|
||||
protectedRooms.map((roomID, i) =>
|
||||
// I would have liked this to be ban, but for some reason bot-sdk
|
||||
// doesn't allow informing of rooms you are banned from!!!
|
||||
moderator.kickUser(
|
||||
draupnir.clientUserID,
|
||||
roomID,
|
||||
i === 0 ? "don't want this bot" : undefined
|
||||
)
|
||||
)
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
expect(
|
||||
protectedRooms.every(
|
||||
(roomID: StringRoomID) =>
|
||||
!draupnir.protectedRoomsSet.isProtectedRoom(roomID)
|
||||
)
|
||||
).toBe(true);
|
||||
// allow for messages to send to the mangement room.
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
} as unknown as Mocha.AsyncFunc
|
||||
);
|
||||
});
|
||||
@@ -2593,18 +2593,18 @@ matrix-appservice@^2.0.0:
|
||||
request-promise "^4.2.6"
|
||||
sanitize-html "^2.11.0"
|
||||
|
||||
"matrix-protection-suite-for-matrix-bot-sdk@npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@gnuxie/matrix-protection-suite-for-matrix-bot-sdk/-/matrix-protection-suite-for-matrix-bot-sdk-2.6.0.tgz#dd1ffbf5ea1cbeba891248d989701d9ba268d074"
|
||||
integrity sha512-JH3FMJddRGSA5SKbnlT9ZRPJ8+/6VWWeAa9hGv+FHBn2FeyfndEKWChoFE8s6s1c06OJvjLg5o/efuVhZzKyEw==
|
||||
"matrix-protection-suite-for-matrix-bot-sdk@npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@2.7.0":
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@gnuxie/matrix-protection-suite-for-matrix-bot-sdk/-/matrix-protection-suite-for-matrix-bot-sdk-2.7.0.tgz#82e3b053033d635ecdceab034c5548640bbe2332"
|
||||
integrity sha512-Om+2CSojGJFqGnC9S7W9OXfccmI288nH93DACESRBjrlqVUO6mkdNSk789s1UTW5LIqZ3bjiC72F3STDASTdfg==
|
||||
dependencies:
|
||||
"@gnuxie/typescript-result" "^1.0.0"
|
||||
await-lock "^2.2.2"
|
||||
|
||||
"matrix-protection-suite@npm:@gnuxie/matrix-protection-suite@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@gnuxie/matrix-protection-suite/-/matrix-protection-suite-2.6.0.tgz#ce3dfecb72f5215467b74ed55f388043bbb2365f"
|
||||
integrity sha512-MQxsFZQ7Xyx8GdpEcr4Pxg0XHA7Apn1aXPnTVM6FiEKTsWOyS0uYxuxfM0Q4kfYf5mVCdvCHB/xNJGqn6IV06A==
|
||||
"matrix-protection-suite@npm:@gnuxie/matrix-protection-suite@2.7.0":
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@gnuxie/matrix-protection-suite/-/matrix-protection-suite-2.7.0.tgz#953f89e3b02fd9b9edb21f0901f1ed22a2f41f48"
|
||||
integrity sha512-ZTbz5t1Gh0Yxx+9+7EUp51HfBaccTazSpLKNPQDmjgjoVo6PT3da0CfTvGJR11EqLlBMey/D0zV418ajxjixrw==
|
||||
dependencies:
|
||||
"@gnuxie/typescript-result" "^1.0.0"
|
||||
await-lock "^2.2.2"
|
||||
|
||||
Reference in New Issue
Block a user