Team Ai
Apppublic

oex2003/evolution-api

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
instance.controller.ts446 linesDownload Raw Back to controllers
1import { InstanceDto, SetPresenceDto } from '@api/dto/instance.dto';2import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service';3import { ProviderFiles } from '@api/provider/sessions';4import { PrismaRepository } from '@api/repository/repository.service';5import { channelController, eventManager } from '@api/server.module';6import { CacheService } from '@api/services/cache.service';7import { WAMonitoringService } from '@api/services/monitor.service';8import { SettingsService } from '@api/services/settings.service';9import { Events, Integration, wa } from '@api/types/wa.types';10import { Auth, Chatwoot, ConfigService, HttpServer, WaBusiness } from '@config/env.config';11import { Logger } from '@config/logger.config';12import { BadRequestException, InternalServerErrorException, UnauthorizedException } from '@exceptions';13import { delay } from 'baileys';14import { isArray, isURL } from 'class-validator';15import EventEmitter2 from 'eventemitter2';16import { v4 } from 'uuid';17 18import { ProxyController } from './proxy.controller';19 20export class InstanceController {21  constructor(22    private readonly waMonitor: WAMonitoringService,23    private readonly configService: ConfigService,24    private readonly prismaRepository: PrismaRepository,25    private readonly eventEmitter: EventEmitter2,26    private readonly chatwootService: ChatwootService,27    private readonly settingsService: SettingsService,28    private readonly proxyService: ProxyController,29    private readonly cache: CacheService,30    private readonly chatwootCache: CacheService,31    private readonly baileysCache: CacheService,32    private readonly providerFiles: ProviderFiles,33  ) {}34 35  private readonly logger = new Logger('InstanceController');36 37  public async createInstance(instanceData: InstanceDto) {38    try {39      const instance = channelController.init(instanceData, {40        configService: this.configService,41        eventEmitter: this.eventEmitter,42        prismaRepository: this.prismaRepository,43        cache: this.cache,44        chatwootCache: this.chatwootCache,45        baileysCache: this.baileysCache,46        providerFiles: this.providerFiles,47      });48 49      if (!instance) {50        throw new BadRequestException('Invalid integration');51      }52 53      const instanceId = v4();54 55      instanceData.instanceId = instanceId;56 57      let hash: string;58 59      if (!instanceData.token) hash = v4().toUpperCase();60      else hash = instanceData.token;61 62      await this.waMonitor.saveInstance({63        instanceId,64        integration: instanceData.integration,65        instanceName: instanceData.instanceName,66        ownerJid: instanceData.ownerJid,67        profileName: instanceData.profileName,68        profilePicUrl: instanceData.profilePicUrl,69        hash,70        number: instanceData.number,71        businessId: instanceData.businessId,72        status: instanceData.status,73      });74 75      instance.setInstance({76        instanceName: instanceData.instanceName,77        instanceId,78        integration: instanceData.integration,79        token: hash,80        number: instanceData.number,81        businessId: instanceData.businessId,82      });83 84      this.waMonitor.waInstances[instance.instanceName] = instance;85      this.waMonitor.delInstanceTime(instance.instanceName);86 87      // set events88      await eventManager.setInstance(instance.instanceName, instanceData);89 90      instance.sendDataWebhook(Events.INSTANCE_CREATE, {91        instanceName: instanceData.instanceName,92        instanceId: instanceId,93      });94 95      if (instanceData.proxyHost && instanceData.proxyPort && instanceData.proxyProtocol) {96        const testProxy = await this.proxyService.testProxy({97          host: instanceData.proxyHost,98          port: instanceData.proxyPort,99          protocol: instanceData.proxyProtocol,100          username: instanceData.proxyUsername,101          password: instanceData.proxyPassword,102        });103        if (!testProxy) {104          throw new BadRequestException('Invalid proxy');105        }106 107        await this.proxyService.createProxy(instance, {108          enabled: true,109          host: instanceData.proxyHost,110          port: instanceData.proxyPort,111          protocol: instanceData.proxyProtocol,112          username: instanceData.proxyUsername,113          password: instanceData.proxyPassword,114        });115      }116 117      const settings: wa.LocalSettings = {118        rejectCall: instanceData.rejectCall === true,119        msgCall: instanceData.msgCall || '',120        groupsIgnore: instanceData.groupsIgnore === true,121        alwaysOnline: instanceData.alwaysOnline === true,122        readMessages: instanceData.readMessages === true,123        readStatus: instanceData.readStatus === true,124        syncFullHistory: instanceData.syncFullHistory === true,125        wavoipToken: instanceData.wavoipToken || '',126      };127 128      await this.settingsService.create(instance, settings);129 130      let webhookWaBusiness = null,131        accessTokenWaBusiness = '';132 133      if (instanceData.integration === Integration.WHATSAPP_BUSINESS) {134        if (!instanceData.number) {135          throw new BadRequestException('number is required');136        }137        const urlServer = this.configService.get<HttpServer>('SERVER').URL;138        webhookWaBusiness = `${urlServer}/webhook/meta`;139        accessTokenWaBusiness = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;140      }141 142      if (!instanceData.chatwootAccountId || !instanceData.chatwootToken || !instanceData.chatwootUrl) {143        let getQrcode: wa.QrCode;144 145        if (instanceData.qrcode && instanceData.integration === Integration.WHATSAPP_BAILEYS) {146          await instance.connectToWhatsapp(instanceData.number);147          await delay(5000);148          getQrcode = instance.qrCode;149        }150 151        const result = {152          instance: {153            instanceName: instance.instanceName,154            instanceId: instanceId,155            integration: instanceData.integration,156            webhookWaBusiness,157            accessTokenWaBusiness,158            status: instance.connectionStatus.state,159          },160          hash,161          webhook: {162            webhookUrl: instanceData?.webhook?.url,163            webhookHeaders: instanceData?.webhook?.headers,164            webhookByEvents: instanceData?.webhook?.byEvents,165            webhookBase64: instanceData?.webhook?.base64,166          },167          websocket: {168            enabled: instanceData?.websocket?.enabled,169          },170          rabbitmq: {171            enabled: instanceData?.rabbitmq?.enabled,172          },173          nats: {174            enabled: instanceData?.nats?.enabled,175          },176          sqs: {177            enabled: instanceData?.sqs?.enabled,178          },179          settings,180          qrcode: getQrcode,181        };182 183        return result;184      }185 186      if (!this.configService.get<Chatwoot>('CHATWOOT').ENABLED)187        throw new BadRequestException('Chatwoot is not enabled');188 189      if (!instanceData.chatwootAccountId) {190        throw new BadRequestException('accountId is required');191      }192 193      if (!instanceData.chatwootToken) {194        throw new BadRequestException('token is required');195      }196 197      if (!instanceData.chatwootUrl) {198        throw new BadRequestException('url is required');199      }200 201      if (!isURL(instanceData.chatwootUrl, { require_tld: false })) {202        throw new BadRequestException('Invalid "url" property in chatwoot');203      }204 205      if (instanceData.chatwootSignMsg !== true && instanceData.chatwootSignMsg !== false) {206        throw new BadRequestException('signMsg is required');207      }208 209      if (instanceData.chatwootReopenConversation !== true && instanceData.chatwootReopenConversation !== false) {210        throw new BadRequestException('reopenConversation is required');211      }212 213      if (instanceData.chatwootConversationPending !== true && instanceData.chatwootConversationPending !== false) {214        throw new BadRequestException('conversationPending is required');215      }216 217      const urlServer = this.configService.get<HttpServer>('SERVER').URL;218 219      try {220        this.chatwootService.create(instance, {221          enabled: true,222          accountId: instanceData.chatwootAccountId,223          token: instanceData.chatwootToken,224          url: instanceData.chatwootUrl,225          signMsg: instanceData.chatwootSignMsg || false,226          nameInbox: instanceData.chatwootNameInbox ?? instance.instanceName.split('-cwId-')[0],227          number: instanceData.number,228          reopenConversation: instanceData.chatwootReopenConversation || false,229          conversationPending: instanceData.chatwootConversationPending || false,230          importContacts: instanceData.chatwootImportContacts ?? true,231          mergeBrazilContacts: instanceData.chatwootMergeBrazilContacts ?? false,232          importMessages: instanceData.chatwootImportMessages ?? true,233          daysLimitImportMessages: instanceData.chatwootDaysLimitImportMessages ?? 60,234          organization: instanceData.chatwootOrganization,235          logo: instanceData.chatwootLogo,236          autoCreate: instanceData.chatwootAutoCreate !== false,237        });238      } catch (error) {239        this.logger.log(error);240      }241 242      return {243        instance: {244          instanceName: instance.instanceName,245          instanceId: instanceId,246          integration: instanceData.integration,247          webhookWaBusiness,248          accessTokenWaBusiness,249          status: instance.connectionStatus.state,250        },251        hash,252        webhook: {253          webhookUrl: instanceData?.webhook?.url,254          webhookHeaders: instanceData?.webhook?.headers,255          webhookByEvents: instanceData?.webhook?.byEvents,256          webhookBase64: instanceData?.webhook?.base64,257        },258        websocket: {259          enabled: instanceData?.websocket?.enabled,260        },261        rabbitmq: {262          enabled: instanceData?.rabbitmq?.enabled,263        },264        nats: {265          enabled: instanceData?.nats?.enabled,266        },267        sqs: {268          enabled: instanceData?.sqs?.enabled,269        },270        settings,271        chatwoot: {272          enabled: true,273          accountId: instanceData.chatwootAccountId,274          token: instanceData.chatwootToken,275          url: instanceData.chatwootUrl,276          signMsg: instanceData.chatwootSignMsg || false,277          reopenConversation: instanceData.chatwootReopenConversation || false,278          conversationPending: instanceData.chatwootConversationPending || false,279          mergeBrazilContacts: instanceData.chatwootMergeBrazilContacts ?? false,280          importContacts: instanceData.chatwootImportContacts ?? true,281          importMessages: instanceData.chatwootImportMessages ?? true,282          daysLimitImportMessages: instanceData.chatwootDaysLimitImportMessages || 60,283          number: instanceData.number,284          nameInbox: instanceData.chatwootNameInbox ?? instance.instanceName,285          webhookUrl: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,286        },287      };288    } catch (error) {289      this.waMonitor.deleteInstance(instanceData.instanceName);290      this.logger.error(isArray(error.message) ? error.message[0] : error.message);291      throw new BadRequestException(isArray(error.message) ? error.message[0] : error.message);292    }293  }294 295  public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) {296    try {297      const instance = this.waMonitor.waInstances[instanceName];298      const state = instance?.connectionStatus?.state;299 300      if (!state) {301        throw new BadRequestException('The "' + instanceName + '" instance does not exist');302      }303 304      if (state == 'open') {305        return await this.connectionState({ instanceName });306      }307 308      if (state == 'connecting') {309        return instance.qrCode;310      }311 312      if (state == 'close') {313        await instance.connectToWhatsapp(number);314 315        await delay(2000);316        return instance.qrCode;317      }318 319      return {320        instance: {321          instanceName: instanceName,322          status: state,323        },324        qrcode: instance?.qrCode,325      };326    } catch (error) {327      this.logger.error(error);328      return { error: true, message: error.toString() };329    }330  }331 332  public async restartInstance({ instanceName }: InstanceDto) {333    try {334      const instance = this.waMonitor.waInstances[instanceName];335      const state = instance?.connectionStatus?.state;336 337      if (!state) {338        throw new BadRequestException('The "' + instanceName + '" instance does not exist');339      }340 341      if (state == 'close') {342        throw new BadRequestException('The "' + instanceName + '" instance is not connected');343      } else if (state == 'open') {344        if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED) instance.clearCacheChatwoot();345        this.logger.info('restarting instance' + instanceName);346 347        instance.client?.ws?.close();348        instance.client?.end(new Error('restart'));349        return await this.connectToWhatsapp({ instanceName });350      } else if (state == 'connecting') {351        instance.client?.ws?.close();352        instance.client?.end(new Error('restart'));353        return await this.connectToWhatsapp({ instanceName });354      }355    } catch (error) {356      this.logger.error(error);357      return { error: true, message: error.toString() };358    }359  }360 361  public async connectionState({ instanceName }: InstanceDto) {362    return {363      instance: {364        instanceName: instanceName,365        state: this.waMonitor.waInstances[instanceName]?.connectionStatus?.state,366      },367    };368  }369 370  public async fetchInstances({ instanceName, instanceId, number }: InstanceDto, key: string) {371    const env = this.configService.get<Auth>('AUTHENTICATION').API_KEY;372 373    if (env.KEY !== key) {374      const instancesByKey = await this.prismaRepository.instance.findMany({375        where: {376          token: key,377          name: instanceName || undefined,378          id: instanceId || undefined,379        },380      });381 382      if (instancesByKey.length > 0) {383        const names = instancesByKey.map((instance) => instance.name);384 385        return this.waMonitor.instanceInfo(names);386      } else {387        throw new UnauthorizedException();388      }389    }390 391    if (instanceId || number) {392      return this.waMonitor.instanceInfoById(instanceId, number);393    }394 395    const instanceNames = instanceName ? [instanceName] : null;396 397    return this.waMonitor.instanceInfo(instanceNames);398  }399 400  public async setPresence({ instanceName }: InstanceDto, data: SetPresenceDto) {401    return await this.waMonitor.waInstances[instanceName].setPresence(data);402  }403 404  public async logout({ instanceName }: InstanceDto) {405    const { instance } = await this.connectionState({ instanceName });406 407    if (instance.state === 'close') {408      throw new BadRequestException('The "' + instanceName + '" instance is not connected');409    }410 411    try {412      this.waMonitor.waInstances[instanceName]?.logoutInstance();413 414      return { status: 'SUCCESS', error: false, response: { message: 'Instance logged out' } };415    } catch (error) {416      throw new InternalServerErrorException(error.toString());417    }418  }419 420  public async deleteInstance({ instanceName }: InstanceDto) {421    const { instance } = await this.connectionState({ instanceName });422    try {423      const waInstances = this.waMonitor.waInstances[instanceName];424      if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED) waInstances?.clearCacheChatwoot();425 426      if (instance.state === 'connecting' || instance.state === 'open') {427        await this.logout({ instanceName });428      }429 430      try {431        waInstances?.sendDataWebhook(Events.INSTANCE_DELETE, {432          instanceName,433          instanceId: waInstances.instanceId,434        });435      } catch (error) {436        this.logger.error(error);437      }438 439      this.eventEmitter.emit('remove.instance', instanceName, 'inner');440      return { status: 'SUCCESS', error: false, response: { message: 'Instance deleted' } };441    } catch (error) {442      throw new BadRequestException(error.toString());443    }444  }445}446