mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-26 18:38:39 -06:00
Merge branch 'main' into heroku-button
This commit is contained in:
commit
9e977d45ab
15
CHANGELOG.md
15
CHANGELOG.md
@ -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)
|
||||||
|
|
||||||
|
@ -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
@ -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",
|
||||||
|
@ -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',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
13
src/main.ts
13
src/main.ts
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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');
|
||||||
|
@ -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>({
|
||||||
|
@ -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 }
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -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)
|
||||||
|
@ -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');
|
||||||
|
@ -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 () => {
|
||||||
|
@ -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 = {
|
||||||
|
@ -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');
|
||||||
@ -1900,17 +1903,17 @@ export class WAStartupService {
|
|||||||
|
|
||||||
public async fetchProfile(instanceName: string, number?: string) {
|
public async fetchProfile(instanceName: string, number?: string) {
|
||||||
const jid = number ? this.createJid(number) : this.client?.user?.id;
|
const jid = number ? this.createJid(number) : this.client?.user?.id;
|
||||||
|
|
||||||
this.logger.verbose('Getting profile with jid: ' + jid);
|
this.logger.verbose('Getting profile with jid: ' + jid);
|
||||||
try {
|
try {
|
||||||
this.logger.verbose('Getting profile info');
|
this.logger.verbose('Getting profile info');
|
||||||
|
|
||||||
if (number) {
|
if (number) {
|
||||||
const info = (await this.whatsappNumber({ numbers: [jid] }))?.shift();
|
const info = (await this.whatsappNumber({ numbers: [jid] }))?.shift();
|
||||||
const picture = await this.profilePicture(info?.jid);
|
const picture = await this.profilePicture(info?.jid);
|
||||||
const status = await this.getStatus(info?.jid);
|
const status = await this.getStatus(info?.jid);
|
||||||
const business = await this.fetchBusinessProfile(info?.jid);
|
const business = await this.fetchBusinessProfile(info?.jid);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wuid: info?.jid || jid,
|
wuid: info?.jid || jid,
|
||||||
name: info?.name,
|
name: info?.name,
|
||||||
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user