Merge branch 'develop' of github.com:EvolutionAPI/evolution-api into develop

This commit is contained in:
Davidson Gomes 2024-02-28 13:48:07 -03:00
commit aef3495a79
10 changed files with 175 additions and 41 deletions

View File

@ -2,5 +2,24 @@
"qrgeneratedsuccesfully": "QRCode successfully generated!", "qrgeneratedsuccesfully": "QRCode successfully generated!",
"scanqr": "Scan this QR code within the next 40 seconds.", "scanqr": "Scan this QR code within the next 40 seconds.",
"qrlimitreached": "QRCode generation limit reached, to generate a new QRCode, send the 'init' message again.", "qrlimitreached": "QRCode generation limit reached, to generate a new QRCode, send the 'init' message again.",
"numbernotinwhatsapp": "The message was not sent as the contact is not a valid Whatsapp number." "numbernotinwhatsapp": "The message was not sent as the contact is not a valid Whatsapp number.",
"cw.inbox.connected": "🚀 Connection successfully established!",
"cw.inbox.disconnect": "🚨 Disconnecting WhatsApp from inbox *{{inboxName}}*.",
"cw.inbox.alreadyConnected": "🚨 {{inboxName}} instance is connected.",
"cw.inbox.clearCache": "✅ {{inboxName}} instance cache cleared.",
"cw.inbox.notFound": "⚠️ {{inboxName}} instance not found.",
"cw.inbox.status": "⚠️ {{inboxName}} instance status: *{{state}}*.",
"cw.import.startImport": "💬 Starting to import messages. Please wait...",
"cw.import.importingMessages": "💬 Importing messages. More one moment...",
"cw.import.messagesImported": "💬 {{totalMessagesImported}} messages imported. Refresh page to see the new messages.",
"cw.import.messagesException": "💬 Something went wrong in importing messages.",
"cw.locationMessage.location": "Location",
"cw.locationMessage.latitude": "Latitude",
"cw.locationMessage.longitude": "Longitude",
"cw.locationMessage.locationName": "Name",
"cw.locationMessage.locationAddress": "Address",
"cw.locationMessage.locationUrl": "URL",
"cw.contactMessage.contact": "Contact",
"cw.contactMessage.name": "Name",
"cw.contactMessage.number": "Number"
} }

View File

@ -1,6 +1,25 @@
{ {
"qrgeneratedsuccesfully": "QRCode gerado com sucesso!", "qrgeneratedsuccesfully": "QRCode gerado com sucesso!",
"scanqr": "Escanei o QRCode com o Whatsapp nos próximos 40 segundos.", "scanqr": "Escaneie o QRCode com o WhatsApp nos próximos 40 segundos.",
"qrlimitreached": "Limite de geração de QRCode atingido! Para gerar um novo QRCode, envie o texto 'init' nesta conversa.", "qrlimitreached": "Limite de geração de QRCode atingido! Para gerar um novo QRCode, envie o texto 'init' nesta conversa.",
"numbernotinwhatsapp": "A mensagem não foi enviada, pois o contato não é um número válido do Whatsapp." "numbernotinwhatsapp": "A mensagem não foi enviada, pois o contato não é um número válido do WhatsApp.",
"cw.inbox.connected": "🚀 Conectado com sucesso!",
"cw.inbox.disconnect": "🚨 Instância *{{inboxName}}* desconectada do WhatsApp.",
"cw.inbox.alreadyConnected": "🚨 Instância *{{inboxName}}* já está conectada.",
"cw.inbox.clearCache": "✅ Instância *{{inboxName}}* cache removido.",
"cw.inbox.notFound": "⚠️ Instância *{{inboxName}}* não encontrada.",
"cw.inbox.status": "⚠️ Status da instância {{inboxName}}: *{{state}}*.",
"cw.import.startImport": "💬 Iniciando importação de mensagens. Por favor, aguarde...",
"cw.import.importingMessages": "💬 Importando mensagens. Mais um momento...",
"cw.import.messagesImported": "💬 {{totalMessagesImported}} mensagens importadas. Atualize a página para ver as novas mensagens.",
"cw.import.messagesException": "💬 Não foi possível importar as mensagens.",
"cw.locationMessage.location": "Localização",
"cw.locationMessage.latitude": "Latitude",
"cw.locationMessage.longitude": "Longitude",
"cw.locationMessage.locationName": "Nome",
"cw.locationMessage.locationAddress": "Endereço",
"cw.locationMessage.locationUrl": "URL",
"cw.contactMessage.contact": "Contato",
"cw.contactMessage.name": "Nome",
"cw.contactMessage.number": "Número"
} }

View File

