Skip to content

Commit

Permalink
Add Experimental MentionLimitProtection. (#518)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gnuxie authored Aug 24, 2024
1 parent 74ec70d commit 7150b21
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export interface IConfig {
serverNames: string[];
banMessage: string;
};
mentionLimitProtection: {
maxMentions: number;
redactReason: string;
};
};
health: {
healthz: {
Expand Down Expand Up @@ -178,6 +182,11 @@ const defaultConfig: IConfig = {
banMessage:
"Unfortunately we cannot accept new users from your homeserver at this time.",
},
mentionLimitProtection: {
maxMentions: 3,
redactReason:
"You have mentioned too many users in this message, so we have had to redact it.",
},
},
health: {
healthz: {
Expand Down
1 change: 1 addition & 0 deletions src/protections/DraupnirProtectionsIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import "./BasicFlooding";
import "./FirstMessageIsImage";
import "./JoinWaveShortCircuit";
import "./RedactionSynchronisation";
import "./MentionLimitProtection";
import "./MessageIsMedia";
import "./MessageIsVoice";
import "./NewJoinerProtection";
Expand Down
113 changes: 113 additions & 0 deletions src/protections/MentionLimitProtection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2024 Gnuxie <[email protected]>
//
// SPDX-License-Identifier: AFL-3.0

import {
AbstractProtection,
ActionResult,
EventConsequences,
Ok,
ProtectedRoomsSet,
Protection,
ProtectionDescription,
RoomEvent,
UnknownSettings,
Value,
describeProtection,
} from "matrix-protection-suite";
import { Draupnir } from "../Draupnir";
import { IConfig } from "../config";
import { MatrixRoomID } from "@the-draupnir-project/matrix-basic-types";
import { Type } from "@sinclair/typebox";

const MentionsContentSchema = Type.Object({
"m.mentions": Type.Object({
user_ids: Type.Array(Type.String()),
}),
});

const NewContentMentionsSchema = Type.Object({
"m.new_content": MentionsContentSchema,
});

export type MentionLimitProtectionDescription = ProtectionDescription<
unknown,
UnknownSettings<string>,
MentionLimitProtectionCapabilities
>;

export class MentionLimitProtection
extends AbstractProtection<MentionLimitProtectionDescription>
implements Protection<MentionLimitProtectionDescription>
{
private readonly eventConsequences: EventConsequences;
private readonly redactReason: string;
private readonly maxMentions: number;
constructor(
description: MentionLimitProtectionDescription,
capabilities: MentionLimitProtectionCapabilities,
protectedRoomsSet: ProtectedRoomsSet,
draupnirConfig: IConfig
) {
super(description, capabilities, protectedRoomsSet, {});
this.eventConsequences = capabilities.eventConsequences;
this.maxMentions =
draupnirConfig.protections.mentionLimitProtection.maxMentions;
this.redactReason =
draupnirConfig.protections.mentionLimitProtection.redactReason;
}
public async handleTimelineEvent(
_room: MatrixRoomID,
event: RoomEvent
): Promise<ActionResult<void>> {
const isOverLimit = (user_ids: string[]): boolean =>
user_ids.length > this.maxMentions;
if (
Value.Check(NewContentMentionsSchema, event.content) &&
isOverLimit(event.content["m.new_content"]["m.mentions"].user_ids)
) {
return await this.eventConsequences.consequenceForEvent(
event.room_id,
event.event_id,
this.redactReason
);
} else if (
Value.Check(MentionsContentSchema, event.content) &&
isOverLimit(event.content["m.mentions"].user_ids)
) {
return await this.eventConsequences.consequenceForEvent(
event.room_id,
event.event_id,
this.redactReason
);
} else {
return Ok(undefined);
}
}
}

export type MentionLimitProtectionCapabilities = {
eventConsequences: EventConsequences;
};

describeProtection<MentionLimitProtectionCapabilities, Draupnir>({
name: "MentionLimitProtection",
description: `Highly experimental protection that will remove any messages with
a number of mentions over a preconfigured limit.
Please read the documentation https://the-draupnir-project.github.io/draupnir-documentation/protections/mention-limit-protection.`,
capabilityInterfaces: {
eventConsequences: "EventConsequences",
},
defaultCapabilities: {
eventConsequences: "StandardEventConsequences",
},
factory: (decription, protectedRoomsSet, draupnir, capabilitySet) =>
Ok(
new MentionLimitProtection(
decription,
capabilitySet,
protectedRoomsSet,
draupnir.config
)
),
});

0 comments on commit 7150b21

Please sign in to comment.