ams-ssk
Version:
NestJS AMS Library for file management
272 lines • 12.8 kB
JavaScript
"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) {
const bot = this.loadBalancer.getNextBot();
try {
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} ${(await bot.getMe()).username} 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... ${(await bot.getMe()).username}, Text: ${message.text}, Channel: ${channelId}`, 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