import { delay } from '@whiskeysockets/baileys'; import EventEmitter2 from 'eventemitter2'; import { Auth, ConfigService } from '../../config/env.config'; import { BadRequestException, InternalServerErrorException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; import { RepositoryBroker } from '../repository/repository.manager'; import { AuthService, OldToken } from '../services/auth.service'; import { WAMonitoringService } from '../services/monitor.service'; import { WAStartupService } from '../services/whatsapp.service'; import { WebhookService } from '../services/webhook.service'; import { Logger } from '../../config/logger.config'; import { wa } from '../types/wa.types'; import { RedisCache } from '../../db/redis.client'; export class InstanceController { constructor( private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService, private readonly repository: RepositoryBroker, private readonly eventEmitter: EventEmitter2, private readonly authService: AuthService, private readonly webhookService: WebhookService, private readonly cache: RedisCache, ) {} private readonly logger = new Logger(InstanceController.name); public async createInstance({ instanceName, webhook, webhook_by_events, events, qrcode, token, }: InstanceDto) { this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); const mode = this.configService.get('AUTHENTICATION').INSTANCE.MODE; if (mode === 'container') { this.logger.verbose('container mode'); if (Object.keys(this.waMonitor.waInstances).length > 0) { throw new BadRequestException([ 'Instance already created', 'Only one instance can be created', ]); } this.logger.verbose('checking duplicate token'); await this.authService.checkDuplicateToken(token); this.logger.verbose('creating instance'); const instance = new WAStartupService( this.configService, this.eventEmitter, this.repository, this.cache, ); instance.instanceName = instanceName; this.logger.verbose('instance: ' + instance.instanceName + ' created'); this.waMonitor.waInstances[instance.instanceName] = instance; this.waMonitor.delInstanceTime(instance.instanceName); this.logger.verbose('generating hash'); const hash = await this.authService.generateHash( { instanceName: instance.instanceName, }, token, ); this.logger.verbose('hash: ' + hash + ' generated'); let getEvents: string[]; if (webhook) { this.logger.verbose('creating webhook'); try { this.webhookService.create(instance, { enabled: true, url: webhook, events, webhook_by_events, }); getEvents = (await this.webhookService.find(instance)).events; } catch (error) { this.logger.log(error); } } this.logger.verbose('instance created'); this.logger.verbose({ instance: { instanceName: instance.instanceName, status: 'created', }, hash, webhook, events: getEvents, }); return { instance: { instanceName: instance.instanceName, status: 'created', }, hash, webhook, events: getEvents, }; } else { this.logger.verbose('server mode'); this.logger.verbose('checking duplicate token'); await this.authService.checkDuplicateToken(token); this.logger.verbose('creating instance'); const instance = new WAStartupService( this.configService, this.eventEmitter, this.repository, this.cache, ); instance.instanceName = instanceName; this.logger.verbose('instance: ' + instance.instanceName + ' created'); this.waMonitor.waInstances[instance.instanceName] = instance; this.waMonitor.delInstanceTime(instance.instanceName); this.logger.verbose('generating hash'); const hash = await this.authService.generateHash( { instanceName: instance.instanceName, }, token, ); this.logger.verbose('hash: ' + hash + ' generated'); let getEvents: string[]; if (webhook) { this.logger.verbose('creating webhook'); try { this.webhookService.create(instance, { enabled: true, url: webhook, events, webhook_by_events, }); getEvents = (await this.webhookService.find(instance)).events; } catch (error) { this.logger.log(error); } } let getQrcode: wa.QrCode; if (qrcode) { this.logger.verbose('creating qrcode'); await instance.connectToWhatsapp(); await delay(2000); getQrcode = instance.qrCode; } this.logger.verbose('instance created'); this.logger.verbose({ instance: { instanceName: instance.instanceName, status: 'created', }, hash, webhook, webhook_by_events, events: getEvents, qrcode: getQrcode, }); return { instance: { instanceName: instance.instanceName, status: 'created', }, hash, webhook, webhook_by_events, events: getEvents, qrcode: getQrcode, }; } } public async connectToWhatsapp({ instanceName }: InstanceDto) { try { this.logger.verbose( 'requested connectToWhatsapp from ' + instanceName + ' instance', ); const instance = this.waMonitor.waInstances[instanceName]; const state = instance?.connectionStatus?.state; this.logger.verbose('state: ' + state); switch (state) { case 'close': this.logger.verbose('connecting'); await instance.connectToWhatsapp(); await delay(2000); return instance.qrCode; case 'connecting': return instance.qrCode; default: return await this.connectionState({ instanceName }); } } catch (error) { this.logger.error(error); } } public async restartInstance({ instanceName }: InstanceDto) { try { this.logger.verbose('requested restartInstance from ' + instanceName + ' instance'); this.logger.verbose('deleting instance: ' + instanceName); delete this.waMonitor.waInstances[instanceName]; this.logger.verbose('creating instance: ' + instanceName); const instance = new WAStartupService( this.configService, this.eventEmitter, this.repository, this.cache, ); instance.instanceName = instanceName; this.logger.verbose('instance: ' + instance.instanceName + ' created'); this.logger.verbose('connecting instance: ' + instanceName); await instance.connectToWhatsapp(); this.waMonitor.waInstances[instance.instanceName] = instance; return { error: false, message: 'Instance restarted' }; } catch (error) { this.logger.error(error); } } public async connectionState({ instanceName }: InstanceDto) { this.logger.verbose('requested connectionState from ' + instanceName + ' instance'); return this.waMonitor.waInstances[instanceName]?.connectionStatus; } public async fetchInstances({ instanceName }: InstanceDto) { this.logger.verbose('requested fetchInstances from ' + instanceName + ' instance'); if (instanceName) { this.logger.verbose('instanceName: ' + instanceName); return this.waMonitor.instanceInfo(instanceName); } return this.waMonitor.instanceInfo(); } public async logout({ instanceName }: InstanceDto) { this.logger.verbose('requested logout from ' + instanceName + ' instance'); try { this.logger.verbose('logging out instance: ' + instanceName); await this.waMonitor.waInstances[instanceName]?.client?.logout( 'Log out instance: ' + instanceName, ); this.logger.verbose('close connection instance: ' + instanceName); this.waMonitor.waInstances[instanceName]?.client?.ws?.close(); return { error: false, message: 'Instance logged out' }; } catch (error) { throw new InternalServerErrorException(error.toString()); } } public async deleteInstance({ instanceName }: InstanceDto) { this.logger.verbose('requested deleteInstance from ' + instanceName + ' instance'); const stateConn = await this.connectionState({ instanceName }); if (stateConn.state === 'open') { throw new BadRequestException([ 'Deletion failed', 'The instance needs to be disconnected', ]); } try { if (stateConn.state === 'connecting') { this.logger.verbose('logging out instance: ' + instanceName); await this.logout({ instanceName }); delete this.waMonitor.waInstances[instanceName]; return { error: false, message: 'Instance deleted' }; } else { this.logger.verbose('deleting instance: ' + instanceName); delete this.waMonitor.waInstances[instanceName]; this.eventEmitter.emit('remove.instance', instanceName, 'inner'); return { error: false, message: 'Instance deleted' }; } } catch (error) { throw new BadRequestException(error.toString()); } } public async refreshToken(_: InstanceDto, oldToken: OldToken) { this.logger.verbose('requested refreshToken'); return await this.authService.refreshToken(oldToken); } }