@ -539,6 +539,17 @@ export const privacySettingsSchema: JSONSchema7 = {
required: ['privacySettings'], required: ['privacySettings'],
}; };
export const blockUserSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
number: { type: 'string' },
status: { type: 'string', enum: ['block', 'unblock'] },
},
required: ['number', 'status'],
...isNotEmpty('number', 'status'),
};
export const archiveChatSchema: JSONSchema7 = { export const archiveChatSchema: JSONSchema7 = {
$id: v4(), $id: v4(),
type: 'object', type: 'object',

View File

@ -1,6 +1,7 @@
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
ArchiveChatDto, ArchiveChatDto,
BlockUserDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
NumberDto, NumberDto,
@ -123,4 +124,9 @@ export class ChatController {
logger.verbose('requested updateMessage from ' + instanceName + ' instance'); logger.verbose('requested updateMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].updateMessage(data); return await this.waMonitor.waInstances[instanceName].updateMessage(data);
} }
public async blockUser({ instanceName }: InstanceDto, data: BlockUserDto) {
logger.verbose('requested blockUser from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].blockUser(data);
}
} }

View File

@ -115,3 +115,8 @@ export class UpdateMessageDto extends Metadata {
key: proto.IMessageKey; key: proto.IMessageKey;
text: string; text: string;
} }
export class BlockUserDto {
number: string;
status: 'block' | 'unblock';
}

View File

@ -3,6 +3,7 @@ import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { import {
archiveChatSchema, archiveChatSchema,
blockUserSchema,
contactValidateSchema, contactValidateSchema,
deleteMessageSchema, deleteMessageSchema,
messageUpSchema, messageUpSchema,
@ -20,6 +21,7 @@ import {
import { RouterBroker } from '../abstract/abstract.router'; import { RouterBroker } from '../abstract/abstract.router';
import { import {
ArchiveChatDto, ArchiveChatDto,
BlockUserDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
NumberDto, NumberDto,
@ -384,6 +386,23 @@ export class ChatRouter extends RouterBroker {
}); });
return res.status(HttpStatus.OK).json(response); return res.status(HttpStatus.OK).json(response);
})
.put(this.routerPath('updateBlockStatus'), ...guards, async (req, res) => {
logger.verbose('request received in updateBlockStatus');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<BlockUserDto>({
request: req,
schema: blockUserSchema,
ClassRef: BlockUserDto,
execute: (instance, data) => chatController.blockUser(instance, data),
});
return res.status(HttpStatus.CREATED).json(response);
}); });
} }

View File

@ -1191,14 +1191,26 @@ export class ChatwootService {
await waInstance.connectToWhatsapp(number); await waInstance.connectToWhatsapp(number);
} else { } else {
this.logger.verbose('whatsapp already connected'); this.logger.verbose('whatsapp already connected');
await this.createBotMessage(instance, `🚨 ${body.inbox.name} instance is connected.`, 'incoming'); await this.createBotMessage(
instance,
i18next.t('cw.inbox.alreadyConnected', {
inboxName: body.inbox.name,
}),
'incoming',
);
} }
} }
if (command === 'clearcache') { if (command === 'clearcache') {
this.logger.verbose('command clearcache found'); this.logger.verbose('command clearcache found');
waInstance.clearCacheChatwoot(); waInstance.clearCacheChatwoot();
await this.createBotMessage(instance, `${body.inbox.name} instance cache cleared.`, 'incoming'); await this.createBotMessage(
instance,
i18next.t('cw.inbox.clearCache', {
inboxName: body.inbox.name,
}),
'incoming',
);
} }
if (command === 'status') { if (command === 'status') {
@ -1208,19 +1220,34 @@ export class ChatwootService {
if (!state) { if (!state) {
this.logger.verbose('state not found'); this.logger.verbose('state not found');
await this.createBotMessage(instance, `⚠️ ${body.inbox.name} instance not found.`, 'incoming'); await this.createBotMessage(
instance,
i18next.t('cw.inbox.notFound', {
inboxName: body.inbox.name,
}),
'incoming',
);
} }
if (state) { if (state) {
this.logger.verbose('state: ' + state + ' found'); this.logger.verbose('state: ' + state + ' found');
await this.createBotMessage(instance, `⚠️ ${body.inbox.name} instance status: *${state}*`, 'incoming'); await this.createBotMessage(
instance,
i18next.t('cw.inbox.status', {
inboxName: body.inbox.name,
state: state,
}),
'incoming',
);
} }
} }
if (command === 'disconnect' || command === 'desconectar') { if (command === 'disconnect' || command === 'desconectar') {
this.logger.verbose('command disconnect found'); this.logger.verbose('command disconnect found');
const msgLogout = `🚨 Disconnecting Whatsapp from inbox *${body.inbox.name}*: `; const msgLogout = i18next.t('cw.inbox.disconnect', {
inboxName: body.inbox.name,
});
this.logger.verbose('send message to chatwoot'); this.logger.verbose('send message to chatwoot');
await this.createBotMessage(instance, msgLogout, 'incoming'); await this.createBotMessage(instance, msgLogout, 'incoming');
@ -1509,27 +1536,17 @@ export class ChatwootService {
const latitude = result.degreesLatitude; const latitude = result.degreesLatitude;
const longitude = result.degreesLongitude; const longitude = result.degreesLongitude;
const locationName = result?.name || 'Unknown'; const locationName = result?.name;
const locationAddress = result?.address || 'Unknown'; const locationAddress = result?.address;
const formattedLocation = const formattedLocation =
'*Localização:*\n\n' + `*${i18next.t('cw.locationMessage.location')}:*\n\n` +
'_Latitude:_ ' + `_${i18next.t('cw.locationMessage.latitude')}:_ ${latitude} \n` +
latitude + `_${i18next.t('cw.locationMessage.longitude')}:_ ${longitude} \n` +
'\n' + (locationName ? `_${i18next.t('cw.locationMessage.locationName')}:_ ${locationName}\n` : '') +
'_Longitude:_ ' + (locationAddress ? `_${i18next.t('cw.locationMessage.locationAddress')}:_ ${locationAddress} \n` : '') +
longitude + `_${i18next.t('cw.locationMessage.locationUrl')}:_ ` +
'\n' + `https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`;
'_Nome:_ ' +
locationName +
'\n' +
'_Endereço:_ ' +
locationAddress +
'\n' +
'_Url:_ https://www.google.com/maps/search/?api=1&query=' +
latitude +
',' +
longitude;
this.logger.verbose('message content: ' + formattedLocation); this.logger.verbose('message content: ' + formattedLocation);
@ -1547,17 +1564,19 @@ export class ChatwootService {
} }
}); });
let formattedContact = '*Contact:*\n\n' + '_Name:_ ' + contactInfo['FN']; let formattedContact =
`*${i18next.t('cw.contactMessage.contact')}:*\n\n` +
`_${i18next.t('cw.contactMessage.name')}:_ ${contactInfo['FN']}`;
let numberCount = 1; let numberCount = 1;
Object.keys(contactInfo).forEach((key) => { Object.keys(contactInfo).forEach((key) => {
if (key.startsWith('item') && key.includes('TEL')) { if (key.startsWith('item') && key.includes('TEL')) {
const phoneNumber = contactInfo[key]; const phoneNumber = contactInfo[key];
formattedContact += '\n_Number (' + numberCount + '):_ ' + phoneNumber; formattedContact += `\n_${i18next.t('cw.contactMessage.number')} (${numberCount}):_ ${phoneNumber}`;
numberCount++; numberCount++;
} else if (key.includes('TEL')) { } else if (key.includes('TEL')) {
const phoneNumber = contactInfo[key]; const phoneNumber = contactInfo[key];
formattedContact += '\n_Number (' + numberCount + '):_ ' + phoneNumber; formattedContact += `\n_${i18next.t('cw.contactMessage.number')} (${numberCount}):_ ${phoneNumber}`;
numberCount++; numberCount++;
} }
}); });
@ -1578,17 +1597,19 @@ export class ChatwootService {
} }
}); });
let formattedContact = '*Contact:*\n\n' + '_Name:_ ' + contact.displayName; let formattedContact = `*${i18next.t('cw.contactMessage.contact')}:*\n\n_${i18next.t(
'cw.contactMessage.name',
)}:_ ${contact.displayName}`;
let numberCount = 1; let numberCount = 1;
Object.keys(contactInfo).forEach((key) => { Object.keys(contactInfo).forEach((key) => {
if (key.startsWith('item') && key.includes('TEL')) { if (key.startsWith('item') && key.includes('TEL')) {
const phoneNumber = contactInfo[key]; const phoneNumber = contactInfo[key];
formattedContact += '\n_Number (' + numberCount + '):_ ' + phoneNumber; formattedContact += `\n_${i18next.t('cw.contactMessage.number')} (${numberCount}):_ ${phoneNumber}`;
numberCount++; numberCount++;
} else if (key.includes('TEL')) { } else if (key.includes('TEL')) {
const phoneNumber = contactInfo[key]; const phoneNumber = contactInfo[key];
formattedContact += '\n_Number (' + numberCount + '):_ ' + phoneNumber; formattedContact += `\n_${i18next.t('cw.contactMessage.number')} (${numberCount}):_ ${phoneNumber}`;
numberCount++; numberCount++;
} }
}); });
@ -2074,7 +2095,10 @@ export class ChatwootService {
return; return;
} }
const msgStatus = `⚡️ Instance status ${inbox.name}: ${data.status}`; const msgStatus = i18next.t('cw.inbox.status', {
inboxName: inbox.name,
state: data.status,
});
this.logger.verbose('send message to chatwoot'); this.logger.verbose('send message to chatwoot');
await this.createBotMessage(instance, msgStatus, 'incoming'); await this.createBotMessage(instance, msgStatus, 'incoming');
@ -2086,7 +2110,7 @@ export class ChatwootService {
if (body.status === 'open') { if (body.status === 'open') {
// if we have qrcode count then we understand that a new connection was established // if we have qrcode count then we understand that a new connection was established
if (this.waMonitor.waInstances[instance.instanceName].qrCode.count > 0) { if (this.waMonitor.waInstances[instance.instanceName].qrCode.count > 0) {
const msgConnection = `🚀 Connection successfully established!`; const msgConnection = i18next.t('cw.inbox.connected');
this.logger.verbose('send message to chatwoot'); this.logger.verbose('send message to chatwoot');
await this.createBotMessage(instance, msgConnection, 'incoming'); await this.createBotMessage(instance, msgConnection, 'incoming');
this.waMonitor.waInstances[instance.instanceName].qrCode.count = 0; this.waMonitor.waInstances[instance.instanceName].qrCode.count = 0;
@ -2147,7 +2171,7 @@ export class ChatwootService {
return; return;
} }
this.createBotMessage(instance, `💬 Starting to import messages. Please wait...`, 'incoming'); this.createBotMessage(instance, i18next.t('cw.import.startImport'), 'incoming');
} }
public isImportHistoryAvailable() { public isImportHistoryAvailable() {
@ -2180,7 +2204,7 @@ export class ChatwootService {
return; return;
} }
this.createBotMessage(instance, '💬 Importing messages. More one moment...', 'incoming'); this.createBotMessage(instance, i18next.t('cw.import.importingMessages'), 'incoming');
const totalMessagesImported = await chatwootImport.importHistoryMessages( const totalMessagesImported = await chatwootImport.importHistoryMessages(
instance, instance,
@ -2191,10 +2215,10 @@ export class ChatwootService {
this.updateContactAvatarInRecentConversations(instance); this.updateContactAvatarInRecentConversations(instance);
const msg = Number.isInteger(totalMessagesImported) const msg = Number.isInteger(totalMessagesImported)
? `${totalMessagesImported} messages imported. Refresh page to see the new messages` ? i18next.t('cw.import.messagesImported', { totalMessagesImported })
: `Something went wrong in importing messages`; : i18next.t('cw.import.messagesException');
this.createBotMessage(instance, `💬 ${msg}`, 'incoming'); this.createBotMessage(instance, msg, 'incoming');
return totalMessagesImported; return totalMessagesImported;
} }

View File

@ -64,6 +64,7 @@ import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-d
import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db';
import { import {
ArchiveChatDto, ArchiveChatDto,
BlockUserDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
LastMessage, LastMessage,
@ -2817,6 +2818,29 @@ export class BaileysStartupService extends WAStartupService {
} }
} }
public async blockUser(data: BlockUserDto) {
this.logger.verbose('Blocking user: ' + data.number);
try {
const { number } = data;
this.logger.verbose(`Check if number "${number}" is WhatsApp`);
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')) {
throw new BadRequestException(isWA);
}
const sender = isWA.jid;
await this.client.updateBlockStatus(sender, data.status);
return { block: 'success' };
} catch (error) {
throw new InternalServerErrorException('Error blocking user', error.toString());
}
}
public async updateMessage(data: UpdateMessageDto) { public async updateMessage(data: UpdateMessageDto) {
try { try {
const jid = this.createJid(data.number); const jid = this.createJid(data.number);

View File

@ -1159,6 +1159,9 @@ export class BusinessStartupService extends WAStartupService {
public async removeProfilePicture() { public async removeProfilePicture() {
throw new BadRequestException('Method not available on WhatsApp Business API'); throw new BadRequestException('Method not available on WhatsApp Business API');
} }
public async blockUser() {
throw new BadRequestException('Method not available on WhatsApp Business API');
}
public async updateMessage() { public async updateMessage() {
throw new BadRequestException('Method not available on WhatsApp Business API'); throw new BadRequestException('Method not available on WhatsApp Business API');
} }

View File

@ -18,5 +18,9 @@
"incremental": true, "incremental": true,
"noImplicitAny": false "noImplicitAny": false
}, },
"exclude": ["node_modules", "./test", "./dist", "./prisma"] "exclude": ["node_modules", "./test", "./dist", "./prisma"],
"include": [
"src/**/*",
"src/**/*.json"
]
} }