new(whatsapp.gblib): New WhatsApp provider: Meta.
This commit is contained in:
		
							parent
							
								
									3e9170d446
								
							
						
					
					
						commit
						37d9028f98
					
				
					 3 changed files with 88 additions and 43 deletions
				
			
		| 
						 | 
					@ -214,6 +214,7 @@
 | 
				
			||||||
    "vm2-process": "2.1.1",
 | 
					    "vm2-process": "2.1.1",
 | 
				
			||||||
    "walk-promise": "0.2.0",
 | 
					    "walk-promise": "0.2.0",
 | 
				
			||||||
    "washyourmouthoutwithsoap": "1.0.2",
 | 
					    "washyourmouthoutwithsoap": "1.0.2",
 | 
				
			||||||
 | 
					    "whatsapp-cloud-api": "^0.3.1",
 | 
				
			||||||
    "whatsapp-web.js": "https://github.com/Julzk/whatsapp-web.js/tarball/jkr_hotfix_7",
 | 
					    "whatsapp-web.js": "https://github.com/Julzk/whatsapp-web.js/tarball/jkr_hotfix_7",
 | 
				
			||||||
    "winston": "3.8.2",
 | 
					    "winston": "3.8.2",
 | 
				
			||||||
    "ws": "8.14.2",
 | 
					    "ws": "8.14.2",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -442,9 +442,24 @@ export class GBMinService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Setups official handler for WhatsApp.
 | 
					    // Setups official handler for WhatsApp.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GBServer.globals.server.post(`/${min.instance.botId}/whatsapp`, async (req, res) => {
 | 
					    GBServer.globals.server.all(`/${min.instance.botId}/whatsapp`, async (req, res) => {
 | 
				
			||||||
      const to = req.body.To.replace(/whatsapp\:\+/gi, '');
 | 
					
 | 
				
			||||||
      const whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
 | 
					      if (req.query['hub.mode'] === 'subscribe') {
 | 
				
			||||||
 | 
					        const val = req.query['hub.challenge'];
 | 
				
			||||||
 | 
					        res.send(val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let whatsAppDirectLine = min.whatsAppDirectLine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Not meta, multiples bots on root bot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!req.body.object) {
 | 
				
			||||||
 | 
					        const to = req.body.To.replace(/whatsapp\:\+/gi, '');
 | 
				
			||||||
 | 
					        whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
 | 
					      await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
 | 
				
			||||||
    }).bind(min);
 | 
					    }).bind(min);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -455,6 +470,11 @@ export class GBMinService {
 | 
				
			||||||
    if (!res) {
 | 
					    if (!res) {
 | 
				
			||||||
      return 'GeneralBots';
 | 
					      return 'GeneralBots';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (req.body.entry) {
 | 
				
			||||||
 | 
					      return 'meta';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (req.body?.AccountSid) {
 | 
					    if (req.body?.AccountSid) {
 | 
				
			||||||
      return 'official';
 | 
					      return 'official';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -637,10 +657,10 @@ export class GBMinService {
 | 
				
			||||||
    if (botId === '[default]' || botId === undefined) {
 | 
					    if (botId === '[default]' || botId === undefined) {
 | 
				
			||||||
      botId = GBConfigService.get('BOT_ID');
 | 
					      botId = GBConfigService.get('BOT_ID');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    // Loads by the botId itself or by the activationCode field.
 | 
					    // Loads by the botId itself or by the activationCode field.
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    let instance = await this.core.loadInstanceByBotId(botId);
 | 
					    let instance = await this.core.loadInstanceByBotId(botId);
 | 
				
			||||||
    if (instance === null) {
 | 
					    if (instance === null) {
 | 
				
			||||||
      instance = await this.core.loadInstanceByActivationCode(botId);
 | 
					      instance = await this.core.loadInstanceByActivationCode(botId);
 | 
				
			||||||
| 
						 | 
					@ -1076,7 +1096,7 @@ export class GBMinService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Required for F0 handling of persisted conversations.
 | 
					        // Required for F0 handling of persisted conversations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        GBLogEx.info(min, 
 | 
					        GBLogEx.info(min,
 | 
				
			||||||
          `Input> ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})`
 | 
					          `Input> ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})`
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1114,7 +1134,7 @@ export class GBMinService {
 | 
				
			||||||
              ) {
 | 
					              ) {
 | 
				
			||||||
                min['conversationWelcomed'][step.context.activity.conversation.id] = true;
 | 
					                min['conversationWelcomed'][step.context.activity.conversation.id] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                GBLogEx.info(min, 
 | 
					                GBLogEx.info(min,
 | 
				
			||||||
                  `Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
 | 
					                  `Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
 | 
					                await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
 | 
				
			||||||
| 
						 | 
					@ -1413,12 +1433,12 @@ export class GBMinService {
 | 
				
			||||||
        startDialog &&
 | 
					        startDialog &&
 | 
				
			||||||
        startDialog !== '' &&
 | 
					        startDialog !== '' &&
 | 
				
			||||||
        !min['conversationWelcomed'][step.context.activity.conversation.id] &&
 | 
					        !min['conversationWelcomed'][step.context.activity.conversation.id] &&
 | 
				
			||||||
        !min['apiConversations'][pid]  &&
 | 
					        !min['apiConversations'][pid] &&
 | 
				
			||||||
        !step.context.activity['group']
 | 
					        !step.context.activity['group']
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
        await sec.setParam(userId, 'welcomed', 'true');
 | 
					        await sec.setParam(userId, 'welcomed', 'true');
 | 
				
			||||||
        min['conversationWelcomed'][step.context.activity.conversation.id] = true;
 | 
					        min['conversationWelcomed'][step.context.activity.conversation.id] = true;
 | 
				
			||||||
        GBLogEx.info(min, 
 | 
					        GBLogEx.info(min,
 | 
				
			||||||
          `Auto start (4) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
 | 
					          `Auto start (4) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
 | 
					        await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
 | 
				
			||||||
| 
						 | 
					@ -1625,26 +1645,25 @@ export class GBMinService {
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          let pid = data?.pid;
 | 
					          let pid = data?.pid;
 | 
				
			||||||
          if (script === 'start'){
 | 
					          if (script === 'start') {
 | 
				
			||||||
            pid = GBVMService.createProcessInfo(user, min, 'api', null);
 | 
					            pid = GBVMService.createProcessInfo(user, min, 'api', null);
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
          
 | 
					 | 
				
			||||||
          const client = await new SwaggerClient({
 | 
					 | 
				
			||||||
            spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
 | 
					 | 
				
			||||||
            requestInterceptor: req => {
 | 
					 | 
				
			||||||
              req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          const response = await client.apis.Conversations.Conversations_StartConversation();
 | 
					 | 
				
			||||||
          
 | 
					 | 
				
			||||||
          min['apiConversations'][pid] = {conversation: response.obj, client: client};
 | 
					 | 
				
			||||||
          min['conversationWelcomed'][response.obj.id] = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          let ret =  await GBVMService.callVM(script, min, null, pid, false, data);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (script === 'start')
 | 
					            const client = await new SwaggerClient({
 | 
				
			||||||
          {
 | 
					              spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
 | 
				
			||||||
 | 
					              requestInterceptor: req => {
 | 
				
			||||||
 | 
					                req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            const response = await client.apis.Conversations.Conversations_StartConversation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            min['apiConversations'][pid] = { conversation: response.obj, client: client };
 | 
				
			||||||
 | 
					            min['conversationWelcomed'][response.obj.id] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          let ret = await GBVMService.callVM(script, min, null, pid, false, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (script === 'start') {
 | 
				
			||||||
            ret = pid;
 | 
					            ret = pid;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          return ret;
 | 
					          return ret;
 | 
				
			||||||
| 
						 | 
					@ -1680,7 +1699,7 @@ export class GBMinService {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GBServer.globals.server.apiServer = createKoaHttpServer(
 | 
					    GBServer.globals.server.apiServer = createKoaHttpServer(
 | 
				
			||||||
      GBVMService.API_PORT, 
 | 
					      GBVMService.API_PORT,
 | 
				
			||||||
      getRemoteId, { prefix: `api/v3` });
 | 
					      getRemoteId, { prefix: `api/v3` });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    createRpcServer(
 | 
					    createRpcServer(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@ const { WAState, Client, MessageMedia } = pkg;
 | 
				
			||||||
import twilio from 'twilio';
 | 
					import twilio from 'twilio';
 | 
				
			||||||
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
 | 
					import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
 | 
				
			||||||
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
 | 
					import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
 | 
				
			||||||
 | 
					import { createBot } from 'whatsapp-cloud-api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Support for Whatsapp.
 | 
					 * Support for Whatsapp.
 | 
				
			||||||
| 
						 | 
					@ -103,9 +103,8 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
    this.whatsappServiceKey = whatsappServiceKey;
 | 
					    this.whatsappServiceKey = whatsappServiceKey;
 | 
				
			||||||
    this.whatsappServiceNumber = whatsappServiceNumber;
 | 
					    this.whatsappServiceNumber = whatsappServiceNumber;
 | 
				
			||||||
    this.whatsappServiceUrl = whatsappServiceUrl;
 | 
					    this.whatsappServiceUrl = whatsappServiceUrl;
 | 
				
			||||||
    this.provider =
 | 
					    this.provider = whatsappServiceKey === 'internal'
 | 
				
			||||||
      whatsappServiceKey === 'internal'
 | 
					        ? 'GeneralBots' : 'meta';
 | 
				
			||||||
        ? 'GeneralBots' : 'official';
 | 
					 | 
				
			||||||
    this.groupId = groupId;
 | 
					    this.groupId = groupId;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -253,12 +252,8 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static providerFromRequest(req: any) {
 | 
					 | 
				
			||||||
    return req.body.ProfileName ? 'official' : 'GeneralBots';
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public async received(req, res) {
 | 
					  public async received(req, res) {
 | 
				
			||||||
    const provider = WhatsappDirectLine.providerFromRequest(req);
 | 
					    const provider = GBMinService.getProviderName(req, res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let message, to, from, fromName, text: string;
 | 
					    let message, to, from, fromName, text: string;
 | 
				
			||||||
    let group = '';
 | 
					    let group = '';
 | 
				
			||||||
| 
						 | 
					@ -266,7 +261,13 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
    let attachments = null;
 | 
					    let attachments = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (provider) {
 | 
					    switch (provider) {
 | 
				
			||||||
 | 
					      case 'meta':
 | 
				
			||||||
 | 
					        from = req.body.entry[0].changes[0].value.messages[0].from;
 | 
				
			||||||
 | 
					        text = req.body.entry[0].changes[0].value.messages[0].text.body
 | 
				
			||||||
 | 
					        to = this.min.core.getParam<string>(this.min.instance, 'Bot Number', null);
 | 
				
			||||||
 | 
					        fromName = req.body.entry[0].changes[0].value.contacts[0].profile.name;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
      case 'official':
 | 
					      case 'official':
 | 
				
			||||||
        message = req.body;
 | 
					        message = req.body;
 | 
				
			||||||
        from = req.body.From.replace(/whatsapp\:\+/gi, '');
 | 
					        from = req.body.From.replace(/whatsapp\:\+/gi, '');
 | 
				
			||||||
| 
						 | 
					@ -512,7 +513,7 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        WhatsappDirectLine.mobiles[generatedConversationId] = from;
 | 
					        WhatsappDirectLine.mobiles[generatedConversationId] = from;
 | 
				
			||||||
        WhatsappDirectLine.usernames[from] = fromName;
 | 
					        WhatsappDirectLine.usernames[from] = fromName;
 | 
				
			||||||
        WhatsappDirectLine.chatIds[generatedConversationId] = message.chatId;
 | 
					        WhatsappDirectLine.chatIds[generatedConversationId] = message?.chatId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.pollMessages(client, generatedConversationId, from, fromName);
 | 
					        this.pollMessages(client, generatedConversationId, from, fromName);
 | 
				
			||||||
        this.inputMessage(client, generatedConversationId, text, from, fromName, group, attachments);
 | 
					        this.inputMessage(client, generatedConversationId, text, from, fromName, group, attachments);
 | 
				
			||||||
| 
						 | 
					@ -594,7 +595,8 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
        await this.printMessages(response.obj.activities, conversationId, from, fromName);
 | 
					        await this.printMessages(response.obj.activities, conversationId, from, fromName);
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
        GBLog.error(
 | 
					        GBLog.error(
 | 
				
			||||||
          `Error calling printMessages on Whatsapp channel ${err.data === undefined ? err : err.data} ${err.errObj ? err.errObj.message : ''
 | 
					          `Error calling printMessages on Whatsapp channel ${err.data === undefined ? 
 | 
				
			||||||
 | 
					            err : err.data} ${err.errObj ? err.errObj.message : ''
 | 
				
			||||||
          }`
 | 
					          }`
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -718,17 +720,27 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return await this.sendTextAsAudioToDevice(to, msg, chatId);
 | 
					      return await this.sendTextAsAudioToDevice(to, msg, chatId);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      let options;
 | 
					      let options, messages;
 | 
				
			||||||
 | 
					      const botNumber = this.min.core.getParam(this.min.instance, 'Bot Number', null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      switch (this.provider) {
 | 
					      switch (this.provider) {
 | 
				
			||||||
 | 
					        case 'meta':
 | 
				
			||||||
 | 
					          const bot = createBot(this.min.instance.whatsappServiceNumber,
 | 
				
			||||||
 | 
					             this.min.instance.whatsappServiceKey);
 | 
				
			||||||
 | 
					             messages = msg.match(/(.|[\r\n]){1,4096}/g)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             await CollectionUtil.asyncForEach(messages, async msg => {
 | 
				
			||||||
 | 
					               await GBUtil.sleep(3000);
 | 
				
			||||||
 | 
					               await bot.sendText(to, msg);
 | 
				
			||||||
 | 
					             });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
        case 'official':
 | 
					        case 'official':
 | 
				
			||||||
          const botNumber = this.min.core.getParam(this.min.instance, 'Bot Number', null);
 | 
					 | 
				
			||||||
          if (to.charAt(0) !== '+') {
 | 
					          if (to.charAt(0) !== '+') {
 | 
				
			||||||
            to = `+${to}`
 | 
					            to = `+${to}`
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          let messages = msg.match(/(.|[\r\n]){1,1000}/g)
 | 
					           messages = msg.match(/(.|[\r\n]){1,1000}/g)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          await CollectionUtil.asyncForEach(messages, async msg => {
 | 
					          await CollectionUtil.asyncForEach(messages, async msg => {
 | 
				
			||||||
            await GBUtil.sleep(3000);
 | 
					            await GBUtil.sleep(3000);
 | 
				
			||||||
| 
						 | 
					@ -793,6 +805,19 @@ export class WhatsappDirectLine extends GBService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      switch (provider) {
 | 
					      switch (provider) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 'meta':
 | 
				
			||||||
 | 
					          if (req.body.entry[0].changes[0].value.statuses){
 | 
				
			||||||
 | 
					            GBLogEx.info(this.min, `WhatsApp: ${req.body.entry[0].changes[0].value.statuses[0].status}.`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          id = req.body.entry[0].changes[0].value.messages[0].from;
 | 
				
			||||||
 | 
					          text = req.body.entry[0].changes[0].value.messages[0].text.body
 | 
				
			||||||
 | 
					          senderName = req.body.entry[0].changes[0].value.contacts[0].profile.name;
 | 
				
			||||||
 | 
					          botId = this.botId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case 'official':
 | 
					        case 'official':
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          const { body } = req;
 | 
					          const { body } = req;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue