"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processFollowActivity = void 0;
const tslib_1 = require("tslib");
const blocklist_1 = require("../../blocklist");
const account_1 = require("../../../models/account/account");
const application_1 = require("../../../models/application/application");
const database_utils_1 = require("../../../helpers/database-utils");
const logger_1 = require("../../../helpers/logger");
const config_1 = require("../../../initializers/config");
const database_1 = require("../../../initializers/database");
const activity_1 = require("../../../lib/activitypub/activity");
const actor_1 = require("../../../models/actor/actor");
const actor_follow_1 = require("../../../models/actor/actor-follow");
const notifier_1 = require("../../notifier");
const follow_1 = require("../follow");
const send_1 = require("../send");
function processFollowActivity(options) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { activity, byActor } = options;
        const activityId = activity.id;
        const objectId = (0, activity_1.getAPId)(activity.object);
        return (0, database_utils_1.retryTransactionWrapper)(processFollow, byActor, activityId, objectId);
    });
}
exports.processFollowActivity = processFollowActivity;
function processFollow(byActor, activityId, targetActorURL) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const { actorFollow, created, targetActor } = yield database_1.sequelizeTypescript.transaction((t) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const targetActor = yield actor_1.ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t);
            if (!targetActor)
                throw new Error('Unknown actor');
            if (targetActor.isOwned() === false)
                throw new Error('This is not a local actor.');
            if (yield rejectIfInstanceFollowDisabled(byActor, activityId, targetActor))
                return { actorFollow: undefined };
            if (yield rejectIfMuted(byActor, activityId, targetActor))
                return { actorFollow: undefined };
            const [actorFollow, created] = yield actor_follow_1.ActorFollowModel.findOrCreateCustom({
                byActor,
                targetActor,
                activityId,
                state: (yield isFollowingInstance(targetActor)) && config_1.CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL
                    ? 'pending'
                    : 'accepted',
                transaction: t
            });
            if (rejectIfAlreadyRejected(actorFollow, byActor, activityId, targetActor))
                return { actorFollow: undefined };
            yield acceptIfNeeded(actorFollow, targetActor, t);
            yield fixFollowURLIfNeeded(actorFollow, activityId, t);
            actorFollow.ActorFollower = byActor;
            actorFollow.ActorFollowing = targetActor;
            if (actorFollow.state === 'accepted') {
                (0, send_1.sendAccept)(actorFollow);
                yield (0, follow_1.autoFollowBackIfNeeded)(actorFollow, t);
            }
            return { actorFollow, created, targetActor };
        }));
        if (!actorFollow)
            return;
        if (created) {
            const follower = yield actor_1.ActorModel.loadFull(byActor.id);
            const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower });
            if (yield isFollowingInstance(targetActor)) {
                notifier_1.Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull);
            }
            else {
                notifier_1.Notifier.Instance.notifyOfNewUserFollow(actorFollowFull);
            }
        }
        logger_1.logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url);
    });
}
function rejectIfInstanceFollowDisabled(byActor, activityId, targetActor) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        if ((yield isFollowingInstance(targetActor)) && config_1.CONFIG.FOLLOWERS.INSTANCE.ENABLED === false) {
            logger_1.logger.info('Rejecting %s because instance followers are disabled.', targetActor.url);
            (0, send_1.sendReject)(activityId, byActor, targetActor);
            return true;
        }
        return false;
    });
}
function rejectIfMuted(byActor, activityId, targetActor) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const followerAccount = yield account_1.AccountModel.load(byActor.Account.id);
        const followingAccountId = targetActor.Account;
        if (followerAccount && (yield (0, blocklist_1.isBlockedByServerOrAccount)(followerAccount, followingAccountId))) {
            logger_1.logger.info('Rejecting %s because follower is muted.', byActor.url);
            (0, send_1.sendReject)(activityId, byActor, targetActor);
            return true;
        }
        return false;
    });
}
function rejectIfAlreadyRejected(actorFollow, byActor, activityId, targetActor) {
    if (actorFollow.state === 'rejected') {
        logger_1.logger.info('Rejecting %s because follow is already rejected.', byActor.url);
        (0, send_1.sendReject)(activityId, byActor, targetActor);
        return true;
    }
    return false;
}
function acceptIfNeeded(actorFollow, targetActor, transaction) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        if (actorFollow.state === 'accepted')
            return;
        if (!(yield isFollowingInstance(targetActor)))
            return;
        if (config_1.CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === true && (yield isFollowingInstance(targetActor)))
            return;
        actorFollow.state = 'accepted';
        yield actorFollow.save({ transaction });
    });
}
function fixFollowURLIfNeeded(actorFollow, activityId, transaction) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        if (!actorFollow.url) {
            actorFollow.url = activityId;
            yield actorFollow.save({ transaction });
        }
    });
}
function isFollowingInstance(targetActor) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const serverActor = yield (0, application_1.getServerActor)();
        return targetActor.id === serverActor.id;
    });
}
//# sourceMappingURL=process-follow.js.map