// SPDX-License-Identifier: AGPL-3.0-or-later import type {ChannelID, UserID} from '@app/api/BrandedTypes'; import {createUserID} from '@app/api/BrandedTypes'; import type {ChannelRepository} from '@app/api/channel/ChannelRepository'; import {ChannelAuthService} from '@app/api/channel/services/channel_data/ChannelAuthService'; import type { ChannelTypeConversion, ChannelUpdateData, } from '@app/api/channel/services/channel_data/ChannelOperationsService'; import {ChannelOperationsService} from '@app/api/channel/services/channel_data/ChannelOperationsService'; import {ChannelUtilsService} from '@app/api/channel/services/channel_data/ChannelUtilsService'; import {GroupDmUpdateService} from '@app/api/channel/services/channel_data/GroupDmUpdateService'; import type {MessagePersistenceService} from '@app/api/channel/services/message/MessagePersistenceService'; import {ThreadModifyService} from '@app/api/channel/services/thread/ThreadModifyService'; import {pickThreadParentSettings} from '@app/api/channel/services/thread/ThreadParentSettings'; import type {ThreadViewer} from '@app/api/experiment/ChannelThreadsGate'; import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService'; import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository'; import type {AvatarService} from '@app/api/infrastructure/AvatarService'; import type {IPurgeQueue} from '@app/api/infrastructure/CachePurgeQueue'; import type {IGatewayService} from '@app/api/infrastructure/IGatewayService'; import type {ILiveKitService} from '@app/api/infrastructure/ILiveKitService'; import type {ISnowflakeService} from '@app/api/infrastructure/ISnowflakeService'; import type {IStorageService} from '@app/api/infrastructure/IStorageService'; import type {IVoiceRoomStore} from '@app/api/infrastructure/IVoiceRoomStore'; import type {UserCacheService} from '@app/api/infrastructure/UserCacheService'; import type {InviteRepository} from '@app/api/invite/InviteRepository'; import type {LimitConfigService} from '@app/api/limits/LimitConfigService'; import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware'; import type {Channel} from '@app/api/models/Channel'; import type {UserRepository} from '@app/api/user/repositories/UserRepository'; import type {VoiceAvailabilityService} from '@app/api/voice/VoiceAvailabilityService'; import type {WebhookRepository} from '@app/api/webhook/WebhookRepository'; import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants'; import type { ChannelUpdateGatedRequest, ChannelUpdateNonThreadRequest, ChannelUpdateThreadRequest, } from '@fluxer/schema/src/domains/channel/ChannelRequestSchemas'; import type {ICacheService} from '@pkgs/cache/src/ICacheService'; import type {IRateLimitService} from '@pkgs/rate_limit/src/IRateLimitService'; type GuildChannelUpdateRequest = Exclude< ChannelUpdateNonThreadRequest, { type: typeof ChannelTypes.GROUP_DM; } >; type GuildChannelUpdatePayload = Omit; export class ChannelDataService { public readonly auth: ChannelAuthService; public readonly operations: ChannelOperationsService; public readonly groupDmUpdate: GroupDmUpdateService; public readonly utils: ChannelUtilsService; public readonly threadModify: ThreadModifyService; constructor( channelRepository: ChannelRepository, userRepository: UserRepository, guildRepository: GuildRepository, userCacheService: UserCacheService, storageService: IStorageService, gatewayService: IGatewayService, avatarService: AvatarService, snowflakeService: ISnowflakeService, purgeQueue: IPurgeQueue, voiceRoomStore: IVoiceRoomStore, liveKitService: ILiveKitService, voiceAvailabilityService: VoiceAvailabilityService | null, messagePersistenceService: MessagePersistenceService, guildAuditLogService: GuildAuditLogService, inviteRepository: InviteRepository, webhookRepository: WebhookRepository, limitConfigService: LimitConfigService, rateLimitService: IRateLimitService, cacheService: ICacheService, ) { this.utils = new ChannelUtilsService( channelRepository, userCacheService, storageService, gatewayService, purgeQueue, ); this.auth = new ChannelAuthService(channelRepository, userRepository, guildRepository, gatewayService); this.operations = new ChannelOperationsService( channelRepository, userRepository, gatewayService, this.auth, this.utils, voiceRoomStore, liveKitService, voiceAvailabilityService, guildAuditLogService, inviteRepository, webhookRepository, guildRepository, limitConfigService, rateLimitService, cacheService, snowflakeService, ); this.threadModify = new ThreadModifyService({ channelRepository, gatewayService, guildAuditLogService, rateLimitService, cacheService, snowflakeService, messagePersistence: messagePersistenceService, utils: this.utils, }); this.groupDmUpdate = new GroupDmUpdateService( channelRepository, userRepository, avatarService, snowflakeService, this.utils, messagePersistenceService, ); } async deleteThread({ userId, viewer, channelId, requestCache, auditLogReason, }: { userId: UserID; viewer: ThreadViewer; channelId: ChannelID; requestCache: RequestCache; auditLogReason: string | null; }): Promise { const authChannel = await this.auth.getChannelAuthenticated({userId, channelId, viewer, skipNsfwValidation: true}); await this.threadModify.deleteThread({authChannel, userId, requestCache, auditLogReason}); } async editChannel({ userId, viewer, channelId, data, clientFeatures, requestCache, auditLogReason, typeConversion, countryCode, }: { userId: UserID; viewer: ThreadViewer; channelId: ChannelID; data: Omit; clientFeatures: ReadonlySet; requestCache: RequestCache; auditLogReason: string | null; typeConversion?: ChannelTypeConversion | null; countryCode?: string | null; }): Promise { const authChannel = await this.auth.getChannelAuthenticated({userId, channelId, viewer, skipNsfwValidation: true}); const {channel} = authChannel; if (authChannel.thread) { return this.threadModify.updateThread({ authChannel, userId, data: data as Omit, requestCache, auditLogReason, }); } if (channel.type === ChannelTypes.GROUP_DM) { const groupDmData = data as Omit, 'type'>; return await this.groupDmUpdate.updateGroupDmChannel({ userId, channelId, name: groupDmData.name !== undefined ? groupDmData.name : undefined, icon: groupDmData.icon !== undefined ? groupDmData.icon : undefined, ownerId: groupDmData.owner_id ? createUserID(groupDmData.owner_id) : undefined, nicks: groupDmData.nicks, nsfw: groupDmData.nsfw ?? undefined, requestCache, }); } const guildChannelData = data as GuildChannelUpdatePayload; const channelUpdateData: ChannelUpdateData = {}; if ('name' in guildChannelData && guildChannelData.name !== undefined && guildChannelData.name !== null) { channelUpdateData.name = guildChannelData.name; } if (guildChannelData.topic !== undefined) { channelUpdateData.topic = guildChannelData.topic ?? null; } if (guildChannelData.url !== undefined) { channelUpdateData.url = guildChannelData.url ?? null; } if (guildChannelData.parent_id !== undefined) { channelUpdateData.parent_id = guildChannelData.parent_id ?? null; } if (guildChannelData.bitrate !== undefined) { channelUpdateData.bitrate = guildChannelData.bitrate ?? null; } if (guildChannelData.user_limit !== undefined) { channelUpdateData.user_limit = guildChannelData.user_limit ?? null; } if (guildChannelData.voice_connection_limit !== undefined) { channelUpdateData.voice_connection_limit = guildChannelData.voice_connection_limit ?? null; } if (guildChannelData.nsfw !== undefined) { channelUpdateData.nsfw = guildChannelData.nsfw ?? undefined; } if (guildChannelData.nsfw_override !== undefined) { channelUpdateData.nsfw_override = guildChannelData.nsfw_override ?? null; } if (guildChannelData.content_warning_level !== undefined) { channelUpdateData.content_warning_level = guildChannelData.content_warning_level; } if (guildChannelData.content_warning_text !== undefined) { channelUpdateData.content_warning_text = guildChannelData.content_warning_text ?? null; } if (guildChannelData.rate_limit_per_user !== undefined) { channelUpdateData.rate_limit_per_user = guildChannelData.rate_limit_per_user ?? undefined; } if (guildChannelData.permission_overwrites !== undefined) { channelUpdateData.permission_overwrites = guildChannelData.permission_overwrites ?? null; } if (guildChannelData.rtc_region !== undefined) { channelUpdateData.rtc_region = guildChannelData.rtc_region ?? null; } if (guildChannelData.rtc_p2p !== undefined) { channelUpdateData.rtc_p2p = guildChannelData.rtc_p2p; } if (guildChannelData.icon !== undefined) { channelUpdateData.icon = guildChannelData.icon ?? null; } if (guildChannelData.owner_id !== undefined) { channelUpdateData.owner_id = guildChannelData.owner_id ?? null; } if (guildChannelData.nicks !== undefined) { channelUpdateData.nicks = guildChannelData.nicks ?? null; } return this.operations.editChannel({ userId, viewer, channelId, data: channelUpdateData, threadParent: pickThreadParentSettings(channel.type, guildChannelData), clientFeatures, requestCache, auditLogReason, typeConversion, countryCode, }); } }