UNPKG

ams-ssk

Version:

NestJS AMS Library for file management

272 lines 12.7 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var botService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.botService = void 0; const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); const bot_load_balancer_1 = require("./bot.load-balancer"); const bot_dto_1 = require("./dto/bot.dto"); let botService = botService_1 = class botService { constructor(configService, loadBalancer) { this.configService = configService; this.loadBalancer = loadBalancer; this.logger = new common_1.Logger(botService_1.name); this.MAX_RETRIES = 3; this.RETRY_DELAY = 1000; this.messageQueue = new Map(); this.config = this.configService.get('bot'); } async onModuleInit() { this.logger.log('Initializing bot service...'); await this.initializeBots(); this.startMessageQueueProcessor(); this.logger.log('bot service initialized successfully'); } async initializeBots() { if (!this.config?.bots?.length) { this.logger.warn('No bots configured in bot config'); return; } this.logger.debug(`Initializing ${this.config.bots.length} bots...`); const initPromises = this.config.bots.map(async (botConfig) => { try { this.loadBalancer.addBot(botConfig); const bot = this.loadBalancer.getBotByToken(botConfig.token); if (bot) { this.setupMessageHandlers(bot); const botInfo = await bot.getMe(); this.logger.log(`Bot @${botInfo.username} initialized successfully`); } } catch (error) { this.logger.error(`Failed to initialize bot with token ${botConfig.token.slice(0, 6)}...`, error.stack); this.queueBotRecovery(botConfig); } }); await Promise.allSettled(initPromises); } queueBotRecovery(botConfig) { this.logger.debug(`Queuing recovery for bot with token ${botConfig.token.slice(0, 6)}...`); setTimeout(() => { try { this.loadBalancer.addBot(botConfig); const bot = this.loadBalancer.getBotByToken(botConfig.token); if (bot) { this.setupMessageHandlers(bot); this.logger.log(`Successfully recovered bot with token ${botConfig.token.slice(0, 6)}...`); } } catch (error) { this.logger.error(`Failed to recover bot with token ${botConfig.token.slice(0, 6)}...`, error.stack); this.logger.warn(`Bot recovery failed, retrying... Token: ${botConfig.token.slice(0, 6)}...`); this.queueBotRecovery(botConfig); } }, this.RETRY_DELAY); } startMessageQueueProcessor() { this.logger.log('Starting message queue processor'); setInterval(() => this.processMessageQueue(), 1000); } async processMessageQueue() { if (this.messageQueue.size === 0) return; this.logger.debug(`Processing message queue. Size: ${this.messageQueue.size}`); for (const [messageId, queueItem] of this.messageQueue) { try { const bot = this.loadBalancer.getNextBot(); await this.forwardMessage(bot, queueItem.message, queueItem.channelId); this.messageQueue.delete(messageId); this.logger.debug(`Successfully processed queued message ${messageId}`); } catch (error) { if (queueItem.retries >= this.MAX_RETRIES) { this.messageQueue.delete(messageId); this.logger.error(`Failed to forward message ${messageId} after ${this.MAX_RETRIES} retries:`, error.stack); this.notifyAdmin(`Failed to forward message after ${this.MAX_RETRIES} retries: ${error.message}`); } else { queueItem.retries++; this.logger.warn(`Retry ${queueItem.retries}/${this.MAX_RETRIES} for message ${messageId}`); } } } } setupMessageHandlers(bot) { bot.on('message', (msg) => { this.handleIncomingMessage(bot, msg).catch((error) => this.handleMessageError(error, msg, bot)); }); bot.on('error', (error) => { this.notifyAdmin(`Bot error occurred: ${error.message}`); }); } async handleMessageError(error, message, bot) { const messageId = `${message.chat.id}-${message.message_id}`; const botToken = this.loadBalancer.getBotToken(bot); const matchingChannel = this.config.channels.find((channel) => channel.botTokens.includes(botToken)); if (!this.messageQueue.has(messageId)) { this.messageQueue.set(messageId, { retries: 0, message, channelId: matchingChannel?.channelId, }); } await this.notifyAdmin(`Message queued for retry: ${error.message}`); } async notifyAdmin(message) { if (!this.config.adminChatId) return; try { const bot = this.loadBalancer.getNextBot(); await bot.sendMessage(this.config.adminChatId, message); } catch { } } async handleIncomingMessage(bot, message) { const messageId = `${message.chat.id}-${message.message_id}`; if (message.text?.toLowerCase().startsWith('start')) { this.logger.debug(`Skipping start command message ${messageId}`); return; } const botToken = this.loadBalancer.getBotToken(bot); const matchingChannel = this.config.channels.find((channel) => channel.botTokens.includes(botToken)); if (!matchingChannel) { this.logger.warn(`No channel configured for bot token: ${botToken.slice(0, 6)}...`); await this.notifyAdmin(`⚠️ Configuration issue: No channel configured for bot token: ${botToken.slice(0, 6)}...`); return; } try { await this.forwardMessageWithRetry(bot, message, matchingChannel.channelId); await this.notifyAdmin(`Message forwarded to channel ${matchingChannel.channelId}`); } catch (error) { this.logger.warn(`Failed to forward message ${messageId}, queueing for retry: ${error.message}`); this.messageQueue.set(messageId, { retries: 0, message, channelId: matchingChannel.channelId, }); await this.notifyAdmin(`Message queued for retry: ${error.message}`); } } async forwardMessageWithRetry(bot, message, channelId, retryCount = 0) { const messageId = `${message.chat.id}-${message.message_id}`; try { await this.forwardMessage(bot, message, channelId); } catch (error) { if (retryCount < this.MAX_RETRIES) { this.logger.debug(`Retry attempt ${retryCount + 1} failed, switching to next bot`); await new Promise((resolve) => setTimeout(resolve, this.RETRY_DELAY)); const nextBot = this.loadBalancer.getNextBot(); return this.forwardMessageWithRetry(nextBot, message, channelId, retryCount + 1); } this.logger.error(`All retry attempts failed for message ${messageId}`); throw error; } } async forwardMessage(bot, message, channelId) { const messageId = `${message.chat.id}-${message.message_id}`; try { await bot.forwardMessage(channelId, message.chat.id, message.message_id); this.logger.debug(`Message ${messageId} forwarded successfully`); } catch (error) { this.logger.error(`Failed to forward message ${messageId} using bot...`, error.stack); this.logger.debug(`Forward attempt failed, trying fallback bot for message ${messageId}`); const fallbackBot = this.loadBalancer.getNextBot(); if (fallbackBot === bot) { this.logger.error(`No available bots for forwarding message ${messageId}`); throw new Error('No available bots for forwarding'); } await fallbackBot.forwardMessage(channelId, message.chat.id, message.message_id); this.logger.debug(`Message ${messageId} forwarded successfully using fallback bot`); } } async broadcastMessage(messageDto) { this.logger.debug('Starting broadcast message operation'); try { const bot = this.loadBalancer.getNextBot(); const channel = this.config.channels[0]; const result = await this.sendMessageByType(bot, channel.channelId, messageDto); this.logger.log(`Successfully broadcasted message with ID: ${result.message_id}`); return { success: true, messageId: result.message_id.toString(), timestamp: new Date().toISOString(), }; } catch (error) { this.logger.error('Failed to broadcast message:', error.stack); return { success: false, timestamp: new Date().toISOString(), }; } } async sendMessageByType(bot, channelId, messageDto) { this.logger.debug(`Sending ${messageDto.type} message to channel ${channelId}`); switch (messageDto.type) { case bot_dto_1.MessageType.PHOTO: if (!messageDto.mediaUrl) { this.logger.error('Media URL is required for photo messages'); throw new Error('Media URL is required for photo messages'); } return bot.sendPhoto(channelId, messageDto.mediaUrl, { caption: messageDto.message, }); case bot_dto_1.MessageType.VIDEO: if (!messageDto.mediaUrl) { this.logger.error('Media URL is required for video messages'); throw new Error('Media URL is required for video messages'); } return bot.sendVideo(channelId, messageDto.mediaUrl, { caption: messageDto.message, }); case bot_dto_1.MessageType.TEXT: default: return bot.sendMessage(channelId, messageDto.message); } } async getBotStatus() { this.logger.debug('Fetching bot status information'); const bots = this.loadBalancer.getBots() || []; return bots.map((botInfo, index) => { const operations = this.loadBalancer.getBotOperationCount(botInfo.bot); const maxOps = this.loadBalancer.getBotMaxOperations(botInfo.bot); return { id: index + 1, activeOperations: operations, maxOperations: maxOps, utilizationPercentage: this.loadBalancer.getBotUtilizationPercentage(botInfo.bot), }; }); } async getConfiguration() { this.logger.debug('Fetching bot service configuration'); const firstBot = this.loadBalancer.getBots()?.[0]?.bot; return { channelConfigured: this.config.channels?.length > 0, botsCount: this.config.bots?.length || 0, maxOperationsPerBot: firstBot ? this.loadBalancer.getBotMaxOperations(firstBot) : 0, }; } }; exports.botService = botService; exports.botService = botService = botService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [config_1.ConfigService, bot_load_balancer_1.botLoadBalancer]) ], botService); //# sourceMappingURL=bot.service.js.map