2019-06-22 07:06:21 -03:00
|
|
|
/*****************************************************************************\
|
2024-01-09 17:40:48 -03:00
|
|
|
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
|
|
|
|
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
|
|
|
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
|
|
|
|
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
|
|
|
|
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
|
2019-06-22 07:06:21 -03:00
|
|
|
| |
|
2024-08-17 20:30:00 -03:00
|
|
|
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
|
2019-06-22 07:06:21 -03:00
|
|
|
| Licensed under the AGPL-3.0. |
|
|
|
|
| |
|
|
|
|
| According to our dual licensing model, this program can be used either |
|
|
|
|
| under the terms of the GNU Affero General Public License, version 3, |
|
|
|
|
| or under a proprietary license. |
|
|
|
|
| |
|
|
|
|
| The texts of the GNU Affero General Public License with an additional |
|
|
|
|
| permission and of our proprietary license can be found at and |
|
|
|
|
| in the LICENSE file you have received along with this program. |
|
|
|
|
| |
|
|
|
|
| This program is distributed in the hope that it will be useful, |
|
|
|
|
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
|
|
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
|
| GNU Affero General Public License for more details. |
|
|
|
|
| |
|
2024-08-17 20:30:00 -03:00
|
|
|
| "General Bots" is a registered trademark of pragmatismo.cloud. |
|
2019-06-22 07:06:21 -03:00
|
|
|
| The licensing of the program under the AGPLv3 does not imply a |
|
|
|
|
| trademark license. Therefore any rights, title and interest in |
|
|
|
|
| our trademarks remain entirely with us. |
|
|
|
|
| |
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
2023-08-10 22:28:59 -03:00
|
|
|
import mime from 'mime-types';
|
2022-11-18 22:39:14 -03:00
|
|
|
import urlJoin from 'url-join';
|
2023-03-06 11:45:56 -03:00
|
|
|
import SwaggerClient from 'swagger-client';
|
2022-11-18 22:39:14 -03:00
|
|
|
import Path from 'path';
|
|
|
|
import Fs from 'fs';
|
2024-04-13 13:42:04 -03:00
|
|
|
import { GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib';
|
2020-12-31 15:36:19 -03:00
|
|
|
import { CollectionUtil } from 'pragmatismo-io-framework';
|
2022-11-18 22:39:14 -03:00
|
|
|
import { GBServer } from '../../../src/app.js';
|
|
|
|
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
|
|
|
|
import { SecService } from '../../security.gbapp/services/SecService.js';
|
|
|
|
import { Messages } from '../strings.js';
|
|
|
|
import { GuaribasUser } from '../../security.gbapp/models/index.js';
|
|
|
|
import { GBMinService } from '../../core.gbapp/services/GBMinService.js';
|
|
|
|
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
|
|
|
|
import qrcode from 'qrcode-terminal';
|
|
|
|
import express from 'express';
|
2023-03-06 11:45:56 -03:00
|
|
|
import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
|
2023-11-12 10:09:21 -03:00
|
|
|
import pkg from 'whatsapp-web.js';
|
2024-08-04 17:16:04 -03:00
|
|
|
import fetch, { Response } from 'node-fetch';
|
2023-03-09 18:56:55 -03:00
|
|
|
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
|
2023-08-02 13:58:11 -03:00
|
|
|
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
|
2023-08-10 22:28:59 -03:00
|
|
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
|
2024-01-10 15:01:02 -03:00
|
|
|
import { GBUtil } from '../../../src/util.js';
|
2024-04-13 13:42:17 -03:00
|
|
|
const { WAState, Client, MessageMedia } = pkg;
|
2024-03-08 17:10:13 -03:00
|
|
|
import twilio from 'twilio';
|
2024-04-13 13:42:04 -03:00
|
|
|
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
|
2024-04-21 23:39:39 -03:00
|
|
|
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
|
2024-04-28 00:28:47 -03:00
|
|
|
import { createBot } from 'whatsapp-cloud-api';
|
2024-08-04 17:16:04 -03:00
|
|
|
import { promisify } from 'util';
|
|
|
|
const stat = promisify(Fs.stat);
|
2022-10-02 13:37:37 -03:00
|
|
|
|
2019-03-09 16:59:31 -03:00
|
|
|
/**
|
|
|
|
* Support for Whatsapp.
|
|
|
|
*/
|
2018-05-07 20:45:11 -03:00
|
|
|
export class WhatsappDirectLine extends GBService {
|
2020-12-31 15:36:19 -03:00
|
|
|
public static conversationIds = {};
|
2023-09-03 14:48:05 -03:00
|
|
|
public static botsByNumber = {};
|
2021-10-13 09:39:24 -03:00
|
|
|
public static mobiles = {};
|
2022-06-09 13:31:24 -03:00
|
|
|
public static phones = {};
|
2021-12-18 21:31:49 -03:00
|
|
|
public static chatIds = {};
|
2021-12-31 09:39:23 -03:00
|
|
|
public static usernames = {};
|
2022-05-03 17:05:57 -03:00
|
|
|
public static state = {}; // 2: Waiting, 3: MessageArrived.
|
|
|
|
public static lastMessage = {}; // 2: Waiting, 3: MessageArrived.
|
2022-10-12 00:30:34 -03:00
|
|
|
public static botGroups = {};
|
2021-12-18 21:31:49 -03:00
|
|
|
|
2021-12-25 22:13:52 -03:00
|
|
|
public pollInterval = 3000;
|
2019-03-08 06:37:13 -03:00
|
|
|
public directLineClientName = 'DirectLineClient';
|
|
|
|
|
|
|
|
public directLineClient: any;
|
|
|
|
public whatsappServiceKey: string;
|
|
|
|
public whatsappServiceNumber: string;
|
|
|
|
public whatsappServiceUrl: string;
|
2024-08-04 17:16:04 -03:00
|
|
|
public whatsappBusinessManagerId: string;
|
|
|
|
public whatsappFBAppId: string;
|
2019-03-08 06:37:13 -03:00
|
|
|
public botId: string;
|
2024-08-04 17:16:04 -03:00
|
|
|
public botNumber: string;
|
2020-12-31 15:36:19 -03:00
|
|
|
public min: GBMinInstance;
|
2020-05-02 21:28:13 -03:00
|
|
|
private locale: string = 'pt-BR';
|
2022-07-12 13:30:12 -03:00
|
|
|
provider: any;
|
2022-06-07 17:27:03 -03:00
|
|
|
INSTANCE_URL = 'https://api.maytapi.com/api';
|
2023-01-25 10:49:00 -03:00
|
|
|
private customClient: any;
|
2019-03-08 06:37:13 -03:00
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
constructor(
|
2020-04-13 19:14:55 -03:00
|
|
|
min: GBMinInstance,
|
2019-03-08 06:37:13 -03:00
|
|
|
botId,
|
|
|
|
directLineSecret,
|
|
|
|
whatsappServiceKey,
|
|
|
|
whatsappServiceNumber,
|
2022-10-12 00:30:34 -03:00
|
|
|
whatsappServiceUrl,
|
|
|
|
groupId
|
2019-03-08 06:37:13 -03:00
|
|
|
) {
|
|
|
|
super();
|
|
|
|
|
2020-04-13 19:14:55 -03:00
|
|
|
this.min = min;
|
2024-08-18 17:51:03 -03:00
|
|
|
this.botId = botId;
|
2019-03-08 06:37:13 -03:00
|
|
|
this.whatsappServiceKey = whatsappServiceKey;
|
|
|
|
this.whatsappServiceNumber = whatsappServiceNumber;
|
|
|
|
this.whatsappServiceUrl = whatsappServiceUrl;
|
2024-05-28 14:06:41 -03:00
|
|
|
this.provider = whatsappServiceKey === 'internal' ? 'GeneralBots' : 'meta';
|
2024-08-18 17:51:03 -03:00
|
|
|
|
2020-04-08 21:33:27 -03:00
|
|
|
}
|
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public static async asyncForEach(array, callback) {
|
2020-12-31 15:36:19 -03:00
|
|
|
for (let index = 0; index < array.length; index++) {
|
|
|
|
await callback(array[index], index, array);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-25 10:49:00 -03:00
|
|
|
public async setup(setUrl: boolean) {
|
2023-03-06 11:45:56 -03:00
|
|
|
const client = await new SwaggerClient({
|
2024-08-18 17:51:03 -03:00
|
|
|
url: 'http://127.0.0.1:3978/api/messages', // TODO:
|
2022-11-19 23:34:58 -03:00
|
|
|
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
|
2023-03-06 11:45:56 -03:00
|
|
|
requestInterceptor: req => {
|
2023-11-03 11:39:50 -03:00
|
|
|
req.headers['Authorization'] = `Bearer ${this.min.instance.webchatKey}`;
|
2023-03-06 11:45:56 -03:00
|
|
|
}
|
2022-11-19 23:34:58 -03:00
|
|
|
});
|
2024-08-10 13:35:34 -03:00
|
|
|
|
2023-03-06 11:45:56 -03:00
|
|
|
this.directLineClient = client;
|
2024-08-10 13:35:34 -03:00
|
|
|
|
|
|
|
// Warms up MSBF.
|
|
|
|
|
|
|
|
await client.apis.Conversations.Conversations_StartConversation();
|
2023-03-06 11:45:56 -03:00
|
|
|
|
2023-01-25 10:49:00 -03:00
|
|
|
let url: string;
|
|
|
|
let options: any;
|
2020-04-08 21:33:27 -03:00
|
|
|
|
2022-07-12 13:30:12 -03:00
|
|
|
switch (this.provider) {
|
2024-08-04 17:16:04 -03:00
|
|
|
case 'meta':
|
|
|
|
this.botNumber = this.min.core.getParam<string>(this.min.instance, 'Bot Number', null);
|
|
|
|
let whatsappServiceNumber, whatsappServiceKey, url;
|
|
|
|
if (this.botNumber && this.min.instance.whatsappServiceNumber) {
|
|
|
|
whatsappServiceNumber = this.min.instance.whatsappServiceNumber;
|
|
|
|
whatsappServiceKey = this.min.instance.whatsappServiceKey;
|
|
|
|
url = this.min.instance.whatsappServiceUrl;
|
|
|
|
} else {
|
|
|
|
whatsappServiceNumber = GBServer.globals.minBoot.instance.whatsappServiceNumber;
|
|
|
|
whatsappServiceKey = GBServer.globals.minBoot.instance.whatsappServiceKey;
|
|
|
|
url = GBServer.globals.minBoot.instance.whatsappServiceUrl;
|
|
|
|
}
|
|
|
|
if (url) {
|
|
|
|
const parts = url.split(';');
|
|
|
|
this.whatsappBusinessManagerId = parts[0];
|
|
|
|
this.whatsappFBAppId = parts[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.customClient = createBot(whatsappServiceNumber, whatsappServiceKey);
|
|
|
|
break;
|
2024-03-09 14:02:17 -03:00
|
|
|
case 'official':
|
2024-03-08 17:10:13 -03:00
|
|
|
const accountSid = process.env.TWILIO_ACCOUNT_SID;
|
|
|
|
const authToken = process.env.TWILIO_AUTH_TOKEN;
|
2024-03-09 14:02:17 -03:00
|
|
|
this.customClient = twilio(null, authToken, { accountSid: accountSid });
|
2024-03-08 17:10:13 -03:00
|
|
|
|
2024-03-09 14:02:17 -03:00
|
|
|
break;
|
2022-07-12 13:30:12 -03:00
|
|
|
case 'GeneralBots':
|
2023-02-14 13:58:17 -03:00
|
|
|
const minBoot = GBServer.globals.minBoot;
|
2023-03-06 20:36:27 -03:00
|
|
|
// Initialize the browser using a local profile for each bot.
|
2024-04-13 12:24:08 -03:00
|
|
|
const gbaiPath = DialogKeywords.getGBAIPath(this.min.botId);
|
2024-06-23 14:52:04 -03:00
|
|
|
const webVersion = '2.2412.51';
|
2024-04-13 12:24:08 -03:00
|
|
|
const localName = Path.join('work', gbaiPath, 'profile');
|
2023-03-06 20:36:27 -03:00
|
|
|
const createClient = () => {
|
|
|
|
const client = (this.customClient = new Client({
|
2024-05-28 14:06:41 -03:00
|
|
|
puppeteer: GBSSR.preparePuppeteer(localName),
|
|
|
|
webVersionCache: {
|
2024-04-28 16:17:00 -03:00
|
|
|
type: 'remote',
|
|
|
|
remotePath: `https://raw.githubusercontent.com/wppconnect-team/wa-version/main/html/${webVersion}.html`
|
|
|
|
}
|
2023-03-06 20:36:27 -03:00
|
|
|
}));
|
|
|
|
client.on(
|
|
|
|
'message',
|
|
|
|
(async message => {
|
|
|
|
await this.WhatsAppCallback(message, null);
|
|
|
|
}).bind(this)
|
|
|
|
);
|
|
|
|
client.on(
|
|
|
|
'qr',
|
|
|
|
(async 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);
|
2024-04-13 14:14:03 -03:00
|
|
|
const pid = GBVMService.createProcessInfo(null, this.min, 'wppboot', null);
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2023-03-06 20:36:27 -03:00
|
|
|
// Sends QR Code to boot bot admin.
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2023-03-06 20:36:27 -03:00
|
|
|
const msg = `Please, scan QR Code with for bot ${this.botId}.`;
|
|
|
|
qrcode.generate(qr, { small: true, scale: 0.5 });
|
2024-02-17 20:33:08 -03:00
|
|
|
|
2024-04-13 12:24:08 -03:00
|
|
|
const s = new DialogKeywords();
|
2024-04-28 16:17:00 -03:00
|
|
|
const qrBuf = await s.getQRCode({ pid, text: qr });
|
2024-05-28 14:06:41 -03:00
|
|
|
const localName = Path.join(
|
|
|
|
'work',
|
|
|
|
gbaiPath,
|
2024-04-13 12:24:08 -03:00
|
|
|
'cache',
|
2024-05-28 14:06:41 -03:00
|
|
|
`qr${GBAdminService.getRndReadableIdentifier()}.png`
|
2024-04-13 12:24:08 -03:00
|
|
|
);
|
2024-05-28 14:06:41 -03:00
|
|
|
Fs.writeFileSync(localName, qrBuf.data);
|
|
|
|
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName));
|
2024-04-28 16:17:00 -03:00
|
|
|
|
|
|
|
if (adminNumber) {
|
2024-05-28 14:06:41 -03:00
|
|
|
await GBServer.globals.minBoot.whatsAppDirectLine.sendFileToDevice(
|
|
|
|
adminNumber,
|
|
|
|
url,
|
|
|
|
Path.basename(localName),
|
|
|
|
msg
|
|
|
|
);
|
2024-04-13 12:24:08 -03:00
|
|
|
}
|
2024-04-28 16:17:00 -03:00
|
|
|
|
|
|
|
if (adminEmail) {
|
|
|
|
await s.sendEmail({
|
2024-05-28 14:06:41 -03:00
|
|
|
pid,
|
|
|
|
to: adminEmail,
|
|
|
|
subject: `Check your WhatsApp for bot ${this.min.botId}`,
|
2024-04-13 12:24:08 -03:00
|
|
|
body: msg
|
|
|
|
});
|
|
|
|
}
|
2023-03-06 20:36:27 -03:00
|
|
|
}).bind(this)
|
|
|
|
);
|
|
|
|
client.on('authenticated', async () => {
|
|
|
|
GBLog.verbose(`GBWhatsApp: QR Code authenticated for ${this.botId}.`);
|
|
|
|
});
|
|
|
|
client.on('ready', async () => {
|
|
|
|
GBLog.verbose(`GBWhatsApp: Emptying chat list for ${this.botId}...`);
|
2023-03-19 20:09:54 -03:00
|
|
|
// TODO: await client.pupPage['minimize']();
|
2023-03-06 20:36:27 -03:00
|
|
|
// Keeps the chat list cleaned.
|
|
|
|
const chats = await client.getChats();
|
|
|
|
await CollectionUtil.asyncForEach(chats, async chat => {
|
|
|
|
const wait = Math.floor(Math.random() * 5000) + 1000;
|
2024-01-10 15:01:02 -03:00
|
|
|
await GBUtil.sleep(wait);
|
2023-03-06 20:36:27 -03:00
|
|
|
if (chat.isGroup) {
|
|
|
|
// await chat.clearMessages();
|
|
|
|
} else if (!chat.pinned) {
|
2024-02-17 21:42:45 -03:00
|
|
|
await chat.delete();
|
2023-03-06 20:36:27 -03:00
|
|
|
}
|
2023-02-14 13:58:17 -03:00
|
|
|
});
|
2023-03-06 20:36:27 -03:00
|
|
|
});
|
|
|
|
client.initialize();
|
|
|
|
};
|
2023-05-25 21:20:40 -03:00
|
|
|
if (setUrl) {
|
2023-03-19 20:09:54 -03:00
|
|
|
createClient.bind(this)();
|
2023-09-09 13:04:20 -03:00
|
|
|
} else {
|
2023-03-19 20:09:54 -03:00
|
|
|
this.customClient = minBoot.whatsAppDirectLine.customClient;
|
|
|
|
}
|
2023-03-06 20:36:27 -03:00
|
|
|
setUrl = false;
|
2023-03-19 20:09:54 -03:00
|
|
|
|
2022-07-12 13:30:12 -03:00
|
|
|
break;
|
2023-02-14 13:58:17 -03:00
|
|
|
}
|
2022-07-12 13:30:12 -03:00
|
|
|
|
2022-11-19 23:34:58 -03:00
|
|
|
if (setUrl && options && this.whatsappServiceUrl) {
|
2020-04-28 20:54:04 -03:00
|
|
|
GBServer.globals.server.use(`/audios`, express.static('work'));
|
2020-04-08 21:33:27 -03:00
|
|
|
}
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
2018-05-07 20:45:11 -03:00
|
|
|
|
2023-01-25 10:49:00 -03:00
|
|
|
public async resetConversationId(botId: string, number: number, group = '') {
|
2022-06-09 14:19:09 -03:00
|
|
|
WhatsappDirectLine.conversationIds[botId + number + group] = undefined;
|
2020-04-15 01:42:54 +00:00
|
|
|
}
|
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public async check() {
|
2022-10-09 17:23:04 -03:00
|
|
|
switch (this.provider) {
|
|
|
|
case 'GeneralBots':
|
2023-07-25 08:32:48 -03:00
|
|
|
const state = await this.customClient.getState();
|
2023-07-25 08:04:34 -03:00
|
|
|
return state === 'CONNECTED';
|
2022-12-14 08:23:39 -03:00
|
|
|
|
2022-10-09 17:23:04 -03:00
|
|
|
default:
|
2022-10-09 23:35:13 -03:00
|
|
|
GBLog.verbose(`GBWhatsapp: Checking server...`);
|
2022-11-30 09:40:09 -03:00
|
|
|
let url = urlJoin(this.whatsappServiceUrl, 'status') + `?token=${this.min.instance.whatsappServiceKey}`;
|
2022-10-09 17:23:04 -03:00
|
|
|
const options = {
|
2022-11-30 09:40:09 -03:00
|
|
|
url: url,
|
2022-10-09 17:23:04 -03:00
|
|
|
method: 'GET'
|
|
|
|
};
|
2020-05-12 19:20:59 -03:00
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
const res = await fetch(url, options);
|
2023-01-01 14:24:53 -03:00
|
|
|
const json = await res.json();
|
|
|
|
return json['accountStatus'] === 'authenticated';
|
2022-10-09 17:23:04 -03:00
|
|
|
}
|
2020-05-12 19:20:59 -03:00
|
|
|
}
|
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public async received(req, res) {
|
2024-04-28 00:28:47 -03:00
|
|
|
const provider = GBMinService.getProviderName(req, res);
|
2019-06-28 11:17:41 -03:00
|
|
|
|
2023-09-03 14:48:05 -03:00
|
|
|
let message, to, from, fromName, text: string;
|
2022-11-19 23:34:58 -03:00
|
|
|
let group = '';
|
2021-12-25 22:13:52 -03:00
|
|
|
let answerText = null;
|
2022-08-05 21:26:23 -03:00
|
|
|
let attachments = null;
|
2021-12-18 21:31:49 -03:00
|
|
|
|
2022-07-17 13:37:41 -03:00
|
|
|
switch (provider) {
|
2024-04-28 00:28:47 -03:00
|
|
|
case 'meta':
|
2024-04-28 16:17:00 -03:00
|
|
|
if (req.body.entry[0].changes[0].value.messages[0].text) {
|
|
|
|
text = req.body.entry[0].changes[0].value.messages[0].text.body;
|
2024-05-28 14:06:41 -03:00
|
|
|
} else if (req.body.entry[0].changes[0].value.messages[0].button) {
|
2024-04-28 16:17:00 -03:00
|
|
|
text = req.body.entry[0].changes[0].value.messages[0].button.text;
|
2024-05-28 14:06:41 -03:00
|
|
|
} else {
|
2024-04-29 13:57:31 -03:00
|
|
|
res.status(200);
|
|
|
|
res.end();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2024-05-28 14:06:41 -03:00
|
|
|
|
2024-04-28 00:28:47 -03:00
|
|
|
from = req.body.entry[0].changes[0].value.messages[0].from;
|
|
|
|
to = this.min.core.getParam<string>(this.min.instance, 'Bot Number', null);
|
|
|
|
fromName = req.body.entry[0].changes[0].value.contacts[0].profile.name;
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2024-04-28 00:28:47 -03:00
|
|
|
break;
|
2024-03-09 14:02:17 -03:00
|
|
|
case 'official':
|
|
|
|
message = req.body;
|
|
|
|
from = req.body.From.replace(/whatsapp\:\+/gi, '');
|
|
|
|
to = req.body.To.replace(/whatsapp\:\+/gi, '');
|
|
|
|
text = req.body.Body;
|
|
|
|
fromName = req.body.ProfileName;
|
|
|
|
break;
|
2022-07-12 13:30:12 -03:00
|
|
|
case 'GeneralBots':
|
|
|
|
message = req;
|
2023-09-09 13:04:20 -03:00
|
|
|
to = message.to.endsWith('@g.us') ? message.to.split('@')[0] : message.to.split('@')[0];
|
|
|
|
const newThis = WhatsappDirectLine.botsByNumber[to];
|
2023-09-09 13:59:14 -03:00
|
|
|
|
2023-09-09 16:38:47 -03:00
|
|
|
// If there is a number specified, checks if it
|
|
|
|
// is related to a custom bot and reroutes immediately.
|
2023-09-09 13:59:14 -03:00
|
|
|
|
2023-11-03 11:39:50 -03:00
|
|
|
if (newThis && newThis !== this && newThis.min.botId !== GBServer.globals.minBoot.botId) {
|
2023-09-09 16:38:47 -03:00
|
|
|
await newThis.received(req, res);
|
2023-09-09 13:59:14 -03:00
|
|
|
|
2023-09-09 16:38:47 -03:00
|
|
|
return;
|
2023-09-03 14:48:05 -03:00
|
|
|
}
|
|
|
|
|
2022-07-15 09:05:16 -03:00
|
|
|
text = message.body;
|
2022-11-19 23:34:58 -03:00
|
|
|
from = message.from.endsWith('@g.us') ? message.author.split('@')[0] : message.from.split('@')[0];
|
2022-07-15 09:05:16 -03:00
|
|
|
fromName = message._data.notifyName;
|
|
|
|
|
2022-08-05 21:26:23 -03:00
|
|
|
if (message.hasMedia) {
|
|
|
|
const base64Image = await message.downloadMedia();
|
2023-08-10 22:28:59 -03:00
|
|
|
|
2023-09-09 13:04:20 -03:00
|
|
|
let buf: any = Buffer.from(base64Image.data, 'base64');
|
2023-08-10 22:28:59 -03:00
|
|
|
const gbaiName = DialogKeywords.getGBAIPath(this.min.botId);
|
2023-09-09 13:04:20 -03:00
|
|
|
const localName = Path.join(
|
|
|
|
'work',
|
|
|
|
gbaiName,
|
|
|
|
'cache',
|
|
|
|
`tmp${GBAdminService.getRndReadableIdentifier()}.docx`
|
|
|
|
);
|
2023-08-10 22:28:59 -03:00
|
|
|
Fs.writeFileSync(localName, buf, { encoding: null });
|
|
|
|
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName));
|
|
|
|
|
2022-08-05 21:26:23 -03:00
|
|
|
attachments = [];
|
2022-11-19 23:34:58 -03:00
|
|
|
attachments.push({
|
2023-08-10 22:28:59 -03:00
|
|
|
name: `${new Date().toISOString().replace(/\:/g, '')}.${mime.extension(base64Image.mimetype)}`,
|
|
|
|
noName: true,
|
2022-11-19 23:34:58 -03:00
|
|
|
contentType: base64Image.mimetype,
|
2023-08-10 22:28:59 -03:00
|
|
|
contentUrl: url
|
2022-11-19 23:34:58 -03:00
|
|
|
});
|
2022-08-05 21:26:23 -03:00
|
|
|
}
|
|
|
|
|
2022-07-12 13:30:12 -03:00
|
|
|
break;
|
2022-06-07 18:37:29 -03:00
|
|
|
}
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-07-12 13:30:12 -03:00
|
|
|
text = text.replace(/\@\d+ /gi, '');
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(0, `GBWhatsapp: RCV ${from}(${fromName}): ${text})`);
|
2022-07-12 13:30:12 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
let botGroupID = WhatsappDirectLine.botGroups[this.min.botId];
|
2022-10-12 02:05:26 -03:00
|
|
|
let botShortcuts = this.min.core.getParam<string>(this.min.instance, 'WhatsApp Group Shortcuts', null);
|
2022-10-12 00:30:34 -03:00
|
|
|
if (!botShortcuts) {
|
2022-11-19 23:34:58 -03:00
|
|
|
botShortcuts = new Array();
|
|
|
|
} else {
|
2022-10-12 00:30:34 -03:00
|
|
|
botShortcuts = botShortcuts.split(' ');
|
|
|
|
}
|
|
|
|
|
2024-03-09 14:02:17 -03:00
|
|
|
if (provider === 'GeneralBots') {
|
2022-10-12 00:30:34 -03:00
|
|
|
if (message.from.endsWith('@g.us')) {
|
|
|
|
group = message.from;
|
|
|
|
}
|
|
|
|
}
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
if (group) {
|
|
|
|
const parts = text.split(' ');
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
// Bot name must be specified on config.
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
if (botGroupID === group) {
|
|
|
|
// Shortcut has been mentioned?
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
let found = false;
|
|
|
|
parts.forEach(e1 => {
|
|
|
|
botShortcuts.forEach(e2 => {
|
|
|
|
if (e1 === e2 && !found) {
|
|
|
|
found = true;
|
|
|
|
text = text.replace(e2, '');
|
|
|
|
}
|
|
|
|
});
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
// Verify if it is a group cache answer.
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
const questions = this.min['groupCache'];
|
|
|
|
if (questions && questions.length > 0) {
|
|
|
|
questions.forEach(q => {
|
|
|
|
if (q.content === e1 && !found) {
|
2022-11-19 23:34:58 -03:00
|
|
|
const answer = this.min.kbService['getAnswerById'](this.min.instance.instanceId, q.answerId);
|
2022-10-12 00:30:34 -03:00
|
|
|
answerText = answer.content;
|
2023-03-08 22:10:27 -03:00
|
|
|
|
2023-03-08 22:10:43 -03:00
|
|
|
answerText = answerText.replace(/\$username/gi, fromName);
|
2022-10-12 00:30:34 -03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-09-16 09:41:31 -03:00
|
|
|
});
|
2021-12-25 22:13:52 -03:00
|
|
|
|
2023-09-16 09:41:31 -03:00
|
|
|
// Ignore group messages without the mention to Bot.
|
2022-06-07 18:37:29 -03:00
|
|
|
|
2024-08-04 17:16:04 -03:00
|
|
|
if (this.botNumber && !answerText && !found) {
|
|
|
|
let n = this.botNumber.replace('+', '');
|
|
|
|
if (!message.body.startsWith('@' + n)) {
|
2023-09-16 09:41:31 -03:00
|
|
|
return;
|
2022-10-12 00:30:34 -03:00
|
|
|
}
|
2023-09-16 09:41:31 -03:00
|
|
|
}
|
2021-12-25 22:13:52 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-21 17:46:46 -03:00
|
|
|
const botId = this.min.instance.botId;
|
|
|
|
const state = WhatsappDirectLine.state[botId + from];
|
2022-07-07 11:14:02 -03:00
|
|
|
if (state) {
|
2022-10-16 10:29:59 -03:00
|
|
|
GBLog.verbose(`BASIC: Continuing HEAR from WhatsApp...`);
|
2022-06-21 17:46:46 -03:00
|
|
|
WhatsappDirectLine.state[botId + from] = null;
|
2022-10-16 10:29:59 -03:00
|
|
|
await state.promise(null, text);
|
2022-07-07 11:14:02 -03:00
|
|
|
|
2022-06-21 17:46:46 -03:00
|
|
|
return; // Exit here.
|
2022-11-19 23:34:58 -03:00
|
|
|
}
|
2022-06-21 17:46:46 -03:00
|
|
|
|
|
|
|
// Processes .gbapp message interception.
|
2022-06-07 17:27:03 -03:00
|
|
|
|
2020-12-14 09:28:12 -03:00
|
|
|
await CollectionUtil.asyncForEach(this.min.appPackages, async (e: IGBPackage) => {
|
2022-08-31 23:29:14 -03:00
|
|
|
await e.onExchangeData(this.min, 'whatsappMessage', { from, fromName });
|
2020-06-12 15:55:18 -03:00
|
|
|
});
|
2020-05-17 22:36:00 +00:00
|
|
|
|
2022-07-13 09:38:13 -03:00
|
|
|
const sec = new SecService();
|
2024-02-05 12:36:20 -03:00
|
|
|
const user = await sec.ensureUser(this.min, from, fromName, '', 'whatsapp', fromName, null);
|
2020-05-27 23:01:44 -03:00
|
|
|
const locale = user.locale ? user.locale : 'pt';
|
2021-12-25 22:13:52 -03:00
|
|
|
|
|
|
|
if (answerText) {
|
|
|
|
await this.sendToDeviceEx(user.userSystemId, answerText, locale, null);
|
2022-06-21 17:46:46 -03:00
|
|
|
|
2022-07-07 11:14:02 -03:00
|
|
|
return; // Exit here.
|
2021-12-25 22:13:52 -03:00
|
|
|
}
|
|
|
|
|
2022-07-13 09:38:13 -03:00
|
|
|
const conversationId = WhatsappDirectLine.conversationIds[botId + from + group];
|
2020-12-31 15:36:19 -03:00
|
|
|
const client = await this.directLineClient;
|
2022-06-09 14:19:09 -03:00
|
|
|
WhatsappDirectLine.lastMessage[botId + from] = message;
|
2022-05-03 17:05:57 -03:00
|
|
|
|
2021-03-03 16:46:18 -03:00
|
|
|
// Check if this message is from a Human Agent itself.
|
|
|
|
|
2020-12-31 15:36:19 -03:00
|
|
|
if (user.agentMode === 'self') {
|
2021-03-03 16:46:18 -03:00
|
|
|
// Check if there is someone being handled by this Human Agent.
|
|
|
|
|
2022-06-07 18:37:29 -03:00
|
|
|
const manualUser = await sec.getUserFromAgentSystemId(from);
|
2020-05-16 16:40:44 -03:00
|
|
|
if (manualUser === null) {
|
2022-06-07 18:37:29 -03:00
|
|
|
await sec.updateHumanAgent(from, this.min.instance.instanceId, null);
|
2020-12-31 15:36:19 -03:00
|
|
|
} else {
|
2021-03-03 16:46:18 -03:00
|
|
|
const agent = await sec.getUserFromSystemId(user.agentSystemId);
|
|
|
|
|
2020-05-16 16:40:44 -03:00
|
|
|
const cmd = '/reply ';
|
|
|
|
if (text.startsWith(cmd)) {
|
2020-12-31 15:36:19 -03:00
|
|
|
const filename = text.substr(cmd.length);
|
|
|
|
const message = await this.min.kbService.getAnswerTextByMediaName(this.min.instance.instanceId, filename);
|
2020-05-16 16:40:44 -03:00
|
|
|
|
|
|
|
if (message === null) {
|
2022-11-19 23:34:58 -03:00
|
|
|
await this.sendToDeviceEx(
|
|
|
|
user.userSystemId,
|
|
|
|
`File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`,
|
|
|
|
locale,
|
|
|
|
null
|
|
|
|
);
|
2020-05-16 16:40:44 -03:00
|
|
|
} else {
|
|
|
|
await this.min.conversationalService.sendMarkdownToMobile(this.min, null, user.userSystemId, message);
|
|
|
|
}
|
|
|
|
} else if (text === '/qt') {
|
2022-12-26 13:37:11 -03:00
|
|
|
// https://github.com/GeneralBots/BotServer/issues/307
|
|
|
|
|
2022-11-19 23:34:58 -03:00
|
|
|
await this.sendToDeviceEx(
|
|
|
|
manualUser.userSystemId,
|
|
|
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
|
|
|
|
locale,
|
|
|
|
null
|
|
|
|
);
|
|
|
|
|
2023-08-01 13:03:19 -03:00
|
|
|
if (user.agentSystemId.indexOf('@') !== -1) {
|
2022-11-19 23:34:58 -03:00
|
|
|
// Agent is from Teams.
|
|
|
|
await this.min.conversationalService['sendOnConversation'](
|
|
|
|
this.min,
|
|
|
|
agent,
|
|
|
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
await this.sendToDeviceEx(
|
|
|
|
user.agentSystemId,
|
|
|
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
|
|
|
|
locale,
|
|
|
|
null
|
|
|
|
);
|
2021-03-03 16:46:18 -03:00
|
|
|
}
|
|
|
|
await sec.updateHumanAgent(manualUser.userSystemId, this.min.instance.instanceId, null);
|
|
|
|
await sec.updateHumanAgent(user.agentSystemId, this.min.instance.instanceId, null);
|
2020-12-31 15:36:19 -03:00
|
|
|
} else {
|
2024-05-28 14:06:41 -03:00
|
|
|
GBLogEx.info(
|
|
|
|
this.min,
|
|
|
|
`HUMAN AGENT (${manualUser.agentSystemId}) TO USER ${manualUser.userSystemId}: ${text}`
|
|
|
|
);
|
2023-08-01 13:03:19 -03:00
|
|
|
await this.sendToDeviceEx(manualUser.userSystemId, `AGENT: *${text}*`, locale, null);
|
2020-05-16 16:40:44 -03:00
|
|
|
}
|
2020-05-02 21:28:13 -03:00
|
|
|
}
|
2020-12-31 15:36:19 -03:00
|
|
|
} else if (user.agentMode === 'human') {
|
|
|
|
const agent = await sec.getUserFromSystemId(user.agentSystemId);
|
2020-05-02 21:28:13 -03:00
|
|
|
if (text === '/t') {
|
2022-11-19 23:34:58 -03:00
|
|
|
await this.sendToDeviceEx(
|
|
|
|
user.userSystemId,
|
|
|
|
`Você já está sendo atendido por ${agent.userSystemId}.`,
|
|
|
|
locale,
|
|
|
|
null
|
|
|
|
);
|
2023-01-01 14:24:53 -03:00
|
|
|
} else if (text === '/qt' || GBMinService.isGlobalQuitUtterance(locale, text)) {
|
2022-06-07 18:37:29 -03:00
|
|
|
await this.endTransfer(from, locale, user, agent, sec);
|
2020-12-31 15:36:19 -03:00
|
|
|
} else {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `USER (${from}) TO AGENT ${agent.userSystemId}: ${text}`);
|
2023-09-09 13:04:20 -03:00
|
|
|
|
2023-08-02 13:58:11 -03:00
|
|
|
const prompt = `the person said: ${text}. what can I tell her?`;
|
2023-08-02 14:04:10 -03:00
|
|
|
const answer = await ChatServices.continue(this.min, prompt, 0);
|
2023-09-09 13:04:20 -03:00
|
|
|
text = `${text} \n\nGeneral Bots: ${answer}`;
|
2021-03-03 16:46:18 -03:00
|
|
|
|
2023-08-01 13:03:19 -03:00
|
|
|
if (user.agentSystemId.indexOf('@') !== -1) {
|
2022-11-19 23:34:58 -03:00
|
|
|
// Agent is from Teams or Google Chat.
|
2021-03-03 16:46:18 -03:00
|
|
|
await this.min.conversationalService['sendOnConversation'](this.min, agent, text);
|
2022-11-19 23:34:58 -03:00
|
|
|
} else {
|
|
|
|
await this.sendToDeviceEx(
|
|
|
|
user.agentSystemId,
|
|
|
|
`Bot: ${this.min.instance.botId}\n${from}: ${text}`,
|
|
|
|
locale,
|
|
|
|
null
|
|
|
|
);
|
2021-03-03 16:46:18 -03:00
|
|
|
}
|
2020-05-02 21:28:13 -03:00
|
|
|
}
|
2020-12-31 15:36:19 -03:00
|
|
|
} else if (user.agentMode === 'bot' || user.agentMode === null || user.agentMode === undefined) {
|
2022-06-09 14:19:09 -03:00
|
|
|
if (WhatsappDirectLine.conversationIds[botId + from + group] === undefined) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `GBWhatsapp: Starting new conversation on Bot.`);
|
2023-03-06 11:45:56 -03:00
|
|
|
const response = await client.apis.Conversations.Conversations_StartConversation();
|
2020-05-02 21:28:13 -03:00
|
|
|
const generatedConversationId = response.obj.conversationId;
|
|
|
|
|
2022-06-09 14:19:09 -03:00
|
|
|
WhatsappDirectLine.conversationIds[botId + from + group] = generatedConversationId;
|
2022-11-19 23:34:58 -03:00
|
|
|
if (provider === 'GeneralBots') {
|
2022-07-15 09:05:16 -03:00
|
|
|
WhatsappDirectLine.chatIds[generatedConversationId] = message.from;
|
|
|
|
}
|
2022-07-17 13:37:41 -03:00
|
|
|
WhatsappDirectLine.mobiles[generatedConversationId] = from;
|
2021-12-31 09:39:23 -03:00
|
|
|
WhatsappDirectLine.usernames[from] = fromName;
|
2024-04-28 00:28:47 -03:00
|
|
|
WhatsappDirectLine.chatIds[generatedConversationId] = message?.chatId;
|
2022-06-07 17:27:03 -03:00
|
|
|
|
2020-05-02 21:28:13 -03:00
|
|
|
this.pollMessages(client, generatedConversationId, from, fromName);
|
2022-08-05 21:26:23 -03:00
|
|
|
this.inputMessage(client, generatedConversationId, text, from, fromName, group, attachments);
|
2020-05-02 21:28:13 -03:00
|
|
|
} else {
|
2022-08-05 21:26:23 -03:00
|
|
|
this.inputMessage(client, conversationId, text, from, fromName, group, attachments);
|
2020-05-02 21:28:13 -03:00
|
|
|
}
|
2022-07-17 13:37:41 -03:00
|
|
|
} else {
|
2020-05-02 21:28:13 -03:00
|
|
|
GBLog.warn(`Inconsistencty found: Invalid agentMode on User Table: ${user.agentMode}`);
|
|
|
|
}
|
|
|
|
|
2022-08-05 21:26:23 -03:00
|
|
|
if (res) {
|
2022-07-21 15:36:41 -03:00
|
|
|
res.end();
|
|
|
|
}
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
|
|
|
|
2023-02-09 19:40:16 -03:00
|
|
|
private async endTransfer(id: string, locale: string, user: GuaribasUser, agent: GuaribasUser, sec: SecService) {
|
2022-11-19 23:34:58 -03:00
|
|
|
await this.sendToDeviceEx(id, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
|
2022-05-19 10:22:22 -03:00
|
|
|
|
2023-08-01 13:03:19 -03:00
|
|
|
if (user.agentSystemId.indexOf('@') !== -1) {
|
2022-11-19 23:34:58 -03:00
|
|
|
// Agent is from Teams.
|
2023-09-09 13:04:20 -03:00
|
|
|
|
2022-11-19 23:34:58 -03:00
|
|
|
await this.min.conversationalService['sendOnConversation'](
|
|
|
|
this.min,
|
|
|
|
agent,
|
|
|
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
await this.sendToDeviceEx(
|
|
|
|
user.agentSystemId,
|
|
|
|
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
|
|
|
|
locale,
|
|
|
|
null
|
|
|
|
);
|
2022-05-19 10:22:22 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
await sec.updateHumanAgent(id, this.min.instance.instanceId, null);
|
|
|
|
}
|
|
|
|
|
2023-02-09 19:40:16 -03:00
|
|
|
public inputMessage(client, conversationId: string, text: string, from, fromName: string, group, attachments: File) {
|
2023-03-10 15:49:30 -03:00
|
|
|
try {
|
|
|
|
return client.apis.Conversations.Conversations_PostActivity({
|
|
|
|
conversationId: conversationId,
|
|
|
|
activity: {
|
|
|
|
textFormat: 'plain',
|
|
|
|
text: text,
|
|
|
|
type: 'message',
|
|
|
|
mobile: from,
|
|
|
|
group: group,
|
|
|
|
attachments: attachments,
|
|
|
|
|
|
|
|
// Use from container to transport information to GBMinService.receiver.
|
|
|
|
|
|
|
|
from: {
|
|
|
|
id: from,
|
|
|
|
name: fromName,
|
|
|
|
channelIdEx: 'whatsapp',
|
|
|
|
group: group
|
|
|
|
},
|
|
|
|
replyToId: from
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (e) {
|
2023-09-09 13:04:20 -03:00
|
|
|
GBLog.error(e);
|
2023-03-10 15:49:30 -03:00
|
|
|
}
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public pollMessages(client, conversationId, from, fromName) {
|
2024-08-10 13:35:34 -03:00
|
|
|
GBLogEx.info(this.min, `GBWhatsapp: Starting timer of message polling(${from}, ${conversationId}).`);
|
2019-06-23 07:03:32 -03:00
|
|
|
|
2019-06-23 07:24:35 -03:00
|
|
|
let watermark: any;
|
|
|
|
|
2019-06-23 07:03:32 -03:00
|
|
|
const worker = async () => {
|
|
|
|
try {
|
2023-03-06 11:45:56 -03:00
|
|
|
const response = await client.apis.Conversations.Conversations_GetActivities({
|
2019-06-23 07:03:32 -03:00
|
|
|
conversationId: conversationId,
|
2019-06-23 07:24:35 -03:00
|
|
|
watermark: watermark
|
2019-03-08 06:37:13 -03:00
|
|
|
});
|
2019-06-23 07:24:35 -03:00
|
|
|
watermark = response.obj.watermark;
|
2019-06-23 07:03:32 -03:00
|
|
|
await this.printMessages(response.obj.activities, conversationId, from, fromName);
|
2024-04-30 20:12:11 -03:00
|
|
|
} catch (error) {
|
2024-08-10 13:35:34 -03:00
|
|
|
GBLog.error(`Error pooling messages from Whatsapp channel ${GBUtil.toYAML(error)}`);
|
2019-06-23 07:03:32 -03:00
|
|
|
}
|
2019-06-22 07:06:21 -03:00
|
|
|
};
|
2019-06-23 07:25:59 -03:00
|
|
|
setInterval(worker, this.pollInterval);
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
2018-10-16 10:19:34 -03:00
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public async printMessages(activities, conversationId, from, fromName) {
|
2019-03-08 06:37:13 -03:00
|
|
|
if (activities && activities.length) {
|
|
|
|
// Ignore own messages.
|
2018-11-12 12:20:44 -02:00
|
|
|
|
2019-05-15 22:30:14 -03:00
|
|
|
activities = activities.filter(m => m.from.id === this.botId && m.type === 'message');
|
2018-05-07 20:45:11 -03:00
|
|
|
|
2019-03-08 06:37:13 -03:00
|
|
|
if (activities.length) {
|
|
|
|
// Print other messages.
|
2018-05-12 13:40:34 -03:00
|
|
|
|
2019-06-22 07:51:04 -03:00
|
|
|
await WhatsappDirectLine.asyncForEach(activities, async activity => {
|
|
|
|
await this.printMessage(activity, conversationId, from, fromName);
|
2019-03-08 06:37:13 -03:00
|
|
|
});
|
|
|
|
}
|
2018-05-07 20:45:11 -03:00
|
|
|
}
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
2018-05-07 20:45:11 -03:00
|
|
|
|
2023-07-23 16:55:31 -03:00
|
|
|
public async printMessage(activity, conversationId, to, toName) {
|
2019-03-08 06:37:13 -03:00
|
|
|
let output = '';
|
2018-05-12 13:40:34 -03:00
|
|
|
|
2019-03-08 06:37:13 -03:00
|
|
|
if (activity.text) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `GBWhatsapp: SND ${to}(${toName}): ${activity.text}`);
|
2019-03-08 06:37:13 -03:00
|
|
|
output = activity.text;
|
2018-05-07 20:45:11 -03:00
|
|
|
}
|
|
|
|
|
2019-03-08 06:37:13 -03:00
|
|
|
if (activity.attachments) {
|
2023-07-23 16:55:31 -03:00
|
|
|
await CollectionUtil.asyncForEach(activity.attachments, async attachment => {
|
2019-03-08 06:37:13 -03:00
|
|
|
switch (attachment.contentType) {
|
|
|
|
case 'application/vnd.microsoft.card.hero':
|
|
|
|
output += `\n${this.renderHeroCard(attachment)}`;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'image/png':
|
2023-09-09 13:04:20 -03:00
|
|
|
await this.sendFileToDevice(to, attachment.contentUrl, attachment.name, attachment.name, 0);
|
2023-07-23 16:55:31 -03:00
|
|
|
|
2023-09-09 13:04:20 -03:00
|
|
|
return;
|
2023-07-23 16:55:31 -03:00
|
|
|
|
2019-03-09 16:59:31 -03:00
|
|
|
default:
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `Unknown content type: ${attachment.contentType}`);
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
|
|
|
});
|
2018-05-07 20:45:11 -03:00
|
|
|
}
|
2018-05-11 22:18:38 -03:00
|
|
|
|
2023-07-23 16:55:31 -03:00
|
|
|
await this.sendToDevice(to, output, conversationId);
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public renderHeroCard(attachment) {
|
2019-03-08 06:37:13 -03:00
|
|
|
return `${attachment.content.title} - ${attachment.content.text}`;
|
|
|
|
}
|
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public async sendFileToDevice(to, url, filename, caption, chatId) {
|
2022-06-07 17:27:03 -03:00
|
|
|
let options;
|
2022-07-12 13:30:12 -03:00
|
|
|
switch (this.provider) {
|
2024-05-28 14:06:41 -03:00
|
|
|
case 'meta':
|
|
|
|
let whatsappServiceNumber, whatsappServiceKey;
|
|
|
|
let botNumber = this.min.core.getParam<string>(this.min.instance, 'Bot Number', null);
|
|
|
|
|
|
|
|
if (botNumber && this.min.instance.whatsappServiceNumber) {
|
|
|
|
whatsappServiceNumber = this.min.instance.whatsappServiceNumber;
|
|
|
|
whatsappServiceKey = this.min.instance.whatsappServiceKey;
|
|
|
|
} else {
|
|
|
|
whatsappServiceNumber = GBServer.globals.minBoot.instance.whatsappServiceNumber;
|
|
|
|
whatsappServiceKey = GBServer.globals.minBoot.instance.whatsappServiceKey;
|
|
|
|
}
|
|
|
|
const driver = createBot(whatsappServiceNumber, whatsappServiceKey);
|
|
|
|
await driver.sendImage(to, url, { caption: caption });
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2022-07-12 13:30:12 -03:00
|
|
|
case 'GeneralBots':
|
2023-02-14 13:58:17 -03:00
|
|
|
const attachment = await MessageMedia.fromUrl(url);
|
2023-09-03 16:38:40 -03:00
|
|
|
to = to.replace('+', '');
|
2022-10-12 02:05:26 -03:00
|
|
|
if (to.indexOf('@') == -1) {
|
|
|
|
if (to.length == 18) {
|
|
|
|
to = to + '@g.us';
|
2022-11-19 23:34:58 -03:00
|
|
|
} else {
|
2022-10-12 02:05:26 -03:00
|
|
|
to = to + '@c.us';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await this.customClient.sendMessage(to, attachment, { caption: caption });
|
2022-07-12 13:30:12 -03:00
|
|
|
break;
|
|
|
|
}
|
2023-02-09 19:40:16 -03:00
|
|
|
|
2022-09-04 18:50:36 -03:00
|
|
|
if (options) {
|
|
|
|
try {
|
|
|
|
// tslint:disable-next-line: await-promise
|
2022-11-30 09:40:09 -03:00
|
|
|
const result = await fetch(url, options);
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `File ${url} sent to ${to}: ${result}`);
|
2022-09-04 18:50:36 -03:00
|
|
|
} catch (error) {
|
|
|
|
GBLog.error(`Error sending file to Whatsapp provider ${error.message}`);
|
|
|
|
}
|
2019-08-24 12:22:52 -03:00
|
|
|
}
|
2022-06-21 17:46:46 -03:00
|
|
|
}
|
2019-08-24 12:22:52 -03:00
|
|
|
|
2024-04-13 13:42:17 -03:00
|
|
|
public async sendAudioToDevice(to, url) {
|
2022-07-12 13:30:12 -03:00
|
|
|
let options;
|
|
|
|
switch (this.provider) {
|
|
|
|
case 'GeneralBots':
|
2023-02-14 13:58:17 -03:00
|
|
|
const attachment = MessageMedia.fromUrl(url);
|
2022-07-12 13:30:12 -03:00
|
|
|
await this.customClient.sendMessage(to, attachment);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2019-08-24 18:46:04 -03:00
|
|
|
|
2022-09-02 10:32:07 -03:00
|
|
|
if (options) {
|
|
|
|
try {
|
2022-11-30 09:40:09 -03:00
|
|
|
const result = await fetch(url, options);
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `Audio ${url} sent to ${to}: ${result}`);
|
2022-09-02 10:32:07 -03:00
|
|
|
} catch (error) {
|
|
|
|
GBLog.error(`Error sending audio message to Whatsapp provider ${error.message}`);
|
|
|
|
}
|
2019-08-24 18:46:04 -03:00
|
|
|
}
|
|
|
|
}
|
2019-08-24 12:22:52 -03:00
|
|
|
|
2023-02-09 19:40:16 -03:00
|
|
|
public async sendTextAsAudioToDevice(to, msg: string, chatId) {
|
2022-11-19 23:34:58 -03:00
|
|
|
const url = await GBConversationalService.getAudioBufferFromText(msg);
|
2022-06-07 17:27:03 -03:00
|
|
|
|
2021-12-31 08:39:51 -03:00
|
|
|
await this.sendFileToDevice(to, url, 'Audio', msg, chatId);
|
2020-04-28 20:54:04 -03:00
|
|
|
}
|
|
|
|
|
2024-08-04 17:16:04 -03:00
|
|
|
// Function to create or update a template using WhatsApp Business API
|
|
|
|
|
|
|
|
public async createOrUpdateTemplate(min: GBMinInstance, template, text) {
|
|
|
|
|
|
|
|
template = template.replace(/\-/gi, '_')
|
|
|
|
template = template.replace(/\./gi, '_')
|
|
|
|
|
|
|
|
let image = /(.*)\n/gim.exec(text)[0].trim();
|
|
|
|
|
|
|
|
let path = DialogKeywords.getGBAIPath(min.botId, `gbkb`);
|
|
|
|
path = Path.join(process.env.PWD, 'work', path, 'images', image);
|
|
|
|
|
|
|
|
text = text.substring(image.length + 1).trim();
|
|
|
|
text = text.replace(/\n/g, '\\n');
|
|
|
|
|
|
|
|
const handleImage = await min.whatsAppDirectLine.uploadLargeFile(min, path);
|
|
|
|
|
|
|
|
let data: any = {
|
|
|
|
name: template,
|
|
|
|
components: [
|
|
|
|
{
|
|
|
|
type: 'HEADER',
|
|
|
|
format: 'IMAGE',
|
|
|
|
example: { header_handle: [handleImage] }
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'BODY',
|
|
|
|
text: text
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
const name = data.name;
|
|
|
|
|
|
|
|
// Define the API base URL and endpoints
|
|
|
|
|
|
|
|
const baseUrl = 'https://graph.facebook.com/v20.0'; // API version 20.0
|
|
|
|
const businessAccountId = this.whatsappBusinessManagerId;
|
|
|
|
const accessToken = this.whatsappServiceKey;
|
|
|
|
|
|
|
|
// Endpoint for listing templates
|
|
|
|
|
|
|
|
const listTemplatesEndpoint = `${baseUrl}/${businessAccountId}/message_templates?access_token=${accessToken}`;
|
|
|
|
|
|
|
|
// Step 1: Check if the template exists
|
|
|
|
|
|
|
|
const listResponse = await fetch(listTemplatesEndpoint, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: {
|
|
|
|
Authorization: `Bearer ${accessToken}`
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!listResponse.ok) {
|
|
|
|
throw new Error('Failed to list templates');
|
|
|
|
}
|
|
|
|
|
|
|
|
const templates = await listResponse.json();
|
|
|
|
const templateExists = templates.data.find(template => template.name === name);
|
|
|
|
|
|
|
|
if (templateExists) {
|
|
|
|
// Step 2: Update the template
|
|
|
|
const updateTemplateEndpoint = `${baseUrl}/${templateExists.id}`;
|
|
|
|
|
|
|
|
// Removes the first HEADER element.
|
|
|
|
|
|
|
|
data.components.shift();
|
|
|
|
|
|
|
|
const updateResponse = await fetch(updateTemplateEndpoint, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
Authorization: `Bearer ${accessToken}`,
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
components: data.components
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!updateResponse.ok) {
|
|
|
|
throw new Error(`Failed to update template: ${name} ${await updateResponse.text()}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
GBLogEx.info(min, `Template updated: ${name}`);
|
|
|
|
} else {
|
|
|
|
// Step 3: Create the template
|
|
|
|
const createTemplateEndpoint = `${baseUrl}/${businessAccountId}/message_templates`;
|
|
|
|
|
|
|
|
const createResponse = await fetch(createTemplateEndpoint, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
Authorization: `Bearer ${accessToken}`,
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
name: data['name'],
|
|
|
|
language: 'pt_BR',
|
|
|
|
category: 'MARKETING',
|
|
|
|
components: data.components
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!createResponse.ok) {
|
|
|
|
const body = await createResponse.text();
|
|
|
|
throw new Error(`Failed to create template: ${name} ${body}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
GBLogEx.info(min, `Template created: ${name}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
await GBUtil.sleep(20 * 1000);
|
|
|
|
}
|
|
|
|
|
2024-04-28 16:17:00 -03:00
|
|
|
public async sendToDevice(to: any, msg: string, conversationId) {
|
2024-06-13 22:24:36 -03:00
|
|
|
try {
|
|
|
|
const cmd = '/audio ';
|
|
|
|
let url;
|
|
|
|
let chatId = WhatsappDirectLine.chatIds[conversationId];
|
2021-12-31 08:39:51 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
if (typeof msg !== 'object' && msg.startsWith(cmd)) {
|
|
|
|
msg = msg.substr(cmd.length);
|
2020-06-05 14:40:21 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
return await this.sendTextAsAudioToDevice(to, msg, chatId);
|
|
|
|
} else {
|
|
|
|
let options, messages;
|
|
|
|
const botNumber = this.min.core.getParam(this.min.instance, 'Bot Number', null);
|
|
|
|
|
|
|
|
switch (this.provider) {
|
|
|
|
case 'meta':
|
|
|
|
if (msg['name']) {
|
2024-08-04 17:16:04 -03:00
|
|
|
await this.customClient.sendTemplate(to, msg['name'], 'pt_BR', msg['components']);
|
2024-06-13 22:24:36 -03:00
|
|
|
} else {
|
|
|
|
messages = msg.match(/(.|[\r\n]){1,4096}/g);
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
await CollectionUtil.asyncForEach(messages, async msg => {
|
2024-08-04 17:16:04 -03:00
|
|
|
await this.customClient.sendText(to, msg);
|
2024-05-28 14:06:41 -03:00
|
|
|
|
2024-08-04 17:16:04 -03:00
|
|
|
if (messages.length > 1) {
|
|
|
|
await GBUtil.sleep(3000);
|
|
|
|
}
|
2024-06-13 22:24:36 -03:00
|
|
|
});
|
|
|
|
}
|
2024-03-08 17:10:13 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
break;
|
|
|
|
case 'official':
|
|
|
|
if (to.charAt(0) !== '+') {
|
|
|
|
to = `+${to}`;
|
|
|
|
}
|
2024-03-09 14:02:17 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
messages = msg.match(/(.|[\r\n]){1,1000}/g);
|
2024-03-09 14:02:17 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
await CollectionUtil.asyncForEach(messages, async msg => {
|
|
|
|
await GBUtil.sleep(3000);
|
|
|
|
await this.customClient.messages.create({
|
|
|
|
body: msg,
|
|
|
|
from: `whatsapp:${botNumber}`,
|
|
|
|
to: `whatsapp:${to}`
|
|
|
|
// TODO: mediaUrl.
|
|
|
|
});
|
2024-05-28 14:06:41 -03:00
|
|
|
});
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
break;
|
2024-03-08 17:10:13 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
case 'GeneralBots':
|
|
|
|
to = to.replace('+', '');
|
|
|
|
if (to.indexOf('@') == -1) {
|
|
|
|
if (to.length == 18) {
|
|
|
|
to = to + '@g.us';
|
|
|
|
} else {
|
|
|
|
to = to + '@c.us';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((await this.customClient.getState()) === WAState.CONNECTED) {
|
|
|
|
await this.customClient.sendMessage(to, msg);
|
2022-11-19 23:34:58 -03:00
|
|
|
} else {
|
2024-06-13 22:24:36 -03:00
|
|
|
GBLogEx.info(this.min, `WhatsApp OFFLINE ${to}: ${msg}`);
|
2022-10-12 02:05:26 -03:00
|
|
|
}
|
2022-06-07 17:27:03 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
break;
|
|
|
|
}
|
2021-12-18 21:31:49 -03:00
|
|
|
|
2024-06-13 22:24:36 -03:00
|
|
|
if (options) {
|
|
|
|
try {
|
|
|
|
GBLogEx.info(this.min, `Message [${msg}] is being sent to ${to}...`);
|
|
|
|
await fetch(url, options);
|
|
|
|
} catch (error) {
|
|
|
|
GBLog.error(`Error sending message to Whatsapp provider ${JSON.stringify(error)}`);
|
|
|
|
}
|
2022-09-02 10:32:07 -03:00
|
|
|
}
|
2020-04-28 20:54:04 -03:00
|
|
|
}
|
2024-06-11 23:24:29 -03:00
|
|
|
} catch (error) {
|
|
|
|
GBLog.error(`GBWhatsApp ERR: ${GBUtil.toYAML(error)}`);
|
|
|
|
}
|
2019-03-08 06:37:13 -03:00
|
|
|
}
|
2020-12-31 15:36:19 -03:00
|
|
|
|
2022-11-30 09:40:09 -03:00
|
|
|
public async sendToDeviceEx(to, text, locale, conversationId) {
|
2022-11-19 23:34:58 -03:00
|
|
|
text = await this.min.conversationalService.translate(this.min, text, locale);
|
2021-12-18 21:31:49 -03:00
|
|
|
await this.sendToDevice(to, text, conversationId);
|
2020-12-31 15:36:19 -03:00
|
|
|
}
|
2022-09-01 08:46:29 -03:00
|
|
|
|
2024-03-09 14:02:17 -03:00
|
|
|
private async WhatsAppCallback(req, res, botId = null) {
|
2022-09-01 08:46:29 -03:00
|
|
|
try {
|
2024-02-18 01:15:47 -03:00
|
|
|
if (!req.body && req.type !== 'ptt') {
|
2022-09-01 08:46:29 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-03-09 14:02:17 -03:00
|
|
|
let provider = GBMinService.getProviderName(req, res);
|
2022-09-01 08:46:29 -03:00
|
|
|
let id;
|
|
|
|
let senderName;
|
|
|
|
let text;
|
2022-10-12 02:05:26 -03:00
|
|
|
|
2022-09-01 08:46:29 -03:00
|
|
|
switch (provider) {
|
2024-04-28 00:28:47 -03:00
|
|
|
case 'meta':
|
2024-04-28 16:17:00 -03:00
|
|
|
if (req.body.entry[0].changes[0].value.statuses) {
|
2024-05-28 14:06:41 -03:00
|
|
|
if (req.body.entry[0].changes[0].value.statuses[0].status === 'failed') {
|
2024-04-28 23:28:07 -03:00
|
|
|
GBLogEx.error(this.min, `WhatsApp:${id} ${senderName} ${JSON.stringify(req.body.entry[0].changes[0])}.`);
|
|
|
|
}
|
2024-04-28 16:17:00 -03:00
|
|
|
res.status(200);
|
|
|
|
res.end();
|
2024-04-28 00:28:47 -03:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2024-05-28 14:06:41 -03:00
|
|
|
|
2024-04-28 16:17:00 -03:00
|
|
|
if (req.body.entry[0].changes[0].value.messages[0].text) {
|
|
|
|
text = req.body.entry[0].changes[0].value.messages[0].text.body;
|
2024-05-28 14:06:41 -03:00
|
|
|
} else if (req.body.entry[0].changes[0].value.messages[0].button) {
|
2024-04-28 16:17:00 -03:00
|
|
|
text = req.body.entry[0].changes[0].value.messages[0].button.text;
|
2024-05-28 14:06:41 -03:00
|
|
|
} else {
|
2024-04-29 13:57:31 -03:00
|
|
|
res.status(200);
|
|
|
|
res.end();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2024-04-28 00:28:47 -03:00
|
|
|
id = req.body.entry[0].changes[0].value.messages[0].from;
|
|
|
|
senderName = req.body.entry[0].changes[0].value.contacts[0].profile.name;
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2024-04-28 00:28:47 -03:00
|
|
|
botId = this.botId;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2024-03-09 14:02:17 -03:00
|
|
|
case 'official':
|
|
|
|
const { body } = req;
|
2024-03-08 17:10:13 -03:00
|
|
|
|
2024-03-09 14:02:17 -03:00
|
|
|
id = body.From.replace(/whatsapp\:\+/, '');
|
|
|
|
senderName = body.ProfileName;
|
|
|
|
text = body.Body;
|
|
|
|
|
|
|
|
break;
|
2024-03-08 17:10:13 -03:00
|
|
|
|
2022-11-19 23:34:58 -03:00
|
|
|
case 'GeneralBots':
|
2023-09-09 13:04:20 -03:00
|
|
|
// Ignore E2E messages and status updates.
|
2022-10-11 20:18:12 -03:00
|
|
|
|
2023-09-09 13:04:20 -03:00
|
|
|
if ((req.type && req.type === 'e2e_notification') || req.isStatus) {
|
2022-10-09 21:30:14 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-09-04 18:50:36 -03:00
|
|
|
id = req.from.split('@')[0];
|
2022-09-01 08:46:29 -03:00
|
|
|
senderName = req._data.notifyName;
|
|
|
|
text = req.body;
|
2024-04-28 16:17:00 -03:00
|
|
|
botId = botId ?? this.botId;
|
2022-09-01 08:46:29 -03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const sec = new SecService();
|
2024-02-17 20:33:08 -03:00
|
|
|
|
2023-09-09 16:19:17 -03:00
|
|
|
// Tries to find if user wants to switch bots.
|
|
|
|
|
|
|
|
let toSwitchMin = GBServer.globals.minInstances.filter(
|
|
|
|
p => p.instance.botId.toLowerCase() === text.toLowerCase()
|
|
|
|
)[0];
|
|
|
|
|
2024-04-28 16:17:00 -03:00
|
|
|
botId = botId ?? GBServer.globals.minBoot.botId;
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `A WhatsApp mobile requested instance for: ${botId}.`);
|
2024-04-28 16:17:00 -03:00
|
|
|
|
2022-11-19 23:34:58 -03:00
|
|
|
let urlMin: any = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
|
2024-02-18 01:15:47 -03:00
|
|
|
// Detects user typed language and updates their locale profile if applies.
|
|
|
|
let min = urlMin;
|
2024-02-17 20:33:08 -03:00
|
|
|
|
2024-02-06 22:44:34 -03:00
|
|
|
let user = await sec.getUserFromSystemId(id);
|
2022-09-01 08:46:29 -03:00
|
|
|
|
2023-09-09 12:50:45 -03:00
|
|
|
const botNumber = urlMin ? urlMin.core.getParam(urlMin.instance, 'Bot Number', null) : null;
|
|
|
|
if (botNumber && GBServer.globals.minBoot.botId !== urlMin.botId) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `${id} fixed by bot number talked to: ${botId}.`);
|
2024-05-28 14:06:41 -03:00
|
|
|
let locale = user?.locale
|
|
|
|
? user.locale
|
|
|
|
: min.core.getParam(min.instance, 'Default User Language', GBConfigService.get('DEFAULT_USER_LANGUAGE'));
|
2024-02-18 01:15:47 -03:00
|
|
|
if (!user) {
|
2024-05-28 14:06:41 -03:00
|
|
|
const detectLanguage = min.core.getParam(min.instance, 'Language Detector', false) != false;
|
2024-02-17 20:33:08 -03:00
|
|
|
|
|
|
|
if (text != '' && detectLanguage) {
|
|
|
|
locale = await min.conversationalService.getLanguage(min, text);
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `${locale} defined for first time mobile: ${id}.`);
|
2024-02-17 20:33:08 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
user = await sec.ensureUser(urlMin, id, '', '', 'omnichannel', '', '');
|
2024-02-06 22:44:34 -03:00
|
|
|
user = await sec.updateUserInstance(id, urlMin.instance.instanceId);
|
2024-02-18 01:15:47 -03:00
|
|
|
if (locale) {
|
2024-02-17 22:21:40 -03:00
|
|
|
user = await sec.updateUserLocale(user.userId, locale);
|
|
|
|
}
|
2023-09-09 12:50:45 -03:00
|
|
|
}
|
2024-02-18 01:15:47 -03:00
|
|
|
if (req.type === 'ptt') {
|
|
|
|
if (process.env.AUDIO_DISABLED !== 'true') {
|
|
|
|
const media = await req.downloadMedia();
|
|
|
|
const buf = Buffer.from(media.data, 'base64');
|
|
|
|
|
|
|
|
text = await GBConversationalService.getTextFromAudioBuffer(
|
|
|
|
this.min.instance.speechKey,
|
|
|
|
this.min.instance.cloudLocation,
|
|
|
|
buf,
|
|
|
|
user.locale
|
|
|
|
);
|
2024-03-09 14:02:17 -03:00
|
|
|
|
2024-02-18 01:15:47 -03:00
|
|
|
req.body = text;
|
|
|
|
} else {
|
2024-05-28 14:06:41 -03:00
|
|
|
await this.sendToDevice(
|
|
|
|
user.userSystemId,
|
|
|
|
`No momento estou apenas conseguindo ler mensagens de texto.`,
|
|
|
|
null
|
|
|
|
);
|
2024-02-18 01:15:47 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-01 08:46:29 -03:00
|
|
|
let activeMin;
|
2023-09-09 13:04:20 -03:00
|
|
|
|
2022-09-01 08:46:29 -03:00
|
|
|
// Processes group behaviour.
|
|
|
|
|
2023-05-25 21:20:40 -03:00
|
|
|
text = text.replace(/\@\d+ /gi, '');
|
2023-09-09 13:04:20 -03:00
|
|
|
|
2022-10-11 20:18:12 -03:00
|
|
|
let group;
|
2024-03-09 14:02:17 -03:00
|
|
|
if (provider === 'GeneralBots') {
|
2022-10-11 20:18:12 -03:00
|
|
|
// Ensures that the bot group is the active bot for the user (like switching).
|
2023-09-09 13:04:20 -03:00
|
|
|
|
2022-10-12 00:30:34 -03:00
|
|
|
const message = req;
|
|
|
|
if (message.from.endsWith('@g.us')) {
|
|
|
|
group = message.from;
|
2022-10-11 20:18:12 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `Group: ${group}`);
|
2022-11-30 09:40:09 -03:00
|
|
|
function getKeyByValue(object, value) {
|
2022-10-12 00:30:34 -03:00
|
|
|
return Object.keys(object).find(key => object[key] === value);
|
|
|
|
}
|
2022-10-12 02:05:26 -03:00
|
|
|
const botId = getKeyByValue(WhatsappDirectLine.botGroups, group);
|
2022-11-19 23:34:58 -03:00
|
|
|
if ((botId && user.instanceId !== this.min.instance.instanceId) || !user) {
|
2024-02-05 12:36:20 -03:00
|
|
|
user = await sec.ensureUser(this.min, id, senderName, '', 'whatsApp', senderName, null);
|
2022-10-11 20:18:12 -03:00
|
|
|
}
|
2022-10-12 00:30:34 -03:00
|
|
|
if (botId) {
|
2022-11-19 23:34:58 -03:00
|
|
|
activeMin = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
|
2022-10-11 20:18:12 -03:00
|
|
|
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
|
|
|
return; // EXIT HERE.
|
2023-03-10 15:49:30 -03:00
|
|
|
} else {
|
2023-03-08 11:17:04 -03:00
|
|
|
GBLog.warn(`Group: ${group} not associated with botId:${botId}.`);
|
2024-03-06 23:59:32 -03:00
|
|
|
return;
|
2023-03-08 11:17:04 -03:00
|
|
|
}
|
2022-09-01 08:46:29 -03:00
|
|
|
}
|
2023-09-09 13:04:20 -03:00
|
|
|
|
2023-03-20 16:14:58 -03:00
|
|
|
// Detects if the welcome message is enabled.
|
2022-09-01 08:46:29 -03:00
|
|
|
|
2023-09-09 13:04:20 -03:00
|
|
|
if (process.env.WHATSAPP_WELCOME_DISABLED === 'true') {
|
|
|
|
let minInstance = GBServer.globals.minInstances.filter(
|
|
|
|
p => p.instance.botId.toLowerCase() === botId.toLowerCase()
|
|
|
|
)[0];
|
|
|
|
|
|
|
|
// Just pass the message to the receiver.
|
|
|
|
|
|
|
|
await minInstance.whatsAppDirectLine.received(req, res);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!toSwitchMin) {
|
|
|
|
toSwitchMin = GBServer.globals.minInstances.filter(p =>
|
|
|
|
p.instance.activationCode ? p.instance.activationCode.toLowerCase() === text.toLowerCase() : false
|
2022-09-01 08:46:29 -03:00
|
|
|
)[0];
|
2023-09-09 13:04:20 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If bot has a fixed Find active bot instance.
|
|
|
|
|
|
|
|
activeMin = botNumber ? urlMin : toSwitchMin ? toSwitchMin : GBServer.globals.minBoot;
|
2024-02-18 01:15:47 -03:00
|
|
|
min = activeMin;
|
2023-09-09 13:04:20 -03:00
|
|
|
// If it is the first time for the user, tries to auto-execute
|
|
|
|
// start dialog if any is specified in Config.xlsx.
|
|
|
|
|
|
|
|
if (user === null || user.hearOnDialog) {
|
2024-02-05 12:36:20 -03:00
|
|
|
user = await sec.ensureUser(activeMin, id, senderName, '', 'whatsapp', senderName, null);
|
2023-09-09 13:04:20 -03:00
|
|
|
|
|
|
|
const startDialog = user.hearOnDialog
|
|
|
|
? user.hearOnDialog
|
|
|
|
: activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
|
2022-09-01 08:46:29 -03:00
|
|
|
|
2023-09-09 13:04:20 -03:00
|
|
|
if (startDialog) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`);
|
2024-03-09 14:02:17 -03:00
|
|
|
if (provider === 'GeneralBots') {
|
2023-09-09 13:04:20 -03:00
|
|
|
req.body = `/start`;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resets HEAR ON DIALOG value to none and passes
|
|
|
|
// current dialog to the direct line.
|
|
|
|
|
|
|
|
await sec.updateUserHearOnDialog(user.userId, null);
|
|
|
|
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
|
|
|
} else {
|
|
|
|
if (res) {
|
|
|
|
res.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// User wants to switch bots.
|
2022-09-01 08:46:29 -03:00
|
|
|
|
2023-09-09 22:33:06 -03:00
|
|
|
if (toSwitchMin) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `Switching bots from ${botId} to ${toSwitchMin.botId}...`);
|
2022-09-01 08:46:29 -03:00
|
|
|
|
2023-09-09 13:04:20 -03:00
|
|
|
// So gets the new bot instance information and prepares to
|
|
|
|
// auto start dialog if any is specified.
|
2023-09-09 16:19:17 -03:00
|
|
|
|
2023-09-09 13:09:49 -03:00
|
|
|
activeMin = toSwitchMin;
|
2023-09-09 13:04:20 -03:00
|
|
|
const instance = await this.min.core.loadInstanceByBotId(activeMin.botId);
|
2023-09-09 21:57:26 -03:00
|
|
|
user = await sec.updateUserInstance(id, instance.instanceId);
|
2023-09-09 13:04:20 -03:00
|
|
|
await (activeMin as any).whatsAppDirectLine.resetConversationId(activeMin.botId, id, '');
|
|
|
|
const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
|
2024-05-28 14:06:41 -03:00
|
|
|
ChatServices.memoryMap[user.userSystemId] = null;
|
2022-09-01 08:46:29 -03:00
|
|
|
if (startDialog) {
|
2024-04-21 23:39:39 -03:00
|
|
|
GBLogEx.info(this.min, `Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`);
|
2024-03-09 14:02:17 -03:00
|
|
|
if (provider === 'GeneralBots') {
|
2022-09-01 08:50:04 -03:00
|
|
|
req.body = `/start`;
|
|
|
|
}
|
2022-09-01 08:46:29 -03:00
|
|
|
|
|
|
|
await (activeMin as any).whatsAppDirectLine.received(req, res);
|
|
|
|
} else {
|
|
|
|
await (activeMin as any).whatsAppDirectLine.sendToDevice(
|
|
|
|
id,
|
2023-09-09 13:04:20 -03:00
|
|
|
`Agora falando com ${activeMin.instance.title}...`,
|
2022-11-19 23:34:58 -03:00
|
|
|
null
|
|
|
|
);
|
2023-09-09 13:04:20 -03:00
|
|
|
}
|
|
|
|
if (res) {
|
|
|
|
res.end();
|
2022-09-01 08:46:29 -03:00
|
|
|
}
|
|
|
|
} else {
|
2023-09-09 13:04:20 -03:00
|
|
|
let t;
|
|
|
|
activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0];
|
2024-08-10 13:35:34 -03:00
|
|
|
if (activeMin === undefined) {
|
2023-09-09 13:04:20 -03:00
|
|
|
activeMin = GBServer.globals.minBoot;
|
|
|
|
t = (activeMin as any).whatsAppDirectLine;
|
|
|
|
await t.sendToDevice(
|
|
|
|
id,
|
|
|
|
`O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...`
|
|
|
|
);
|
2022-09-01 08:46:29 -03:00
|
|
|
} else {
|
2023-09-09 13:04:20 -03:00
|
|
|
if ((activeMin as any).whatsAppDirectLine) {
|
2023-03-19 20:09:54 -03:00
|
|
|
t = (activeMin as any).whatsAppDirectLine;
|
2023-09-09 13:04:20 -03:00
|
|
|
} else {
|
|
|
|
t = (GBServer.globals.minBoot as any).whatsAppDirectLine;
|
2023-03-19 20:09:54 -03:00
|
|
|
}
|
2022-09-01 08:46:29 -03:00
|
|
|
}
|
|
|
|
|
2023-09-09 22:33:06 -03:00
|
|
|
await t.received(req, res);
|
2023-09-09 13:04:20 -03:00
|
|
|
}
|
2022-09-01 08:46:29 -03:00
|
|
|
}
|
|
|
|
} catch (error) {
|
2024-06-11 23:24:29 -03:00
|
|
|
GBLog.error(`Error on Whatsapp callback: ${GBUtil.toYAML(error)}`);
|
2022-09-01 08:46:29 -03:00
|
|
|
}
|
|
|
|
}
|
2024-08-04 17:16:04 -03:00
|
|
|
|
|
|
|
public async uploadLargeFile(min, filePath) {
|
|
|
|
const CHUNK_SIZE = 4 * 1024 * 1024; // 4MB chunks
|
|
|
|
let uploadSessionId;
|
|
|
|
const fileSize = (await stat(filePath)).size;
|
|
|
|
const fileName = filePath.split('/').pop();
|
|
|
|
const fileType = mime.lookup(filePath);
|
|
|
|
const appId = this.whatsappFBAppId;
|
|
|
|
const userAccessToken = this.whatsappServiceKey;
|
|
|
|
let h;
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (!fileType) {
|
|
|
|
throw new Error('Unsupported file type');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 1: Start an upload session
|
|
|
|
const startResponse = await fetch(
|
|
|
|
`https://graph.facebook.com/v20.0/${appId}/uploads?file_name=${fileName}&file_length=${fileSize}&file_type=${fileType}&access_token=${userAccessToken}`,
|
|
|
|
{
|
|
|
|
method: 'POST'
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const startData = await startResponse.json();
|
|
|
|
if (!startResponse.ok) {
|
|
|
|
throw new Error(startData.error.message);
|
|
|
|
}
|
|
|
|
uploadSessionId = startData.id.split(':')[1];
|
|
|
|
|
|
|
|
// Step 2: Upload the file in chunks
|
|
|
|
let startOffset = 0;
|
|
|
|
|
|
|
|
while (startOffset < fileSize) {
|
|
|
|
const endOffset = Math.min(startOffset + CHUNK_SIZE, fileSize);
|
|
|
|
const fileStream = Fs.createReadStream(filePath, { start: startOffset, end: endOffset - 1 });
|
|
|
|
const chunkSize = endOffset - startOffset;
|
|
|
|
|
|
|
|
const uploadResponse = await fetch(`https://graph.facebook.com/v20.0/upload:${uploadSessionId}`, {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {
|
|
|
|
Authorization: `OAuth ${userAccessToken}`,
|
|
|
|
file_offset: startOffset.toString(),
|
|
|
|
'Content-Length': chunkSize.toString()
|
|
|
|
},
|
|
|
|
body: fileStream
|
|
|
|
});
|
|
|
|
|
|
|
|
const uploadData = await uploadResponse.json();
|
|
|
|
if (!h) {
|
|
|
|
h = uploadData.h;
|
|
|
|
}
|
|
|
|
if (!uploadResponse.ok) {
|
|
|
|
throw new Error(uploadData.error.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
startOffset = endOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3: Get the file handle
|
|
|
|
const finalizeResponse = await fetch(`https://graph.facebook.com/v20.0/upload:${uploadSessionId}`, {
|
|
|
|
method: 'GET',
|
|
|
|
headers: {
|
|
|
|
Authorization: `OAuth ${userAccessToken}`
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const finalizeData = await finalizeResponse.json();
|
|
|
|
if (!finalizeResponse.ok) {
|
|
|
|
throw new Error(finalizeData.error.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('Upload completed successfully with file handle:', finalizeData.h);
|
|
|
|
return h;
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error during file upload:', error);
|
|
|
|
}
|
|
|
|
}
|
2018-10-16 10:19:34 -03:00
|
|
|
}
|