Merge branch 'main' into heroku-button

This commit is contained in:
Gabriel Pastori 2023-10-24 20:32:20 -03:00 committed by GitHub
commit 9e977d45ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 326 additions and 82 deletions

View File

@ -1,4 +1,15 @@
# 1.5.1 (homolog)
# 1.5.2 (2023-09-28 17:56)
### Fixed
* Fix chatwootSchema in chatwoot model to store reopen_conversation and conversation_pending options
* Problem resolved when sending files from minio to typebot
* Improvement in the "startTypebot" method to create persistent session when triggered
* New manager for Evo 1.5.2 - Set Typebot update
* Resolved problems when reading/querying instances
# 1.5.1 (2023-09-17 13:50)
### Feature ### Feature
@ -7,10 +18,12 @@
* Added webhooks for typebot events * Added webhooks for typebot events
* Added ChamaAI integration * Added ChamaAI integration
* Added webhook to send errors * Added webhook to send errors
* Added support for messaging with ads on chatwoot
### Fixed ### Fixed
* Fix looping connection messages in chatwoot * Fix looping connection messages in chatwoot
* Improved performance of fetch instances
# 1.5.0 (2023-08-18 12:47) # 1.5.0 (2023-08-18 12:47)

View File

@ -1,6 +1,6 @@
FROM node:16.18-alpine FROM node:16.18-alpine
LABEL version="1.5.1" description="Api to control whatsapp features through http requests." LABEL version="1.5.2" description="Api to control whatsapp features through http requests."
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
LABEL contact="contato@agenciadgcode.com" LABEL contact="contato@agenciadgcode.com"

File diff suppressed because one or more lines are too long

View File

@ -1 +1,2 @@
web: node ./dist/src/main.js web: node ./dist/src/main.js

View File

