new(whatsapp.gblib): General Bots WhatsApp provider.
This commit is contained in:
parent
844004fa01
commit
9e82beaf19
7 changed files with 466 additions and 223 deletions
57
package-lock.json
generated
57
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "botserver",
|
||||
"version": "2.0.153",
|
||||
"version": "2.0.156",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -3240,6 +3240,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-0.18.2.tgz",
|
||||
"integrity": "sha512-+0P+PrP9qSFVaayNdek4P1OAGE+PEl2SsufuHDRmUpOY25Wzjo7Atyar56Trjc32jkNy4lID6ZFT6BahsR9P9A=="
|
||||
},
|
||||
"@pedroslopez/moduleraid": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@pedroslopez/moduleraid/-/moduleraid-5.0.2.tgz",
|
||||
"integrity": "sha512-wtnBAETBVYZ9GvcbgdswRVSLkFkYAGv1KzwBBTeRXvGT9sb9cPllOgFFWXCn9PyARQ0H+Ijz6mmoRrGateUDxQ=="
|
||||
},
|
||||
"@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
|
@ -12464,6 +12469,15 @@
|
|||
"integrity": "sha512-iEjGZ94OBMcESxnLorXNjJmtd/JtQYXUVrQpfwvtAKkuyawRmv+2LM6nqyOsOJkISEYbyY6ziudRE0u4VyPSVA==",
|
||||
"dev": true
|
||||
},
|
||||
"fluent-ffmpeg": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
|
||||
"integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==",
|
||||
"requires": {
|
||||
"async": ">=0.2.9",
|
||||
"which": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"fn.name": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||
|
@ -15166,6 +15180,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"jsqr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz",
|
||||
"integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A=="
|
||||
},
|
||||
"jszip": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
|
||||
|
@ -17223,6 +17242,11 @@
|
|||
"resolved": "https://registry.npmjs.org/node-tesseract-ocr/-/node-tesseract-ocr-2.2.1.tgz",
|
||||
"integrity": "sha512-Q9cD79JGpPNQBxbi1fV+OAsTxYKLpx22sagsxSyKbu1u+t6UarApf5m32uVc8a5QAP1Wk7fIPN0aJFGGEE9DyQ=="
|
||||
},
|
||||
"node-webpmux": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-webpmux/-/node-webpmux-3.1.1.tgz",
|
||||
"integrity": "sha512-vG75BAe9zKghN+Y+XsJMPdOfVyesn1MmGvd/DMxeQ6gtpB3U053yCWXO1Gl2QWXTfU1++7flTihv/yB6EEdtKQ=="
|
||||
},
|
||||
"nodesecurity-npm-utils": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nodesecurity-npm-utils/-/nodesecurity-npm-utils-6.0.0.tgz",
|
||||
|
@ -20475,6 +20499,11 @@
|
|||
"uniq": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"pptxtemplater": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pptxtemplater/-/pptxtemplater-1.0.5.tgz",
|
||||
"integrity": "sha512-Fr9LzjpHMG12bq1gps3i9Jy75aVVXRUzv0fPnOsd0DfZnt7doPTulkXD6mjmTf30PrEu3YvIhZSsn5R88Lylmg=="
|
||||
},
|
||||
"pragmatismo-io-framework": {
|
||||
"version": "1.0.20",
|
||||
"resolved": "https://registry.npmjs.org/pragmatismo-io-framework/-/pragmatismo-io-framework-1.0.20.tgz",
|
||||
|
@ -21362,6 +21391,11 @@
|
|||
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
|
||||
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
|
||||
},
|
||||
"qrcode-terminal": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz",
|
||||
"integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
|
@ -25939,6 +25973,27 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"whatsapp-web.js": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/whatsapp-web.js/-/whatsapp-web.js-1.17.0.tgz",
|
||||
"integrity": "sha512-oLl5ExnBOt0d6oIx+ksRJKkMNshnmg5b2fU2LKMPCF7XtRaAcwtrdZSEXSnBLAQShJWKMKrG5l0D32U3hOub/w==",
|
||||
"requires": {
|
||||
"@pedroslopez/moduleraid": "^5.0.2",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"jsqr": "^1.3.1",
|
||||
"mime": "^3.0.0",
|
||||
"node-fetch": "^2.6.5",
|
||||
"node-webpmux": "^3.1.0",
|
||||
"puppeteer": "^13.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mime": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
|
|
|
@ -107,10 +107,12 @@
|
|||
"pdf-extraction": "1.0.2",
|
||||
"pdfkit": "^0.13.0",
|
||||
"phone": "2.4.21",
|
||||
"pptxtemplater": "1.0.5",
|
||||
"pragmatismo-io-framework": "1.0.20",
|
||||
"prism-media": "1.3.1",
|
||||
"public-ip": "4.0.4",
|
||||
"puppeteer": "13.7.0",
|
||||
"qrcode-terminal": "0.12.0",
|
||||
"readline": "1.3.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"request-promise": "4.2.5",
|
||||
|
@ -134,7 +136,8 @@
|
|||
"url-join": "4.0.1",
|
||||
"vbscript-to-typescript": "1.0.8",
|
||||
"walk-promise": "0.2.0",
|
||||
"washyourmouthoutwithsoap": "1.0.2"
|
||||
"washyourmouthoutwithsoap": "1.0.2",
|
||||
"whatsapp-web.js": "1.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/puppeteer": "5.4.6",
|
||||
|
|
|
@ -48,6 +48,9 @@ const DateDiff = require('date-diff');
|
|||
const puppeteer = require('puppeteer');
|
||||
const Path = require('path');
|
||||
const sgMail = require('@sendgrid/mail');
|
||||
const PizZip = require("pizzip");
|
||||
const Docxtemplater = require("docxtemplater");
|
||||
var mammoth = require("mammoth");
|
||||
|
||||
/**
|
||||
* Base services of conversation to be called by BASIC which
|
||||
|
@ -564,6 +567,14 @@ export class DialogKeywords {
|
|||
GBLog.info(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`);
|
||||
const emailToken = process.env.EMAIL_API_KEY;
|
||||
|
||||
// Inline word document used as e-mail body.
|
||||
|
||||
if (typeof (body) === "object" )
|
||||
{
|
||||
const result = await mammoth.convertToHtml({buffer: body});
|
||||
body = result.value;
|
||||
}
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
sgMail.setApiKey(emailToken);
|
||||
const msg = {
|
||||
|
|
|
@ -475,6 +475,10 @@ export class GBVMService extends GBService {
|
|||
return `${$1} = sys().asPdf(${$2})\n`;
|
||||
});
|
||||
|
||||
code = code.replace(/(\w+)\s*\=\s*FILL\s(.*)\sWITH\s(.*)/gi, ($0, $1, $2, $3) => {
|
||||
return `${1} = sys().fill(${$2}, ${$1})\n`;
|
||||
});
|
||||
|
||||
code = code.replace(/save\s(.*)\sas\s(.*)/gi, ($0, $1, $2, $3) => {
|
||||
return `sys().saveFile(${$2}, ${$1})\n`;
|
||||
});
|
||||
|
|
|
@ -30,17 +30,15 @@
|
|||
| |
|
||||
\*****************************************************************************/
|
||||
'use strict';
|
||||
import { GBDialogStep, GBLog, GBMinInstance } from 'botlib';
|
||||
import { GBLog, GBMinInstance } from 'botlib';
|
||||
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
||||
import { CollectionUtil } from 'pragmatismo-io-framework';
|
||||
import * as request from 'request-promise-native';
|
||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||
import { DialogKeywords } from './DialogKeywords';
|
||||
import { Tabulator } from 'tabulator-tables';
|
||||
import { GBServer } from '../../../src/app';
|
||||
import * as fs from 'fs';
|
||||
import { jar } from 'request-promise';
|
||||
const Fs = require('fs');
|
||||
const Excel = require('exceljs');
|
||||
|
||||
|
@ -51,7 +49,9 @@ const Path = require('path');
|
|||
const ComputerVisionClient = require('@azure/cognitiveservices-computervision').ComputerVisionClient;
|
||||
const ApiKeyCredentials = require('@azure/ms-rest-js').ApiKeyCredentials;
|
||||
const alasql = require('alasql');
|
||||
const DateDiff = require('date-diff');
|
||||
const PizZip = require("pizzip");
|
||||
const Docxtemplater = require("docxtemplater");
|
||||
const pptxTemplaterModule = require('pptxtemplater');
|
||||
|
||||
|
||||
/**
|
||||
|
@ -194,20 +194,19 @@ export class SystemKeywords {
|
|||
if (isObject || JSON.parse(data) !== null) {
|
||||
|
||||
let keys = Object.keys(data[0]);
|
||||
|
||||
|
||||
|
||||
if (headers) {
|
||||
output[0] = [];
|
||||
// Copies headers as the first element.
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
|
||||
|
||||
output[0][i] = keys[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output.push({ 'gbarray': '0' }); ;
|
||||
else {
|
||||
output.push({ 'gbarray': '0' });;
|
||||
}
|
||||
|
||||
// Copies data from JSON format into simple array.
|
||||
|
@ -504,7 +503,8 @@ export class SystemKeywords {
|
|||
* @exaple SAVE variable as "my.txt"
|
||||
*
|
||||
*/
|
||||
public async saveFile(file: string, data: any): Promise<any> {
|
||||
public async saveFile(file: any, data: any): Promise<any> {
|
||||
|
||||
GBLog.info(`BASIC: Saving '${file}' (SAVE file).`);
|
||||
let [baseUrl, client] = await GBDeployer.internalGetDriveClient(this.min);
|
||||
const botId = this.min.instance.botId;
|
||||
|
@ -816,6 +816,7 @@ export class SystemKeywords {
|
|||
let foundIndex = 1;
|
||||
|
||||
// Fills the row variable.
|
||||
|
||||
let rowCount = 0;
|
||||
for (; foundIndex < rows.length; foundIndex++) {
|
||||
let filterAcceptCount = 0;
|
||||
|
@ -952,8 +953,8 @@ export class SystemKeywords {
|
|||
}
|
||||
row[propertyName] = value;
|
||||
}
|
||||
row['line'] = rowCount;
|
||||
row['originalLine'] = foundIndex + 1;
|
||||
row['ordinal'] = rowCount;
|
||||
row['line'] = foundIndex + 1;
|
||||
table.push(row);
|
||||
}
|
||||
|
||||
|
@ -1393,4 +1394,45 @@ export class SystemKeywords {
|
|||
return text.replace(/\D/gi, '');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Fills a .docx or .pptx with template data.
|
||||
*
|
||||
* doc = FILL "templates/template.docx", data
|
||||
*
|
||||
*/
|
||||
public async fill(templateName, data) {
|
||||
|
||||
const botId = this.min.instance.botId;
|
||||
const gbaiName = `${botId}.gbai`;
|
||||
const path = `/${botId}.gbai/${botId}.gbdata`;
|
||||
|
||||
// Downloads template from .gbdrive.
|
||||
|
||||
let [baseUrl, client] = await GBDeployer.internalGetDriveClient(this.min);
|
||||
let template = await this.internalGetDocument(client, baseUrl, path, templateName);
|
||||
const url = template['@microsoft.graph.downloadUrl'];
|
||||
const localName = Path.join('work', gbaiName, 'cache', ``);
|
||||
const response = await request({ uri: url, encoding: null });
|
||||
Fs.writeFileSync(localName, response, { encoding: null });
|
||||
|
||||
// Loads the file as binary content.
|
||||
|
||||
const content = fs.readFileSync(localName, "binary");
|
||||
const zip = new PizZip(content);
|
||||
const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true, });
|
||||
if (localName.endsWith('.pptx')) {
|
||||
doc.attachModule(pptxTemplaterModule);
|
||||
}
|
||||
|
||||
// Renders the document (Replace {first_name} by John, {last_name} by Doe, ...)
|
||||
|
||||
doc.render(data);
|
||||
|
||||
// Returns the buffer to be used with SAVE AS for example.
|
||||
|
||||
const buf = doc.getZip().generate({ type: "nodebuffer", compression: "DEFLATE", });
|
||||
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ const wash = require('washyourmouthoutwithsoap');
|
|||
const { FacebookAdapter } = require('botbuilder-adapter-facebook');
|
||||
const path = require('path');
|
||||
const { NerManager } = require('node-nlp');
|
||||
const mkdirp = require('mkdirp');
|
||||
import {
|
||||
AutoSaveStateMiddleware,
|
||||
BotFrameworkAdapter,
|
||||
|
@ -245,6 +246,16 @@ export class GBMinService {
|
|||
await this.deployer.deployPackage(min, packagePath);
|
||||
}
|
||||
|
||||
let dir = `work/${min.botId}.gbai/cache`;
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
mkdirp.sync(dir);
|
||||
}
|
||||
dir = `work/${min.botId}.gbai/profile`;
|
||||
if (!fs.existsSync(dir)) {
|
||||
mkdirp.sync(dir);
|
||||
}
|
||||
|
||||
// Loads Named Entity data for this bot.
|
||||
|
||||
await KBService.RefreshNER(min);
|
||||
|
@ -320,8 +331,11 @@ export class GBMinService {
|
|||
this.createCheckHealthAddress(GBServer.globals.server, min, min.instance);
|
||||
}
|
||||
|
||||
public static isChatAPI(req) {
|
||||
return req.body.phone_id ? false : true;
|
||||
public static isChatAPI(req, res) {
|
||||
if (!res) {
|
||||
return "GeneralBots";
|
||||
}
|
||||
return req.body.phone_id ? "maytapi" : "chatapi";
|
||||
}
|
||||
|
||||
private async WhatsAppCallback(req, res) {
|
||||
|
@ -334,72 +348,80 @@ export class GBMinService {
|
|||
return;
|
||||
}
|
||||
|
||||
let chatapi = GBMinService.isChatAPI(req);
|
||||
let provider = GBMinService.isChatAPI(req, res);
|
||||
let id = provider ? req.body.messages[0].author.split('@')[0] : req.body.user.phone;
|
||||
let senderName = provider ? req.body.messages[0].senderName : req.body.user.name;
|
||||
let botId;
|
||||
let text;
|
||||
|
||||
if (chatapi) {
|
||||
if (req.body.ack) {
|
||||
res.status(200);
|
||||
res.end();
|
||||
switch (provider) {
|
||||
case "GeneralBots":
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (req.body.type !== 'message') {
|
||||
res.status(200);
|
||||
res.end();
|
||||
id = req.body.messages[0].author.split('@')[0];
|
||||
senderName = req.body.messages[0].senderName;
|
||||
text = req.body;
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case "chatapi":
|
||||
|
||||
id = req.body.messages[0].author.split('@')[0];
|
||||
senderName = req.body.messages[0].senderName;
|
||||
text = req.body.messages[0].body;
|
||||
|
||||
if (req.body.ack) {
|
||||
res.status(200);
|
||||
res.end();
|
||||
|
||||
return;
|
||||
}
|
||||
if (req.body.messages[0].fromMe) {
|
||||
res.end();
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
botId = req.params.botId;
|
||||
if (botId === '[default]' || botId === undefined) {
|
||||
botId = GBConfigService.get('BOT_ID');
|
||||
}
|
||||
break;
|
||||
case "maytapi":
|
||||
|
||||
id = req.body.user.phone;
|
||||
senderName = req.body.user.name;
|
||||
text = req.body.message.text;
|
||||
|
||||
if (req.body.type !== 'message') {
|
||||
res.status(200);
|
||||
res.end();
|
||||
|
||||
return;
|
||||
}
|
||||
if (req.body.message.fromMe) {
|
||||
res.end();
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
botId = WhatsappDirectLine.phones[req.body.phoneId];
|
||||
break;
|
||||
}
|
||||
|
||||
// Detects if the message is echo from itself.
|
||||
|
||||
const id = chatapi ? req.body.messages[0].author.split('@')[0] : req.body.user.phone;
|
||||
const senderName = chatapi ? req.body.messages[0].senderName : req.body.user.name;
|
||||
const sec = new SecService();
|
||||
let user = await sec.getUserFromSystemId(id);
|
||||
|
||||
if (chatapi) {
|
||||
if (req.body.messages[0].fromMe) {
|
||||
res.end();
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
} else {
|
||||
if (req.body.message.fromMe) {
|
||||
res.end();
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
}
|
||||
|
||||
let botId;
|
||||
|
||||
if (chatapi) {
|
||||
botId = req.params.botId;
|
||||
if (botId === '[default]' || botId === undefined) {
|
||||
botId = GBConfigService.get('BOT_ID');
|
||||
}
|
||||
}
|
||||
else {
|
||||
botId = WhatsappDirectLine.phones[req.body.phoneId];
|
||||
}
|
||||
|
||||
GBLog.info(`A WhatsApp mobile requested instance for: ${botId}.`);
|
||||
|
||||
let urlMin: any = GBServer.globals.minInstances.filter
|
||||
(p => p.instance.botId === botId)[0];
|
||||
|
||||
const botNumber = urlMin ? urlMin.core.getParam(urlMin.instance, 'Bot Number', null) : null;
|
||||
|
||||
let activeMin;
|
||||
|
||||
// Processes group behaviour.
|
||||
|
||||
let text = chatapi ? req.body.messages[0].body : req.body.message.text;
|
||||
text = text.replace(/\@\d+ /gi, '');
|
||||
|
||||
if (chatapi) {
|
||||
if (provider === "chatapi") {
|
||||
|
||||
// Ensures that the bot group is the active bot for the user (like switching).
|
||||
|
||||
|
@ -452,10 +474,10 @@ export class GBMinService {
|
|||
|
||||
if (startDialog) {
|
||||
GBLog.info(`Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`);
|
||||
if (chatapi) {
|
||||
if (provider === "chatapi") {
|
||||
req.body.messages[0].body = `/start`;
|
||||
}
|
||||
else {
|
||||
else if (provider === "maytapi") {
|
||||
req.body.message = `/start`;
|
||||
}
|
||||
|
||||
|
@ -488,10 +510,10 @@ export class GBMinService {
|
|||
|
||||
if (startDialog) {
|
||||
GBLog.info(`Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`);
|
||||
if (chatapi) {
|
||||
if (provider === "chatapi") {
|
||||
req.body.messages[0].body = `/start`;
|
||||
}
|
||||
else {
|
||||
else if (provider === "maytapi") {
|
||||
req.body.message = `/start`;
|
||||
}
|
||||
|
||||
|
@ -821,7 +843,7 @@ export class GBMinService {
|
|||
// If there is WhatsApp configuration specified, initialize
|
||||
// infrastructure objects.
|
||||
|
||||
if (min.instance.whatsappServiceUrl) {
|
||||
if (min.instance.whatsappServiceKey) {
|
||||
min.whatsAppDirectLine = new WhatsappDirectLine(
|
||||
min,
|
||||
min.botId,
|
||||
|
@ -1024,10 +1046,6 @@ export class GBMinService {
|
|||
const folder = `work/${min.instance.botId}.gbai/cache`;
|
||||
const filename = `${GBAdminService.generateUuid()}.png`;
|
||||
|
||||
if (!Fs.existsSync(folder)) {
|
||||
Fs.mkdirSync(folder);
|
||||
}
|
||||
|
||||
Fs.writeFileSync(path.join(folder, filename), data);
|
||||
step.context.activity.text = urlJoin(GBServer.globals.publicAddress, `${min.instance.botId}`, 'cache', filename);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,10 @@ import { SecService } from '../../security.gbapp/services/SecService';
|
|||
import { Messages } from '../strings';
|
||||
import { GuaribasUser } from '../../security.gbapp/models';
|
||||
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords';
|
||||
const { MessageMedia, Client, LocalAuth } = require('whatsapp-web.js');
|
||||
const qrcode = require('qrcode-terminal');
|
||||
|
||||
/**
|
||||
* Support for Whatsapp.
|
||||
|
@ -68,8 +72,9 @@ export class WhatsappDirectLine extends GBService {
|
|||
public min: GBMinInstance;
|
||||
private directLineSecret: string;
|
||||
private locale: string = 'pt-BR';
|
||||
chatapi: any;
|
||||
provider: any;
|
||||
INSTANCE_URL = 'https://api.maytapi.com/api';
|
||||
private customClient;
|
||||
|
||||
constructor(
|
||||
min: GBMinInstance,
|
||||
|
@ -87,7 +92,8 @@ export class WhatsappDirectLine extends GBService {
|
|||
this.whatsappServiceKey = whatsappServiceKey;
|
||||
this.whatsappServiceNumber = whatsappServiceNumber;
|
||||
this.whatsappServiceUrl = whatsappServiceUrl;
|
||||
this.chatapi = whatsappServiceNumber.indexOf(';') > -1 ? false : false;
|
||||
this.provider = whatsappServiceKey === "gbnative" ?
|
||||
'GeneralBots' : whatsappServiceNumber.indexOf(';') > -1 ? 'maytapi' : 'chatapi';
|
||||
}
|
||||
|
||||
public static async asyncForEach(array, callback) {
|
||||
|
@ -111,44 +117,101 @@ export class WhatsappDirectLine extends GBService {
|
|||
);
|
||||
let options;
|
||||
|
||||
if (this.chatapi) {
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'webhook'),
|
||||
timeout: 10000,
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
webhookUrl: `${GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
|
||||
set: true
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
switch (this.provider) {
|
||||
case 'GeneralBots':
|
||||
|
||||
const Path = require('path');
|
||||
|
||||
const gbaiName = `${this.min.botId}.gbai`;
|
||||
let localName = Path.join('work', gbaiName, 'profile');
|
||||
|
||||
let client = this.customClient = new Client({
|
||||
authStrategy: new LocalAuth(),
|
||||
puppeteer: {
|
||||
headless: false, args: ['--disable-features=site-per-process',
|
||||
`--user-data-dir=${localName}`]
|
||||
}
|
||||
});
|
||||
|
||||
client.initialize();
|
||||
|
||||
client.on('message', (async message => {
|
||||
await this.received(message, null);
|
||||
}).bind(this));
|
||||
|
||||
client.on('qr', ((qr) => {
|
||||
|
||||
const adminNumber = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null);
|
||||
const adminEmail = this.min.core.getParam(this.min.instance, 'Bot Admin E-mail', null);
|
||||
|
||||
// Sends QR Code to boot bot admin.
|
||||
|
||||
const info = this.customClient.info;
|
||||
const msg = `Please, scan QR Code with ${info.wid.user}(${info.pushname}) for bot ${this.botId}: ${qr}.`;
|
||||
GBLog.info(msg);
|
||||
qrcode.generate(qr, { small: true, scale: 0.5 });
|
||||
|
||||
|
||||
// While handling other bots uses boot instance of this class to send QR Codes.
|
||||
|
||||
if (this.botId !== GBServer.globals.minBoot.botId) {
|
||||
GBServer.globals.minBoot.whatsAppDirectLine.sendMessage(msg);
|
||||
GBServer.globals.minBoot.whatsAppDirectLine.sendMessage(adminNumber, qr);
|
||||
|
||||
const s = new DialogKeywords(null, null, null, null);
|
||||
s.sendEmail(adminEmail, `Check your WhatsApp for bot ${this.botId}`, msg);
|
||||
}
|
||||
|
||||
}).bind(this));
|
||||
|
||||
client.on('authenticated', () => {
|
||||
GBLog.info(`WhatsApp QR Code authenticated for ${this.botId}.`);
|
||||
});
|
||||
|
||||
setUrl = false;
|
||||
break;
|
||||
|
||||
case 'chatapi':
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'webhook'),
|
||||
timeout: 10000,
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
webhookUrl: `${GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
|
||||
set: true
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case 'maytapi':
|
||||
|
||||
let phoneId = this.whatsappServiceNumber.split(';')[0];
|
||||
let productId = this.whatsappServiceNumber.split(';')[1]
|
||||
|
||||
let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/config`;
|
||||
WhatsappDirectLine.phones[phoneId] = this.botId;
|
||||
|
||||
options = {
|
||||
url: url,
|
||||
method: 'POST',
|
||||
body: {
|
||||
webhook: `${GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
|
||||
"ack_delivery": false
|
||||
},
|
||||
headers: {
|
||||
'x-maytapi-key': this.whatsappServiceKey,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json: true,
|
||||
};
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
||||
let phoneId = this.whatsappServiceNumber.split(';')[0];
|
||||
let productId = this.whatsappServiceNumber.split(';')[1]
|
||||
|
||||
let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/config`;
|
||||
WhatsappDirectLine.phones[phoneId] = this.botId;
|
||||
|
||||
options = {
|
||||
url: url,
|
||||
method: 'POST',
|
||||
body: {
|
||||
webhook: `${GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
|
||||
"ack_delivery": false
|
||||
},
|
||||
headers: {
|
||||
'x-maytapi-key': this.whatsappServiceKey,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json: true,
|
||||
};
|
||||
}
|
||||
if (setUrl) {
|
||||
const express = require('express');
|
||||
GBServer.globals.server.use(`/audios`, express.static('work'));
|
||||
|
@ -186,43 +249,54 @@ export class WhatsappDirectLine extends GBService {
|
|||
}
|
||||
|
||||
public async received(req, res) {
|
||||
if (this.chatapi && req.body.messages === undefined) {
|
||||
|
||||
if (this.provider === "chatapi" && req.body.messages === undefined) {
|
||||
res.end();
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
|
||||
|
||||
const message = this.chatapi ? req.body.messages[0] : req.body.message;
|
||||
let message, from, fromName, text;
|
||||
let group = "";
|
||||
|
||||
let answerText = null;
|
||||
|
||||
switch (this.provider) {
|
||||
case 'GeneralBots':
|
||||
message = req;
|
||||
break;
|
||||
|
||||
let text = this.chatapi ? message.body : message.text;
|
||||
text = text.replace(/\@\d+ /gi, '');
|
||||
case 'chatapi':
|
||||
message = req.body.messages[0];
|
||||
text = message.body;
|
||||
from = req.body.messages[0].author.split('@')[0];
|
||||
fromName = req.body.messages[0].senderName;
|
||||
|
||||
const from = this.chatapi ? req.body.messages[0].author.split('@')[0] : req.body.user.phone;
|
||||
const fromName = this.chatapi ? req.body.messages[0].senderName : req.body.user.name;
|
||||
if (req.body.messages[0].fromMe) {
|
||||
res.end();
|
||||
|
||||
GBLog.info(`GBWhatsapp: RCV ${from}(${fromName}): ${text})`);
|
||||
return; // Exit here.
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
if (this.chatapi) {
|
||||
if (req.body.messages[0].fromMe) {
|
||||
res.end();
|
||||
case 'maytapi':
|
||||
message = req.body.message;
|
||||
text = message.text;
|
||||
from = req.body.user.phone;
|
||||
fromName = req.body.user.name;
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
} else {
|
||||
if (req.body.message.fromMe) {
|
||||
res.end();
|
||||
if (req.body.message.fromMe) {
|
||||
res.end();
|
||||
|
||||
return; // Exit here.
|
||||
}
|
||||
return; // Exit here.
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.chatapi) {
|
||||
text = text.replace(/\@\d+ /gi, '');
|
||||
GBLog.info(`GBWhatsapp: RCV ${from}(${fromName}): ${text})`);
|
||||
|
||||
if (this.provider === "chatapi") {
|
||||
if (message.chatName.charAt(0) !== '+') {
|
||||
group = message.chatName;
|
||||
|
||||
|
@ -283,7 +357,6 @@ export class WhatsappDirectLine extends GBService {
|
|||
}
|
||||
|
||||
const botId = this.min.instance.botId;
|
||||
|
||||
const state = WhatsappDirectLine.state[botId + from];
|
||||
if (state) {
|
||||
WhatsappDirectLine.state[botId + from] = null;
|
||||
|
@ -302,7 +375,6 @@ export class WhatsappDirectLine extends GBService {
|
|||
|
||||
const user = await sec.ensureUser(this.min.instance.instanceId, from,
|
||||
fromName, '', 'whatsapp', fromName, null);
|
||||
|
||||
const locale = user.locale ? user.locale : 'pt';
|
||||
|
||||
if (answerText) {
|
||||
|
@ -315,7 +387,7 @@ export class WhatsappDirectLine extends GBService {
|
|||
|
||||
if (process.env.AUDIO_DISABLED !== 'true') {
|
||||
const options = {
|
||||
url: this.chatapi ? message.body : message.text,
|
||||
url: this.provider ? message.body : message.text,
|
||||
method: 'GET',
|
||||
encoding: 'binary'
|
||||
};
|
||||
|
@ -535,51 +607,57 @@ export class WhatsappDirectLine extends GBService {
|
|||
public async sendFileToDevice(to, url, filename, caption, chatId) {
|
||||
|
||||
let options;
|
||||
if (this.chatapi) {
|
||||
switch (this.provider) {
|
||||
case 'GeneralBots':
|
||||
const attachment = MessageMedia.fromurl(url);
|
||||
await this.customClient.sendMessage(to, attachment, { caption: caption });
|
||||
break;
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'sendFile'),
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
phone: chatId ? null : to,
|
||||
chatId: chatId,
|
||||
body: url,
|
||||
filename: filename,
|
||||
caption: caption
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
case 'chatapi':
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'sendFile'),
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
phone: chatId ? null : to,
|
||||
chatId: chatId,
|
||||
body: url,
|
||||
filename: filename,
|
||||
caption: caption
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case 'maytapi':
|
||||
|
||||
let contents = 0;
|
||||
let body = {
|
||||
to_number: to,
|
||||
type: "media",
|
||||
message: url,
|
||||
text: caption
|
||||
};
|
||||
|
||||
let phoneId = this.whatsappServiceNumber.split(';')[0];
|
||||
let productId = this.whatsappServiceNumber.split(';')[1]
|
||||
|
||||
options = {
|
||||
url: `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`,
|
||||
method: 'post',
|
||||
json: true,
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-maytapi-key': this.whatsappServiceKey,
|
||||
},
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
||||
let contents = 0;
|
||||
let body = {
|
||||
to_number: to,
|
||||
type: "media",
|
||||
message: url,
|
||||
text: caption
|
||||
};
|
||||
|
||||
let phoneId = this.whatsappServiceNumber.split(';')[0];
|
||||
let productId = this.whatsappServiceNumber.split(';')[1]
|
||||
|
||||
options = {
|
||||
url: `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`,
|
||||
method: 'post',
|
||||
json: true,
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-maytapi-key': this.whatsappServiceKey,
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
// tslint:disable-next-line: await-promise
|
||||
const result = await request.post(options);
|
||||
|
@ -590,21 +668,42 @@ export class WhatsappDirectLine extends GBService {
|
|||
}
|
||||
|
||||
public async sendAudioToDevice(to, url, chatId) {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'sendPTT'),
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
phone: chatId ? null : to,
|
||||
chatId: chatId,
|
||||
body: url
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
|
||||
let options;
|
||||
switch (this.provider) {
|
||||
case 'GeneralBots':
|
||||
|
||||
const attachment = MessageMedia.fromurl(url);
|
||||
await this.customClient.sendMessage(to, attachment);
|
||||
|
||||
break;
|
||||
|
||||
case 'chatapi':
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'sendPTT'),
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
phone: chatId ? null : to,
|
||||
chatId: chatId,
|
||||
body: url
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case 'maytapi':
|
||||
|
||||
options = {}; // TODO: Code Maytapi.
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// tslint:disable-next-line: await-promise
|
||||
const result = await request.post(options);
|
||||
GBLog.info(`Audio ${url} sent to ${to}: ${result}`);
|
||||
|
@ -635,48 +734,59 @@ export class WhatsappDirectLine extends GBService {
|
|||
} else {
|
||||
|
||||
let options;
|
||||
if (this.chatapi) {
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'message'),
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
phone: chatId ? null : to,
|
||||
chatId: chatId,
|
||||
body: msg
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
switch (this.provider) {
|
||||
case 'GeneralBots':
|
||||
|
||||
this.customClient.sendMessage(to, msg);
|
||||
|
||||
break;
|
||||
|
||||
case 'chatapi':
|
||||
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: urlJoin(this.whatsappServiceUrl, 'message'),
|
||||
qs: {
|
||||
token: this.whatsappServiceKey,
|
||||
phone: chatId ? null : to,
|
||||
chatId: chatId,
|
||||
body: msg
|
||||
},
|
||||
headers: {
|
||||
'cache-control': 'no-cache'
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'maytapi':
|
||||
let phoneId = this.whatsappServiceNumber.split(';')[0];
|
||||
let productId = this.whatsappServiceNumber.split(';')[1]
|
||||
|
||||
|
||||
let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`;
|
||||
|
||||
|
||||
options = {
|
||||
url: url,
|
||||
method: 'post',
|
||||
json: true,
|
||||
body: { type: 'text', message: msg, to_number: to },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-maytapi-key': this.whatsappServiceKey,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
||||
let phoneId = this.whatsappServiceNumber.split(';')[0];
|
||||
let productId = this.whatsappServiceNumber.split(';')[1]
|
||||
|
||||
|
||||
let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`;
|
||||
|
||||
|
||||
options = {
|
||||
url: url,
|
||||
method: 'post',
|
||||
json: true,
|
||||
body: { type: 'text', message: msg, to_number: to },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-maytapi-key': this.whatsappServiceKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// tslint:disable-next-line: await-promise
|
||||
const result = await request.post(options);
|
||||
GBLog.info(`Message [${msg}] sent to ${to}: ${result}`);
|
||||
if (this.provider !== "GeneralBots") {
|
||||
await request.post(options);
|
||||
}
|
||||
GBLog.info(`Message [${msg}] sent to ${to}.`);
|
||||
|
||||
} catch (error) {
|
||||
GBLog.error(`Error sending message to Whatsapp provider ${error.message}`);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue