"use strict";
// SPDX-FileCopyrightText: 2024 - 2025 Gnuxie <Gnuxie@protonmail.com>
//
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedactionSynchronisationProtection = exports.StandardRedactionSynchronisation = void 0;
const matrix_protection_suite_1 = require("matrix-protection-suite");
const matrix_basic_types_1 = require("@the-draupnir-project/matrix-basic-types");
const typescript_result_1 = require("@gnuxie/typescript-result");
const typebox_1 = require("@sinclair/typebox");
const UnbanUsers_1 = require("../commands/unban/UnbanUsers");
(0, matrix_protection_suite_1.describeCapabilityInterface)({
    name: "RedactionSynchronisationConsequences",
    description: "Consequences for the RedactionSynchronisationProtection",
    schema: typebox_1.Type.Object({
        redactMessagesIn: matrix_protection_suite_1.CapabilityMethodSchema,
    }),
});
(0, matrix_protection_suite_1.describeCapabilityProvider)({
    name: "StandardRedactionSynchronisationConsequences",
    interface: "RedactionSynchronisationConsequences",
    description: "redacts events and rejects invitations send by the target",
    factory(description, draupnir) {
        return Object.freeze({
            requiredPermissions: [
                matrix_protection_suite_1.PowerLevelPermission.Redact,
                matrix_protection_suite_1.PowerLevelPermission.Kick,
            ],
            requiredStatePermissions: [],
            requiredEventPermissions: [],
            async redactMessagesIn(userIDOrGlob, reason, roomIDs) {
                const redactionResults = await Promise.all(roomIDs.map((roomID) => draupnir.timelineRedactionQueue.enqueueRedaction(userIDOrGlob, roomID)));
                const firstError = redactionResults.find((result) => (0, typescript_result_1.isError)(result));
                if (firstError) {
                    return firstError;
                }
                else {
                    return (0, matrix_protection_suite_1.Ok)(undefined);
                }
            },
            async rejectInvite(roomID, _sender, target, reason) {
                return await draupnir.clientPlatform
                    .toRoomKicker()
                    .kickUser(roomID, target, reason);
            },
        });
    },
});
// FIXME: We really need capability rendering to be configurable.
(0, matrix_protection_suite_1.describeCapabilityProvider)({
    name: "SimulatedRedactionSynchronisationConsequences",
    interface: "RedactionSynchronisationConsequences",
    description: "Simulated redaction consequences",
    factory(_description, _context) {
        return Object.freeze({
            requiredPermissions: [],
            requiredStatePermissions: [],
            requiredEventPermissions: [],
            isSimulated: true,
            async redactMessagesIn() {
                return (0, matrix_protection_suite_1.Ok)(undefined);
            },
            async rejectInvite() {
                return (0, matrix_protection_suite_1.Ok)(undefined);
            },
        });
    },
});
(0, matrix_protection_suite_1.describeCapabilityRenderer)({
    name: "StandardRedactionSynchronisationConsequencesRenderer",
    interface: "RedactionSynchronisationConsequences",
    description: "Doesn't render anything tbh, because it would be too annoying",
    isDefaultForInterface: true,
    factory(_protectionDescription, _context, provider) {
        return Object.freeze({
            ...(provider.isSimulated ? { isSimulated: true } : {}),
            requiredPermissions: provider.requiredPermissions,
            requiredStatePermissions: provider.requiredStatePermissions,
            requiredEventPermissions: provider.requiredEventPermissions,
            async redactMessagesIn(userIDOrGlob, reason, roomIDs) {
                return await provider.redactMessagesIn(userIDOrGlob, reason, roomIDs);
            },
            async rejectInvite(roomID, sender, reciever, reason) {
                return await provider.rejectInvite(roomID, sender, reciever, reason);
            },
        });
    },
});
class StandardRedactionSynchronisation {
    automaticRedactionReasons;
    consequences;
    watchedPolicyRooms;
    setRoomMembership;
    setPoliciesMatchingMembership;
    constructor(automaticRedactionReasons, consequences, watchedPolicyRooms, setRoomMembership, setPoliciesMatchingMembership) {
        this.automaticRedactionReasons = automaticRedactionReasons;
        this.consequences = consequences;
        this.watchedPolicyRooms = watchedPolicyRooms;
        this.setRoomMembership = setRoomMembership;
        this.setPoliciesMatchingMembership = setPoliciesMatchingMembership;
        // nothing to do.
    }
    handlePermissionRequirementsMet(roomID) {
        const membershipRevision = this.setRoomMembership.getRevision(roomID);
        if (membershipRevision !== undefined) {
            this.checkRoomInvitations(membershipRevision);
        }
        for (const match of this.setPoliciesMatchingMembership.currentRevision.allMembersWithRules()) {
            if (membershipRevision?.membershipForUser(match.userID)) {
                const policyRequiringRedaction = match.policies.find((policy) => this.isPolicyRequiringRedaction(policy));
                if (policyRequiringRedaction !== undefined) {
                    void (0, matrix_protection_suite_1.Task)(this.consequences.redactMessagesIn(match.userID, policyRequiringRedaction.reason, [roomID]));
                }
            }
        }
    }
    handlePolicyChange(change) {
        const policy = change.rule;
        if (change.changeType === matrix_protection_suite_1.PolicyRuleChangeType.Removed) {
            return;
        }
        if (policy.kind !== matrix_protection_suite_1.PolicyRuleType.User) {
            return;
        }
        if (policy.matchType === matrix_protection_suite_1.PolicyRuleMatchType.HashedLiteral) {
            return;
        }
        if (!this.isPolicyRequiringRedaction(policy)) {
            return;
        }
        const roomsRequiringRedaction = policy.matchType === matrix_protection_suite_1.PolicyRuleMatchType.Literal
            ? this.setRoomMembership.allRooms.filter((revision) => revision.membershipForUser((0, matrix_basic_types_1.StringUserID)(policy.entity)))
            : this.setRoomMembership.allRooms;
        void (0, matrix_protection_suite_1.Task)(this.consequences.redactMessagesIn((0, matrix_basic_types_1.StringUserID)(policy.entity), policy.reason, roomsRequiringRedaction.map((revision) => revision.room.toRoomIDOrAlias())));
        for (const revision of roomsRequiringRedaction) {
            this.checkRoomInvitations(revision);
        }
    }
    handleMembershipChange(change) {
        if (change.membershipChangeType === matrix_protection_suite_1.MembershipChangeType.Banned &&
            this.automaticRedactionReasons.some((reason) => reason.test(change.content.reason ?? "<no reason supplied>"))) {
            void (0, matrix_protection_suite_1.Task)(this.consequences.redactMessagesIn(change.userID, undefined, [
                change.roomID,
            ]));
            const membershipRevision = this.setRoomMembership.getRevision(change.roomID);
            if (membershipRevision) {
                this.checkRoomInvitations(membershipRevision);
            }
        }
    }
    isPolicyRequiringRedaction(policy) {
        return this.automaticRedactionReasons.some((reason) => reason.test(policy.reason ?? "<no reason supplied>"));
    }
    checkRoomInvitations(membershipRevision) {
        const invites = membershipRevision.membersOfMembership(matrix_protection_suite_1.Membership.Invite);
        for (const invite of invites) {
            const relevantRules = (0, UnbanUsers_1.revisionRulesMatchingUser)(invite.sender, [matrix_protection_suite_1.Recommendation.Takedown, matrix_protection_suite_1.Recommendation.Ban], this.watchedPolicyRooms.currentRevision).filter((policy) => this.isPolicyRequiringRedaction(policy));
            if (relevantRules.length > 0) {
                void (0, matrix_protection_suite_1.Task)(this.consequences.rejectInvite(invite.roomID, invite.sender, invite.userID, relevantRules.find((policy) => policy.reason !== undefined)?.reason));
            }
        }
    }
}
exports.StandardRedactionSynchronisation = StandardRedactionSynchronisation;
class RedactionSynchronisationProtection extends matrix_protection_suite_1.AbstractProtection {
    redactionSynchronisation;
    constructor(description, lifetime, capabilities, protectedRoomsSet, automaticallyRedactForReasons) {
        super(description, lifetime, capabilities, protectedRoomsSet, {});
        this.redactionSynchronisation = new StandardRedactionSynchronisation(automaticallyRedactForReasons.map((reason) => new matrix_basic_types_1.MatrixGlob(reason)), capabilities.consequences, protectedRoomsSet.watchedPolicyRooms, protectedRoomsSet.setRoomMembership, protectedRoomsSet.setPoliciesMatchingMembership);
    }
    async handlePolicyChange(_revision, changes) {
        changes.forEach((change) => {
            this.redactionSynchronisation.handlePolicyChange(change);
        });
        return (0, matrix_protection_suite_1.Ok)(undefined);
    }
    // Scan again on ban to make sure we mopped everything up.
    async handleMembershipChange(_revision, changes) {
        changes.forEach((change) => {
            this.redactionSynchronisation.handleMembershipChange(change);
        });
        return (0, matrix_protection_suite_1.Ok)(undefined);
    }
}
exports.RedactionSynchronisationProtection = RedactionSynchronisationProtection;
(0, matrix_protection_suite_1.describeProtection)({
    name: RedactionSynchronisationProtection.name,
    description: "Redacts messages when a new ban policy has been issued that matches config.automaticallyRedactForReasons. Work in progress.",
    capabilityInterfaces: {
        consequences: "RedactionSynchronisationConsequences",
    },
    defaultCapabilities: {
        consequences: "StandardRedactionSynchronisationConsequences",
    },
    async factory(description, lifetime, protectedRoomsSet, draupnir, capabilities) {
        return (0, matrix_protection_suite_1.Ok)(new RedactionSynchronisationProtection(description, lifetime, capabilities, protectedRoomsSet, draupnir.config.automaticallyRedactForReasons));
    },
});
//# sourceMappingURL=RedactionSynchronisation.js.map