@ -1,6 +1,6 @@
{ {
"name": "evolution-api", "name": "evolution-api",
"version": "1.5.1", "version": "1.5.2",
"description": "Rest api for communication with WhatsApp", "description": "Rest api for communication with WhatsApp",
"main": "./dist/src/main.js", "main": "./dist/src/main.js",
"scripts": { "scripts": {
@ -43,10 +43,10 @@
"dependencies": { "dependencies": {
"@adiwajshing/keyed-db": "^0.2.4", "@adiwajshing/keyed-db": "^0.2.4",
"@ffmpeg-installer/ffmpeg": "^1.1.0", "@ffmpeg-installer/ffmpeg": "^1.1.0",
"@figuro/chatwoot-sdk": "^1.1.14", "@figuro/chatwoot-sdk": "^1.1.16",
"@hapi/boom": "^10.0.1", "@hapi/boom": "^10.0.1",
"@sentry/node": "^7.59.2", "@sentry/node": "^7.59.2",
"@whiskeysockets/baileys": "^6.4.1", "@whiskeysockets/baileys": "^6.5.0",
"amqplib": "^0.10.3", "amqplib": "^0.10.3",
"axios": "^1.3.5", "axios": "^1.3.5",
"class-validator": "^0.13.2", "class-validator": "^0.13.2",

View File

@ -76,6 +76,10 @@ export type Websocket = {
ENABLED: boolean; ENABLED: boolean;
}; };
export type Chatwoot = {
USE_REPLY_ID: boolean;
};
export type EventsWebhook = { export type EventsWebhook = {
APPLICATION_STARTUP: boolean; APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean; QRCODE_UPDATED: boolean;
@ -145,6 +149,7 @@ export interface Env {
QRCODE: QrCode; QRCODE: QrCode;
AUTHENTICATION: Auth; AUTHENTICATION: Auth;
PRODUCTION?: Production; PRODUCTION?: Production;
CHATWOOT?: Chatwoot;
} }
export type Key = keyof Env; export type Key = keyof Env;
@ -166,11 +171,10 @@ export class ConfigService {
? this.envYaml() ? this.envYaml()
: this.envProcess(); : this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD'; this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
// if ((process.env?.DOCKER_ENV === 'true', process.env?.HEROKU_ENV === 'true')) {
if ((process.env?.DOCKER_ENV === 'true', process.env?.HEROKU_ENV === 'true')) { // this.env.SERVER.TYPE = 'http';
this.env.SERVER.TYPE = 'http'; // this.env.SERVER.PORT = parseInt(process.env.PORT) || 8080;
this.env.SERVER.PORT = parseInt(process.env.PORT) || 8080; // }
}
} }
private envYaml(): Env { private envYaml(): Env {
@ -250,9 +254,7 @@ export class ConfigService {
COLOR: process.env?.LOG_COLOR === 'true', COLOR: process.env?.LOG_COLOR === 'true',
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error', BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
}, },
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE) DEL_INSTANCE: process.env?.DEL_INSTANCE === 'true' ? 5 : Number.parseInt(process.env.DEL_INSTANCE) || false,
? process.env.DEL_INSTANCE === 'true'
: Number.parseInt(process.env.DEL_INSTANCE) || false,
WEBHOOK: { WEBHOOK: {
GLOBAL: { GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL || '', URL: process.env?.WEBHOOK_GLOBAL_URL || '',
@ -309,6 +311,9 @@ export class ConfigService {
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`', SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
}, },
}, },
CHATWOOT: {
USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true',
},
}; };
} }
} }

View File

@ -156,3 +156,7 @@ AUTHENTICATION:
JWT: JWT:
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
SECRET: L=0YWt]b2w[WF>#>:&E` SECRET: L=0YWt]b2w[WF>#>:&E`
# Configure to chatwoot
CHATWOOT:
USE_REPLY_ID: false

View File

@ -11,7 +11,7 @@ let io: SocketIO;
const cors = configService.get<Cors>('CORS').ORIGIN; const cors = configService.get<Cors>('CORS').ORIGIN;
export const initIO = (httpServer: Server) => { export const initIO = (httpServer: Server) => {
if (configService.get<Websocket>('WEBSOCKET').ENABLED) { if (configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
io = new SocketIO(httpServer, { io = new SocketIO(httpServer, {
cors: { cors: {
origin: cors, origin: cors,

View File

@ -6,7 +6,7 @@ import cors from 'cors';
import express, { json, NextFunction, Request, Response, urlencoded } from 'express'; import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
import { join } from 'path'; import { join } from 'path';
import { configService, Cors, HttpServer, Rabbitmq, Webhook } from './config/env.config'; import { Auth, configService, Cors, HttpServer, Rabbitmq, Webhook } from './config/env.config';
import { onUnexpectedError } from './config/error.config'; import { onUnexpectedError } from './config/error.config';
import { Logger } from './config/logger.config'; import { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config'; import { ROOT_DIR } from './config/path.config';
@ -61,6 +61,8 @@ function bootstrap() {
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const localISOTime = new Date(Date.now() - tzoffset).toISOString(); const localISOTime = new Date(Date.now() - tzoffset).toISOString();
const now = localISOTime; const now = localISOTime;
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
const serverUrl = configService.get<HttpServer>('SERVER').URL;
const errorData = { const errorData = {
event: 'error', event: 'error',
@ -73,6 +75,8 @@ function bootstrap() {
}, },
}, },
date_time: now, date_time: now,
api_key: globalApiKey,
server_url: serverUrl,
}; };
logger.error(errorData); logger.error(errorData);
@ -83,11 +87,6 @@ function bootstrap() {
httpService.post('', errorData); httpService.post('', errorData);
} }
if (err['message'].includes('No sessions')) {
console.log(err['message']);
process.exit(1);
}
return res.status(err['status'] || 500).json({ return res.status(err['status'] || 500).json({
status: err['status'] || 500, status: err['status'] || 500,
error: err['error'] || 'Internal Server Error', error: err['error'] || 'Internal Server Error',
@ -125,7 +124,7 @@ function bootstrap() {
initIO(server); initIO(server);
if (configService.get<Rabbitmq>('RABBITMQ').ENABLED) initAMQP(); if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
onUnexpectedError(); onUnexpectedError();
} }

View File

@ -4,6 +4,13 @@ export class Session {
status?: string; status?: string;
createdAt?: number; createdAt?: number;
updateAt?: number; updateAt?: number;
prefilledVariables?: PrefilledVariables;
}
export class PrefilledVariables {
remoteJid?: string;
pushName?: string;
additionalData?: { [key: string]: any };
} }
export class TypebotDto { export class TypebotDto {

View File

@ -24,6 +24,8 @@ const chatwootSchema = new Schema<ChatwootRaw>({
name_inbox: { type: String, required: true }, name_inbox: { type: String, required: true },
sign_msg: { type: Boolean, required: true }, sign_msg: { type: Boolean, required: true },
number: { type: String, required: true }, number: { type: String, required: true },
reopen_conversation: { type: Boolean, required: true },
conversation_pending: { type: Boolean, required: true },
}); });
export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot'); export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');

View File

@ -20,6 +20,8 @@ export class MessageRaw {
messageTimestamp?: number | Long.Long; messageTimestamp?: number | Long.Long;
owner: string; owner: string;
source?: 'android' | 'web' | 'ios'; source?: 'android' | 'web' | 'ios';
source_id?: string;
source_reply_id?: string;
} }
const messageSchema = new Schema<MessageRaw>({ const messageSchema = new Schema<MessageRaw>({

View File

@ -8,6 +8,11 @@ class Session {
status?: string; status?: string;
createdAt?: number; createdAt?: number;
updateAt?: number; updateAt?: number;
prefilledVariables?: {
remoteJid?: string;
pushName?: string;
additionalData?: { [key: string]: any };
};
} }
export class TypebotRaw { export class TypebotRaw {
@ -40,6 +45,11 @@ const typebotSchema = new Schema<TypebotRaw>({
status: { type: String, required: true }, status: { type: String, required: true },
createdAt: { type: Number, required: true }, createdAt: { type: Number, required: true },
updateAt: { type: Number, required: true }, updateAt: { type: Number, required: true },
prefilledVariables: {
remoteJid: { type: String, required: false },
pushName: { type: String, required: false },
additionalData: { type: Schema.Types.Mixed, required: false }
},
}, },
], ],
}); });

View File

@ -36,8 +36,6 @@ const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')); const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
// Hide index if needed
if (!httpServer.HIDE_INDEX) if (!httpServer.HIDE_INDEX)
router.get('/', (req, res) => { router.get('/', (req, res) => {
res.status(HttpStatus.OK).json({ res.status(HttpStatus.OK).json({
@ -46,13 +44,10 @@ if (!httpServer.HIDE_INDEX)
version: packageJson.version, version: packageJson.version,
}); });
}); });
// Hide manager if needed
if (!httpServer.HIDE_MANAGER) router.use('/manager', new ViewsRouter().router); if (!httpServer.HIDE_MANAGER) router.use('/manager', new ViewsRouter().router);
router router
.use('/instance', new InstanceRouter(configService, ...guards).router) .use('/instance', new InstanceRouter(configService, ...guards).router)
.use('/manager', new ViewsRouter().router)
.use('/message', new MessageRouter(...guards).router) .use('/message', new MessageRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router) .use('/chat', new ChatRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router) .use('/group', new GroupRouter(...guards).router)

View File

@ -2,6 +2,7 @@ import ChatwootClient from '@figuro/chatwoot-sdk';
import axios from 'axios'; import axios from 'axios';
import FormData from 'form-data'; import FormData from 'form-data';
import { createReadStream, readFileSync, unlinkSync, writeFileSync } from 'fs'; import { createReadStream, readFileSync, unlinkSync, writeFileSync } from 'fs';
import Jimp from 'jimp';
import mimeTypes from 'mime-types'; import mimeTypes from 'mime-types';
import path from 'path'; import path from 'path';
@ -949,7 +950,6 @@ export class ChatwootService {
public async receiveWebhook(instance: InstanceDto, body: any) { public async receiveWebhook(instance: InstanceDto, body: any) {
try { try {
// espera 500ms para evitar duplicidade de mensagens
await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500));
this.logger.verbose('receive webhook to chatwoot instance: ' + instance.instanceName); this.logger.verbose('receive webhook to chatwoot instance: ' + instance.instanceName);
@ -1125,6 +1125,20 @@ export class ChatwootService {
return result; return result;
} }
private getAdsMessage(msg: any) {
interface AdsMessage {
title: string;
body: string;
thumbnailUrl: string;
sourceUrl: string;
}
const adsMessage: AdsMessage | undefined = msg.extendedTextMessage?.contextInfo.externalAdReply;
this.logger.verbose('Get ads message if it exist');
adsMessage && this.logger.verbose('Ads message: ' + adsMessage);
return adsMessage;
}
private getTypeMessage(msg: any) { private getTypeMessage(msg: any) {
this.logger.verbose('get type message'); this.logger.verbose('get type message');
@ -1278,15 +1292,17 @@ export class ChatwootService {
const isMedia = this.isMediaMessage(body.message); const isMedia = this.isMediaMessage(body.message);
const adsMessage = this.getAdsMessage(body.message);
if (!bodyMessage && !isMedia) { if (!bodyMessage && !isMedia) {
this.logger.warn('no body message found'); this.logger.warn('no body message found');
return; return;
} }
this.logger.verbose('get conversation in chatwoot'); this.logger.verbose('get conversation in chatwoot');
const getConversion = await this.createConversation(instance, body); const getConversation = await this.createConversation(instance, body);
if (!getConversion) { if (!getConversation) {
this.logger.warn('conversation not found'); this.logger.warn('conversation not found');
return; return;
} }
@ -1337,7 +1353,7 @@ export class ChatwootService {
} }
this.logger.verbose('send data to chatwoot'); this.logger.verbose('send data to chatwoot');
const send = await this.sendData(getConversion, fileName, messageType, content); const send = await this.sendData(getConversation, fileName, messageType, content);
if (!send) { if (!send) {
this.logger.warn('message not sent'); this.logger.warn('message not sent');
@ -1358,7 +1374,7 @@ export class ChatwootService {
this.logger.verbose('message is not group'); this.logger.verbose('message is not group');
this.logger.verbose('send data to chatwoot'); this.logger.verbose('send data to chatwoot');
const send = await this.sendData(getConversion, fileName, messageType, bodyMessage); const send = await this.sendData(getConversation, fileName, messageType, bodyMessage);
if (!send) { if (!send) {
this.logger.warn('message not sent'); this.logger.warn('message not sent');
@ -1378,6 +1394,67 @@ export class ChatwootService {
} }
} }
this.logger.verbose('check if has Ads Message');
if (adsMessage) {
this.logger.verbose('message is from Ads');
this.logger.verbose('get base64 from media ads message');
const imgBuffer = await axios.get(adsMessage.thumbnailUrl, { responseType: 'arraybuffer' });
const extension = mimeTypes.extension(imgBuffer.headers['content-type']);
const mimeType = extension && mimeTypes.lookup(extension);
if (!mimeType) {
this.logger.warn('mimetype of Ads message not found');
return;
}
const random = Math.random().toString(36).substring(7);
const nameFile = `${random}.${mimeTypes.extension(mimeType)}`;
const fileData = Buffer.from(imgBuffer.data, 'binary');
const fileName = `${path.join(waInstance?.storePath, 'temp', `${nameFile}`)}`;
this.logger.verbose('temp file name: ' + nameFile);
this.logger.verbose('create temp file');
await Jimp.read(fileData)
.then(async (img) => {
await img.cover(320, 180).writeAsync(fileName);
})
.catch((err) => {
this.logger.error(`image is not write: ${err}`);
});
const truncStr = (str: string, len: number) => {
return str.length > len ? str.substring(0, len) + '...' : str;
};
const title = truncStr(adsMessage.title, 40);
const description = truncStr(adsMessage.body, 75);
this.logger.verbose('send data to chatwoot');
const send = await this.sendData(
getConversation,
fileName,
messageType,
`${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`,
);
if (!send) {
this.logger.warn('message not sent');
return;
}
this.messageCacheFile = path.join(ROOT_DIR, 'store', 'chatwoot', `${instance.instanceName}_cache.txt`);
this.messageCache = this.loadMessageCache();
this.messageCache.add(send.id.toString());
this.logger.verbose('save message cache');
this.saveMessageCache();
return send;
}
this.logger.verbose('check if is group'); this.logger.verbose('check if is group');
if (body.key.remoteJid.includes('@g.us')) { if (body.key.remoteJid.includes('@g.us')) {
this.logger.verbose('message is group'); this.logger.verbose('message is group');
@ -1394,7 +1471,7 @@ export class ChatwootService {
} }
this.logger.verbose('send data to chatwoot'); this.logger.verbose('send data to chatwoot');
const send = await this.createMessage(instance, getConversion, content, messageType); const send = await this.createMessage(instance, getConversation, content, messageType);
if (!send) { if (!send) {
this.logger.warn('message not sent'); this.logger.warn('message not sent');
@ -1415,7 +1492,7 @@ export class ChatwootService {
this.logger.verbose('message is not group'); this.logger.verbose('message is not group');
this.logger.verbose('send data to chatwoot'); this.logger.verbose('send data to chatwoot');
const send = await this.createMessage(instance, getConversion, bodyMessage, messageType); const send = await this.createMessage(instance, getConversation, bodyMessage, messageType);
if (!send) { if (!send) {
this.logger.warn('message not sent'); this.logger.warn('message not sent');

View File

@ -7,6 +7,9 @@ import { join } from 'path';
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config'; import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config'; import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
// inserido por francis inicio
import { NotFoundException } from '../../exceptions';
// inserido por francis fim
import { dbserver } from '../../libs/db.connect'; import { dbserver } from '../../libs/db.connect';
import { RedisCache } from '../../libs/redis.client'; import { RedisCache } from '../../libs/redis.client';
import { import {
@ -72,7 +75,7 @@ export class WAMonitoringService {
}, 1000 * 60 * time); }, 1000 * 60 * time);
} }
} }
/* ocultado por francis inicio
public async instanceInfo(instanceName?: string) { public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info'); this.logger.verbose('get instance info');
@ -128,6 +131,96 @@ export class WAMonitoringService {
return instances; return instances;
} }
ocultado por francis fim */
// inserido por francis inicio
public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info');
if (instanceName && !this.waInstances[instanceName]) {
throw new NotFoundException(`Instance "${instanceName}" not found`);
}
const instances: any[] = [];
for await (const [key, value] of Object.entries(this.waInstances)) {
if (value) {
this.logger.verbose('get instance info: ' + key);
let chatwoot: any;
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const findChatwoot = await this.waInstances[key].findChatwoot();
if (findChatwoot && findChatwoot.enabled) {
chatwoot = {
...findChatwoot,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`,
};
}
if (value.connectionStatus.state === 'open') {
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
const instanceData = {
instance: {
instanceName: key,
owner: value.wuid,
profileName: (await value.getProfileName()) || 'not loaded',
profilePictureUrl: value.profilePictureUrl,
profileStatus: (await value.getProfileStatus()) || '',
status: value.connectionStatus.state,
},
};
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey;
instanceData.instance['chatwoot'] = chatwoot;
}
instances.push(instanceData);
} else {
this.logger.verbose('instance: ' + key + ' - connectionStatus: ' + value.connectionStatus.state);
const instanceData = {
instance: {
instanceName: key,
status: value.connectionStatus.state,
},
};
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey;
instanceData.instance['chatwoot'] = chatwoot;
}
instances.push(instanceData);
}
}
}
this.logger.verbose('return instance info: ' + instances.length);
return instances.find((i) => i.instance.instanceName === instanceName) ?? instances;
}
// inserido por francis fim
private delInstanceFiles() { private delInstanceFiles() {
this.logger.verbose('cron to delete instance files started'); this.logger.verbose('cron to delete instance files started');
setInterval(async () => { setInterval(async () => {

View File

@ -100,6 +100,13 @@ export class TypebotService {
const url = data.url; const url = data.url;
const typebot = data.typebot; const typebot = data.typebot;
const variables = data.variables; const variables = data.variables;
const findTypebot = await this.find(instance);
const sessions = (findTypebot.sessions as Session[]) ?? [];
const expire = findTypebot.expire;
const keyword_finish = findTypebot.keyword_finish;
const delay_message = findTypebot.delay_message;
const unknown_message = findTypebot.unknown_message;
const listening_from_me = findTypebot.listening_from_me;
const prefilledVariables = { const prefilledVariables = {
remoteJid: remoteJid, remoteJid: remoteJid,
@ -109,34 +116,39 @@ export class TypebotService {
prefilledVariables[variable.name] = variable.value; prefilledVariables[variable.name] = variable.value;
}); });
const id = Math.floor(Math.random() * 10000000000).toString(); const response = await this.createNewSession(instance, {
const reqData = {
sessionId: id,
startParams: {
typebot: data.typebot,
prefilledVariables: prefilledVariables,
},
};
const request = await axios.post(data.url + '/api/v1/sendMessage', reqData);
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
);
this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, {
remoteJid: remoteJid,
url: url, url: url,
typebot: typebot, typebot: typebot,
variables: variables, remoteJid: remoteJid,
sessionId: id, expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: sessions,
prefilledVariables: prefilledVariables,
}); });
if (response.sessionId) {
await this.sendWAMessage(
instance,
remoteJid,
response.messages,
response.input,
response.clientSideActions,
);
this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, {
remoteJid: remoteJid,
url: url,
typebot: typebot,
prefilledVariables: prefilledVariables,
sessionId: `${response.sessionId}`,
});
} else {
throw new Error("Session ID not found in response");
}
return { return {
typebot: { typebot: {
...instance, ...instance,
@ -144,7 +156,7 @@ export class TypebotService {
url: url, url: url,
remoteJid: remoteJid, remoteJid: remoteJid,
typebot: typebot, typebot: typebot,
variables: variables, prefilledVariables: prefilledVariables,
}, },
}, },
}; };
@ -193,8 +205,9 @@ export class TypebotService {
startParams: { startParams: {
typebot: data.typebot, typebot: data.typebot,
prefilledVariables: { prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid, remoteJid: data.remoteJid,
pushName: data.pushName, pushName: data.pushName || 'Default Name',
instanceName: instance.instanceName, instanceName: instance.instanceName,
}, },
}, },
@ -209,6 +222,12 @@ export class TypebotService {
status: 'opened', status: 'opened',
createdAt: Date.now(), createdAt: Date.now(),
updateAt: Date.now(), updateAt: Date.now(),
prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid,
pushName: data.pushName || 'Default Name',
instanceName: instance.instanceName,
}
}); });
const typebotData = { const typebotData = {

View File

@ -654,15 +654,17 @@ export class WAStartupService {
const amqp = getAMQP(); const amqp = getAMQP();
if (amqp) { if (amqp) {
this.logger.verbose('Sending data to rabbitMQ on channel: ' + this.instance.name);
if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) { if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) {
const exchangeName = this.instanceName ?? 'evolution_exchange'; this.logger.verbose('Sending data to rabbitMQ on event: ' + event);
const exchangeName = this.instance.name ?? 'evolution_exchange';
amqp.assertExchange(exchangeName, 'topic', { amqp.assertExchange(exchangeName, 'topic', {
durable: true, durable: true,
autoDelete: false, autoDelete: false,
}); });
const queueName = `${this.instanceName}.${event}`; const queueName = `${this.instance.name}.${event}`;
amqp.assertQueue(queueName, { amqp.assertQueue(queueName, {
durable: true, durable: true,
@ -711,7 +713,7 @@ export class WAStartupService {
} }
} }
if (this.configService.get<Websocket>('WEBSOCKET').ENABLED && this.localWebsocket.enabled) { if (this.configService.get<Websocket>('WEBSOCKET')?.ENABLED && this.localWebsocket.enabled) {
this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name); this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name);
if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) { if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) {
this.logger.verbose('Sending data to websocket on event: ' + event); this.logger.verbose('Sending data to websocket on event: ' + event);
@ -1434,8 +1436,9 @@ export class WAStartupService {
if ( if (
type !== 'notify' || type !== 'notify' ||
!received.message || !received?.message ||
received.message?.protocolMessage || received.message?.protocolMessage ||
received.message.senderKeyDistributionMessage ||
received.message?.pollUpdateMessage received.message?.pollUpdateMessage
) { ) {
this.logger.verbose('message rejected'); this.logger.verbose('message rejected');
@ -1954,9 +1957,10 @@ export class WAStartupService {
private async sendMessageWithTyping<T = proto.IMessage>(number: string, message: T, options?: Options) { private async sendMessageWithTyping<T = proto.IMessage>(number: string, message: T, options?: Options) {
this.logger.verbose('Sending message with typing'); this.logger.verbose('Sending message with typing');
const numberWA = await this.whatsappNumber({ numbers: [number] }); this.logger.verbose(`Check if number "${number}" is WhatsApp`);
const isWA = numberWA[0]; const isWA = (await this.whatsappNumber({ numbers: [number] }))?.shift();
this.logger.verbose(`Exists: "${isWA.exists}" | jid: ${isWA.jid}`);
if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) { if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) {
throw new BadRequestException(isWA); throw new BadRequestException(isWA);
} }
@ -1980,7 +1984,7 @@ export class WAStartupService {
this.logger.verbose('Sending presence update: paused'); this.logger.verbose('Sending presence update: paused');
} }
const linkPreview = options?.linkPreview != false ? undefined : false; const linkPreview = options?.linkPreview == true ? undefined : options?.linkPreview || false;
let quoted: WAMessage; let quoted: WAMessage;
@ -2000,9 +2004,9 @@ export class WAStartupService {
let mentions: string[]; let mentions: string[];
if (isJidGroup(sender)) { if (isJidGroup(sender)) {
try { try {
const groupMetadata = await this.client.groupMetadata(sender); const group = await this.findGroup({ groupJid: sender }, 'inner');
if (!groupMetadata) { if (!group) {
throw new NotFoundException('Group not found'); throw new NotFoundException('Group not found');
} }
@ -2013,7 +2017,7 @@ export class WAStartupService {
this.logger.verbose('Mentions everyone'); this.logger.verbose('Mentions everyone');
this.logger.verbose('Getting group metadata'); this.logger.verbose('Getting group metadata');
mentions = groupMetadata.participants.map((participant) => participant.id); mentions = group.participants.map((participant) => participant.id);
this.logger.verbose('Getting group metadata for mentions'); this.logger.verbose('Getting group metadata for mentions');
} else if (options.mentions?.mentioned?.length) { } else if (options.mentions?.mentioned?.length) {
this.logger.verbose('Mentions manually defined'); this.logger.verbose('Mentions manually defined');
@ -2021,7 +2025,6 @@ export class WAStartupService {
const jid = this.createJid(mention); const jid = this.createJid(mention);
if (isJidGroup(jid)) { if (isJidGroup(jid)) {
return null; return null;
// throw new BadRequestException('Mentions must be a number');
} }
return jid; return jid;
}); });
@ -2307,22 +2310,36 @@ export class WAStartupService {
mediaMessage.fileName = arrayMatch[1]; mediaMessage.fileName = arrayMatch[1];
this.logger.verbose('File name: ' + mediaMessage.fileName); this.logger.verbose('File name: ' + mediaMessage.fileName);
} }
// *inserido francis inicio
let mimetype: string;
// *inserido francis final
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) { if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
mediaMessage.fileName = 'image.png'; mediaMessage.fileName = 'image.png';
// inserido francis inicio
mimetype = 'image/png';
// inserido francis inicio
} }
if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) { if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
mediaMessage.fileName = 'video.mp4'; mediaMessage.fileName = 'video.mp4';
// inserido francis inicio
mimetype = 'video/mp4';
// inserido francis final
} }
let mimetype: string; // ocultado francis inicio
// let mimetype: string;
if (isURL(mediaMessage.media)) {
mimetype = getMIMEType(mediaMessage.media); // if (isURL(mediaMessage.media)) {
} else { // mimetype = getMIMEType(mediaMessage.media);
mimetype = getMIMEType(mediaMessage.fileName); // } else {
} // mimetype = getMIMEType(mediaMessage.fileName);
// }
// ocultado francis final
this.logger.verbose('Mimetype: ' + mimetype); this.logger.verbose('Mimetype: ' + mimetype);