wip: addAllKnowingSharedInbox

This commit is contained in:
tamaina 2025-07-17 11:00:58 +09:00
parent bb4af983b7
commit 1fc2ae025c
1 changed files with 51 additions and 9 deletions

View File

@ -9,10 +9,12 @@ import { DI } from '@/di-symbols.js';
import type { FollowingsRepository } from '@/models/_.js'; import type { FollowingsRepository } from '@/models/_.js';
import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js'; import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { IActivity } from '@/core/activitypub/type.js'; import type { IActivity } from '@/core/activitypub/type.js';
import { ThinUser } from '@/queue/types.js'; import { ThinUser } from '@/queue/types.js';
import { AccountUpdateService } from '@/core/AccountUpdateService.js';
import type Logger from '@/logger.js';
import { ApLoggerService } from './ApLoggerService.js';
interface IRecipe { interface IRecipe {
type: string; type: string;
@ -27,12 +29,19 @@ interface IDirectRecipe extends IRecipe {
to: MiRemoteUser; to: MiRemoteUser;
} }
interface IAllKnowingSharedInboxRecipe extends IRecipe {
type: 'AllKnowingSharedInbox';
}
const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe => const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
recipe.type === 'Followers'; recipe.type === 'Followers';
const isDirect = (recipe: IRecipe): recipe is IDirectRecipe => const isDirect = (recipe: IRecipe): recipe is IDirectRecipe =>
recipe.type === 'Direct'; recipe.type === 'Direct';
const isAllKnowingSharedInbox = (recipe: IRecipe): recipe is IAllKnowingSharedInboxRecipe =>
recipe.type === 'AllKnowingSharedInbox';
class DeliverManager { class DeliverManager {
private actor: ThinUser; private actor: ThinUser;
private activity: IActivity | null; private activity: IActivity | null;
@ -40,16 +49,15 @@ class DeliverManager {
/** /**
* Constructor * Constructor
* @param userEntityService
* @param followingsRepository * @param followingsRepository
* @param queueService * @param queueService
* @param actor Actor * @param actor Actor
* @param activity Activity to deliver * @param activity Activity to deliver
*/ */
constructor( constructor(
private userEntityService: UserEntityService,
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private queueService: QueueService, private queueService: QueueService,
private logger: Logger,
actor: { id: MiUser['id']; host: null; }, actor: { id: MiUser['id']; host: null; },
activity: IActivity | null, activity: IActivity | null,
@ -91,6 +99,18 @@ class DeliverManager {
this.addRecipe(recipe); this.addRecipe(recipe);
} }
/**
* Add recipe for all-knowing shared inbox deliver
*/
@bindThis
public addAllKnowingSharedInboxRecipe(): void {
const deliver: IAllKnowingSharedInboxRecipe = {
type: 'AllKnowingSharedInbox',
};
this.addRecipe(deliver);
}
/** /**
* Add recipe * Add recipe
* @param recipe Recipe * @param recipe Recipe
@ -105,10 +125,27 @@ class DeliverManager {
*/ */
@bindThis @bindThis
public async execute(): Promise<void> { public async execute(): Promise<void> {
//#region collect inboxes by recipes
// The value flags whether it is shared or not. // The value flags whether it is shared or not.
// key: inbox URL, value: whether it is sharedInbox // key: inbox URL, value: whether it is sharedInbox
const inboxes = new Map<string, boolean>(); const inboxes = new Map<string, boolean>();
if (this.recipes.some(r => isAllKnowingSharedInbox(r))) {
// all-knowing shared inbox
const followings = await this.followingsRepository.find({
where: [
{ followerSharedInbox: Not(IsNull()) },
{ followeeSharedInbox: Not(IsNull()) },
],
select: ['followerSharedInbox', 'followeeSharedInbox'],
});
for (const following of followings) {
if (following.followeeSharedInbox) inboxes.set(following.followeeSharedInbox, true);
if (following.followerSharedInbox) inboxes.set(following.followerSharedInbox, true);
}
}
// build inbox list // build inbox list
// Process follower recipes first to avoid duplication when processing direct recipes later. // Process follower recipes first to avoid duplication when processing direct recipes later.
if (this.recipes.some(r => isFollowers(r))) { if (this.recipes.some(r => isFollowers(r))) {
@ -143,34 +180,40 @@ class DeliverManager {
inboxes.set(recipe.to.inbox, false); inboxes.set(recipe.to.inbox, false);
} }
//#endregion
// deliver // deliver
await this.queueService.deliverMany(this.actor, this.activity, inboxes); await this.queueService.deliverMany(this.actor, this.activity, inboxes);
this.logger.info(`Deliver queues dispatched: inboxes=${inboxes.size} actorId=${this.actor.id} activityId=${this.activity?.id}`);
} }
} }
@Injectable() @Injectable()
export class ApDeliverManagerService { export class ApDeliverManagerService {
private logger: Logger;
constructor( constructor(
@Inject(DI.followingsRepository) @Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private userEntityService: UserEntityService,
private queueService: QueueService, private queueService: QueueService,
private apLoggerService: ApLoggerService,
) { ) {
this.logger = this.apLoggerService.logger.createSubLogger('deliver-manager');
} }
/** /**
* Deliver activity to followers * Deliver activity to followers
* @param actor * @param actor
* @param activity Activity * @param activity Activity
* @param forceMainKey Force to use main (rsa) key
*/ */
@bindThis @bindThis
public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> { public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> {
const manager = new DeliverManager( const manager = new DeliverManager(
this.userEntityService,
this.followingsRepository, this.followingsRepository,
this.queueService, this.queueService,
this.logger,
actor, actor,
activity, activity,
); );
@ -187,9 +230,9 @@ export class ApDeliverManagerService {
@bindThis @bindThis
public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> { public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> {
const manager = new DeliverManager( const manager = new DeliverManager(
this.userEntityService,
this.followingsRepository, this.followingsRepository,
this.queueService, this.queueService,
this.logger,
actor, actor,
activity, activity,
); );
@ -206,9 +249,9 @@ export class ApDeliverManagerService {
@bindThis @bindThis
public async deliverToUsers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, targets: MiRemoteUser[]): Promise<void> { public async deliverToUsers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, targets: MiRemoteUser[]): Promise<void> {
const manager = new DeliverManager( const manager = new DeliverManager(
this.userEntityService,
this.followingsRepository, this.followingsRepository,
this.queueService, this.queueService,
this.logger,
actor, actor,
activity, activity,
); );
@ -219,10 +262,9 @@ export class ApDeliverManagerService {
@bindThis @bindThis
public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager { public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
return new DeliverManager( return new DeliverManager(
this.userEntityService,
this.followingsRepository, this.followingsRepository,
this.queueService, this.queueService,
this.logger,
actor, actor,
activity, activity,
); );