import { Global, Inject, Logger, Module, type OnModuleDestroy, type OnModuleInit, } from '@nestjs/common'; import { DiscordPlugin } from '@mosaic/discord-plugin'; import { TelegramPlugin } from '@mosaic/telegram-plugin'; import { PluginService } from './plugin.service.js'; import type { IChannelPlugin } from './plugin.interface.js'; import { PLUGIN_REGISTRY } from './plugin.tokens.js'; class DiscordChannelPluginAdapter implements IChannelPlugin { readonly name = 'discord'; constructor(private readonly plugin: DiscordPlugin) {} async start(): Promise { await this.plugin.start(); } async stop(): Promise { await this.plugin.stop(); } async onProjectCreated(project: { id: string; name: string; description?: string; }): Promise<{ channelId: string } | null> { return this.plugin.createProjectChannel(project); } } class TelegramChannelPluginAdapter implements IChannelPlugin { readonly name = 'telegram'; constructor(private readonly plugin: TelegramPlugin) {} async start(): Promise { await this.plugin.start(); } async stop(): Promise { await this.plugin.stop(); } } const DEFAULT_GATEWAY_URL = 'http://localhost:14242'; function createPluginRegistry(): IChannelPlugin[] { const plugins: IChannelPlugin[] = []; const discordToken = process.env['DISCORD_BOT_TOKEN']; const discordGuildId = process.env['DISCORD_GUILD_ID']; const discordGatewayUrl = process.env['DISCORD_GATEWAY_URL'] ?? DEFAULT_GATEWAY_URL; if (discordToken) { plugins.push( new DiscordChannelPluginAdapter( new DiscordPlugin({ token: discordToken, guildId: discordGuildId, gatewayUrl: discordGatewayUrl, }), ), ); } const telegramToken = process.env['TELEGRAM_BOT_TOKEN']; const telegramGatewayUrl = process.env['TELEGRAM_GATEWAY_URL'] ?? DEFAULT_GATEWAY_URL; if (telegramToken) { plugins.push( new TelegramChannelPluginAdapter( new TelegramPlugin({ token: telegramToken, gatewayUrl: telegramGatewayUrl, }), ), ); } return plugins; } @Global() @Module({ providers: [ { provide: PLUGIN_REGISTRY, useFactory: (): IChannelPlugin[] => createPluginRegistry(), }, PluginService, ], exports: [PluginService, PLUGIN_REGISTRY], }) export class PluginModule implements OnModuleInit, OnModuleDestroy { private readonly logger = new Logger(PluginModule.name); constructor(@Inject(PLUGIN_REGISTRY) private readonly plugins: IChannelPlugin[]) {} async onModuleInit(): Promise { for (const plugin of this.plugins) { this.logger.log(`Starting plugin: ${plugin.name}`); await plugin.start(); } } async onModuleDestroy(): Promise { for (const plugin of [...this.plugins].reverse()) { this.logger.log(`Stopping plugin: ${plugin.name}`); await plugin.stop(); } } }