"use strict";
/**
 * Synchronises Matrix `m.policy.rule` events with the bridge to filter specific
 * users from using the service.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.MatrixBanSync = void 0;
const matrix_appservice_bridge_1 = require("matrix-appservice-bridge");
const matrix_bot_sdk_1 = require("matrix-bot-sdk");
const logging_1 = require("../logging");
const log = (0, logging_1.getLogger)("MatrixBanSync");
var BanEntityType;
(function (BanEntityType) {
    BanEntityType["Server"] = "m.policy.rule.server";
    BanEntityType["User"] = "m.policy.rule.user";
})(BanEntityType || (BanEntityType = {}));
function eventTypeToBanEntityType(eventType) {
    // `m.room.*` variants were in use until https://github.com/matrix-org/mjolnir/pull/336 by Mjolnir. For compatibilty we accept it.
    switch (eventType) {
        case "m.policy.rule.user":
        case "m.room.rule.user":
        case "org.matrix.mjolnir.rule.user":
            return BanEntityType.User;
        case "m.policy.rule.server":
        case "m.room.rule.server":
        case "org.matrix.mjolnir.rule.server":
            return BanEntityType.Server;
        default:
            return null;
    }
}
const supportedRecommendations = [
    "org.matrix.mjolnir.ban", // Used historically.
    "m.ban"
];
class MatrixBanSync {
    config;
    bannedEntites = new Map();
    subscribedRooms = new Set();
    constructor(config) {
        this.config = config;
    }
    async syncRules(intent) {
        this.bannedEntites.clear();
        this.subscribedRooms.clear();
        for (const roomIdOrAlias of this.config.rooms) {
            try {
                const roomId = await intent.join(roomIdOrAlias);
                this.subscribedRooms.add(roomId);
                const roomState = await intent.roomState(roomId, false);
                for (const evt of roomState) {
                    this.handleIncomingState(evt, roomId);
                }
            }
            catch (ex) {
                log.error(`Failed to read ban list from ${roomIdOrAlias}`, ex);
            }
        }
    }
    /**
     * Is the given room considered part of the bridge's ban list set.
     * @param roomId A Matrix room ID.
     * @returns true if state should be handled from the room, false otherwise.
     */
    isTrackingRoomState(roomId) {
        return this.subscribedRooms.has(roomId);
    }
    /**
     * Checks to see if the incoming state is a recommendation entry.
     * @param evt A Matrix state event. Unknown state events will be filtered out.
     * @param roomId The Matrix roomID where the event came from.
     * @returns `true` if the event was a new ban, and existing clients should be checked. `false` otherwise.
     */
    handleIncomingState(evt, roomId) {
        const content = evt.content;
        const entityType = eventTypeToBanEntityType(evt.type);
        if (!entityType) {
            return false;
        }
        const key = `${roomId}:${evt.state_key}`;
        if (evt.content.entity === undefined) {
            // Empty, delete instead.
            log.info(`Deleted ban rule ${evt.type}/$ matching ${key}`);
            this.bannedEntites.delete(key);
            return false;
        }
        if (!supportedRecommendations.includes(content.recommendation)) {
            return false;
        }
        if (typeof content.entity !== "string" || content.entity === "") {
            throw Error('`entity` key is not valid, must be a non-empty string');
        }
        this.bannedEntites.set(key, {
            matcher: new matrix_bot_sdk_1.MatrixGlob(content.entity),
            entityType,
            reason: content.reason || "No reason given",
        });
        log.info(`New ban rule ${evt.type} matching ${content.entity}`);
        return true;
    }
    /**
     * Check if a user is banned by via a ban list.
     * @param user A userId string or a MatrixUser object.
     * @returns Either a string reason for the ban, or false if the user was not banned.
     */
    isUserBanned(user) {
        const matrixUser = typeof user === "string" ? new matrix_appservice_bridge_1.MatrixUser(user) : user;
        for (const entry of this.bannedEntites.values()) {
            if (entry.entityType === BanEntityType.Server && entry.matcher.test(matrixUser.host)) {
                return entry.reason;
            }
            if (entry.entityType === BanEntityType.User && entry.matcher.test(matrixUser.userId)) {
                return entry.reason;
            }
        }
        return false;
    }
    /**
     * Should be called when the bridge config has been updated.
     * @param config The new config.
     * @param intent The bot user intent.
     */
    async updateConfig(config, intent) {
        this.config = config;
        await this.syncRules(intent);
    }
}
exports.MatrixBanSync = MatrixBanSync;
//# sourceMappingURL=MatrixBanSync.js.map