"use strict";
// Copyright 2022 Gnuxie <Gnuxie@protonmail.com>
// Copyright 2020 Emi Tatsuo Simpson et al.
//
// SPDX-License-Identifier: Apache-2.0
//
// SPDX-FileAttributionText: <text>
// This modified file incorporates work from mjolnir
// https://github.com/matrix-org/mjolnir
// </text>
Object.defineProperty(exports, "__esModule", { value: true });
exports.WordListProtection = void 0;
const matrix_protection_suite_1 = require("matrix-protection-suite");
const matrix_protection_suite_for_matrix_bot_sdk_1 = require("matrix-protection-suite-for-matrix-bot-sdk");
const log = new matrix_protection_suite_1.Logger("WordList");
(0, matrix_protection_suite_1.describeProtection)({
    name: "WordListProtection",
    description: "If a user posts a monitored word a set amount of time after joining, they\
    will be banned from that room.  This will not publish the ban to a ban list.\
    This protection only targets recently joined users.",
    capabilityInterfaces: {
        userConsequences: "UserConsequences",
        eventConsequences: "EventConsequences",
    },
    defaultCapabilities: {
        userConsequences: "StandardUserConsequences",
        eventConsequences: "StandardEventConsequences",
    },
    factory: async function (description, lifetime, protectedRoomsSet, draupnir, capabilities, _settings) {
        return (0, matrix_protection_suite_1.Ok)(new WordListProtection(description, lifetime, capabilities, protectedRoomsSet, draupnir));
    },
});
class WordListProtection extends matrix_protection_suite_1.AbstractProtection {
    draupnir;
    justJoined = new Map();
    badWords;
    userConsequences;
    eventConsequences;
    constructor(description, lifetime, capabilities, protectedRoomsSet, draupnir) {
        super(description, lifetime, capabilities, protectedRoomsSet, {});
        this.draupnir = draupnir;
        this.userConsequences = capabilities.userConsequences;
        this.eventConsequences = capabilities.eventConsequences;
        // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
        const escapeRegExp = (string) => {
            return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
        };
        // Create a mega-regex from all the tiny words.
        const words = this.draupnir.config.protections.wordlist.words
            .filter((word) => word.length !== 0)
            .map(escapeRegExp);
        this.badWords = new RegExp(words.join("|"), "i");
    }
    async handleMembershipChange(revision, changes) {
        const roomID = revision.room.toRoomIDOrAlias();
        const minsBeforeTrusting = this.draupnir.config.protections.wordlist.minutesBeforeTrusting;
        if (minsBeforeTrusting > 0) {
            for (const change of changes) {
                const entryForRoom = this.justJoined.get(roomID) ??
                    ((entry) => (this.justJoined.set(roomID, entry), entry))(new Map());
                // When a new member logs in, store the time they joined.  This will be useful
                // when we need to check if a message was sent within 20 minutes of joining
                if (change.membershipChangeType === matrix_protection_suite_1.MembershipChangeType.Joined) {
                    const now = new Date();
                    entryForRoom.set(change.userID, now);
                    log.debug(`${change.userID} joined ${roomID} at ${now.toDateString()}`);
                }
                else if (change.membershipChangeType === matrix_protection_suite_1.MembershipChangeType.Left ||
                    change.membershipChangeType === matrix_protection_suite_1.MembershipChangeType.Banned ||
                    change.membershipChangeType === matrix_protection_suite_1.MembershipChangeType.Kicked) {
                    entryForRoom.delete(change.userID);
                }
            }
        }
        return (0, matrix_protection_suite_1.Ok)(undefined);
    }
    handleTimelineEventMixins(room, event) {
        // If the sender is draupnir, ignore the message
        if (event.sourceEvent["sender"] === this.draupnir.clientUserID) {
            log.debug(`Ignoring message from self: ${event.sourceEvent.event_id}`);
            return;
        }
        const bodies = [
            ((mixin) => (mixin?.isErroneous === true ? undefined : mixin?.body))(event.findMixin(matrix_protection_suite_1.RoomMessageBodyMixinDescription)),
            ((mixin) => (mixin?.isErroneous ? undefined : mixin?.formatted_body))(event.findMixin(matrix_protection_suite_1.RoomMessageFormattedBodyMixinDescription)),
            ...(((mixin) => mixin?.isErroneous
                ? undefined
                : mixin?.representations.map((representation) => representation.body))(event.findMixin(matrix_protection_suite_1.ExtensibleTextMixinDescription)) ?? []),
        ].filter((mixin) => mixin !== undefined);
        if (bodies.length === 0) {
            return;
        }
        const minsBeforeTrusting = this.draupnir.config.protections.wordlist.minutesBeforeTrusting;
        const roomID = room.toRoomIDOrAlias();
        // Check conditions first
        if (minsBeforeTrusting > 0) {
            const roomEntry = this.justJoined.get(roomID);
            const joinTime = roomEntry?.get(event.sourceEvent["sender"]);
            if (joinTime !== undefined) {
                // Disregard if the user isn't recently joined
                // Check if they did join recently, was it within the timeframe
                const now = new Date();
                if (now.valueOf() - joinTime.valueOf() >
                    minsBeforeTrusting * 60 * 1000) {
                    roomEntry?.delete(event.sourceEvent["sender"]); // Remove the user
                    log.info(`${event.sourceEvent["sender"]} is no longer considered suspect`);
                    return;
                }
            }
            else {
                // The user isn't in the recently joined users list, no need to keep
                // looking
                return;
            }
        }
        const match = bodies.find((body) => this.badWords.exec(body));
        if (!match) {
            return;
        }
        const reason = `Said a bad word. Moderators, consult the management room for more information.`;
        void (0, matrix_protection_suite_1.Task)((async () => {
            await this.userConsequences.consequenceForUserInRoom(roomID, event.sourceEvent.sender, reason);
            const messageResult = await this.draupnir.client
                .sendMessage(this.draupnir.managementRoomID, {
                msgtype: "m.notice",
                body: `Banned ${event.sourceEvent.sender} in ${roomID} for saying '${match[0]}'.`,
            })
                .then((_) => (0, matrix_protection_suite_1.Ok)(undefined), matrix_protection_suite_for_matrix_bot_sdk_1.resultifyBotSDKRequestError);
            if ((0, matrix_protection_suite_1.isError)(messageResult)) {
                log.error(`Failed to send a message to the management room after banning ${event.sourceEvent.sender} in ${roomID} for saying '${match[0]}'.`, messageResult.error);
            }
            await this.eventConsequences.consequenceForEvent(roomID, event.sourceEvent.event_id, reason);
        })(), {
            log,
        });
    }
}
exports.WordListProtection = WordListProtection;
//# sourceMappingURL=WordList.js.map