// SPDX-License-Identifier: AGPL-3.0-or-later import type {ICacheService} from '@pkgs/cache/src/ICacheService'; import type {IEmailService} from '@pkgs/email/src/IEmailService'; import type {IKVProvider} from '@pkgs/kv_client/src/IKVProvider'; import type {RateLimitService} from '@pkgs/rate_limit/src/RateLimitService'; import type {IVirusScanService} from '@pkgs/virus_scan/src/IVirusScanService'; import type {IWorkerService} from '@pkgs/worker/src/contracts/IWorkerService'; import Stripe from 'stripe'; import type {AdminRepository} from '../admin/AdminRepository'; import type {AdminArchiveRepository} from '../admin/repositories/AdminArchiveRepository'; import {BillingRepository} from '../billing/repositories/BillingRepository'; import {Config} from '../Config'; import {createApiContext} from '../CreateApiContext'; import type {ChannelRepository} from '../channel/ChannelRepository'; import type {ChannelService} from '../channel/services/ChannelService'; import type {ConnectionRepository} from '../connection/ConnectionRepository'; import {ConnectionService} from '../connection/ConnectionService'; import type {NcmecSubmissionService} from '../csam/NcmecSubmissionService'; import {DonationRepository} from '../donation/DonationRepository'; import type {IDonationRepository} from '../donation/IDonationRepository'; import type {FavoriteMemeRepository} from '../favorite_meme/FavoriteMemeRepository'; import type {GuildAuditLogService} from '../guild/GuildAuditLogService'; import type {GuildRepository} from '../guild/repositories/GuildRepository'; import type {GuildService} from '../guild/services/GuildService'; import type {AvatarService} from '../infrastructure/AvatarService'; import type {IPurgeQueue} from '../infrastructure/BunnyPurgeQueue'; import {DisabledLiveKitService} from '../infrastructure/DisabledLiveKitService'; import type {DiscriminatorService} from '../infrastructure/DiscriminatorService'; import type {EmbedService} from '../infrastructure/EmbedService'; import type {IAssetDeletionQueue} from '../infrastructure/IAssetDeletionQueue'; import type {IGatewayService} from '../infrastructure/IGatewayService'; import type {ILiveKitService} from '../infrastructure/ILiveKitService'; import type {IMediaService} from '../infrastructure/IMediaService'; import {InMemoryVoiceRoomStore} from '../infrastructure/InMemoryVoiceRoomStore'; import type {ISnowflakeService} from '../infrastructure/ISnowflakeService'; import type {IStorageService} from '../infrastructure/IStorageService'; import type {IUnfurlerService} from '../infrastructure/IUnfurlerService'; import type {IVoiceRoomStore} from '../infrastructure/IVoiceRoomStore'; import type {KVAccountDeletionQueueService} from '../infrastructure/KVAccountDeletionQueueService'; import type {KVActivityTracker} from '../infrastructure/KVActivityTracker'; import type {KVBulkMessageDeletionQueueService} from '../infrastructure/KVBulkMessageDeletionQueueService'; import type {KVScheduledJobQueueService} from '../infrastructure/KVScheduledJobQueueService'; import type {PremiumStateReconciliationQueueService} from '../infrastructure/PremiumStateReconciliationQueueService'; import type {UserCacheService} from '../infrastructure/UserCacheService'; import type {InstanceConfigRepository} from '../instance/InstanceConfigRepository'; import type {InviteService} from '../invite/InviteService'; import {Logger} from '../Logger'; import type {LimitConfigService} from '../limits/LimitConfigService'; import {createGuildStackServices} from '../middleware/GuildStackServiceFactory'; import {getIpInfoService} from '../middleware/ServiceMiddleware'; import { ensureVoiceResourcesInitialized, getGatewayService, getKVClient, getLiveKitServiceInstance, getMediaService, getVoiceAvailabilityService, getVoiceRoomStoreInstance, getVoiceTopology, getWorkerService, resolveBlueskyOAuthService, } from '../middleware/ServiceRegistry'; import { createUserCacheService, ensureVirusScanInitialized, getAdminArchiveRepository, getAdminRepository, getApplicationRepository, getAssetDeletionQueue, getAttachmentUploadTraceRepository, getAvatarService, getCacheService, getChannelRepository, getConnectionRepository, getContactChangeLogService, getDiscriminatorService, getEmailService, getEmbedService, getEntityAssetService, getExpressionAssetPurger, getFavoriteMemeRepository, getGuildAuditLogService, getGuildRepository, getInstanceConfigRepository, getInviteRepository, getKVAccountDeletionQueue, getKVActivityTracker, getKVBulkMessageDeletionQueue, getKVScheduledJobQueue, getLimitConfigService, getNcmecSubmissionService, getOAuth2TokenRepository, getPackRepository, getPremiumStateReconciliationQueueService, getPurgeQueue, getRateLimitService, getReadStateRepository, getReadStateService, getReportRepository, getStorageService, getUnfurlerService, getUserPermissionUtils, getUserRepository, getVirusScanServiceInstance, getVoiceRepository, getWebhookRepository, } from '../middleware/ServiceSingletons'; import type {ApplicationRepository} from '../oauth/repositories/ApplicationRepository'; import type {OAuth2TokenRepository} from '../oauth/repositories/OAuth2TokenRepository'; import type {ReadStateRepository} from '../read_state/ReadStateRepository'; import type {ReadStateService} from '../read_state/ReadStateService'; import type {ReportRepository} from '../report/ReportRepository'; import {STRIPE_API_VERSION} from '../stripe/StripeApiVersion'; import {PaymentRepository} from '../user/repositories/PaymentRepository'; import type {UserRepository} from '../user/repositories/UserRepository'; import type {UserContactChangeLogService} from '../user/services/UserContactChangeLogService'; import {UserDeletionEligibilityService} from '../user/services/UserDeletionEligibilityService'; import {UserHarvestRepository} from '../user/UserHarvestRepository'; import type {UserPermissionUtils} from '../utils/UserPermissionUtils'; import {VoiceReconciliationWorker} from '../voice/VoiceReconciliationWorker'; import type {VoiceRepository} from '../voice/VoiceRepository'; import type {VoiceTopology} from '../voice/VoiceTopology'; import type {WorkerTaskName} from './WorkerLaneConfig'; export interface WorkerDependencies { kvClient: IKVProvider; snowflakeService: ISnowflakeService; limitConfigService: LimitConfigService; userRepository: UserRepository; channelRepository: ChannelRepository; guildRepository: GuildRepository; favoriteMemeRepository: FavoriteMemeRepository; applicationRepository: ApplicationRepository; oauth2TokenRepository: OAuth2TokenRepository; readStateRepository: ReadStateRepository; adminRepository: AdminRepository; reportRepository: ReportRepository; paymentRepository: PaymentRepository; userHarvestRepository: UserHarvestRepository; adminArchiveRepository: AdminArchiveRepository; voiceRepository: VoiceRepository; connectionRepository: ConnectionRepository; connectionService: ConnectionService; cacheService: ICacheService; userCacheService: UserCacheService; storageService: IStorageService; assetDeletionQueue: IAssetDeletionQueue; purgeQueue: IPurgeQueue; gatewayService: IGatewayService; mediaService: IMediaService; discriminatorService: DiscriminatorService; avatarService: AvatarService; virusScanService: IVirusScanService; rateLimitService: RateLimitService; emailService: IEmailService; instanceConfigRepository: InstanceConfigRepository; inviteService: InviteService; workerService: IWorkerService; unfurlerService: IUnfurlerService; embedService: EmbedService; readStateService: ReadStateService; userPermissionUtils: UserPermissionUtils; activityTracker: KVActivityTracker; deletionQueueService: KVAccountDeletionQueueService; bulkMessageDeletionQueueService: KVBulkMessageDeletionQueueService; scheduledJobQueueService: KVScheduledJobQueueService; premiumStateReconciliationQueueService: PremiumStateReconciliationQueueService; deletionEligibilityService: UserDeletionEligibilityService; voiceRoomStore: IVoiceRoomStore; liveKitService: ILiveKitService; voiceTopology: VoiceTopology | null; voiceReconciliationWorker: VoiceReconciliationWorker | null; channelService: ChannelService; guildAuditLogService: GuildAuditLogService; contactChangeLogService: UserContactChangeLogService; ncmecSubmissionService: NcmecSubmissionService; donationRepository: IDonationRepository; guildService: GuildService; billingRepository: BillingRepository; stripe: Stripe | null; } export async function initializeWorkerDependencies(snowflakeService: ISnowflakeService): Promise { Logger.info('Initializing worker dependencies...'); const kvClient = getKVClient(); const userRepository = getUserRepository(); const channelRepository = getChannelRepository(); const guildRepository = getGuildRepository(); const favoriteMemeRepository = getFavoriteMemeRepository(); const applicationRepository = getApplicationRepository(); const oauth2TokenRepository = getOAuth2TokenRepository(); const readStateRepository = getReadStateRepository(); const adminRepository = getAdminRepository(); const adminArchiveRepository = getAdminArchiveRepository(); const reportRepository = getReportRepository(); const paymentRepository = new PaymentRepository(); const donationRepository = new DonationRepository(); const userHarvestRepository = new UserHarvestRepository(); const connectionRepository = getConnectionRepository(); const cacheService = getCacheService(); const limitConfigService = getLimitConfigService(); await limitConfigService.initialize(); limitConfigService.setAsGlobalInstance(); const userCacheService = createUserCacheService(); const storageService = getStorageService(); const assetDeletionQueue = getAssetDeletionQueue(); const purgeQueue = getPurgeQueue(); const gatewayService = getGatewayService(); const instanceConfigRepository = getInstanceConfigRepository(); const blueskyOAuthService = await resolveBlueskyOAuthService(instanceConfigRepository); const connectionService = new ConnectionService(connectionRepository, gatewayService, blueskyOAuthService); const mediaService = getMediaService(); const discriminatorService = getDiscriminatorService(); const ncmecSubmissionService = getNcmecSubmissionService(); const avatarService = getAvatarService(); const entityAssetService = getEntityAssetService(); await ensureVirusScanInitialized(); const virusScanService = getVirusScanServiceInstance(); const rateLimitService = getRateLimitService(); const packRepository = getPackRepository(); const emailService = getEmailService(); const workerService = getWorkerService(); const guildAuditLogService = getGuildAuditLogService(); const unfurlerService = getUnfurlerService(); const embedService = getEmbedService(); const readStateService = getReadStateService(); const userPermissionUtils = getUserPermissionUtils(); const activityTracker = getKVActivityTracker(); const deletionQueueService = getKVAccountDeletionQueue(); const bulkMessageDeletionQueueService = getKVBulkMessageDeletionQueue(); const scheduledJobQueueService = getKVScheduledJobQueue(); const premiumStateReconciliationQueueService = getPremiumStateReconciliationQueueService(); const deletionEligibilityService = new UserDeletionEligibilityService(kvClient); await ensureVoiceResourcesInitialized(); const voiceRepository = getVoiceRepository(); const voiceTopology = getVoiceTopology(); const voiceRoomStore = getVoiceRoomStoreInstance() ?? new InMemoryVoiceRoomStore(); const liveKitService = getLiveKitServiceInstance() ?? new DisabledLiveKitService(); const voiceAvailabilityService = getVoiceAvailabilityService(); const voiceReconciliationEnabled = Config.worker.enableVoiceReconciliation; const voiceReconciliationWorker = Config.voice.enabled && voiceTopology !== null && voiceReconciliationEnabled ? new VoiceReconciliationWorker({ gatewayService, liveKitService, voiceRoomStore, kvClient, logger: Logger, intervalMs: Config.worker.voiceReconciliation.intervalMs, staggerDelayMs: Config.worker.voiceReconciliation.staggerDelayMs, lockTtlSeconds: Config.worker.voiceReconciliation.lockTtlSeconds, cadenceTtlSeconds: Config.worker.voiceReconciliation.cadenceTtlSeconds, gatewayOnlyGraceMs: Config.worker.voiceReconciliation.gatewayOnlyGraceMs, liveKitOnlyGraceMs: Config.worker.voiceReconciliation.liveKitOnlyGraceMs, }) : null; if (Config.voice.enabled && voiceTopology !== null) { Logger.info({reconciliationEnabled: voiceReconciliationEnabled}, 'Voice services initialized'); } const inviteRepository = getInviteRepository(); const webhookRepository = getWebhookRepository(); const ipInfoService = getIpInfoService(); const contactChangeLogService = getContactChangeLogService(); const apiContext = createApiContext(); const {channelService, guildService, inviteService} = createGuildStackServices({ apiContext, packRepository, channelRepository, userRepository, guildRepository, inviteRepository, webhookRepository, favoriteMemeRepository, avatarService, entityAssetService, assetDeletionQueue, expressionAssetPurger: getExpressionAssetPurger(), userCacheService, limitConfigService, embedService, readStateService, storageService, attachmentUploadTraceRepository: getAttachmentUploadTraceRepository(), virusScanService, purgeQueue, guildAuditLogService, voiceRoomStore, liveKitService, voiceAvailabilityService, ipInfoService, }); const billingRepository = new BillingRepository(snowflakeService, kvClient); let stripe: Stripe | null = null; if (Config.stripe.enabled && Config.stripe.secretKey) { stripe = new Stripe(Config.stripe.secretKey, { apiVersion: STRIPE_API_VERSION, httpClient: Config.dev.testModeEnabled ? Stripe.createFetchHttpClient() : undefined, }); Logger.info('Stripe initialized'); } Logger.info('Worker dependencies initialized successfully'); return { kvClient, snowflakeService, limitConfigService, userRepository, channelRepository, guildRepository, favoriteMemeRepository, applicationRepository, oauth2TokenRepository, readStateRepository, adminRepository, reportRepository, paymentRepository, userHarvestRepository, adminArchiveRepository, voiceRepository, connectionRepository, connectionService, cacheService, userCacheService, storageService, assetDeletionQueue, purgeQueue, gatewayService, mediaService, discriminatorService, avatarService, virusScanService, rateLimitService, emailService, instanceConfigRepository, inviteService, workerService, unfurlerService, embedService, readStateService, userPermissionUtils, activityTracker, deletionQueueService, bulkMessageDeletionQueueService, scheduledJobQueueService, premiumStateReconciliationQueueService, deletionEligibilityService, voiceRoomStore, liveKitService, voiceTopology, voiceReconciliationWorker, channelService, guildService, donationRepository, billingRepository, guildAuditLogService, contactChangeLogService, ncmecSubmissionService, stripe, }; } export async function shutdownWorkerDependencies(deps: WorkerDependencies): Promise { Logger.info('Shutting down worker dependencies...'); if (deps.voiceReconciliationWorker !== null) { deps.voiceReconciliationWorker.stop(); } Logger.info('Worker dependencies shut down successfully'); }