feat: prisma and remove mongodb

This commit is contained in:
Davidson Gomes 2024-06-06 14:47:58 -03:00
parent 35f97e08dd
commit 36ec67cef9
76 changed files with 1663 additions and 3894 deletions

View File

@ -1,7 +1,17 @@
# 1.8.1 (develop)
# 2.0.0 (develop)
### Feature
* New method of saving sessions to a file using worker, made in partnership with [codechat](https://github.com/code-chat-br/whatsapp-api)
* Added prism orm, connection to postgres and mysql
### Fixed
*
### Break changes
* jwt authentication removed
* Connection to mongodb removed
* Standardized all request bodies to use camelCase
* Change in webhook information from owner to instanceId
# 1.8.0 (2024-05-27 16:10)

View File

@ -74,7 +74,6 @@
"jsonwebtoken": "^9.0.2",
"libphonenumber-js": "^1.10.39",
"link-preview-js": "^3.0.4",
"mongoose": "^6.10.5",
"node-cache": "^5.1.2",
"node-mime-types": "^1.1.0",
"node-windows": "^1.0.0-beta.8",

View File

@ -34,9 +34,8 @@ enum TypebotSessionStatus {
}
model Instance {
id Int @id @default(autoincrement())
id String @id @default(cuid())
name String @unique @db.VarChar(255)
description String? @db.VarChar(255)
connectionStatus InstanceConnectionStatus @default(open)
ownerJid String? @db.VarChar(100)
profilePicUrl String? @db.VarChar(500)
@ -60,33 +59,31 @@ model Instance {
}
model Session {
id Int @id @unique @default(autoincrement())
sessionId String @unique @default(cuid())
creds String? @db.Text
createdAt DateTime @default(now())
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique @db.Integer
@@map("sessions")
id Int @id @unique @default(autoincrement())
sessionId String @unique
creds String? @db.Text
createdAt DateTime @default(now())
Instance Instance @relation(fields: [sessionId], references: [id], onDelete: Cascade)
}
model Auth {
id Int @id @default(autoincrement())
apiKey String @unique
apikey String @unique
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime? @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique @db.Integer
instanceId String @unique
}
model Chat {
id Int @id @default(autoincrement())
lastMsgTimestamp DateTime? @db.Timestamp
remoteJid String @db.VarChar(100)
lastMsgTimestamp String? @db.VarChar(100)
labels Json? @db.JsonB
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime? @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int
instanceId String
}
model Contact {
@ -97,79 +94,77 @@ model Contact {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime? @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int
instanceId String
}
model Message {
id Int @id @default(autoincrement())
keyId String @db.VarChar(100)
keyRemoteJid String @db.VarChar(100)
keyFromMe Boolean @db.Boolean
keyParticipant String? @db.VarChar(100)
key Json @db.JsonB
pushName String? @db.VarChar(100)
participant String? @db.VarChar(100)
messageType String @db.VarChar(100)
message Json @db.JsonB
contextInfo Json? @db.JsonB
source DeviceMessage
messageTimestamp Int @db.Integer
messageTimestamp String @db.VarChar(100)
chatwootMessageId Int? @db.Integer
chatwootInboxId Int? @db.Integer
chatwootConversationId Int? @db.Integer
chatwootContactInboxSourceId String? @db.VarChar(100)
chatwotIsRead Boolean? @db.Boolean
chatwootIsRead Boolean? @db.Boolean
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int
instanceId String
typebotSessionId Int?
MessageUpdate MessageUpdate[]
TypebotSession TypebotSession? @relation(fields: [typebotSessionId], references: [id])
@@index([keyId], name: "keyId")
}
model MessageUpdate {
id Int @id @default(autoincrement())
keyId String @db.VarChar(100)
remoteJid String @db.VarChar(100)
fromMe Boolean @db.Boolean
participant String? @db.VarChar(100)
dateTime DateTime @db.Date
pollUpdates Json? @db.JsonB
status String @db.VarChar(30)
Message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
messageId Int
}
model Webhook {
id Int @id @default(autoincrement())
url String @db.VarChar(500)
enabled Boolean? @default(true) @db.Boolean
events Json? @db.JsonB
webhook_by_events Boolean? @default(false) @db.Boolean
webhook_base64 Boolean? @default(false) @db.Boolean
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
id Int @id @default(autoincrement())
url String @db.VarChar(500)
enabled Boolean? @default(true) @db.Boolean
events Json? @db.JsonB
webhookByEvents Boolean? @default(false) @db.Boolean
webhookBase64 Boolean? @default(false) @db.Boolean
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId String @unique
}
model Chatwoot {
id Int @id @default(autoincrement())
enabled Boolean? @default(true) @db.Boolean
account_id String? @db.VarChar(100)
token String? @db.VarChar(100)
url String? @db.VarChar(500)
name_inbox String? @db.VarChar(100)
sign_msg Boolean? @default(false) @db.Boolean
sign_delimiter String? @db.VarChar(100)
number String? @db.VarChar(100)
reopen_conversation Boolean? @default(false) @db.Boolean
conversation_pending Boolean? @default(false) @db.Boolean
merge_brazil_contacts Boolean? @default(false) @db.Boolean
import_contacts Boolean? @default(false) @db.Boolean
import_messages Boolean? @default(false) @db.Boolean
days_limit_import_messages Int? @db.Integer
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
id Int @id @default(autoincrement())
enabled Boolean? @default(true) @db.Boolean
accountId String? @db.VarChar(100)
token String? @db.VarChar(100)
url String? @db.VarChar(500)
nameInbox String? @db.VarChar(100)
signMsg Boolean? @default(false) @db.Boolean
signDelimiter String? @db.VarChar(100)
number String? @db.VarChar(100)
reopenConversation Boolean? @default(false) @db.Boolean
conversationPending Boolean? @default(false) @db.Boolean
mergeBrazilContacts Boolean? @default(false) @db.Boolean
importContacts Boolean? @default(false) @db.Boolean
importMessages Boolean? @default(false) @db.Boolean
daysLimitImportMessages Int? @db.Integer
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId String @unique
}
model Integration {
@ -180,7 +175,7 @@ model Integration {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
instanceId String @unique
}
model Label {
@ -192,20 +187,20 @@ model Label {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int
instanceId String
}
model Proxy {
id Int @id @default(autoincrement())
enabled Boolean? @default(true) @db.Boolean
proxyHost String? @db.VarChar(100)
proxyPort Int? @db.Integer
proxyUsername String? @db.VarChar(100)
proxyPassword String? @db.VarChar(100)
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
id Int @id @default(autoincrement())
enabled Boolean? @default(true) @db.Boolean
host String? @db.VarChar(100)
port String? @db.VarChar(100)
username String? @db.VarChar(100)
password String? @db.VarChar(100)
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId String @unique
}
model Setting {
@ -220,7 +215,7 @@ model Setting {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
instanceId String @unique
}
model Rabbitmq {
@ -230,7 +225,7 @@ model Rabbitmq {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
instanceId String @unique
}
model Sqs {
@ -240,7 +235,7 @@ model Sqs {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
instanceId String @unique
}
model Websocket {
@ -250,7 +245,7 @@ model Websocket {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
instanceId String @unique
}
model Typebot {
@ -266,7 +261,7 @@ model Typebot {
createdAt DateTime? @default(now()) @db.Date
updatedAt DateTime? @updatedAt @db.Date
Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)
instanceId Int @unique
instanceId String @unique
sessions TypebotSession[]
}

View File

@ -1,3 +1,5 @@
import { Contact, Message, MessageUpdate } from '@prisma/client';
import { Logger } from '../../config/logger.config';
import {
ArchiveChatDto,
@ -16,9 +18,7 @@ import {
WhatsAppNumberDto,
} from '../dto/chat.dto';
import { InstanceDto } from '../dto/instance.dto';
import { ContactQuery } from '../repository/mongodb/contact.repository';
import { MessageQuery } from '../repository/mongodb/message.repository';
import { MessageUpQuery } from '../repository/mongodb/messageUp.repository';
import { Query } from '../repository/repository.service';
import { WAMonitoringService } from '../services/monitor.service';
const logger = new Logger('ChatController');
@ -61,7 +61,7 @@ export class ChatController {
return await this.waMonitor.waInstances[instanceName].fetchProfile(instanceName, data.number);
}
public async fetchContacts({ instanceName }: InstanceDto, query: ContactQuery) {
public async fetchContacts({ instanceName }: InstanceDto, query: Query<Contact>) {
logger.verbose('requested fetchContacts from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchContacts(query);
}
@ -71,12 +71,12 @@ export class ChatController {
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data);
}
public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) {
public async fetchMessages({ instanceName }: InstanceDto, query: Query<Message>) {
logger.verbose('requested fetchMessages from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchMessages(query);
}
public async fetchStatusMessage({ instanceName }: InstanceDto, query: MessageUpQuery) {
public async fetchStatusMessage({ instanceName }: InstanceDto, query: Query<MessageUpdate>) {
logger.verbose('requested fetchStatusMessage from ' + instanceName + ' instance');
return await this.waMonitor.waInstances[instanceName].fetchStatusMessage(query);
}

View File

@ -1,3 +1,4 @@
import { JsonValue } from '@prisma/client/runtime/library';
import { delay } from '@whiskeysockets/baileys';
import { isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2';
@ -13,8 +14,7 @@ import { SqsService } from '../integrations/sqs/services/sqs.service';
import { TypebotService } from '../integrations/typebot/services/typebot.service';
import { WebsocketService } from '../integrations/websocket/services/websocket.service';
import { ProviderFiles } from '../provider/sessions';
import { MongodbRepository } from '../repository/mongodb/repository.manager';
import { PrismaRepository } from '../repository/prisma/repository.service';
import { PrismaRepository } from '../repository/repository.service';
import { AuthService } from '../services/auth.service';
import { CacheService } from '../services/cache.service';
import { BaileysStartupService } from '../services/channels/whatsapp.baileys.service';
@ -30,7 +30,6 @@ export class InstanceController {
constructor(
private readonly waMonitor: WAMonitoringService,
private readonly configService: ConfigService,
private readonly mongodbRepository: MongodbRepository,
private readonly prismaRepository: PrismaRepository,
private readonly eventEmitter: EventEmitter2,
private readonly authService: AuthService,
@ -54,45 +53,45 @@ export class InstanceController {
public async createInstance({
instanceName,
webhook,
webhook_by_events,
webhook_base64,
events,
webhookByEvents,
webhookBase64,
webhookEvents,
qrcode,
number,
mobile,
integration,
token,
chatwoot_account_id,
chatwoot_token,
chatwoot_url,
chatwoot_sign_msg,
chatwoot_reopen_conversation,
chatwoot_conversation_pending,
chatwoot_import_contacts,
chatwoot_name_inbox,
chatwoot_merge_brazil_contacts,
chatwoot_import_messages,
chatwoot_days_limit_import_messages,
reject_call,
msg_call,
groups_ignore,
always_online,
read_messages,
read_status,
sync_full_history,
websocket_enabled,
websocket_events,
rabbitmq_enabled,
rabbitmq_events,
sqs_enabled,
sqs_events,
typebot_url,
chatwootAccountId,
chatwootToken,
chatwootUrl,
chatwootSignMsg,
chatwootReopenConversation,
chatwootConversationPending,
chatwootImportContacts,
chatwootNameInbox,
chatwootMergeBrazilContacts,
chatwootImportMessages,
chatwootDaysLimitImportMessages,
rejectCall,
msgCall,
groupsIgnore,
alwaysOnline,
readMessages,
readStatus,
syncFullHistory,
websocketEnabled,
websocketEvents,
rabbitmqEnabled,
rabbitmqEvents,
sqsEnabled,
sqsEvents,
typebotUrl,
typebot,
typebot_expire,
typebot_keyword_finish,
typebot_delay_message,
typebot_unknown_message,
typebot_listening_from_me,
typebotExpire,
typebotKeywordFinish,
typebotDelayMessage,
typebotUnknownMessage,
typebotListeningFromMe,
proxy,
}: InstanceDto) {
try {
@ -111,7 +110,6 @@ export class InstanceController {
instance = new BusinessStartupService(
this.configService,
this.eventEmitter,
this.mongodbRepository,
this.prismaRepository,
this.cache,
this.chatwootCache,
@ -122,7 +120,6 @@ export class InstanceController {
instance = new BaileysStartupService(
this.configService,
this.eventEmitter,
this.mongodbRepository,
this.prismaRepository,
this.cache,
this.chatwootCache,
@ -131,11 +128,12 @@ export class InstanceController {
);
}
await this.waMonitor.saveInstance({ integration, instanceName, token, number, mobile });
const instanceId = v4();
await this.waMonitor.saveInstance({ instanceId, integration, instanceName, token, number, mobile });
instance.instanceName = instanceName;
const instanceId = v4();
instance.instanceId = instanceId;
instance.sendDataWebhook(Events.INSTANCE_CREATE, {
instanceName,
@ -158,7 +156,7 @@ export class InstanceController {
this.logger.verbose('hash: ' + hash + ' generated');
let webhookEvents: string[];
let getWebhookEvents: string[];
if (webhook) {
if (!isURL(webhook, { require_tld: false })) {
@ -168,7 +166,7 @@ export class InstanceController {
this.logger.verbose('creating webhook');
try {
let newEvents: string[] = [];
if (events.length === 0) {
if (webhookEvents.length === 0) {
newEvents = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
@ -196,29 +194,31 @@ export class InstanceController {
'TYPEBOT_CHANGE_STATUS',
];
} else {
newEvents = events;
newEvents = webhookEvents;
}
this.webhookService.create(instance, {
enabled: true,
url: webhook,
events: newEvents,
webhook_by_events,
webhook_base64,
webhookByEvents,
webhookBase64,
});
webhookEvents = (await this.webhookService.find(instance)).events;
const webhookEventsJson: JsonValue = (await this.webhookService.find(instance)).events;
getWebhookEvents = Array.isArray(webhookEventsJson) ? webhookEventsJson.map((event) => String(event)) : [];
} catch (error) {
this.logger.log(error);
}
}
let websocketEvents: string[];
let getWebsocketEvents: string[];
if (websocket_enabled) {
if (websocketEnabled) {
this.logger.verbose('creating websocket');
try {
let newEvents: string[] = [];
if (websocket_events.length === 0) {
if (websocketEvents.length === 0) {
newEvents = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
@ -246,26 +246,31 @@ export class InstanceController {
'TYPEBOT_CHANGE_STATUS',
];
} else {
newEvents = websocket_events;
newEvents = websocketEvents;
}
this.websocketService.create(instance, {
enabled: true,
events: newEvents,
});
websocketEvents = (await this.websocketService.find(instance)).events;
const websocketEventsJson: JsonValue = (await this.websocketService.find(instance)).events;
// websocketEvents = (await this.websocketService.find(instance)).events;
getWebsocketEvents = Array.isArray(websocketEventsJson)
? websocketEventsJson.map((event) => String(event))
: [];
} catch (error) {
this.logger.log(error);
}
}
let rabbitmqEvents: string[];
let getRabbitmqEvents: string[];
if (rabbitmq_enabled) {
if (rabbitmqEnabled) {
this.logger.verbose('creating rabbitmq');
try {
let newEvents: string[] = [];
if (rabbitmq_events.length === 0) {
if (rabbitmqEvents.length === 0) {
newEvents = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
@ -293,26 +298,30 @@ export class InstanceController {
'TYPEBOT_CHANGE_STATUS',
];
} else {
newEvents = rabbitmq_events;
newEvents = rabbitmqEvents;
}
this.rabbitmqService.create(instance, {
enabled: true,
events: newEvents,
});
rabbitmqEvents = (await this.rabbitmqService.find(instance)).events;
const rabbitmqEventsJson: JsonValue = (await this.rabbitmqService.find(instance)).events;
getRabbitmqEvents = Array.isArray(rabbitmqEventsJson) ? rabbitmqEventsJson.map((event) => String(event)) : [];
// rabbitmqEvents = (await this.rabbitmqService.find(instance)).events;
} catch (error) {
this.logger.log(error);
}
}
let sqsEvents: string[];
let getSqsEvents: string[];
if (sqs_enabled) {
if (sqsEnabled) {
this.logger.verbose('creating sqs');
try {
let newEvents: string[] = [];
if (sqs_events.length === 0) {
if (sqsEvents.length === 0) {
newEvents = [
'APPLICATION_STARTUP',
'QRCODE_UPDATED',
@ -340,14 +349,18 @@ export class InstanceController {
'TYPEBOT_CHANGE_STATUS',
];
} else {
newEvents = sqs_events;
newEvents = sqsEvents;
}
this.sqsService.create(instance, {
enabled: true,
events: newEvents,
});
sqsEvents = (await this.sqsService.find(instance)).events;
const sqsEventsJson: JsonValue = (await this.sqsService.find(instance)).events;
getSqsEvents = Array.isArray(sqsEventsJson) ? sqsEventsJson.map((event) => String(event)) : [];
// sqsEvents = (await this.sqsService.find(instance)).events;
} catch (error) {
this.logger.log(error);
}
@ -361,27 +374,31 @@ export class InstanceController {
await this.proxyService.createProxy(instance, {
enabled: true,
proxy,
host: proxy.host,
port: proxy.port,
protocol: proxy.protocol,
username: proxy.username,
password: proxy.password,
});
}
if (typebot_url) {
if (typebotUrl) {
try {
if (!isURL(typebot_url, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property in typebot_url');
if (!isURL(typebotUrl, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property in typebotUrl');
}
this.logger.verbose('creating typebot');
this.typebotService.create(instance, {
enabled: true,
url: typebot_url,
url: typebotUrl,
typebot: typebot,
expire: typebot_expire,
keyword_finish: typebot_keyword_finish,
delay_message: typebot_delay_message,
unknown_message: typebot_unknown_message,
listening_from_me: typebot_listening_from_me,
expire: typebotExpire,
keywordFinish: typebotKeywordFinish,
delayMessage: typebotDelayMessage,
unknownMessage: typebotUnknownMessage,
listeningFromMe: typebotListeningFromMe,
});
} catch (error) {
this.logger.log(error);
@ -390,29 +407,29 @@ export class InstanceController {
this.logger.verbose('creating settings');
const settings: wa.LocalSettings = {
reject_call: reject_call || false,
msg_call: msg_call || '',
groups_ignore: groups_ignore || true,
always_online: always_online || false,
read_messages: read_messages || false,
read_status: read_status || false,
sync_full_history: sync_full_history ?? false,
rejectCall: rejectCall || false,
msgCall: msgCall || '',
groupsIgnore: groupsIgnore || true,
alwaysOnline: alwaysOnline || false,
readMessages: readMessages || false,
readStatus: readStatus || false,
syncFullHistory: syncFullHistory ?? false,
};
this.logger.verbose('settings: ' + JSON.stringify(settings));
this.settingsService.create(instance, settings);
let webhook_wa_business = null,
access_token_wa_business = '';
let webhookWaBusiness = null,
accessTokenWaBusiness = '';
if (integration === Integration.WHATSAPP_BUSINESS) {
if (!number) {
throw new BadRequestException('number is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
webhook_wa_business = `${urlServer}/webhook/whatsapp/${encodeURIComponent(instance.instanceName)}`;
access_token_wa_business = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;
webhookWaBusiness = `${urlServer}/webhook/whatsapp/${encodeURIComponent(instance.instanceName)}`;
accessTokenWaBusiness = this.configService.get<WaBusiness>('WA_BUSINESS').TOKEN_WEBHOOK;
}
this.integrationService.create(instance, {
@ -420,7 +437,7 @@ export class InstanceController {
number,
token,
});
if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) {
if (!chatwootAccountId || !chatwootToken || !chatwootUrl) {
let getQrcode: wa.QrCode;
if (qrcode) {
@ -435,38 +452,38 @@ export class InstanceController {
instanceName: instance.instanceName,
instanceId: instanceId,
integration: integration,
webhook_wa_business,
access_token_wa_business,
webhookWaBusiness,
accessTokenWaBusiness,
status: 'created',
},
hash,
webhook: {
webhook,
webhook_by_events,
webhook_base64,
events: webhookEvents,
webhookByEvents,
webhookBase64,
events: getWebhookEvents,
},
websocket: {
enabled: websocket_enabled,
events: websocketEvents,
enabled: websocketEnabled,
events: getWebsocketEvents,
},
rabbitmq: {
enabled: rabbitmq_enabled,
events: rabbitmqEvents,
enabled: rabbitmqEnabled,
events: getRabbitmqEvents,
},
sqs: {
enabled: sqs_enabled,
events: sqsEvents,
enabled: sqsEnabled,
events: getSqsEvents,
},
typebot: {
enabled: typebot_url ? true : false,
url: typebot_url,
enabled: typebotUrl ? true : false,
url: typebotUrl,
typebot,
expire: typebot_expire,
keyword_finish: typebot_keyword_finish,
delay_message: typebot_delay_message,
unknown_message: typebot_unknown_message,
listening_from_me: typebot_listening_from_me,
expire: typebotExpire,
keywordFinish: typebotKeywordFinish,
delayMessage: typebotDelayMessage,
unknownMessage: typebotUnknownMessage,
listeningFromMe: typebotListeningFromMe,
},
settings,
qrcode: getQrcode,
@ -478,32 +495,32 @@ export class InstanceController {
return result;
}
if (!chatwoot_account_id) {
throw new BadRequestException('account_id is required');
if (!chatwootAccountId) {
throw new BadRequestException('accountId is required');
}
if (!chatwoot_token) {
if (!chatwootToken) {
throw new BadRequestException('token is required');
}
if (!chatwoot_url) {
if (!chatwootUrl) {
throw new BadRequestException('url is required');
}
if (!isURL(chatwoot_url, { require_tld: false })) {
if (!isURL(chatwootUrl, { require_tld: false })) {
throw new BadRequestException('Invalid "url" property in chatwoot');
}
if (chatwoot_sign_msg !== true && chatwoot_sign_msg !== false) {
throw new BadRequestException('sign_msg is required');
if (chatwootSignMsg !== true && chatwootSignMsg !== false) {
throw new BadRequestException('signMsg is required');
}
if (chatwoot_reopen_conversation !== true && chatwoot_reopen_conversation !== false) {
throw new BadRequestException('reopen_conversation is required');
if (chatwootReopenConversation !== true && chatwootReopenConversation !== false) {
throw new BadRequestException('reopenConversation is required');
}
if (chatwoot_conversation_pending !== true && chatwoot_conversation_pending !== false) {
throw new BadRequestException('conversation_pending is required');
if (chatwootConversationPending !== true && chatwootConversationPending !== false) {
throw new BadRequestException('conversationPending is required');
}
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
@ -511,19 +528,19 @@ export class InstanceController {
try {
this.chatwootService.create(instance, {
enabled: true,
account_id: chatwoot_account_id,
token: chatwoot_token,
url: chatwoot_url,
sign_msg: chatwoot_sign_msg || false,
name_inbox: chatwoot_name_inbox ?? instance.instanceName.split('-cwId-')[0],
accountId: chatwootAccountId,
token: chatwootToken,
url: chatwootUrl,
signMsg: chatwootSignMsg || false,
nameInbox: chatwootNameInbox ?? instance.instanceName.split('-cwId-')[0],
number,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
import_contacts: chatwoot_import_contacts ?? true,
merge_brazil_contacts: chatwoot_merge_brazil_contacts ?? false,
import_messages: chatwoot_import_messages ?? true,
days_limit_import_messages: chatwoot_days_limit_import_messages ?? 60,
auto_create: true,
reopenConversation: chatwootReopenConversation || false,
conversationPending: chatwootConversationPending || false,
importContacts: chatwootImportContacts ?? true,
mergeBrazilContacts: chatwootMergeBrazilContacts ?? false,
importMessages: chatwootImportMessages ?? true,
daysLimitImportMessages: chatwootDaysLimitImportMessages ?? 60,
autoCreate: true,
});
} catch (error) {
this.logger.log(error);
@ -534,55 +551,55 @@ export class InstanceController {
instanceName: instance.instanceName,
instanceId: instanceId,
integration: integration,
webhook_wa_business,
access_token_wa_business,
webhookWaBusiness,
accessTokenWaBusiness,
status: 'created',
},
hash,
webhook: {
webhook,
webhook_by_events,
webhook_base64,
events: webhookEvents,
webhookByEvents,
webhookBase64,
events: getWebhookEvents,
},
websocket: {
enabled: websocket_enabled,
events: websocketEvents,
enabled: websocketEnabled,
events: getWebsocketEvents,
},
rabbitmq: {
enabled: rabbitmq_enabled,
events: rabbitmqEvents,
enabled: rabbitmqEnabled,
events: getRabbitmqEvents,
},
sqs: {
enabled: sqs_enabled,
events: sqsEvents,
enabled: sqsEnabled,
events: getSqsEvents,
},
typebot: {
enabled: typebot_url ? true : false,
url: typebot_url,
enabled: typebotUrl ? true : false,
url: typebotUrl,
typebot,
expire: typebot_expire,
keyword_finish: typebot_keyword_finish,
delay_message: typebot_delay_message,
unknown_message: typebot_unknown_message,
listening_from_me: typebot_listening_from_me,
expire: typebotExpire,
keywordFinish: typebotKeywordFinish,
delayMessage: typebotDelayMessage,
unknownMessage: typebotUnknownMessage,
listeningFromMe: typebotListeningFromMe,
},
settings,
chatwoot: {
enabled: true,
account_id: chatwoot_account_id,
token: chatwoot_token,
url: chatwoot_url,
sign_msg: chatwoot_sign_msg || false,
reopen_conversation: chatwoot_reopen_conversation || false,
conversation_pending: chatwoot_conversation_pending || false,
merge_brazil_contacts: chatwoot_merge_brazil_contacts ?? false,
import_contacts: chatwoot_import_contacts ?? true,
import_messages: chatwoot_import_messages ?? true,
days_limit_import_messages: chatwoot_days_limit_import_messages || 60,
accountId: chatwootAccountId,
token: chatwootToken,
url: chatwootUrl,
signMsg: chatwootSignMsg || false,
reopenConversation: chatwootReopenConversation || false,
conversationPending: chatwootConversationPending || false,
mergeBrazilContacts: chatwootMergeBrazilContacts ?? false,
importContacts: chatwootImportContacts ?? true,
importMessages: chatwootImportMessages ?? true,
daysLimitImportMessages: chatwootDaysLimitImportMessages || 60,
number,
name_inbox: chatwoot_name_inbox ?? instance.instanceName,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
nameInbox: chatwootNameInbox ?? instance.instanceName,
webhookUrl: `${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
},
};
} catch (error) {
@ -686,9 +703,17 @@ export class InstanceController {
let arrayReturn = false;
if (env.KEY !== key) {
const instanceByKey = await this.mongodbRepository.auth.findByKey(key);
const instanceByKey = await this.prismaRepository.auth.findUnique({
where: {
apikey: key,
},
include: {
Instance: true,
},
});
if (instanceByKey) {
name = instanceByKey._id;
name = instanceByKey.Instance.name;
arrayReturn = true;
} else {
throw new UnauthorizedException();
@ -737,8 +762,9 @@ export class InstanceController {
throw new BadRequestException('The "' + instanceName + '" instance needs to be disconnected');
}
try {
this.waMonitor.waInstances[instanceName]?.removeRabbitmqQueues();
this.waMonitor.waInstances[instanceName]?.clearCacheChatwoot();
const waInstances = this.waMonitor.waInstances[instanceName];
waInstances?.removeRabbitmqQueues();
waInstances?.clearCacheChatwoot();
if (instance.state === 'connecting') {
this.logger.verbose('logging out instance: ' + instanceName);
@ -749,9 +775,9 @@ export class InstanceController {
this.logger.verbose('deleting instance: ' + instanceName);
try {
this.waMonitor.waInstances[instanceName]?.sendDataWebhook(Events.INSTANCE_DELETE, {
waInstances?.sendDataWebhook(Events.INSTANCE_DELETE, {
instanceName,
instanceId: (await this.mongodbRepository.auth.find(instanceName))?.instanceId,
instanceId: waInstances.instanceId,
});
} catch (error) {
this.logger.error(error);

View File

@ -22,11 +22,15 @@ export class ProxyController {
if (!data.enabled) {
logger.verbose('proxy disabled');
data.proxy = null;
data.host = '';
data.port = '';
data.protocol = '';
data.username = '';
data.password = '';
}
if (data.proxy) {
const testProxy = await this.testProxy(data.proxy);
if (data.host) {
const testProxy = await this.testProxy(data);
if (!testProxy) {
throw new BadRequestException('Invalid proxy');
}
@ -46,7 +50,7 @@ export class ProxyController {
return this.proxyService.find(instance);
}
public async testProxy(proxy: ProxyDto['proxy']) {
public async testProxy(proxy: ProxyDto) {
logger.verbose('requested testProxy');
try {
const serverIp = await axios.get('https://icanhazip.com/');

View File

@ -11,41 +11,41 @@ export class InstanceDto {
integration?: string;
token?: string;
webhook?: string;
webhook_by_events?: boolean;
webhook_base64?: boolean;
events?: string[];
reject_call?: boolean;
msg_call?: string;
groups_ignore?: boolean;
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
chatwoot_account_id?: string;
chatwoot_token?: string;
chatwoot_url?: string;
chatwoot_sign_msg?: boolean;
chatwoot_reopen_conversation?: boolean;
chatwoot_conversation_pending?: boolean;
chatwoot_merge_brazil_contacts?: boolean;
chatwoot_import_contacts?: boolean;
chatwoot_import_messages?: boolean;
chatwoot_days_limit_import_messages?: number;
chatwoot_name_inbox?: string;
websocket_enabled?: boolean;
websocket_events?: string[];
rabbitmq_enabled?: boolean;
rabbitmq_events?: string[];
sqs_enabled?: boolean;
sqs_events?: string[];
typebot_url?: string;
webhookByEvents?: boolean;
webhookBase64?: boolean;
webhookEvents?: string[];
rejectCall?: boolean;
msgCall?: string;
groupsIgnore?: boolean;
alwaysOnline?: boolean;
readMessages?: boolean;
readStatus?: boolean;
syncFullHistory?: boolean;
chatwootAccountId?: string;
chatwootToken?: string;
chatwootUrl?: string;
chatwootSignMsg?: boolean;
chatwootReopenConversation?: boolean;
chatwootConversationPending?: boolean;
chatwootMergeBrazilContacts?: boolean;
chatwootImportContacts?: boolean;
chatwootImportMessages?: boolean;
chatwootDaysLimitImportMessages?: number;
chatwootNameInbox?: string;
websocketEnabled?: boolean;
websocketEvents?: string[];
rabbitmqEnabled?: boolean;
rabbitmqEvents?: string[];
sqsEnabled?: boolean;
sqsEvents?: string[];
typebotUrl?: string;
typebot?: string;
typebot_expire?: number;
typebot_keyword_finish?: string;
typebot_delay_message?: number;
typebot_unknown_message?: string;
typebot_listening_from_me?: boolean;
proxy?: ProxyDto['proxy'];
typebotExpire?: number;
typebotKeywordFinish?: string;
typebotDelayMessage?: number;
typebotUnknownMessage?: string;
typebotListeningFromMe?: boolean;
proxy?: ProxyDto;
}
export class SetPresenceDto {

View File

@ -1,7 +1,7 @@
export class LabelDto {
id?: string;
name: string;
color: number;
color: string;
predefinedId?: string;
}

View File

@ -1,12 +1,8 @@
class Proxy {
export class ProxyDto {
enabled: boolean;
host: string;
port: string;
protocol: string;
username?: string;
password?: string;
}
export class ProxyDto {
enabled: boolean;
proxy: Proxy;
}

View File

@ -1,9 +1,9 @@
export class SettingsDto {
reject_call?: boolean;
msg_call?: string;
groups_ignore?: boolean;
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
rejectCall?: boolean;
msgCall?: string;
groupsIgnore?: boolean;
alwaysOnline?: boolean;
readMessages?: boolean;
readStatus?: boolean;
syncFullHistory?: boolean;
}

View File

@ -2,6 +2,6 @@ export class WebhookDto {
enabled?: boolean;
url?: string;
events?: string[];
webhook_by_events?: boolean;
webhook_base64?: boolean;
webhookByEvents?: boolean;
webhookBase64?: boolean;
}

View File

@ -4,7 +4,7 @@ import { Auth, configService, Database } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { ForbiddenException, UnauthorizedException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto';
import { mongodbRepository } from '../server.module';
import { prismaRepository } from '../server.module';
const logger = new Logger('GUARD');
@ -28,13 +28,18 @@ async function apikey(req: Request, _: Response, next: NextFunction) {
try {
if (param?.instanceName) {
const instanceKey = await mongodbRepository.auth.find(param.instanceName);
if (instanceKey?.apikey === key) {
const instance = await prismaRepository.instance.findUnique({
where: { name: param.instanceName },
include: { Auth: true },
});
if (instance.Auth?.apikey === key) {
return next();
}
} else {
if (req.originalUrl.includes('/instance/fetchInstances') && db.ENABLED) {
const instanceByKey = await mongodbRepository.auth.findByKey(key);
const instanceByKey = await prismaRepository.auth.findFirst({
where: { apikey: key },
});
if (instanceByKey) {
return next();
}

View File

@ -10,7 +10,7 @@ import {
InternalServerErrorException,
NotFoundException,
} from '../../exceptions';
import { mongodbServer } from '../../libs/mongodb.connect';
import { prismaServer } from '../../libs/prisma.connect';
import { InstanceDto } from '../dto/instance.dto';
import { cache, waMonitor } from '../server.module';
@ -28,11 +28,9 @@ async function getInstance(instanceName: string) {
}
if (db.ENABLED) {
const collection = mongodbServer
.getClient()
.db(db.CONNECTION.DB_PREFIX_NAME + '-instances')
.collection(instanceName);
return exists || (await collection.find({}).toArray()).length > 0;
const prisma = prismaServer;
return exists || (await prisma.instance.findMany({ where: { name: instanceName } })).length > 0;
}
return exists || existsSync(join(INSTANCE_DIR, instanceName));

View File

@ -5,8 +5,7 @@ import { ConfigService, HttpServer } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { BadRequestException } from '../../../../exceptions';
import { InstanceDto } from '../../../dto/instance.dto';
import { MongodbRepository } from '../../../repository/mongodb/repository.manager';
import { PrismaRepository } from '../../../repository/prisma/repository.service';
import { PrismaRepository } from '../../../repository/repository.service';
import { waMonitor } from '../../../server.module';
import { CacheService } from '../../../services/cache.service';
import { ChatwootDto } from '../dto/chatwoot.dto';
@ -18,7 +17,6 @@ export class ChatwootController {
constructor(
private readonly chatwootService: ChatwootService,
private readonly configService: ConfigService,
private readonly mongodbRepository: MongodbRepository,
private readonly prismaRepository: PrismaRepository,
) {}
@ -30,39 +28,39 @@ export class ChatwootController {
throw new BadRequestException('url is not valid');
}
if (!data.account_id) {
throw new BadRequestException('account_id is required');
if (!data.accountId) {
throw new BadRequestException('accountId is required');
}
if (!data.token) {
throw new BadRequestException('token is required');
}
if (data.sign_msg !== true && data.sign_msg !== false) {
throw new BadRequestException('sign_msg is required');
if (data.signMsg !== true && data.signMsg !== false) {
throw new BadRequestException('signMsg is required');
}
if (data.sign_msg === false) data.sign_delimiter = null;
if (data.signMsg === false) data.signDelimiter = null;
}
if (!data.enabled) {
logger.verbose('chatwoot disabled');
data.account_id = '';
data.accountId = '';
data.token = '';
data.url = '';
data.sign_msg = false;
data.sign_delimiter = null;
data.reopen_conversation = false;
data.conversation_pending = false;
data.import_contacts = false;
data.import_messages = false;
data.merge_brazil_contacts = false;
data.days_limit_import_messages = 0;
data.auto_create = false;
data.name_inbox = '';
data.signMsg = false;
data.signDelimiter = null;
data.reopenConversation = false;
data.conversationPending = false;
data.importContacts = false;
data.importMessages = false;
data.mergeBrazilContacts = false;
data.daysLimitImportMessages = 0;
data.autoCreate = false;
data.nameInbox = '';
}
if (!data.name_inbox || data.name_inbox === '') {
data.name_inbox = instance.instanceName;
if (!data.nameInbox || data.nameInbox === '') {
data.nameInbox = instance.instanceName;
}
const result = await this.chatwootService.create(instance, data);
@ -87,10 +85,10 @@ export class ChatwootController {
return {
enabled: false,
url: '',
account_id: '',
accountId: '',
token: '',
sign_msg: false,
name_inbox: '',
signMsg: false,
nameInbox: '',
webhook_url: '',
};
}
@ -107,13 +105,7 @@ export class ChatwootController {
logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance');
const chatwootCache = new CacheService(new CacheEngine(this.configService, ChatwootService.name).getEngine());
const chatwootService = new ChatwootService(
waMonitor,
this.configService,
this.mongodbRepository,
this.prismaRepository,
chatwootCache,
);
const chatwootService = new ChatwootService(waMonitor, this.configService, this.prismaRepository, chatwootCache);
return chatwootService.receiveWebhook(instance, data);
}

View File

@ -1,17 +1,17 @@
export class ChatwootDto {
enabled?: boolean;
account_id?: string;
accountId?: string;
token?: string;
url?: string;
name_inbox?: string;
sign_msg?: boolean;
sign_delimiter?: string;
nameInbox?: string;
signMsg?: boolean;
signDelimiter?: string;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
merge_brazil_contacts?: boolean;
import_contacts?: boolean;
import_messages?: boolean;
days_limit_import_messages?: number;
auto_create?: boolean;
reopenConversation?: boolean;
conversationPending?: boolean;
mergeBrazilContacts?: boolean;
importContacts?: boolean;
importMessages?: boolean;
daysLimitImportMessages?: number;
autoCreate?: boolean;
}

View File

@ -1,42 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../../../libs/mongodb.connect';
export class ChatwootRaw {
_id?: string;
enabled?: boolean;
account_id?: string;
token?: string;
url?: string;
name_inbox?: string;
sign_msg?: boolean;
sign_delimiter?: string;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
merge_brazil_contacts?: boolean;
import_contacts?: boolean;
import_messages?: boolean;
days_limit_import_messages?: number;
}
const chatwootSchema = new Schema<ChatwootRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
account_id: { type: String, required: true },
token: { type: String, required: true },
url: { type: String, required: true },
name_inbox: { type: String, required: true },
sign_msg: { type: Boolean, required: true },
sign_delimiter: { type: String, required: false },
number: { type: String, required: true },
reopen_conversation: { type: Boolean, required: true },
conversation_pending: { type: Boolean, required: true },
merge_brazil_contacts: { type: Boolean, required: true },
import_contacts: { type: Boolean, required: true },
import_messages: { type: Boolean, required: true },
days_limit_import_messages: { type: Number, required: true },
});
export const ChatwootModel = mongodbServer?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');
export type IChatwootModel = typeof ChatwootModel;

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ChatwootRaw, IChatwootModel } from '../../../models';
export class ChatwootRepository extends Repository {
constructor(private readonly chatwootModel: IChatwootModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('ChatwootRepository');
public async create(data: ChatwootRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating chatwoot');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving chatwoot to db');
const insert = await this.chatwootModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('chatwoot saved to db: ' + insert.modifiedCount + ' chatwoot');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving chatwoot to store');
this.writeStore<ChatwootRaw>({
path: join(this.storePath, 'chatwoot'),
fileName: instance,
data,
});
this.logger.verbose('chatwoot saved to store in path: ' + join(this.storePath, 'chatwoot') + '/' + instance);
this.logger.verbose('chatwoot created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<ChatwootRaw> {
try {
this.logger.verbose('finding chatwoot');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding chatwoot in db');
return await this.chatwootModel.findOne({ _id: instance });
}
this.logger.verbose('finding chatwoot in store');
return JSON.parse(
readFileSync(join(this.storePath, 'chatwoot', instance + '.json'), {
encoding: 'utf-8',
}),
) as ChatwootRaw;
} catch (error) {
return {};
}
}
}

View File

@ -8,11 +8,12 @@ import ChatwootClient, {
inbox,
} from '@figuro/chatwoot-sdk';
import { request as chatwootRequest } from '@figuro/chatwoot-sdk/dist/core/request';
import { proto } from '@whiskeysockets/baileys';
import { Chatwoot as ChatwootModel, Contact as ContactModel, Message as MessageModel } from '@prisma/client';
import axios from 'axios';
import FormData from 'form-data';
import { createReadStream, unlinkSync, writeFileSync } from 'fs';
import Jimp from 'jimp';
import Long from 'long';
import mimeTypes from 'mime-types';
import path from 'path';
@ -22,14 +23,20 @@ import i18next from '../../../../utils/i18n';
import { ICache } from '../../../abstract/abstract.cache';
import { InstanceDto } from '../../../dto/instance.dto';
import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '../../../dto/sendMessage.dto';
import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../models';
import { MongodbRepository } from '../../../repository/mongodb/repository.manager';
import { PrismaRepository } from '../../../repository/prisma/repository.service';
import { PrismaRepository } from '../../../repository/repository.service';
import { WAMonitoringService } from '../../../services/monitor.service';
import { Events } from '../../../types/wa.types';
import { ChatwootDto } from '../dto/chatwoot.dto';
import { chatwootImport } from '../utils/chatwoot-import-helper';
interface ChatwootMessage {
messageId?: number;
inboxId?: number;
conversationId?: number;
contactInboxSourceId?: string;
isRead?: boolean;
}
export class ChatwootService {
private readonly logger = new Logger(ChatwootService.name);
@ -38,7 +45,6 @@ export class ChatwootService {
constructor(
private readonly waMonitor: WAMonitoringService,
private readonly configService: ConfigService,
private readonly mongodbRepository: MongodbRepository,
private readonly prismaRepository: PrismaRepository,
private readonly cache: ICache,
) {}
@ -46,7 +52,7 @@ export class ChatwootService {
private async getProvider(instance: InstanceDto) {
const cacheKey = `${instance.instanceName}:getProvider`;
if (await this.cache.has(cacheKey)) {
return (await this.cache.get(cacheKey)) as ChatwootRaw;
return (await this.cache.get(cacheKey)) as ChatwootModel;
}
this.logger.verbose('get provider to instance: ' + instance.instanceName);
@ -110,12 +116,12 @@ export class ChatwootService {
this.logger.verbose('chatwoot created');
if (data.auto_create) {
if (data.autoCreate) {
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
await this.initInstanceChatwoot(
instance,
data.name_inbox ?? instance.instanceName.split('-cwId-')[0],
data.nameInbox ?? instance.instanceName.split('-cwId-')[0],
`${urlServer}/chatwoot/webhook/${encodeURIComponent(instance.instanceName)}`,
true,
data.number,
@ -1195,26 +1201,22 @@ export class ChatwootService {
this.logger.verbose('check if is a message deletion');
if (body.event === 'message_updated' && body.content_attributes?.deleted) {
const message = await this.mongodbRepository.message.find({
const message = await this.prismaRepository.message.findFirst({
where: {
owner: instance.instanceName,
chatwoot: {
messageId: body.id,
},
chatwootMessageId: body.id,
instanceId: instance.instanceId,
},
limit: 1,
});
if (message.length && message[0].key?.id) {
if (message) {
this.logger.verbose('deleting message in whatsapp. Message id: ' + message[0].key.id);
await waInstance?.client.sendMessage(message[0].key.remoteJid, { delete: message[0].key });
this.logger.verbose('deleting message in repository. Message id: ' + message[0].key.id);
this.mongodbRepository.message.delete({
this.prismaRepository.message.deleteMany({
where: {
owner: instance.instanceName,
chatwoot: {
messageId: body.id,
},
instanceId: instance.instanceId,
chatwootMessageId: body.id,
},
});
}
@ -1368,9 +1370,7 @@ export class ChatwootService {
messageId: body.id,
inboxId: body.inbox?.id,
conversationId: body.conversation?.id,
contactInbox: {
sourceId: body.conversation?.contact_inbox?.source_id,
},
contactInboxSourceId: body.conversation?.contact_inbox?.source_id,
},
instance,
);
@ -1391,25 +1391,27 @@ export class ChatwootService {
},
};
let messageSent: MessageRaw | proto.WebMessageInfo;
let messageSent: any;
try {
messageSent = await waInstance?.textMessage(data, true);
if (!messageSent) {
throw new Error('Message not sent');
}
if (Long.isLong(messageSent?.messageTimestamp)) {
messageSent.messageTimestamp = messageSent.messageTimestamp?.toNumber();
}
this.updateChatwootMessageId(
{
...messageSent,
owner: instance.instanceName,
instanceId: instance.instanceId,
},
{
messageId: body.id,
inboxId: body.inbox?.id,
conversationId: body.conversation?.id,
contactInbox: {
sourceId: body.conversation?.contact_inbox?.source_id,
},
contactInboxSourceId: body.conversation?.contact_inbox?.source_id,
},
instance,
);
@ -1424,32 +1426,72 @@ export class ChatwootService {
const chatwootRead = this.configService.get<Chatwoot>('CHATWOOT').MESSAGE_READ;
if (chatwootRead) {
const lastMessage = await this.mongodbRepository.message.find({
const lastMessage = await this.prismaRepository.message.findFirst({
where: {
key: {
fromMe: false,
path: ['fromMe'],
equals: false,
},
owner: instance.instanceName,
instanceId: instance.instanceId,
},
limit: 1,
});
if (lastMessage.length > 0 && !lastMessage[0].chatwoot?.isRead) {
if (lastMessage && !lastMessage.chatwootIsRead) {
const key = lastMessage.key as {
id: string;
fromMe: boolean;
remoteJid: string;
participant?: string;
};
waInstance?.markMessageAsRead({
read_messages: lastMessage.map((msg) => ({
id: msg.key?.id,
fromMe: msg.key?.fromMe,
remoteJid: msg.key?.remoteJid,
})),
read_messages: [
{
id: key.id,
fromMe: key.fromMe,
remoteJid: key.remoteJid,
},
],
});
const updateMessage = lastMessage.map((msg) => ({
key: msg.key,
owner: msg.owner,
chatwoot: {
...msg.chatwoot,
isRead: true,
const updateMessage = {
chatwootMessageId: lastMessage.chatwootMessageId,
chatwootConversationId: lastMessage.chatwootConversationId,
chatwootInboxId: lastMessage.chatwootInboxId,
chatwootContactInboxSourceId: lastMessage.chatwootContactInboxSourceId,
chatwootIsRead: true,
};
await this.prismaRepository.message.updateMany({
where: {
instanceId: instance.instanceId,
AND: [
{
key: {
path: ['id'],
equals: key.id,
},
},
{
key: {
path: ['remoteJid'],
equals: key.remoteJid,
},
},
{
key: {
path: ['fromMe'],
equals: key.fromMe,
},
},
{
key: {
path: ['participant'],
equals: key.participant,
},
},
],
},
}));
this.mongodbRepository.message.update(updateMessage, instance.instanceName, true);
data: updateMessage,
});
}
}
}
@ -1481,31 +1523,48 @@ export class ChatwootService {
}
}
private updateChatwootMessageId(
message: MessageRaw,
chatwootMessageIds: MessageRaw['chatwoot'],
instance: InstanceDto,
) {
if (!chatwootMessageIds.messageId || !message?.key?.id) {
private updateChatwootMessageId(message: MessageModel, chatwootMessageIds: ChatwootMessage, instance: InstanceDto) {
const key = message.key as {
id: string;
fromMe: boolean;
remoteJid: string;
participant?: string;
};
if (!chatwootMessageIds.messageId || !key?.id) {
return;
}
message.chatwoot = chatwootMessageIds;
this.mongodbRepository.message.update([message], instance.instanceName, true);
}
private async getMessageByKeyId(instance: InstanceDto, keyId: string): Promise<MessageRaw> {
const messages = await this.mongodbRepository.message.find({
this.prismaRepository.message.updateMany({
where: {
key: {
id: keyId,
path: ['id'],
equals: key.id,
},
owner: instance.instanceName,
instanceId: instance.instanceId,
},
data: {
chatwootMessageId: chatwootMessageIds.messageId,
chatwootConversationId: chatwootMessageIds.conversationId,
chatwootInboxId: chatwootMessageIds.inboxId,
chatwootContactInboxSourceId: chatwootMessageIds.contactInboxSourceId,
chatwootIsRead: chatwootMessageIds.isRead,
},
});
}
private async getMessageByKeyId(instance: InstanceDto, keyId: string): Promise<MessageModel> {
const messages = await this.prismaRepository.message.findFirst({
where: {
key: {
path: ['id'],
equals: keyId,
},
instanceId: instance.instanceId,
},
limit: 1,
});
return messages.length ? messages[0] : null;
return messages || null;
}
private async getReplyToIds(
@ -1519,8 +1578,8 @@ export class ChatwootService {
inReplyToExternalId = msg.message?.extendedTextMessage?.contextInfo?.stanzaId;
if (inReplyToExternalId) {
const message = await this.getMessageByKeyId(instance, inReplyToExternalId);
if (message?.chatwoot?.messageId) {
inReplyTo = message.chatwoot.messageId;
if (message?.chatwootMessageId) {
inReplyTo = message.chatwootMessageId;
}
}
}
@ -1533,16 +1592,21 @@ export class ChatwootService {
private async getQuotedMessage(msg: any, instance: InstanceDto): Promise<Quoted> {
if (msg?.content_attributes?.in_reply_to) {
const message = await this.mongodbRepository.message.find({
const message = await this.prismaRepository.message.findFirst({
where: {
chatwoot: {
messageId: msg?.content_attributes?.in_reply_to,
},
owner: instance.instanceName,
chatwootMessageId: msg?.content_attributes?.in_reply_to,
instanceId: instance.instanceId,
},
limit: 1,
});
if (message.length && message[0]?.key?.id) {
const key = message.key as {
id: string;
fromMe: boolean;
remoteJid: string;
participant?: string;
};
if (message && key?.id) {
return {
key: message[0].key,
message: message[0].message,
@ -1588,7 +1652,12 @@ export class ChatwootService {
private getReactionMessage(msg: any) {
interface ReactionMessage {
key: MessageRaw['key'];
key: {
id: string;
fromMe: boolean;
remoteJid: string;
participant?: string;
};
text: string;
}
const reactionMessage: ReactionMessage | undefined = msg?.reactionMessage;
@ -2131,22 +2200,23 @@ export class ChatwootService {
}
const message = await this.getMessageByKeyId(instance, body.key.id);
if (message?.chatwoot?.messageId && message?.chatwoot?.conversationId) {
if (message?.chatwootMessageId && message?.chatwootConversationId) {
this.logger.verbose('deleting message in repository. Message id: ' + body.key.id);
this.mongodbRepository.message.delete({
this.prismaRepository.message.deleteMany({
where: {
key: {
id: body.key.id,
path: ['id'],
equals: body.key.id,
},
owner: instance.instanceName,
instanceId: instance.instanceId,
},
});
this.logger.verbose('deleting message in chatwoot. Message id: ' + body.key.id);
return await client.messages.delete({
accountId: this.provider.account_id,
conversationId: message.chatwoot.conversationId,
messageId: message.chatwoot.messageId,
conversationId: message.chatwootConversationId,
messageId: message.chatwootMessageId,
});
}
}
@ -2157,18 +2227,25 @@ export class ChatwootService {
body?.editedMessage?.conversation || body?.editedMessage?.extendedTextMessage?.text
}\n\n_\`${i18next.t('cw.message.edited')}.\`_`;
const message = await this.getMessageByKeyId(instance, body?.key?.id);
const messageType = message.key?.fromMe ? 'outgoing' : 'incoming';
const key = message.key as {
id: string;
fromMe: boolean;
remoteJid: string;
participant?: string;
};
if (message && message.chatwoot?.conversationId) {
const messageType = key?.fromMe ? 'outgoing' : 'incoming';
if (message && message.chatwootConversationId) {
const send = await this.createMessage(
instance,
message.chatwoot.conversationId,
message.chatwootConversationId,
editedText,
messageType,
false,
[],
{
message: { extendedTextMessage: { contextInfo: { stanzaId: message.key.id } } },
message: { extendedTextMessage: { contextInfo: { stanzaId: key.id } } },
},
'WAID:' + body.key.id,
);
@ -2189,9 +2266,11 @@ export class ChatwootService {
}
const message = await this.getMessageByKeyId(instance, body.key.id);
const { conversationId, contactInbox } = message?.chatwoot || {};
const conversationId = message?.chatwootConversationId;
const contactInboxSourceId = message?.chatwootContactInboxSourceId;
if (conversationId) {
let sourceId = contactInbox?.sourceId;
let sourceId = contactInboxSourceId;
const inbox = (await this.getInbox(instance)) as inbox & {
inbox_identifier?: string;
};
@ -2317,7 +2396,7 @@ export class ChatwootService {
/* We can't proccess messages exactly in batch because Chatwoot use message id to order
messages in frontend and we are receiving the messages mixed between the batches.
Because this, we need to put all batches together and order after */
public addHistoryMessages(instance: InstanceDto, messagesRaw: MessageRaw[]) {
public addHistoryMessages(instance: InstanceDto, messagesRaw: MessageModel[]) {
if (!this.isImportHistoryAvailable()) {
return;
}
@ -2325,7 +2404,7 @@ export class ChatwootService {
chatwootImport.addHistoryMessages(instance, messagesRaw);
}
public addHistoryContacts(instance: InstanceDto, contactsRaw: ContactRaw[]) {
public addHistoryContacts(instance: InstanceDto, contactsRaw: ContactModel[]) {
if (!this.isImportHistoryAvailable()) {
return;
}
@ -2382,16 +2461,18 @@ export class ChatwootService {
);
const contactsWithProfilePicture = (
await this.mongodbRepository.contact.find({
await this.prismaRepository.contact.findMany({
where: {
owner: instance.instanceName,
instanceId: instance.instanceId,
id: {
$in: recentContacts.map((contact) => contact.identifier),
in: recentContacts.map((contact) => Number(contact.identifier)),
},
profilePicUrl: {
not: null,
},
profilePictureUrl: { $ne: null },
},
} as any)
).reduce((acc: Map<string, ContactRaw>, contact: ContactRaw) => acc.set(contact.id, contact), new Map());
})
).reduce((acc: Map<number, ContactModel>, contact: ContactModel) => acc.set(contact.id, contact), new Map());
recentContacts.forEach(async (contact) => {
if (contactsWithProfilePicture.has(contact.identifier)) {

View File

@ -1,10 +1,11 @@
import { inbox } from '@figuro/chatwoot-sdk';
import { Chatwoot as ChatwootModel, Contact, Message } from '@prisma/client';
import { proto } from '@whiskeysockets/baileys';
import { InstanceDto } from '../../../../api/dto/instance.dto';
import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../../api/models';
import { Chatwoot, configService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { ChatwootDto } from '../dto/chatwoot.dto';
import { postgresClient } from '../libs/postgres.client';
import { ChatwootService } from '../services/chatwoot.service';
@ -29,8 +30,8 @@ type IWebMessageInfo = Omit<proto.IWebMessageInfo, 'key'> & Partial<Pick<proto.I
class ChatwootImport {
private logger = new Logger(ChatwootImport.name);
private repositoryMessagesCache = new Map<string, Set<string>>();
private historyMessages = new Map<string, MessageRaw[]>();
private historyContacts = new Map<string, ContactRaw[]>();
private historyMessages = new Map<string, Message[]>();
private historyContacts = new Map<string, Contact[]>();
public getRepositoryMessagesCache(instance: InstanceDto) {
return this.repositoryMessagesCache.has(instance.instanceName)
@ -46,14 +47,14 @@ class ChatwootImport {
this.repositoryMessagesCache.delete(instance.instanceName);
}
public addHistoryMessages(instance: InstanceDto, messagesRaw: MessageRaw[]) {
public addHistoryMessages(instance: InstanceDto, messagesRaw: Message[]) {
const actualValue = this.historyMessages.has(instance.instanceName)
? this.historyMessages.get(instance.instanceName)
: [];
this.historyMessages.set(instance.instanceName, actualValue.concat(messagesRaw));
}
public addHistoryContacts(instance: InstanceDto, contactsRaw: ContactRaw[]) {
public addHistoryContacts(instance: InstanceDto, contactsRaw: Contact[]) {
const actualValue = this.historyContacts.has(instance.instanceName)
? this.historyContacts.get(instance.instanceName)
: [];
@ -78,7 +79,7 @@ class ChatwootImport {
return this.historyMessages.get(instance.instanceName)?.length ?? 0;
}
public async importHistoryContacts(instance: InstanceDto, provider: ChatwootRaw) {
public async importHistoryContacts(instance: InstanceDto, provider: ChatwootDto) {
try {
if (this.getHistoryMessagesLenght(instance) > 0) {
return;
@ -93,21 +94,21 @@ class ChatwootImport {
return 0;
}
let contactsChunk: ContactRaw[] = this.sliceIntoChunks(contacts, 3000);
let contactsChunk: Contact[] = this.sliceIntoChunks(contacts, 3000);
while (contactsChunk.length > 0) {
// inserting contacts in chatwoot db
let sqlInsert = `INSERT INTO contacts
(name, phone_number, account_id, identifier, created_at, updated_at) VALUES `;
const bindInsert = [provider.account_id];
const bindInsert = [provider.accountId];
for (const contact of contactsChunk) {
bindInsert.push(contact.pushName);
const bindName = `$${bindInsert.length}`;
bindInsert.push(`+${contact.id.split('@')[0]}`);
bindInsert.push(`+${contact.remoteJid.split('@')[0]}`);
const bindPhoneNumber = `$${bindInsert.length}`;
bindInsert.push(contact.id);
bindInsert.push(contact.remoteJid);
const bindIdentifier = `$${bindInsert.length}`;
sqlInsert += `(${bindName}, ${bindPhoneNumber}, $1, ${bindIdentifier}, NOW(), NOW()),`;
@ -137,7 +138,7 @@ class ChatwootImport {
instance: InstanceDto,
chatwootService: ChatwootService,
inbox: inbox,
provider: ChatwootRaw,
provider: ChatwootModel,
) {
try {
const pgClient = postgresClient.getChatwootConnection();
@ -156,8 +157,16 @@ class ChatwootImport {
// ordering messages by number and timestamp asc
messagesOrdered.sort((a, b) => {
const aKey = a.key as {
remoteJid: string;
};
const bKey = b.key as {
remoteJid: string;
};
return (
parseInt(a.key.remoteJid) - parseInt(b.key.remoteJid) ||
parseInt(aKey.remoteJid) - parseInt(bKey.remoteJid) ||
(a.messageTimestamp as number) - (b.messageTimestamp as number)
);
});
@ -165,7 +174,7 @@ class ChatwootImport {
const allMessagesMappedByPhoneNumber = this.createMessagesMapByPhoneNumber(messagesOrdered);
// Map structure: +552199999999 => { first message timestamp from number, last message timestamp from number}
const phoneNumbersWithTimestamp = new Map<string, firstLastTimestamp>();
allMessagesMappedByPhoneNumber.forEach((messages: MessageRaw[], phoneNumber: string) => {
allMessagesMappedByPhoneNumber.forEach((messages: Message[], phoneNumber: string) => {
phoneNumbersWithTimestamp.set(phoneNumber, {
first: messages[0]?.messageTimestamp as number,
last: messages[messages.length - 1]?.messageTimestamp as number,
@ -174,9 +183,9 @@ class ChatwootImport {
// processing messages in batch
const batchSize = 4000;
let messagesChunk: MessageRaw[] = this.sliceIntoChunks(messagesOrdered, batchSize);
let messagesChunk: Message[] = this.sliceIntoChunks(messagesOrdered, batchSize);
while (messagesChunk.length > 0) {
// Map structure: +552199999999 => MessageRaw[]
// Map structure: +552199999999 => Message[]
const messagesByPhoneNumber = this.createMessagesMapByPhoneNumber(messagesChunk);
if (messagesByPhoneNumber.size > 0) {
@ -191,9 +200,9 @@ class ChatwootImport {
let sqlInsertMsg = `INSERT INTO messages
(content, account_id, inbox_id, conversation_id, message_type, private, content_type,
sender_type, sender_id, created_at, updated_at) VALUES `;
const bindInsertMsg = [provider.account_id, inbox.id];
const bindInsertMsg = [provider.accountId, inbox.id];
messagesByPhoneNumber.forEach((messages: MessageRaw[], phoneNumber: string) => {
messagesByPhoneNumber.forEach((messages: any[], phoneNumber: string) => {
const fksChatwoot = fksByNumber.get(phoneNumber);
messages.forEach((message) => {
@ -257,14 +266,14 @@ class ChatwootImport {
}
public async selectOrCreateFksFromChatwoot(
provider: ChatwootRaw,
provider: ChatwootModel,
inbox: inbox,
phoneNumbersWithTimestamp: Map<string, firstLastTimestamp>,
messagesByPhoneNumber: Map<string, MessageRaw[]>,
messagesByPhoneNumber: Map<string, Message[]>,
): Promise<Map<string, FksChatwoot>> {
const pgClient = postgresClient.getChatwootConnection();
const bindValues = [provider.account_id, inbox.id];
const bindValues = [provider.accountId, inbox.id];
const phoneNumberBind = Array.from(messagesByPhoneNumber.keys())
.map((phoneNumber) => {
const phoneNumberTimestamp = phoneNumbersWithTimestamp.get(phoneNumber);
@ -348,7 +357,7 @@ class ChatwootImport {
return new Map(fksFromChatwoot.rows.map((item: FksChatwoot) => [item.phone_number, item]));
}
public async getChatwootUser(provider: ChatwootRaw): Promise<ChatwootUser> {
public async getChatwootUser(provider: ChatwootModel): Promise<ChatwootUser> {
try {
const pgClient = postgresClient.getChatwootConnection();
@ -362,10 +371,13 @@ class ChatwootImport {
}
}
public createMessagesMapByPhoneNumber(messages: MessageRaw[]): Map<string, MessageRaw[]> {
return messages.reduce((acc: Map<string, MessageRaw[]>, message: MessageRaw) => {
if (!this.isIgnorePhoneNumber(message?.key?.remoteJid)) {
const phoneNumber = message?.key?.remoteJid?.split('@')[0];
public createMessagesMapByPhoneNumber(messages: Message[]): Map<string, Message[]> {
return messages.reduce((acc: Map<string, Message[]>, message: Message) => {
const key = message?.key as {
remoteJid: string;
};
if (!this.isIgnorePhoneNumber(key?.remoteJid)) {
const phoneNumber = key?.remoteJid?.split('@')[0];
if (phoneNumber) {
const phoneNumberPlus = `+${phoneNumber}`;
const messages = acc.has(phoneNumberPlus) ? acc.get(phoneNumberPlus) : [];
@ -380,7 +392,7 @@ class ChatwootImport {
public async getContactsOrderByRecentConversations(
inbox: inbox,
provider: ChatwootRaw,
provider: ChatwootModel,
limit = 50,
): Promise<{ id: number; phone_number: string; identifier: string }[]> {
try {
@ -394,7 +406,7 @@ class ChatwootImport {
ORDER BY conversations.last_activity_at DESC
LIMIT $3`;
return (await pgClient.query(sql, [provider.account_id, inbox.id, limit]))?.rows;
return (await pgClient.query(sql, [provider.accountId, inbox.id, limit]))?.rows;
} catch (error) {
this.logger.error(`Error on get recent conversations: ${error.toString()}`);
}

View File

@ -1,3 +1,4 @@
import { JsonValue } from '@prisma/client/runtime/library';
import * as amqp from 'amqplib/callback_api';
import { configService, Rabbitmq } from '../../../../config/env.config';
@ -107,12 +108,14 @@ export const initQueues = (instanceName: string, events: string[]) => {
});
};
export const removeQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
export const removeQueues = (instanceName: string, events: JsonValue) => {
const eventsArray = Array.isArray(events) ? events.map((event) => String(event)) : [];
if (!events || !eventsArray.length) return;
const channel = getAMQP();
const queues = events.map((event) => {
const queues = eventsArray.map((event) => {
return `${event.replace(/_/g, '.').toLowerCase()}`;
});

View File

@ -1,18 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../../../libs/mongodb.connect';
export class RabbitmqRaw {
_id?: string;
enabled?: boolean;
events?: string[];
}
const rabbitmqSchema = new Schema<RabbitmqRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
});
export const RabbitmqModel = mongodbServer?.model(RabbitmqRaw.name, rabbitmqSchema, 'rabbitmq');
export type IRabbitmqModel = typeof RabbitmqModel;

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { IRabbitmqModel, RabbitmqRaw } from '../../../models';
export class RabbitmqRepository extends Repository {
constructor(private readonly rabbitmqModel: IRabbitmqModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('RabbitmqRepository');
public async create(data: RabbitmqRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating rabbitmq');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving rabbitmq to db');
const insert = await this.rabbitmqModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('rabbitmq saved to db: ' + insert.modifiedCount + ' rabbitmq');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving rabbitmq to store');
this.writeStore<RabbitmqRaw>({
path: join(this.storePath, 'rabbitmq'),
fileName: instance,
data,
});
this.logger.verbose('rabbitmq saved to store in path: ' + join(this.storePath, 'rabbitmq') + '/' + instance);
this.logger.verbose('rabbitmq created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<RabbitmqRaw> {
try {
this.logger.verbose('finding rabbitmq');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding rabbitmq in db');
return await this.rabbitmqModel.findOne({ _id: instance });
}
this.logger.verbose('finding rabbitmq in store');
return JSON.parse(
readFileSync(join(this.storePath, 'rabbitmq', instance + '.json'), {
encoding: 'utf-8',
}),
) as RabbitmqRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,6 +1,7 @@
import { Rabbitmq } from '@prisma/client';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { RabbitmqRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { RabbitmqDto } from '../dto/rabbitmq.dto';
import { initQueues } from '../libs/amqp.server';
@ -18,7 +19,7 @@ export class RabbitmqService {
return { rabbitmq: { ...instance, rabbitmq: data } };
}
public async find(instance: InstanceDto): Promise<RabbitmqRaw> {
public async find(instance: InstanceDto): Promise<Rabbitmq> {
try {
this.logger.verbose('find rabbitmq: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findRabbitmq();
@ -29,7 +30,7 @@ export class RabbitmqService {
return result;
} catch (error) {
return { enabled: false, events: [] };
return null;
}
}
}

View File

@ -1,4 +1,5 @@
import { SQS } from '@aws-sdk/client-sqs';
import { JsonValue } from '@prisma/client/runtime/library';
import { configService, Sqs } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
@ -59,12 +60,13 @@ export const initQueues = (instanceName: string, events: string[]) => {
});
};
export const removeQueues = (instanceName: string, events: string[]) => {
if (!events || !events.length) return;
export const removeQueues = (instanceName: string, events: JsonValue) => {
const eventsArray = Array.isArray(events) ? events.map((event) => String(event)) : [];
if (!events || !eventsArray.length) return;
const sqs = getSQS();
const queues = events.map((event) => {
const queues = eventsArray.map((event) => {
return `${event.replace(/_/g, '_').toLowerCase()}`;
});

View File

@ -1,18 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../../../libs/mongodb.connect';
export class SqsRaw {
_id?: string;
enabled?: boolean;
events?: string[];
}
const sqsSchema = new Schema<SqsRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
});
export const SqsModel = mongodbServer?.model(SqsRaw.name, sqsSchema, 'sqs');
export type ISqsModel = typeof SqsModel;

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ISqsModel, SqsRaw } from '../../../models';
export class SqsRepository extends Repository {
constructor(private readonly sqsModel: ISqsModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('SqsRepository');
public async create(data: SqsRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating sqs');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving sqs to db');
const insert = await this.sqsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('sqs saved to db: ' + insert.modifiedCount + ' sqs');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving sqs to store');
this.writeStore<SqsRaw>({
path: join(this.storePath, 'sqs'),
fileName: instance,
data,
});
this.logger.verbose('sqs saved to store in path: ' + join(this.storePath, 'sqs') + '/' + instance);
this.logger.verbose('sqs created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<SqsRaw> {
try {
this.logger.verbose('finding sqs');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding sqs in db');
return await this.sqsModel.findOne({ _id: instance });
}
this.logger.verbose('finding sqs in store');
return JSON.parse(
readFileSync(join(this.storePath, 'sqs', instance + '.json'), {
encoding: 'utf-8',
}),
) as SqsRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,6 +1,7 @@
import { Sqs } from '@prisma/client';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { SqsRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { SqsDto } from '../dto/sqs.dto';
import { initQueues } from '../libs/sqs.server';
@ -18,7 +19,7 @@ export class SqsService {
return { sqs: { ...instance, sqs: data } };
}
public async find(instance: InstanceDto): Promise<SqsRaw> {
public async find(instance: InstanceDto): Promise<Sqs> {
try {
this.logger.verbose('find sqs: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findSqs();
@ -29,7 +30,7 @@ export class SqsService {
return result;
} catch (error) {
return { enabled: false, events: [] };
return null;
}
}
}

View File

@ -1,3 +1,5 @@
import { TypebotSession } from '@prisma/client';
export class Session {
remoteJid?: string;
sessionId?: string;
@ -19,9 +21,9 @@ export class TypebotDto {
url: string;
typebot?: string;
expire?: number;
keyword_finish?: string;
delay_message?: number;
unknown_message?: string;
listening_from_me?: boolean;
sessions?: Session[];
keywordFinish?: string;
delayMessage?: number;
unknownMessage?: string;
listeningFromMe?: boolean;
sessions?: TypebotSession[];
}

View File

@ -1,58 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../../../libs/mongodb.connect';
class Session {
remoteJid?: string;
sessionId?: string;
status?: string;
createdAt?: number;
updateAt?: number;
prefilledVariables?: {
remoteJid?: string;
pushName?: string;
additionalData?: { [key: string]: any };
};
}
export class TypebotRaw {
_id?: string;
enabled?: boolean;
url: string;
typebot?: string;
expire?: number;
keyword_finish?: string;
delay_message?: number;
unknown_message?: string;
listening_from_me?: boolean;
sessions?: Session[];
}
const typebotSchema = new Schema<TypebotRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
url: { type: String, required: true },
typebot: { type: String, required: true },
expire: { type: Number, required: true },
keyword_finish: { type: String, required: true },
delay_message: { type: Number, required: true },
unknown_message: { type: String, required: true },
listening_from_me: { type: Boolean, required: true },
sessions: [
{
remoteJid: { type: String, required: true },
sessionId: { type: String, required: true },
status: { type: String, required: true },
createdAt: { 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 },
},
},
],
});
export const TypebotModel = mongodbServer?.model(TypebotRaw.name, typebotSchema, 'typebot');
export type ITypebotModel = typeof TypebotModel;

View File

@ -1,68 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { ITypebotModel, TypebotRaw } from '../../../models';
export class TypebotRepository extends Repository {
constructor(private readonly typebotModel: ITypebotModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('TypebotRepository');
public async create(data: TypebotRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating typebot');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving typebot to db');
const insert = await this.typebotModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('typebot saved to db: ' + insert.modifiedCount + ' typebot');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving typebot to store');
this.writeStore<TypebotRaw>({
path: join(this.storePath, 'typebot'),
fileName: instance,
data,
});
this.logger.verbose('typebot saved to store in path: ' + join(this.storePath, 'typebot') + '/' + instance);
this.logger.verbose('typebot created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<TypebotRaw> {
try {
this.logger.verbose('finding typebot');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding typebot in db');
return await this.typebotModel.findOne({ _id: instance });
}
this.logger.verbose('finding typebot in store');
return JSON.parse(
readFileSync(join(this.storePath, 'typebot', instance + '.json'), {
encoding: 'utf-8',
}),
) as TypebotRaw;
} catch (error) {
return {
enabled: false,
url: '',
typebot: '',
expire: 0,
sessions: [],
};
}
}
}

View File

@ -1,13 +1,13 @@
import { Message } from '@prisma/client';
import axios from 'axios';
import EventEmitter2 from 'eventemitter2';
import { ConfigService, Typebot } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { MessageRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { Events } from '../../../types/wa.types';
import { Session, TypebotDto } from '../dto/typebot.dto';
import { TypebotDto } from '../dto/typebot.dto';
export class TypebotService {
constructor(
@ -64,10 +64,10 @@ export class TypebotService {
url: findData.url,
typebot: findData.typebot,
expire: findData.expire,
keyword_finish: findData.keyword_finish,
delay_message: findData.delay_message,
unknown_message: findData.unknown_message,
listening_from_me: findData.listening_from_me,
keywordFinish: findData.keywordFinish,
delayMessage: findData.delayMessage,
unknownMessage: findData.unknownMessage,
listeningFromMe: findData.listeningFromMe,
sessions: findData.sessions,
};
@ -82,19 +82,19 @@ export class TypebotService {
}
});
} else if (status === 'paused') {
const session: Session = {
remoteJid: remoteJid,
sessionId: Math.floor(Math.random() * 10000000000).toString(),
status: status,
createdAt: Date.now(),
updateAt: Date.now(),
prefilledVariables: {
remoteJid: remoteJid,
pushName: '',
additionalData: {},
},
};
findData.sessions.push(session);
// const session: Session = {
// remoteJid: remoteJid,
// sessionId: Math.floor(Math.random() * 10000000000).toString(),
// status: status,
// createdAt: Date.now(),
// updateAt: Date.now(),
// prefilledVariables: {
// remoteJid: remoteJid,
// pushName: '',
// additionalData: {},
// },
// };
// findData.sessions.push(session);
}
const typebotData = {
@ -102,10 +102,10 @@ export class TypebotService {
url: findData.url,
typebot: findData.typebot,
expire: findData.expire,
keyword_finish: findData.keyword_finish,
delay_message: findData.delay_message,
unknown_message: findData.unknown_message,
listening_from_me: findData.listening_from_me,
keywordFinish: findData.keywordFinish,
delayMessage: findData.delayMessage,
unknownMessage: findData.unknownMessage,
listeningFromMe: findData.listeningFromMe,
sessions: findData.sessions,
};
@ -124,7 +124,8 @@ export class TypebotService {
public async clearSessions(instance: InstanceDto, remoteJid: string) {
const findTypebot = await this.find(instance);
const sessions = (findTypebot.sessions as Session[]) ?? [];
const sessions = [];
// const sessions = (findTypebot.sessions as Session[]) ?? [];
const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid);
@ -138,10 +139,10 @@ export class TypebotService {
url: findTypebot.url,
typebot: findTypebot.typebot,
expire: findTypebot.expire,
keyword_finish: findTypebot.keyword_finish,
delay_message: findTypebot.delay_message,
unknown_message: findTypebot.unknown_message,
listening_from_me: findTypebot.listening_from_me,
keywordFinish: findTypebot.keywordFinish,
delayMessage: findTypebot.delayMessage,
unknownMessage: findTypebot.unknownMessage,
listeningFromMe: findTypebot.listeningFromMe,
sessions,
};
@ -163,10 +164,10 @@ export class TypebotService {
const variables = data.variables;
const findTypebot = await this.find(instance);
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 keywordFinish = findTypebot.keywordFinish;
const delayMessage = findTypebot.delayMessage;
const unknownMessage = findTypebot.unknownMessage;
const listeningFromMe = findTypebot.listeningFromMe;
const prefilledVariables = {
remoteJid: remoteJid,
@ -188,10 +189,10 @@ export class TypebotService {
typebot: typebot,
remoteJid: remoteJid,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions: newSessions,
prefilledVariables: prefilledVariables,
});
@ -467,10 +468,10 @@ export class TypebotService {
url: data.url,
typebot: data.typebot,
expire: data.expire,
keyword_finish: data.keyword_finish,
delay_message: data.delay_message,
unknown_message: data.unknown_message,
listening_from_me: data.listening_from_me,
keywordFinish: data.keywordFinish,
delayMessage: data.delayMessage,
unknownMessage: data.unknownMessage,
listeningFromMe: data.listeningFromMe,
sessions: data.sessions,
};
@ -585,7 +586,7 @@ export class TypebotService {
await instance.textMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
delay: instance.localTypebot.delayMessage || 1000,
presence: 'composing',
},
textMessage: {
@ -598,7 +599,7 @@ export class TypebotService {
await instance.mediaMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
delay: instance.localTypebot.delayMessage || 1000,
presence: 'composing',
},
mediaMessage: {
@ -612,7 +613,7 @@ export class TypebotService {
await instance.mediaMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
delay: instance.localTypebot.delayMessage || 1000,
presence: 'composing',
},
mediaMessage: {
@ -626,7 +627,7 @@ export class TypebotService {
await instance.audioWhatsapp({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
delay: instance.localTypebot.delayMessage || 1000,
presence: 'recording',
encoding: true,
},
@ -658,7 +659,7 @@ export class TypebotService {
await instance.textMessage({
number: remoteJid.split('@')[0],
options: {
delay: instance.localTypebot.delay_message || 1000,
delay: instance.localTypebot.delayMessage || 1000,
presence: 'composing',
},
textMessage: {
@ -675,16 +676,17 @@ export class TypebotService {
}
}
public async sendTypebot(instance: InstanceDto, remoteJid: string, msg: MessageRaw) {
public async sendTypebot(instance: InstanceDto, remoteJid: string, msg: Message) {
const findTypebot = await this.find(instance);
const url = findTypebot.url;
const typebot = findTypebot.typebot;
const sessions = (findTypebot.sessions as Session[]) ?? [];
// const sessions = (findTypebot.sessions as Session[]) ?? [];
const sessions = [];
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 keywordFinish = findTypebot.keywordFinish;
const delayMessage = findTypebot.delayMessage;
const unknownMessage = findTypebot.unknownMessage;
const listeningFromMe = findTypebot.listeningFromMe;
const messageType = this.getTypeMessage(msg.message).messageType;
const session = sessions.find((session) => session.remoteJid === remoteJid);
@ -705,10 +707,10 @@ export class TypebotService {
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions: newSessions,
remoteJid: remoteJid,
pushName: msg.pushName,
@ -720,22 +722,22 @@ export class TypebotService {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
if (unknownMessage) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
delay: delayMessage || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
text: unknownMessage,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) {
const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = {
@ -743,10 +745,10 @@ export class TypebotService {
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions: newSessions,
};
@ -801,10 +803,10 @@ export class TypebotService {
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions: sessions,
remoteJid: remoteJid,
pushName: msg.pushName,
@ -819,22 +821,22 @@ export class TypebotService {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
if (unknownMessage) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
delay: delayMessage || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
text: unknownMessage,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) {
const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = {
@ -842,10 +844,10 @@ export class TypebotService {
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions: newSessions,
};
@ -899,10 +901,10 @@ export class TypebotService {
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions,
};
@ -911,22 +913,22 @@ export class TypebotService {
const content = this.getConversationMessage(msg.message);
if (!content) {
if (unknown_message) {
if (unknownMessage) {
this.waMonitor.waInstances[instance.instanceName].textMessage({
number: remoteJid.split('@')[0],
options: {
delay: delay_message || 1000,
delay: delayMessage || 1000,
presence: 'composing',
},
textMessage: {
text: unknown_message,
text: unknownMessage,
},
});
}
return;
}
if (keyword_finish && content.toLowerCase() === keyword_finish.toLowerCase()) {
if (keywordFinish && content.toLowerCase() === keywordFinish.toLowerCase()) {
const newSessions = await this.clearSessions(instance, remoteJid);
const typebotData = {
@ -934,10 +936,10 @@ export class TypebotService {
url: url,
typebot: typebot,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
keywordFinish: keywordFinish,
delayMessage: delayMessage,
unknownMessage: unknownMessage,
listeningFromMe: listeningFromMe,
sessions: newSessions,
};

View File

@ -1,18 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../../../libs/mongodb.connect';
export class WebsocketRaw {
_id?: string;
enabled?: boolean;
events?: string[];
}
const websocketSchema = new Schema<WebsocketRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
});
export const WebsocketModel = mongodbServer?.model(WebsocketRaw.name, websocketSchema, 'websocket');
export type IWebsocketModel = typeof WebsocketModel;

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../../config/env.config';
import { Logger } from '../../../../config/logger.config';
import { IInsert, Repository } from '../../../abstract/abstract.repository';
import { IWebsocketModel, WebsocketRaw } from '../../../models';
export class WebsocketRepository extends Repository {
constructor(private readonly websocketModel: IWebsocketModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('WebsocketRepository');
public async create(data: WebsocketRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating websocket');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving websocket to db');
const insert = await this.websocketModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('websocket saved to db: ' + insert.modifiedCount + ' websocket');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving websocket to store');
this.writeStore<WebsocketRaw>({
path: join(this.storePath, 'websocket'),
fileName: instance,
data,
});
this.logger.verbose('websocket saved to store in path: ' + join(this.storePath, 'websocket') + '/' + instance);
this.logger.verbose('websocket created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<WebsocketRaw> {
try {
this.logger.verbose('finding websocket');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding websocket in db');
return await this.websocketModel.findOne({ _id: instance });
}
this.logger.verbose('finding websocket in store');
return JSON.parse(
readFileSync(join(this.storePath, 'websocket', instance + '.json'), {
encoding: 'utf-8',
}),
) as WebsocketRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,6 +1,7 @@
import { Websocket } from '@prisma/client';
import { Logger } from '../../../../config/logger.config';
import { InstanceDto } from '../../../dto/instance.dto';
import { WebsocketRaw } from '../../../models';
import { WAMonitoringService } from '../../../services/monitor.service';
import { WebsocketDto } from '../dto/websocket.dto';
@ -16,7 +17,7 @@ export class WebsocketService {
return { websocket: { ...instance, websocket: data } };
}
public async find(instance: InstanceDto): Promise<WebsocketRaw> {
public async find(instance: InstanceDto): Promise<Websocket> {
try {
this.logger.verbose('find websocket: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findWebsocket();
@ -27,7 +28,7 @@ export class WebsocketService {
return result;
} catch (error) {
return { enabled: false, events: [] };
return null;
}
}
}

View File

@ -1,18 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class AuthRaw {
_id?: string;
apikey?: string;
instanceId?: string;
}
const authSchema = new Schema<AuthRaw>({
_id: { type: String, _id: true },
apikey: { type: String, minlength: 1 },
instanceId: { type: String, minlength: 1 },
});
export const AuthModel = mongodbServer?.model(AuthRaw.name, authSchema, 'authentication');
export type IAuthModel = typeof AuthModel;

View File

@ -1,26 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class ChatRaw {
_id?: string;
id?: string;
owner: string;
lastMsgTimestamp?: number;
labels?: string[];
}
type ChatRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type ChatRawSelect = ChatRawBoolean<ChatRaw>;
const chatSchema = new Schema<ChatRaw>({
_id: { type: String, _id: true },
id: { type: String, required: true, minlength: 1 },
owner: { type: String, required: true, minlength: 1 },
labels: { type: [String], default: [] },
});
export const ChatModel = mongodbServer?.model(ChatRaw.name, chatSchema, 'chats');
export type IChatModel = typeof ChatModel;

View File

@ -1,27 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class ContactRaw {
_id?: string;
pushName?: string;
id?: string;
profilePictureUrl?: string;
owner: string;
}
type ContactRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type ContactRawSelect = ContactRawBoolean<ContactRaw>;
const contactSchema = new Schema<ContactRaw>({
_id: { type: String, _id: true },
pushName: { type: String, minlength: 1 },
id: { type: String, required: true, minlength: 1 },
profilePictureUrl: { type: String, minlength: 1 },
owner: { type: String, required: true, minlength: 1 },
});
export const ContactModel = mongodbServer?.model(ContactRaw.name, contactSchema, 'contacts');
export type IContactModel = typeof ContactModel;

View File

@ -1,14 +0,0 @@
export * from '../integrations/chatwoot/models/chatwoot.model';
export * from '../integrations/rabbitmq/models/rabbitmq.model';
export * from '../integrations/sqs/models/sqs.model';
export * from '../integrations/typebot/models/typebot.model';
export * from '../integrations/websocket/models/websocket.model';
export * from './auth.model';
export * from './chat.model';
export * from './contact.model';
export * from './integration.model';
export * from './label.model';
export * from './message.model';
export * from './proxy.model';
export * from './settings.model';
export * from './webhook.model';

View File

@ -1,20 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class IntegrationRaw {
_id?: string;
integration?: string;
number?: string;
token?: string;
}
const integrationSchema = new Schema<IntegrationRaw>({
_id: { type: String, _id: true },
integration: { type: String, required: true },
number: { type: String, required: true },
token: { type: String, required: true },
});
export const IntegrationModel = mongodbServer?.model(IntegrationRaw.name, integrationSchema, 'integration');
export type IntegrationModel = typeof IntegrationModel;

View File

@ -1,29 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class LabelRaw {
_id?: string;
id?: string;
owner: string;
name: string;
color: number;
predefinedId?: string;
}
type LabelRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type LabelRawSelect = LabelRawBoolean<LabelRaw>;
const labelSchema = new Schema<LabelRaw>({
_id: { type: String, _id: true },
id: { type: String, required: true, minlength: 1 },
owner: { type: String, required: true, minlength: 1 },
name: { type: String, required: true, minlength: 1 },
color: { type: Number, required: true, min: 0, max: 19 },
predefinedId: { type: String },
});
export const LabelModel = mongodbServer?.model(LabelRaw.name, labelSchema, 'labels');
export type ILabelModel = typeof LabelModel;

View File

@ -1,102 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
import { wa } from '../types/wa.types';
class Key {
id?: string;
remoteJid?: string;
fromMe?: boolean;
participant?: string;
}
class ChatwootMessage {
messageId?: number;
inboxId?: number;
conversationId?: number;
contactInbox?: { sourceId: string };
isRead?: boolean;
}
export class MessageRaw {
_id?: string;
key?: Key;
pushName?: string;
participant?: string;
message?: object;
messageType?: string;
messageTimestamp?: number | Long.Long;
owner: string;
source?: 'android' | 'web' | 'ios' | 'unknown' | 'desktop';
source_id?: string;
source_reply_id?: string;
chatwoot?: ChatwootMessage;
contextInfo?: any;
status?: wa.StatusMessage | any;
}
type MessageRawBoolean<T> = {
[P in keyof T]?: 0 | 1;
};
export type MessageRawSelect = Omit<Omit<MessageRawBoolean<MessageRaw>, 'key'>, 'chatwoot'> & {
key?: MessageRawBoolean<Key>;
chatwoot?: MessageRawBoolean<ChatwootMessage>;
};
const messageSchema = new Schema<MessageRaw>({
_id: { type: String, _id: true },
key: {
id: { type: String, required: true, minlength: 1 },
remoteJid: { type: String, required: true, minlength: 1 },
fromMe: { type: Boolean, required: true },
participant: { type: String, minlength: 1 },
},
pushName: { type: String },
participant: { type: String },
messageType: { type: String },
message: { type: Object },
source: { type: String, minlength: 3, enum: ['android', 'web', 'ios', 'unknown', 'desktop'] },
messageTimestamp: { type: Number, required: true },
owner: { type: String, required: true, minlength: 1 },
chatwoot: {
messageId: { type: Number },
inboxId: { type: Number },
conversationId: { type: Number },
contactInbox: { type: Object },
isRead: { type: Boolean },
},
});
messageSchema.index({ 'chatwoot.messageId': 1, owner: 1 });
messageSchema.index({ 'key.id': 1 });
messageSchema.index({ 'key.id': 1, owner: 1 });
messageSchema.index({ owner: 1 });
export const MessageModel = mongodbServer?.model(MessageRaw.name, messageSchema, 'messages');
export type IMessageModel = typeof MessageModel;
export class MessageUpdateRaw {
_id?: string;
remoteJid?: string;
id?: string;
fromMe?: boolean;
participant?: string;
datetime?: number;
status?: wa.StatusMessage;
owner: string;
pollUpdates?: any;
}
const messageUpdateSchema = new Schema<MessageUpdateRaw>({
_id: { type: String, _id: true },
remoteJid: { type: String, required: true, min: 1 },
id: { type: String, required: true, min: 1 },
fromMe: { type: Boolean, required: true },
participant: { type: String, min: 1 },
datetime: { type: Number, required: true, min: 1 },
status: { type: String, required: true },
owner: { type: String, required: true, min: 1 },
});
export const MessageUpModel = mongodbServer?.model(MessageUpdateRaw.name, messageUpdateSchema, 'messageUpdate');
export type IMessageUpModel = typeof MessageUpModel;

View File

@ -1,32 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
class Proxy {
host?: string;
port?: string;
protocol?: string;
username?: string;
password?: string;
}
export class ProxyRaw {
_id?: string;
enabled?: boolean;
proxy?: Proxy;
}
const proxySchema = new Schema<ProxyRaw>({
_id: { type: String, _id: true },
enabled: { type: Boolean, required: true },
proxy: {
host: { type: String, required: true },
port: { type: String, required: true },
protocol: { type: String, required: true },
username: { type: String, required: false },
password: { type: String, required: false },
},
});
export const ProxyModel = mongodbServer?.model(ProxyRaw.name, proxySchema, 'proxy');
export type IProxyModel = typeof ProxyModel;

View File

@ -1,28 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class SettingsRaw {
_id?: string;
reject_call?: boolean;
msg_call?: string;
groups_ignore?: boolean;
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
}
const settingsSchema = new Schema<SettingsRaw>({
_id: { type: String, _id: true },
reject_call: { type: Boolean, required: true },
msg_call: { type: String, required: true },
groups_ignore: { type: Boolean, required: true },
always_online: { type: Boolean, required: true },
read_messages: { type: Boolean, required: true },
read_status: { type: Boolean, required: true },
sync_full_history: { type: Boolean, required: true },
});
export const SettingsModel = mongodbServer?.model(SettingsRaw.name, settingsSchema, 'settings');
export type ISettingsModel = typeof SettingsModel;

View File

@ -1,24 +0,0 @@
import { Schema } from 'mongoose';
import { mongodbServer } from '../../libs/mongodb.connect';
export class WebhookRaw {
_id?: string;
url?: string;
enabled?: boolean;
events?: string[];
webhook_by_events?: boolean;
webhook_base64?: boolean;
}
const webhookSchema = new Schema<WebhookRaw>({
_id: { type: String, _id: true },
url: { type: String, required: true },
enabled: { type: Boolean, required: true },
events: { type: [String], required: true },
webhook_by_events: { type: Boolean, required: true },
webhook_base64: { type: Boolean, required: true },
});
export const WebhookModel = mongodbServer?.model(WebhookRaw.name, webhookSchema, 'webhook');
export type IWebhookModel = typeof WebhookModel;

View File

@ -1,149 +0,0 @@
import { opendirSync, readFileSync } from 'fs';
import { join } from 'path';
import { Auth, ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { AUTH_DIR } from '../../../config/path.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { AuthRaw, IAuthModel, IntegrationModel } from '../../models';
export class AuthRepository extends Repository {
constructor(
private readonly authModel: IAuthModel,
private readonly integrationModel: IntegrationModel,
readonly configService: ConfigService,
) {
super(configService);
this.auth = configService.get<Auth>('AUTHENTICATION');
}
private readonly auth: Auth;
private readonly logger = new Logger('AuthRepository');
public async create(data: AuthRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving auth to db');
const insert = await this.authModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving auth to store');
this.writeStore<AuthRaw>({
path: join(AUTH_DIR, 'apikey'),
fileName: instance,
data,
});
this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, 'apikey') + '/' + instance);
this.logger.verbose('auth created');
return { insertCount: 1 };
} catch (error) {
return { error } as any;
}
}
public async find(instance: string): Promise<AuthRaw> {
try {
this.logger.verbose('finding auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
return await this.authModel.findOne({ _id: instance });
}
this.logger.verbose('finding auth in store');
return JSON.parse(
readFileSync(join(AUTH_DIR, 'apikey', instance + '.json'), {
encoding: 'utf-8',
}),
) as AuthRaw;
} catch (error) {
return {};
}
}
public async findByKey(key: string): Promise<AuthRaw> {
try {
this.logger.verbose('finding auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
return await this.authModel.findOne({ apikey: key });
}
return {};
} catch (error) {
return {};
}
}
public async list(): Promise<AuthRaw[]> {
try {
if (this.dbSettings.ENABLED) {
this.logger.verbose('listing auth in db');
return await this.authModel.find();
}
this.logger.verbose('listing auth in store');
const auths: AuthRaw[] = [];
const openDir = opendirSync(join(AUTH_DIR, 'apikey'), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
auths.push(
JSON.parse(
readFileSync(join(AUTH_DIR, 'apikey', dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
return auths;
} catch (error) {
return [];
}
}
public async findInstanceNameById(instanceId: string): Promise<string | null> {
try {
this.logger.verbose('finding auth by instanceId');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
const response = await this.authModel.findOne({ instanceId });
return response._id;
}
this.logger.verbose('finding auth in store is not supported');
} catch (error) {
return null;
}
}
public async findInstanceNameByNumber(number: string): Promise<string | null> {
try {
this.logger.verbose('finding auth by number');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
const instance = await this.integrationModel.findOne({ number });
const response = await this.authModel.findOne({ _id: instance._id });
return response._id;
}
this.logger.verbose('finding auth in store is not supported');
} catch (error) {
return null;
}
}
}

View File

@ -1,177 +0,0 @@
import { opendirSync, readFileSync, rmSync } from 'fs';
import { join } from 'path';
import { ConfigService, StoreConf } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { ChatRaw, ChatRawSelect, IChatModel } from '../../models';
export class ChatQuery {
select?: ChatRawSelect;
where: ChatRaw;
}
export class ChatRepository extends Repository {
constructor(private readonly chatModel: IChatModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('ChatRepository');
public async insert(data: ChatRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting chats');
if (data.length === 0) {
this.logger.verbose('no chats to insert');
return;
}
try {
this.logger.verbose('saving chats to store');
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving chats to db');
const insert = await this.chatModel.insertMany([...data]);
this.logger.verbose('chats saved to db: ' + insert.length + ' chats');
return { insertCount: insert.length };
}
this.logger.verbose('saving chats to store');
const store = this.configService.get<StoreConf>('STORE');
if (store.CHATS) {
this.logger.verbose('saving chats to store');
data.forEach((chat) => {
this.writeStore<ChatRaw>({
path: join(this.storePath, 'chats', instanceName),
fileName: chat.id,
data: chat,
});
this.logger.verbose(
'chats saved to store in path: ' + join(this.storePath, 'chats', instanceName) + '/' + chat.id,
);
});
this.logger.verbose('chats saved to store');
return { insertCount: data.length };
}
this.logger.verbose('chats not saved to store');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
}
public async find(query: ChatQuery): Promise<ChatRaw[]> {
try {
this.logger.verbose('finding chats');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding chats in db');
return await this.chatModel.find({ owner: query.where.owner }).select(query.select ?? {});
}
this.logger.verbose('finding chats in store');
const chats: ChatRaw[] = [];
const openDir = opendirSync(join(this.storePath, 'chats', query.where.owner));
for await (const dirent of openDir) {
if (dirent.isFile()) {
chats.push(
JSON.parse(
readFileSync(join(this.storePath, 'chats', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
this.logger.verbose('chats found in store: ' + chats.length + ' chats');
return chats;
} catch (error) {
return [];
}
}
public async delete(query: ChatQuery) {
try {
this.logger.verbose('deleting chats');
if (this.dbSettings.ENABLED) {
this.logger.verbose('deleting chats in db');
return await this.chatModel.deleteOne({ ...query.where });
}
this.logger.verbose('deleting chats in store');
rmSync(join(this.storePath, 'chats', query.where.owner, query.where.id + '.josn'), {
force: true,
recursive: true,
});
return { deleted: { chatId: query.where.id } };
} catch (error) {
return { error: error?.toString() };
}
}
public async update(data: ChatRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
try {
this.logger.verbose('updating chats');
if (data.length === 0) {
this.logger.verbose('no chats to update');
return;
}
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('updating chats in db');
const chats = data.map((chat) => {
return {
updateOne: {
filter: { id: chat.id },
update: { ...chat },
upsert: true,
},
};
});
const { nModified } = await this.chatModel.bulkWrite(chats);
this.logger.verbose('chats updated in db: ' + nModified + ' chats');
return { insertCount: nModified };
}
this.logger.verbose('updating chats in store');
const store = this.configService.get<StoreConf>('STORE');
if (store.CONTACTS) {
this.logger.verbose('updating chats in store');
data.forEach((chat) => {
this.writeStore({
path: join(this.storePath, 'chats', instanceName),
fileName: chat.id,
data: chat,
});
this.logger.verbose(
'chats updated in store in path: ' + join(this.storePath, 'chats', instanceName) + '/' + chat.id,
);
});
this.logger.verbose('chats updated in store: ' + data.length + ' chats');
return { insertCount: data.length };
}
this.logger.verbose('chats not updated');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
}
}

View File

@ -1,227 +0,0 @@
import { opendirSync, readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService, StoreConf } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { ContactRaw, ContactRawSelect, IContactModel } from '../../models';
export class ContactQuery {
select?: ContactRawSelect;
where: ContactRaw;
}
export class ContactQueryMany {
owner: ContactRaw['owner'];
ids: ContactRaw['id'][];
}
export class ContactRepository extends Repository {
constructor(private readonly contactModel: IContactModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('ContactRepository');
public async insert(data: ContactRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting contacts');
if (data.length === 0) {
this.logger.verbose('no contacts to insert');
return;
}
try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving contacts to db');
const insert = await this.contactModel.insertMany([...data]);
this.logger.verbose('contacts saved to db: ' + insert.length + ' contacts');
return { insertCount: insert.length };
}
this.logger.verbose('saving contacts to store');
const store = this.configService.get<StoreConf>('STORE');
if (store.CONTACTS) {
this.logger.verbose('saving contacts to store');
data.forEach((contact) => {
this.writeStore({
path: join(this.storePath, 'contacts', instanceName),
fileName: contact.id,
data: contact,
});
this.logger.verbose(
'contacts saved to store in path: ' + join(this.storePath, 'contacts', instanceName) + '/' + contact.id,
);
});
this.logger.verbose('contacts saved to store: ' + data.length + ' contacts');
return { insertCount: data.length };
}
this.logger.verbose('contacts not saved');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
}
public async update(data: ContactRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
try {
this.logger.verbose('updating contacts');
if (data.length === 0) {
this.logger.verbose('no contacts to update');
return;
}
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('updating contacts in db');
const contacts = data.map((contact) => {
return {
updateOne: {
filter: { id: contact.id },
update: { ...contact },
upsert: true,
},
};
});
const { nModified } = await this.contactModel.bulkWrite(contacts);
this.logger.verbose('contacts updated in db: ' + nModified + ' contacts');
return { insertCount: nModified };
}
this.logger.verbose('updating contacts in store');
const store = this.configService.get<StoreConf>('STORE');
if (store.CONTACTS) {
this.logger.verbose('updating contacts in store');
data.forEach((contact) => {
this.writeStore({
path: join(this.storePath, 'contacts', instanceName),
fileName: contact.id,
data: contact,
});
this.logger.verbose(
'contacts updated in store in path: ' + join(this.storePath, 'contacts', instanceName) + '/' + contact.id,
);
});
this.logger.verbose('contacts updated in store: ' + data.length + ' contacts');
return { insertCount: data.length };
}
this.logger.verbose('contacts not updated');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
}
public async find(query: ContactQuery): Promise<ContactRaw[]> {
try {
this.logger.verbose('finding contacts');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding contacts in db');
return await this.contactModel.find({ ...query.where }).select(query.select ?? {});
}
this.logger.verbose('finding contacts in store');
const contacts: ContactRaw[] = [];
if (query?.where?.id) {
this.logger.verbose('finding contacts in store by id');
contacts.push(
JSON.parse(
readFileSync(join(this.storePath, 'contacts', query.where.owner, query.where.id + '.json'), {
encoding: 'utf-8',
}),
),
);
} else {
this.logger.verbose('finding contacts in store by owner');
const openDir = opendirSync(join(this.storePath, 'contacts', query.where.owner), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
contacts.push(
JSON.parse(
readFileSync(join(this.storePath, 'contacts', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
}
this.logger.verbose('contacts found in store: ' + contacts.length + ' contacts');
return contacts;
} catch (error) {
return [];
}
}
public async findManyById(query: ContactQueryMany): Promise<ContactRaw[]> {
try {
this.logger.verbose('finding contacts');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding contacts in db');
return await this.contactModel.find({
owner: query.owner,
id: { $in: query.ids },
});
}
this.logger.verbose('finding contacts in store');
const contacts: ContactRaw[] = [];
if (query.ids.length > 0) {
this.logger.verbose('finding contacts in store by id');
query.ids.forEach((id) => {
contacts.push(
JSON.parse(
readFileSync(join(this.storePath, 'contacts', query.owner, id + '.json'), {
encoding: 'utf-8',
}),
),
);
});
} else {
this.logger.verbose('finding contacts in store by owner');
const openDir = opendirSync(join(this.storePath, 'contacts', query.owner), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
contacts.push(
JSON.parse(
readFileSync(join(this.storePath, 'contacts', query.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
}
this.logger.verbose('contacts found in store: ' + contacts.length + ' contacts');
return contacts;
} catch (error) {
return [];
}
}
}

View File

@ -1,149 +0,0 @@
import { opendirSync, readFileSync } from 'fs';
import { join } from 'path';
import { Auth, ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { AUTH_DIR } from '../../../config/path.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { AuthRaw, IAuthModel, IntegrationModel } from '../../models';
export class AuthRepository extends Repository {
constructor(
private readonly authModel: IAuthModel,
private readonly integrationModel: IntegrationModel,
readonly configService: ConfigService,
) {
super(configService);
this.auth = configService.get<Auth>('AUTHENTICATION');
}
private readonly auth: Auth;
private readonly logger = new Logger('AuthRepository');
public async create(data: AuthRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving auth to db');
const insert = await this.authModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving auth to store');
this.writeStore<AuthRaw>({
path: join(AUTH_DIR, 'apikey'),
fileName: instance,
data,
});
this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, 'apikey') + '/' + instance);
this.logger.verbose('auth created');
return { insertCount: 1 };
} catch (error) {
return { error } as any;
}
}
public async find(instance: string): Promise<AuthRaw> {
try {
this.logger.verbose('finding auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
return await this.authModel.findOne({ _id: instance });
}
this.logger.verbose('finding auth in store');
return JSON.parse(
readFileSync(join(AUTH_DIR, 'apikey', instance + '.json'), {
encoding: 'utf-8',
}),
) as AuthRaw;
} catch (error) {
return {};
}
}
public async findByKey(key: string): Promise<AuthRaw> {
try {
this.logger.verbose('finding auth');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
return await this.authModel.findOne({ apikey: key });
}
return {};
} catch (error) {
return {};
}
}
public async list(): Promise<AuthRaw[]> {
try {
if (this.dbSettings.ENABLED) {
this.logger.verbose('listing auth in db');
return await this.authModel.find();
}
this.logger.verbose('listing auth in store');
const auths: AuthRaw[] = [];
const openDir = opendirSync(join(AUTH_DIR, 'apikey'), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
auths.push(
JSON.parse(
readFileSync(join(AUTH_DIR, 'apikey', dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
return auths;
} catch (error) {
return [];
}
}
public async findInstanceNameById(instanceId: string): Promise<string | null> {
try {
this.logger.verbose('finding auth by instanceId');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
const response = await this.authModel.findOne({ instanceId });
return response._id;
}
this.logger.verbose('finding auth in store is not supported');
} catch (error) {
return null;
}
}
public async findInstanceNameByNumber(number: string): Promise<string | null> {
try {
this.logger.verbose('finding auth by number');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding auth in db');
const instance = await this.integrationModel.findOne({ number });
const response = await this.authModel.findOne({ _id: instance._id });
return response._id;
}
this.logger.verbose('finding auth in store is not supported');
} catch (error) {
return null;
}
}
}

View File

@ -1,64 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { IntegrationModel, IntegrationRaw } from '../../models';
export class IntegrationRepository extends Repository {
constructor(private readonly integrationModel: IntegrationModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('IntegrationRepository');
public async create(data: IntegrationRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating integration');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving integration to db');
const insert = await this.integrationModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('integration saved to db: ' + insert.modifiedCount + ' integration');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving integration to store');
this.writeStore<IntegrationRaw>({
path: join(this.storePath, 'integration'),
fileName: instance,
data,
});
this.logger.verbose(
'integration saved to store in path: ' + join(this.storePath, 'integration') + '/' + instance,
);
this.logger.verbose('integration created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<IntegrationRaw> {
try {
this.logger.verbose('finding integration');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding integration in db');
return await this.integrationModel.findOne({ _id: instance });
}
this.logger.verbose('finding integration in store');
return JSON.parse(
readFileSync(join(this.storePath, 'integration', instance + '.json'), {
encoding: 'utf-8',
}),
) as IntegrationRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,111 +0,0 @@
import { opendirSync, readFileSync, rmSync } from 'fs';
import { join } from 'path';
import { ConfigService, StoreConf } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { ILabelModel, LabelRaw, LabelRawSelect } from '../../models';
export class LabelQuery {
select?: LabelRawSelect;
where: Partial<LabelRaw>;
}
export class LabelRepository extends Repository {
constructor(private readonly labelModel: ILabelModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('LabelRepository');
public async insert(data: LabelRaw, instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting labels');
try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving labels to db');
const insert = await this.labelModel.findOneAndUpdate({ id: data.id }, data, { upsert: true });
this.logger.verbose(`label ${data.name} saved to db`);
return { insertCount: Number(!!insert._id) };
}
this.logger.verbose('saving label to store');
const store = this.configService.get<StoreConf>('STORE');
if (store.LABELS) {
this.logger.verbose('saving label to store');
this.writeStore<LabelRaw>({
path: join(this.storePath, 'labels', instanceName),
fileName: data.id,
data,
});
this.logger.verbose(
'labels saved to store in path: ' + join(this.storePath, 'labels', instanceName) + '/' + data.id,
);
this.logger.verbose(`label ${data.name} saved to store`);
return { insertCount: 1 };
}
this.logger.verbose('labels not saved to store');
return { insertCount: 0 };
} catch (error) {
return error;
} finally {
data = undefined;
}
}
public async find(query: LabelQuery): Promise<LabelRaw[]> {
try {
this.logger.verbose('finding labels');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding labels in db');
return await this.labelModel.find({ owner: query.where.owner }).select(query.select ?? {});
}
this.logger.verbose('finding labels in store');
const labels: LabelRaw[] = [];
const openDir = opendirSync(join(this.storePath, 'labels', query.where.owner));
for await (const dirent of openDir) {
if (dirent.isFile()) {
labels.push(
JSON.parse(
readFileSync(join(this.storePath, 'labels', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
this.logger.verbose('labels found in store: ' + labels.length + ' labels');
return labels;
} catch (error) {
return [];
}
}
public async delete(query: LabelQuery) {
try {
this.logger.verbose('deleting labels');
if (this.dbSettings.ENABLED) {
this.logger.verbose('deleting labels in db');
return await this.labelModel.deleteOne({ ...query.where });
}
this.logger.verbose('deleting labels in store');
rmSync(join(this.storePath, 'labels', query.where.owner, query.where.id + '.josn'), {
force: true,
recursive: true,
});
return { deleted: { labelId: query.where.id } };
} catch (error) {
return { error: error?.toString() };
}
}
}

View File

@ -1,240 +0,0 @@
import { opendirSync, readFileSync, rmSync } from 'fs';
import { join } from 'path';
import { ConfigService, StoreConf } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { IMessageModel, MessageRaw, MessageRawSelect } from '../../models';
export class MessageQuery {
select?: MessageRawSelect;
where: MessageRaw;
limit?: number;
}
export class MessageRepository extends Repository {
constructor(private readonly messageModel: IMessageModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('MessageRepository');
public buildQuery(query: MessageQuery): MessageQuery {
for (const [o, p] of Object.entries(query?.where || {})) {
if (typeof p === 'object' && p !== null && !Array.isArray(p)) {
for (const [k, v] of Object.entries(p)) {
query.where[`${o}.${k}`] = v;
}
delete query.where[o];
}
}
for (const [o, p] of Object.entries(query?.select || {})) {
if (typeof p === 'object' && p !== null && !Array.isArray(p)) {
for (const [k, v] of Object.entries(p)) {
query.select[`${o}.${k}`] = v;
}
delete query.select[o];
}
}
return query;
}
public async insert(data: MessageRaw[], instanceName: string, saveDb = false): Promise<IInsert> {
this.logger.verbose('inserting messages');
if (!Array.isArray(data) || data.length === 0) {
this.logger.verbose('no messages to insert');
return;
}
try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving messages to db');
const cleanedData = data.map((obj) => {
const cleanedObj = { ...obj };
if ('extendedTextMessage' in obj.message) {
const extendedTextMessage = obj.message.extendedTextMessage as {
contextInfo?: {
mentionedJid?: any;
};
};
if (typeof extendedTextMessage === 'object' && extendedTextMessage !== null) {
if ('contextInfo' in extendedTextMessage) {
delete extendedTextMessage.contextInfo?.mentionedJid;
extendedTextMessage.contextInfo = {};
}
}
}
return cleanedObj;
});
const insert = await this.messageModel.insertMany([...cleanedData]);
this.logger.verbose('messages saved to db: ' + insert.length + ' messages');
return { insertCount: insert.length };
}
this.logger.verbose('saving messages to store');
const store = this.configService.get<StoreConf>('STORE');
if (store.MESSAGES) {
this.logger.verbose('saving messages to store');
data.forEach((message) => {
this.writeStore({
path: join(this.storePath, 'messages', instanceName),
fileName: message.key.id,
data: message,
});
this.logger.verbose(
'messages saved to store in path: ' + join(this.storePath, 'messages', instanceName) + '/' + message.key.id,
);
});
this.logger.verbose('messages saved to store: ' + data.length + ' messages');
return { insertCount: data.length };
}
this.logger.verbose('messages not saved to store');
return { insertCount: 0 };
} catch (error) {
console.log('ERROR: ', error);
return error;
} finally {
data = undefined;
}
}
public async find(query: MessageQuery) {
try {
this.logger.verbose('finding messages');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding messages in db');
query = this.buildQuery(query);
return await this.messageModel
.find({ ...query.where })
.select(query.select || {})
.sort({ messageTimestamp: -1 })
.limit(query?.limit ?? 0);
}
this.logger.verbose('finding messages in store');
const messages: MessageRaw[] = [];
if (query?.where?.key?.id) {
this.logger.verbose('finding messages in store by id');
messages.push(
JSON.parse(
readFileSync(join(this.storePath, 'messages', query.where.owner, query.where.key.id + '.json'), {
encoding: 'utf-8',
}),
),
);
} else {
this.logger.verbose('finding messages in store by owner');
const openDir = opendirSync(join(this.storePath, 'messages', query.where.owner), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
messages.push(
JSON.parse(
readFileSync(join(this.storePath, 'messages', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
}
this.logger.verbose('messages found in store: ' + messages.length + ' messages');
return messages
.sort((x, y) => {
return (y.messageTimestamp as number) - (x.messageTimestamp as number);
})
.splice(0, query?.limit ?? messages.length);
} catch (error) {
this.logger.error(`error on message find: ${error.toString()}`);
return [];
}
}
public async update(data: MessageRaw[], instanceName: string, saveDb?: boolean): Promise<IInsert> {
try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('updating messages in db');
const messages = data.map((message) => {
return {
updateOne: {
filter: { 'key.id': message.key.id },
update: { ...message },
},
};
});
const { nModified } = await this.messageModel.bulkWrite(messages);
this.logger.verbose('messages updated in db: ' + nModified + ' messages');
return { insertCount: nModified };
}
this.logger.verbose('updating messages in store');
const store = this.configService.get<StoreConf>('STORE');
if (store.MESSAGES) {
this.logger.verbose('updating messages in store');
data.forEach((message) => {
this.writeStore({
path: join(this.storePath, 'messages', instanceName),
fileName: message.key.id,
data: message,
});
this.logger.verbose(
'messages updated in store in path: ' +
join(this.storePath, 'messages', instanceName) +
'/' +
message.key.id,
);
});
this.logger.verbose('messages updated in store: ' + data.length + ' messages');
return { insertCount: data.length };
}
this.logger.verbose('messages not updated');
return { insertCount: 0 };
} catch (error) {
this.logger.error(error);
}
}
public async delete(query: MessageQuery) {
try {
this.logger.verbose('deleting message');
if (this.dbSettings.ENABLED) {
this.logger.verbose('deleting message in db');
query = this.buildQuery(query);
return await this.messageModel.deleteOne({ ...query.where });
}
this.logger.verbose('deleting message in store');
rmSync(join(this.storePath, 'messages', query.where.owner, query.where.key.id + '.json'), {
force: true,
recursive: true,
});
return { deleted: { messageId: query.where.key.id } };
} catch (error) {
return { error: error?.toString() };
}
}
}

View File

@ -1,120 +0,0 @@
import { opendirSync, readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService, StoreConf } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { IMessageUpModel, MessageUpdateRaw } from '../../models';
export class MessageUpQuery {
where: MessageUpdateRaw;
limit?: number;
}
export class MessageUpRepository extends Repository {
constructor(private readonly messageUpModel: IMessageUpModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('MessageUpRepository');
public async insert(data: MessageUpdateRaw[], instanceName: string, saveDb?: boolean): Promise<IInsert> {
this.logger.verbose('inserting message up');
if (data.length === 0) {
this.logger.verbose('no message up to insert');
return;
}
try {
if (this.dbSettings.ENABLED && saveDb) {
this.logger.verbose('saving message up to db');
const insert = await this.messageUpModel.insertMany([...data]);
this.logger.verbose('message up saved to db: ' + insert.length + ' message up');
return { insertCount: insert.length };
}
this.logger.verbose('saving message up to store');
const store = this.configService.get<StoreConf>('STORE');
if (store.MESSAGE_UP) {
this.logger.verbose('saving message up to store');
data.forEach((update) => {
this.writeStore<MessageUpdateRaw>({
path: join(this.storePath, 'message-up', instanceName),
fileName: update.id,
data: update,
});
this.logger.verbose(
'message up saved to store in path: ' + join(this.storePath, 'message-up', instanceName) + '/' + update.id,
);
});
this.logger.verbose('message up saved to store: ' + data.length + ' message up');
return { insertCount: data.length };
}
this.logger.verbose('message up not saved to store');
return { insertCount: 0 };
} catch (error) {
return error;
}
}
public async find(query: MessageUpQuery) {
try {
this.logger.verbose('finding message up');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding message up in db');
return await this.messageUpModel
.find({ ...query.where })
.sort({ datetime: -1 })
.limit(query?.limit ?? 0);
}
this.logger.verbose('finding message up in store');
const messageUpdate: MessageUpdateRaw[] = [];
if (query?.where?.id) {
this.logger.verbose('finding message up in store by id');
messageUpdate.push(
JSON.parse(
readFileSync(join(this.storePath, 'message-up', query.where.owner, query.where.id + '.json'), {
encoding: 'utf-8',
}),
),
);
} else {
this.logger.verbose('finding message up in store by owner');
const openDir = opendirSync(join(this.storePath, 'message-up', query.where.owner), {
encoding: 'utf-8',
});
for await (const dirent of openDir) {
if (dirent.isFile()) {
messageUpdate.push(
JSON.parse(
readFileSync(join(this.storePath, 'message-up', query.where.owner, dirent.name), {
encoding: 'utf-8',
}),
),
);
}
}
}
this.logger.verbose('message up found in store: ' + messageUpdate.length + ' message up');
return messageUpdate
.sort((x, y) => {
return y.datetime - x.datetime;
})
.splice(0, query?.limit ?? messageUpdate.length);
} catch (error) {
return [];
}
}
}

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { IProxyModel, ProxyRaw } from '../../models';
export class ProxyRepository extends Repository {
constructor(private readonly proxyModel: IProxyModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('ProxyRepository');
public async create(data: ProxyRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating proxy');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving proxy to db');
const insert = await this.proxyModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('proxy saved to db: ' + insert.modifiedCount + ' proxy');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving proxy to store');
this.writeStore<ProxyRaw>({
path: join(this.storePath, 'proxy'),
fileName: instance,
data,
});
this.logger.verbose('proxy saved to store in path: ' + join(this.storePath, 'proxy') + '/' + instance);
this.logger.verbose('proxy created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<ProxyRaw> {
try {
this.logger.verbose('finding proxy');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding proxy in db');
return await this.proxyModel.findOne({ _id: instance });
}
this.logger.verbose('finding proxy in store');
return JSON.parse(
readFileSync(join(this.storePath, 'proxy', instance + '.json'), {
encoding: 'utf-8',
}),
) as ProxyRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,169 +0,0 @@
import { PrismaClient } from '@prisma/client';
import fs from 'fs';
import { MongoClient } from 'mongodb';
import { join } from 'path';
import { ConfigService, Database } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { ChatwootRepository } from '../../integrations/chatwoot/repository/chatwoot.repository';
import { RabbitmqRepository } from '../../integrations/rabbitmq/repository/rabbitmq.repository';
import { SqsRepository } from '../../integrations/sqs/repository/sqs.repository';
import { TypebotRepository } from '../../integrations/typebot/repository/typebot.repository';
import { WebsocketRepository } from '../../integrations/websocket/repository/websocket.repository';
import { AuthRepository } from './auth.repository';
import { ChatRepository } from './chat.repository';
import { ContactRepository } from './contact.repository';
import { IntegrationRepository } from './integration.repository';
import { LabelRepository } from './label.repository';
import { MessageRepository } from './message.repository';
import { MessageUpRepository } from './messageUp.repository';
import { ProxyRepository } from './proxy.repository';
import { SettingsRepository } from './settings.repository';
import { WebhookRepository } from './webhook.repository';
export class MongodbRepository {
constructor(
public readonly message: MessageRepository,
public readonly chat: ChatRepository,
public readonly contact: ContactRepository,
public readonly messageUpdate: MessageUpRepository,
public readonly webhook: WebhookRepository,
public readonly chatwoot: ChatwootRepository,
public readonly settings: SettingsRepository,
public readonly websocket: WebsocketRepository,
public readonly rabbitmq: RabbitmqRepository,
public readonly sqs: SqsRepository,
public readonly typebot: TypebotRepository,
public readonly proxy: ProxyRepository,
public readonly integration: IntegrationRepository,
public readonly auth: AuthRepository,
public readonly labels: LabelRepository,
private configService: ConfigService,
mongodbServer?: MongoClient,
prismaServer?: PrismaClient,
) {
this.mongodbClient = mongodbServer;
this.prismaClient = prismaServer;
this.__init_repo_without_db__();
}
private mongodbClient?: MongoClient;
private prismaClient?: PrismaClient;
private readonly logger = new Logger('RepositoryBroker');
public get mongodbServer() {
return this.mongodbClient;
}
public get prismaServer() {
return this.prismaClient;
}
private __init_repo_without_db__() {
this.logger.verbose('initializing repository without db');
if (!this.configService.get<Database>('DATABASE').ENABLED) {
const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath);
try {
const authDir = join(storePath, 'auth', 'apikey');
const chatsDir = join(storePath, 'chats');
const contactsDir = join(storePath, 'contacts');
const messagesDir = join(storePath, 'messages');
const messageUpDir = join(storePath, 'message-up');
const webhookDir = join(storePath, 'webhook');
const chatwootDir = join(storePath, 'chatwoot');
const settingsDir = join(storePath, 'settings');
const websocketDir = join(storePath, 'websocket');
const rabbitmqDir = join(storePath, 'rabbitmq');
const sqsDir = join(storePath, 'sqs');
const typebotDir = join(storePath, 'typebot');
const proxyDir = join(storePath, 'proxy');
const integrationDir = join(storePath, 'integration');
const tempDir = join(storePath, 'temp');
if (!fs.existsSync(authDir)) {
this.logger.verbose('creating auth dir: ' + authDir);
fs.mkdirSync(authDir, { recursive: true });
}
if (!fs.existsSync(chatsDir)) {
this.logger.verbose('creating chats dir: ' + chatsDir);
fs.mkdirSync(chatsDir, { recursive: true });
}
if (!fs.existsSync(contactsDir)) {
this.logger.verbose('creating contacts dir: ' + contactsDir);
fs.mkdirSync(contactsDir, { recursive: true });
}
if (!fs.existsSync(messagesDir)) {
this.logger.verbose('creating messages dir: ' + messagesDir);
fs.mkdirSync(messagesDir, { recursive: true });
}
if (!fs.existsSync(messageUpDir)) {
this.logger.verbose('creating message-up dir: ' + messageUpDir);
fs.mkdirSync(messageUpDir, { recursive: true });
}
if (!fs.existsSync(webhookDir)) {
this.logger.verbose('creating webhook dir: ' + webhookDir);
fs.mkdirSync(webhookDir, { recursive: true });
}
if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true });
}
if (!fs.existsSync(settingsDir)) {
this.logger.verbose('creating settings dir: ' + settingsDir);
fs.mkdirSync(settingsDir, { recursive: true });
}
if (!fs.existsSync(websocketDir)) {
this.logger.verbose('creating websocket dir: ' + websocketDir);
fs.mkdirSync(websocketDir, { recursive: true });
}
if (!fs.existsSync(rabbitmqDir)) {
this.logger.verbose('creating rabbitmq dir: ' + rabbitmqDir);
fs.mkdirSync(rabbitmqDir, { recursive: true });
}
if (!fs.existsSync(sqsDir)) {
this.logger.verbose('creating sqs dir: ' + sqsDir);
fs.mkdirSync(sqsDir, { recursive: true });
}
if (!fs.existsSync(typebotDir)) {
this.logger.verbose('creating typebot dir: ' + typebotDir);
fs.mkdirSync(typebotDir, { recursive: true });
}
if (!fs.existsSync(proxyDir)) {
this.logger.verbose('creating proxy dir: ' + proxyDir);
fs.mkdirSync(proxyDir, { recursive: true });
}
if (!fs.existsSync(integrationDir)) {
this.logger.verbose('creating integration dir: ' + integrationDir);
fs.mkdirSync(integrationDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true });
}
} catch (error) {
this.logger.error(error);
}
} else {
try {
const storePath = join(process.cwd(), 'store');
this.logger.verbose('creating store path: ' + storePath);
const tempDir = join(storePath, 'temp');
const chatwootDir = join(storePath, 'chatwoot');
if (!fs.existsSync(chatwootDir)) {
this.logger.verbose('creating chatwoot dir: ' + chatwootDir);
fs.mkdirSync(chatwootDir, { recursive: true });
}
if (!fs.existsSync(tempDir)) {
this.logger.verbose('creating temp dir: ' + tempDir);
fs.mkdirSync(tempDir, { recursive: true });
}
} catch (error) {
this.logger.error(error);
}
}
}
}

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { ISettingsModel, SettingsRaw } from '../../models';
export class SettingsRepository extends Repository {
constructor(private readonly settingsModel: ISettingsModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('SettingsRepository');
public async create(data: SettingsRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating settings');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving settings to db');
const insert = await this.settingsModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('settings saved to db: ' + insert.modifiedCount + ' settings');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving settings to store');
this.writeStore<SettingsRaw>({
path: join(this.storePath, 'settings'),
fileName: instance,
data,
});
this.logger.verbose('settings saved to store in path: ' + join(this.storePath, 'settings') + '/' + instance);
this.logger.verbose('settings created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<SettingsRaw> {
try {
this.logger.verbose('finding settings');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding settings in db');
return await this.settingsModel.findOne({ _id: instance });
}
this.logger.verbose('finding settings in store');
return JSON.parse(
readFileSync(join(this.storePath, 'settings', instance + '.json'), {
encoding: 'utf-8',
}),
) as SettingsRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,62 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { IInsert, Repository } from '../../abstract/abstract.repository';
import { IWebhookModel, WebhookRaw } from '../../models';
export class WebhookRepository extends Repository {
constructor(private readonly webhookModel: IWebhookModel, private readonly configService: ConfigService) {
super(configService);
}
private readonly logger = new Logger('WebhookRepository');
public async create(data: WebhookRaw, instance: string): Promise<IInsert> {
try {
this.logger.verbose('creating webhook');
if (this.dbSettings.ENABLED) {
this.logger.verbose('saving webhook to db');
const insert = await this.webhookModel.replaceOne({ _id: instance }, { ...data }, { upsert: true });
this.logger.verbose('webhook saved to db: ' + insert.modifiedCount + ' webhook');
return { insertCount: insert.modifiedCount };
}
this.logger.verbose('saving webhook to store');
this.writeStore<WebhookRaw>({
path: join(this.storePath, 'webhook'),
fileName: instance,
data,
});
this.logger.verbose('webhook saved to store in path: ' + join(this.storePath, 'webhook') + '/' + instance);
this.logger.verbose('webhook created');
return { insertCount: 1 };
} catch (error) {
return error;
}
}
public async find(instance: string): Promise<WebhookRaw> {
try {
this.logger.verbose('finding webhook');
if (this.dbSettings.ENABLED) {
this.logger.verbose('finding webhook in db');
return await this.webhookModel.findOne({ _id: instance });
}
this.logger.verbose('finding webhook in store');
return JSON.parse(
readFileSync(join(this.storePath, 'webhook', instance + '.json'), {
encoding: 'utf-8',
}),
) as WebhookRaw;
} catch (error) {
return {};
}
}
}

View File

@ -1,7 +1,7 @@
import { PrismaClient } from '@prisma/client';
import { ConfigService } from '../../../config/env.config';
import { Logger } from '../../../config/logger.config';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
export class Query<T> {
where?: T;

View File

@ -1,3 +1,4 @@
import { Contact, Message, MessageUpdate } from '@prisma/client';
import { RequestHandler, Router } from 'express';
import { Logger } from '../../config/logger.config';
@ -37,9 +38,7 @@ import {
WhatsAppNumberDto,
} from '../dto/chat.dto';
import { InstanceDto } from '../dto/instance.dto';
import { ContactQuery } from '../repository/mongodb/contact.repository';
import { MessageQuery } from '../repository/mongodb/message.repository';
import { MessageUpQuery } from '../repository/mongodb/messageUp.repository';
import { Query } from '../repository/repository.service';
import { chatController } from '../server.module';
import { HttpStatus } from './index.router';
@ -176,10 +175,10 @@ export class ChatRouter extends RouterBroker {
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<ContactQuery>({
const response = await this.dataValidate<Query<Contact>>({
request: req,
schema: contactValidateSchema,
ClassRef: ContactQuery,
ClassRef: Query<Contact>,
execute: (instance, data) => chatController.fetchContacts(instance, data),
});
@ -210,10 +209,10 @@ export class ChatRouter extends RouterBroker {
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<MessageQuery>({
const response = await this.dataValidate<Query<Message>>({
request: req,
schema: messageValidateSchema,
ClassRef: MessageQuery,
ClassRef: Query<Message>,
execute: (instance, data) => chatController.fetchMessages(instance, data),
});
@ -227,10 +226,10 @@ export class ChatRouter extends RouterBroker {
logger.verbose('request query: ');
logger.verbose(req.query);
const response = await this.dataValidate<MessageUpQuery>({
const response = await this.dataValidate<Query<MessageUpdate>>({
request: req,
schema: messageUpSchema,
ClassRef: MessageUpQuery,
ClassRef: Query<MessageUpdate>,
execute: (instance, data) => chatController.fetchStatusMessage(instance, data),
});

View File

@ -1,8 +1,7 @@
import { RequestHandler, Router } from 'express';
import { ConfigService, Database } from '../../config/env.config';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { mongodbServer } from '../../libs/mongodb.connect';
import { instanceNameSchema, presenceOnlySchema } from '../../validate/validate.schema';
import { RouterBroker } from '../abstract/abstract.router';
import { InstanceDto, SetPresenceDto } from '../dto/instance.dto';
@ -162,28 +161,6 @@ export class InstanceRouter extends RouterBroker {
return res.status(HttpStatus.OK).json(response);
});
this.router.delete('/deleteDatabase', async (req, res) => {
logger.verbose('request received in deleteDatabase');
logger.verbose('request body: ');
logger.verbose(req.body);
logger.verbose('request query: ');
logger.verbose(req.query);
const db = this.configService.get<Database>('DATABASE');
if (db.ENABLED) {
try {
await mongodbServer.dropDatabase();
return res
.status(HttpStatus.CREATED)
.json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } });
} catch (error) {
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: error.message });
}
}
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ error: true, message: 'Database is not enabled' });
});
}
public readonly router = Router();

View File

@ -2,8 +2,6 @@ import { CacheEngine } from '../cache/cacheengine';
import { configService } from '../config/env.config';
import { eventEmitter } from '../config/event.config';
import { Logger } from '../config/logger.config';
import { mongodbServer } from '../libs/mongodb.connect';
import { prismaServer } from '../libs/prisma.connect';
import { ChatController } from './controllers/chat.controller';
import { GroupController } from './controllers/group.controller';
import { InstanceController } from './controllers/instance.controller';
@ -13,50 +11,17 @@ import { SendMessageController } from './controllers/sendMessage.controller';
import { SettingsController } from './controllers/settings.controller';
import { WebhookController } from './controllers/webhook.controller';
import { ChatwootController } from './integrations/chatwoot/controllers/chatwoot.controller';
import { ChatwootRepository } from './integrations/chatwoot/repository/chatwoot.repository';
import { ChatwootService } from './integrations/chatwoot/services/chatwoot.service';
import { RabbitmqController } from './integrations/rabbitmq/controllers/rabbitmq.controller';
import { RabbitmqRepository } from './integrations/rabbitmq/repository/rabbitmq.repository';
import { RabbitmqService } from './integrations/rabbitmq/services/rabbitmq.service';
import { SqsController } from './integrations/sqs/controllers/sqs.controller';
import { SqsRepository } from './integrations/sqs/repository/sqs.repository';
import { SqsService } from './integrations/sqs/services/sqs.service';
import { TypebotController } from './integrations/typebot/controllers/typebot.controller';
import { TypebotRepository } from './integrations/typebot/repository/typebot.repository';
import { TypebotService } from './integrations/typebot/services/typebot.service';
import { WebsocketController } from './integrations/websocket/controllers/websocket.controller';
import { WebsocketRepository } from './integrations/websocket/repository/websocket.repository';
import { WebsocketService } from './integrations/websocket/services/websocket.service';
import {
AuthModel,
ChatModel,
ChatwootModel,
ContactModel,
IntegrationModel,
MessageModel,
MessageUpModel,
ProxyModel,
RabbitmqModel,
SettingsModel,
SqsModel,
TypebotModel,
WebhookModel,
WebsocketModel,
} from './models';
import { LabelModel } from './models/label.model';
import { ProviderFiles } from './provider/sessions';
import { AuthRepository } from './repository/mongodb/auth.repository';
import { ChatRepository } from './repository/mongodb/chat.repository';
import { ContactRepository } from './repository/mongodb/contact.repository';
import { IntegrationRepository } from './repository/mongodb/integration.repository';
import { LabelRepository } from './repository/mongodb/label.repository';
import { MessageRepository } from './repository/mongodb/message.repository';
import { MessageUpRepository } from './repository/mongodb/messageUp.repository';
import { ProxyRepository } from './repository/mongodb/proxy.repository';
import { MongodbRepository } from './repository/mongodb/repository.manager';
import { SettingsRepository } from './repository/mongodb/settings.repository';
import { WebhookRepository } from './repository/mongodb/webhook.repository';
import { PrismaRepository } from './repository/prisma/repository.service';
import { PrismaRepository } from './repository/repository.service';
import { AuthService } from './services/auth.service';
import { CacheService } from './services/cache.service';
import { IntegrationService } from './services/integration.service';
@ -67,62 +32,24 @@ import { WebhookService } from './services/webhook.service';
const logger = new Logger('WA MODULE');
const messageRepository = new MessageRepository(MessageModel, configService);
const chatRepository = new ChatRepository(ChatModel, configService);
const contactRepository = new ContactRepository(ContactModel, configService);
const messageUpdateRepository = new MessageUpRepository(MessageUpModel, configService);
const typebotRepository = new TypebotRepository(TypebotModel, configService);
const webhookRepository = new WebhookRepository(WebhookModel, configService);
const websocketRepository = new WebsocketRepository(WebsocketModel, configService);
const proxyRepository = new ProxyRepository(ProxyModel, configService);
const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService);
const sqsRepository = new SqsRepository(SqsModel, configService);
const integrationRepository = new IntegrationRepository(IntegrationModel, configService);
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
const settingsRepository = new SettingsRepository(SettingsModel, configService);
const authRepository = new AuthRepository(AuthModel, IntegrationModel, configService);
const labelRepository = new LabelRepository(LabelModel, configService);
export const mongodbRepository = new MongodbRepository(
messageRepository,
chatRepository,
contactRepository,
messageUpdateRepository,
webhookRepository,
chatwootRepository,
settingsRepository,
websocketRepository,
rabbitmqRepository,
sqsRepository,
typebotRepository,
proxyRepository,
integrationRepository,
authRepository,
labelRepository,
configService,
mongodbServer.getClient(),
prismaServer,
);
export const prismaRepository = new PrismaRepository(configService);
export const cache = new CacheService(new CacheEngine(configService, 'instance').getEngine());
const chatwootCache = new CacheService(new CacheEngine(configService, ChatwootService.name).getEngine());
const baileysCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine());
const providerFiles = new ProviderFiles(configService);
export const prismaRepository = new PrismaRepository(configService);
export const waMonitor = new WAMonitoringService(
eventEmitter,
configService,
mongodbRepository,
prismaRepository,
providerFiles,
cache,
chatwootCache,
baileysCache,
providerFiles,
);
const authService = new AuthService(waMonitor, mongodbRepository, prismaRepository);
const authService = new AuthService(waMonitor, configService, prismaRepository);
const typebotService = new TypebotService(waMonitor, configService, eventEmitter);
export const typebotController = new TypebotController(typebotService);
@ -144,19 +71,8 @@ export const sqsController = new SqsController(sqsService);
const integrationService = new IntegrationService(waMonitor);
const chatwootService = new ChatwootService(
waMonitor,
configService,
mongodbRepository,
prismaRepository,
chatwootCache,
);
export const chatwootController = new ChatwootController(
chatwootService,
configService,
mongodbRepository,
prismaRepository,
);
const chatwootService = new ChatwootService(waMonitor, configService, prismaRepository, chatwootCache);
export const chatwootController = new ChatwootController(chatwootService, configService, prismaRepository);
const settingsService = new SettingsService(waMonitor);
export const settingsController = new SettingsController(settingsService);
@ -164,7 +80,6 @@ export const settingsController = new SettingsController(settingsService);
export const instanceController = new InstanceController(
waMonitor,
configService,
mongodbRepository,
prismaRepository,
eventEmitter,
authService,

View File

@ -1,16 +1,16 @@
import { v4 } from 'uuid';
import { ConfigService } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { BadRequestException } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto';
import { MongodbRepository } from '../repository/mongodb/repository.manager';
import { PrismaRepository } from '../repository/prisma/repository.service';
import { PrismaRepository } from '../repository/repository.service';
import { WAMonitoringService } from './monitor.service';
export class AuthService {
constructor(
private readonly waMonitor: WAMonitoringService,
private readonly mongodbRepository: MongodbRepository,
private readonly configService: ConfigService,
private readonly prismaRepository: PrismaRepository,
) {}
@ -21,22 +21,28 @@ export class AuthService {
this.logger.verbose(token ? 'APIKEY defined: ' + apikey : 'APIKEY created: ' + apikey);
const auth = await this.mongodbRepository.auth.create(
{ apikey, instanceId: instance.instanceId },
instance.instanceName,
);
const db = this.configService.get('DATABASE');
this.logger.verbose('APIKEY saved in database');
if (db.ENABLED) {
try {
await this.prismaRepository.auth.create({
data: {
apikey: apikey,
instanceId: instance.instanceId,
},
});
if (auth['error']) {
this.logger.error({
localError: AuthService.name + '.apikey',
error: auth['error'],
});
throw new BadRequestException('Authentication error', auth['error']?.toString());
this.logger.verbose('APIKEY saved in database');
return { apikey };
} catch (error) {
this.logger.error({
localError: AuthService.name + '.apikey',
error: error,
});
throw new BadRequestException('Authentication error', error?.toString());
}
}
return { apikey };
}
public async checkDuplicateToken(token: string) {

View File

@ -21,20 +21,21 @@ import {
import { Logger } from '../../config/logger.config';
import { ROOT_DIR } from '../../config/path.config';
import { NotFoundException } from '../../exceptions';
import { ChatwootRaw } from '../integrations/chatwoot/models/chatwoot.model';
import { IntegrationDto } from '../dto/integration.dto';
import { ProxyDto } from '../dto/proxy.dto';
import { SettingsDto } from '../dto/settings.dto';
import { WebhookDto } from '../dto/webhook.dto';
import { ChatwootDto } from '../integrations/chatwoot/dto/chatwoot.dto';
import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service';
import { RabbitmqDto } from '../integrations/rabbitmq/dto/rabbitmq.dto';
import { getAMQP, removeQueues } from '../integrations/rabbitmq/libs/amqp.server';
import { SqsDto } from '../integrations/sqs/dto/sqs.dto';
import { getSQS, removeQueues as removeQueuesSQS } from '../integrations/sqs/libs/sqs.server';
import { TypebotDto } from '../integrations/typebot/dto/typebot.dto';
import { TypebotService } from '../integrations/typebot/services/typebot.service';
import { WebsocketDto } from '../integrations/websocket/dto/websocket.dto';
import { getIO } from '../integrations/websocket/libs/socket.server';
import { WebsocketRaw } from '../integrations/websocket/models/websocket.model';
import { IntegrationRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, SqsRaw, TypebotRaw } from '../models';
import { WebhookRaw } from '../models/webhook.model';
import { ContactQuery } from '../repository/mongodb/contact.repository';
import { MessageQuery } from '../repository/mongodb/message.repository';
import { MessageUpQuery } from '../repository/mongodb/messageUp.repository';
import { MongodbRepository } from '../repository/mongodb/repository.manager';
import { PrismaRepository } from '../repository/prisma/repository.service';
import { PrismaRepository } from '../repository/repository.service';
import { waMonitor } from '../server.module';
import { Events, wa } from '../types/wa.types';
import { CacheService } from './cache.service';
@ -43,7 +44,6 @@ export class ChannelStartupService {
constructor(
public readonly configService: ConfigService,
public readonly eventEmitter: EventEmitter2,
public readonly mongodbRepository: MongodbRepository,
public readonly prismaRepository: PrismaRepository,
public readonly chatwootCache: CacheService,
) {
@ -68,7 +68,6 @@ export class ChannelStartupService {
public chatwootService = new ChatwootService(
waMonitor,
this.configService,
this.mongodbRepository,
this.prismaRepository,
this.chatwootCache,
);
@ -109,6 +108,21 @@ export class ChannelStartupService {
return this.instance.name;
}
public set instanceId(id: string) {
if (!id) {
this.logger.verbose('Instance id not found, generating random id with uuid');
this.instance.id = v4();
return;
}
this.logger.verbose(`Setting instanceId: ${id}`);
this.instance.id = id;
}
public get instanceId() {
this.logger.verbose('Getting instanceId');
return this.instance.id;
}
public get wuid() {
this.logger.verbose('Getting remoteJid of instance');
return this.instance.wuid;
@ -116,7 +130,12 @@ export class ChannelStartupService {
public async loadIntegration() {
this.logger.verbose('Loading webhook');
const data = await this.mongodbRepository.integration.find(this.instanceName);
const data = await this.prismaRepository.integration.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localIntegration.integration = data?.integration;
this.logger.verbose(`Integration: ${this.localIntegration.integration}`);
@ -129,9 +148,26 @@ export class ChannelStartupService {
this.logger.verbose('Integration loaded');
}
public async setIntegration(data: IntegrationRaw) {
public async setIntegration(data: IntegrationDto) {
this.logger.verbose('Setting integration');
await this.mongodbRepository.integration.create(data, this.instanceName);
console.log('setIntegration');
await this.prismaRepository.integration.upsert({
where: {
instanceId: this.instanceId,
},
update: {
integration: data.integration,
number: data.number,
token: data.token,
},
create: {
integration: data.integration,
number: data.number,
token: data.token,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Integration: ${data.integration}`);
this.logger.verbose(`Integration number: ${data.number}`);
this.logger.verbose(`Integration token: ${data.token}`);
@ -141,15 +177,23 @@ export class ChannelStartupService {
public async findIntegration() {
this.logger.verbose('Finding integration');
let data: any;
let data;
data = await this.mongodbRepository.integration.find(this.instanceName);
data = await this.prismaRepository.integration.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.mongodbRepository.integration.create(
{ integration: 'WHATSAPP-BAILEYS', number: '', token: '' },
this.instanceName,
);
await this.prismaRepository.integration.create({
data: {
integration: 'WHATSAPP-BAILEYS',
number: '',
token: '',
instanceId: this.instanceId,
},
});
data = { integration: 'WHATSAPP-BAILEYS', number: '', token: '' };
}
@ -157,84 +201,106 @@ export class ChannelStartupService {
this.logger.verbose(`Integration number: ${data.number}`);
this.logger.verbose(`Integration token: ${data.token}`);
return {
integration: data.integration,
number: data.number,
token: data.token,
};
return data;
}
public async loadSettings() {
this.logger.verbose('Loading settings');
const data = await this.mongodbRepository.settings.find(this.instanceName);
this.localSettings.reject_call = data?.reject_call;
this.logger.verbose(`Settings reject_call: ${this.localSettings.reject_call}`);
const data = await this.prismaRepository.setting.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localSettings.msg_call = data?.msg_call;
this.logger.verbose(`Settings msg_call: ${this.localSettings.msg_call}`);
this.localSettings.rejectCall = data?.rejectCall;
this.logger.verbose(`Settings rejectCall: ${this.localSettings.rejectCall}`);
this.localSettings.groups_ignore = data?.groups_ignore;
this.logger.verbose(`Settings groups_ignore: ${this.localSettings.groups_ignore}`);
this.localSettings.msgCall = data?.msgCall;
this.logger.verbose(`Settings msgCall: ${this.localSettings.msgCall}`);
this.localSettings.always_online = data?.always_online;
this.logger.verbose(`Settings always_online: ${this.localSettings.always_online}`);
this.localSettings.groupsIgnore = data?.groupsIgnore;
this.logger.verbose(`Settings groupsIgnore: ${this.localSettings.groupsIgnore}`);
this.localSettings.read_messages = data?.read_messages;
this.logger.verbose(`Settings read_messages: ${this.localSettings.read_messages}`);
this.localSettings.alwaysOnline = data?.alwaysOnline;
this.logger.verbose(`Settings alwaysOnline: ${this.localSettings.alwaysOnline}`);
this.localSettings.read_status = data?.read_status;
this.logger.verbose(`Settings read_status: ${this.localSettings.read_status}`);
this.localSettings.readMessages = data?.readMessages;
this.logger.verbose(`Settings readMessages: ${this.localSettings.readMessages}`);
this.localSettings.sync_full_history = data?.sync_full_history;
this.logger.verbose(`Settings sync_full_history: ${this.localSettings.sync_full_history}`);
this.localSettings.readStatus = data?.readStatus;
this.logger.verbose(`Settings readStatus: ${this.localSettings.readStatus}`);
this.localSettings.syncFullHistory = data?.syncFullHistory;
this.logger.verbose(`Settings syncFullHistory: ${this.localSettings.syncFullHistory}`);
this.logger.verbose('Settings loaded');
}
public async setSettings(data: SettingsRaw) {
public async setSettings(data: SettingsDto) {
this.logger.verbose('Setting settings');
await this.mongodbRepository.settings.create(data, this.instanceName);
this.logger.verbose(`Settings reject_call: ${data.reject_call}`);
this.logger.verbose(`Settings msg_call: ${data.msg_call}`);
this.logger.verbose(`Settings groups_ignore: ${data.groups_ignore}`);
this.logger.verbose(`Settings always_online: ${data.always_online}`);
this.logger.verbose(`Settings read_messages: ${data.read_messages}`);
this.logger.verbose(`Settings read_status: ${data.read_status}`);
this.logger.verbose(`Settings sync_full_history: ${data.sync_full_history}`);
await this.prismaRepository.setting.create({
data: {
rejectCall: data.rejectCall,
msgCall: data.msgCall,
groupsIgnore: data.groupsIgnore,
alwaysOnline: data.alwaysOnline,
readMessages: data.readMessages,
readStatus: data.readStatus,
syncFullHistory: data.syncFullHistory,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Settings rejectCall: ${data.rejectCall}`);
this.logger.verbose(`Settings msgCall: ${data.msgCall}`);
this.logger.verbose(`Settings groupsIgnore: ${data.groupsIgnore}`);
this.logger.verbose(`Settings alwaysOnline: ${data.alwaysOnline}`);
this.logger.verbose(`Settings readMessages: ${data.readMessages}`);
this.logger.verbose(`Settings readStatus: ${data.readStatus}`);
this.logger.verbose(`Settings syncFullHistory: ${data.syncFullHistory}`);
Object.assign(this.localSettings, data);
this.logger.verbose('Settings set');
}
public async findSettings() {
this.logger.verbose('Finding settings');
const data = await this.mongodbRepository.settings.find(this.instanceName);
const data = await this.prismaRepository.setting.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Settings not found');
return null;
}
this.logger.verbose(`Settings url: ${data.reject_call}`);
this.logger.verbose(`Settings msg_call: ${data.msg_call}`);
this.logger.verbose(`Settings groups_ignore: ${data.groups_ignore}`);
this.logger.verbose(`Settings always_online: ${data.always_online}`);
this.logger.verbose(`Settings read_messages: ${data.read_messages}`);
this.logger.verbose(`Settings read_status: ${data.read_status}`);
this.logger.verbose(`Settings sync_full_history: ${data.sync_full_history}`);
this.logger.verbose(`Settings url: ${data.rejectCall}`);
this.logger.verbose(`Settings msgCall: ${data.msgCall}`);
this.logger.verbose(`Settings groupsIgnore: ${data.groupsIgnore}`);
this.logger.verbose(`Settings alwaysOnline: ${data.alwaysOnline}`);
this.logger.verbose(`Settings readMessages: ${data.readMessages}`);
this.logger.verbose(`Settings readStatus: ${data.readStatus}`);
this.logger.verbose(`Settings syncFullHistory: ${data.syncFullHistory}`);
return {
reject_call: data.reject_call,
msg_call: data.msg_call,
groups_ignore: data.groups_ignore,
always_online: data.always_online,
read_messages: data.read_messages,
read_status: data.read_status,
sync_full_history: data.sync_full_history,
rejectCall: data.rejectCall,
msgCall: data.msgCall,
groupsIgnore: data.groupsIgnore,
alwaysOnline: data.alwaysOnline,
readMessages: data.readMessages,
readStatus: data.readStatus,
syncFullHistory: data.syncFullHistory,
};
}
public async loadWebhook() {
this.logger.verbose('Loading webhook');
const data = await this.mongodbRepository.webhook.find(this.instanceName);
const data = await this.prismaRepository.webhook.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localWebhook.url = data?.url;
this.logger.verbose(`Webhook url: ${this.localWebhook.url}`);
@ -244,18 +310,28 @@ export class ChannelStartupService {
this.localWebhook.events = data?.events;
this.logger.verbose(`Webhook events: ${this.localWebhook.events}`);
this.localWebhook.webhook_by_events = data?.webhook_by_events;
this.logger.verbose(`Webhook by events: ${this.localWebhook.webhook_by_events}`);
this.localWebhook.webhookByEvents = data?.webhookByEvents;
this.logger.verbose(`Webhook by events: ${this.localWebhook.webhookByEvents}`);
this.localWebhook.webhook_base64 = data?.webhook_base64;
this.logger.verbose(`Webhook by webhook_base64: ${this.localWebhook.webhook_base64}`);
this.localWebhook.webhookBase64 = data?.webhookBase64;
this.logger.verbose(`Webhook by webhookBase64: ${this.localWebhook.webhookBase64}`);
this.logger.verbose('Webhook loaded');
}
public async setWebhook(data: WebhookRaw) {
public async setWebhook(data: WebhookDto) {
this.logger.verbose('Setting webhook');
await this.mongodbRepository.webhook.create(data, this.instanceName);
await this.prismaRepository.webhook.create({
data: {
url: data.url,
enabled: data.enabled,
events: data.events,
webhookByEvents: data.webhookByEvents,
webhookBase64: data.webhookBase64,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Webhook url: ${data.url}`);
this.logger.verbose(`Webhook events: ${data.events}`);
Object.assign(this.localWebhook, data);
@ -264,7 +340,11 @@ export class ChannelStartupService {
public async findWebhook() {
this.logger.verbose('Finding webhook');
const data = await this.mongodbRepository.webhook.find(this.instanceName);
const data = await this.prismaRepository.webhook.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Webhook not found');
@ -274,23 +354,22 @@ export class ChannelStartupService {
this.logger.verbose(`Webhook url: ${data.url}`);
this.logger.verbose(`Webhook events: ${data.events}`);
return {
enabled: data.enabled,
url: data.url,
events: data.events,
webhook_by_events: data.webhook_by_events,
webhook_base64: data.webhook_base64,
};
return data;
}
public async loadChatwoot() {
this.logger.verbose('Loading chatwoot');
const data = await this.mongodbRepository.chatwoot.find(this.instanceName);
const data = await this.prismaRepository.chatwoot.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localChatwoot.enabled = data?.enabled;
this.logger.verbose(`Chatwoot enabled: ${this.localChatwoot.enabled}`);
this.localChatwoot.account_id = data?.account_id;
this.logger.verbose(`Chatwoot account id: ${this.localChatwoot.account_id}`);
this.localChatwoot.accountId = data?.accountId;
this.logger.verbose(`Chatwoot account id: ${this.localChatwoot.accountId}`);
this.localChatwoot.token = data?.token;
this.logger.verbose(`Chatwoot token: ${this.localChatwoot.token}`);
@ -298,53 +377,74 @@ export class ChannelStartupService {
this.localChatwoot.url = data?.url;
this.logger.verbose(`Chatwoot url: ${this.localChatwoot.url}`);
this.localChatwoot.name_inbox = data?.name_inbox;
this.logger.verbose(`Chatwoot inbox name: ${this.localChatwoot.name_inbox}`);
this.localChatwoot.nameInbox = data?.nameInbox;
this.logger.verbose(`Chatwoot inbox name: ${this.localChatwoot.nameInbox}`);
this.localChatwoot.sign_msg = data?.sign_msg;
this.logger.verbose(`Chatwoot sign msg: ${this.localChatwoot.sign_msg}`);
this.localChatwoot.signMsg = data?.signMsg;
this.logger.verbose(`Chatwoot sign msg: ${this.localChatwoot.signMsg}`);
this.localChatwoot.signDelimiter = data?.signDelimiter;
this.logger.verbose(`Chatwoot sign delimiter: ${this.localChatwoot.signDelimiter}`);
this.localChatwoot.number = data?.number;
this.logger.verbose(`Chatwoot number: ${this.localChatwoot.number}`);
this.localChatwoot.reopen_conversation = data?.reopen_conversation;
this.logger.verbose(`Chatwoot reopen conversation: ${this.localChatwoot.reopen_conversation}`);
this.localChatwoot.reopenConversation = data?.reopenConversation;
this.logger.verbose(`Chatwoot reopen conversation: ${this.localChatwoot.reopenConversation}`);
this.localChatwoot.conversation_pending = data?.conversation_pending;
this.logger.verbose(`Chatwoot conversation pending: ${this.localChatwoot.conversation_pending}`);
this.localChatwoot.conversationPending = data?.conversationPending;
this.logger.verbose(`Chatwoot conversation pending: ${this.localChatwoot.conversationPending}`);
this.localChatwoot.merge_brazil_contacts = data?.merge_brazil_contacts;
this.logger.verbose(`Chatwoot merge brazil contacts: ${this.localChatwoot.merge_brazil_contacts}`);
this.localChatwoot.mergeBrazilContacts = data?.mergeBrazilContacts;
this.logger.verbose(`Chatwoot merge brazil contacts: ${this.localChatwoot.mergeBrazilContacts}`);
this.localChatwoot.import_contacts = data?.import_contacts;
this.logger.verbose(`Chatwoot import contacts: ${this.localChatwoot.import_contacts}`);
this.localChatwoot.importContacts = data?.importContacts;
this.logger.verbose(`Chatwoot import contacts: ${this.localChatwoot.importContacts}`);
this.localChatwoot.import_messages = data?.import_messages;
this.logger.verbose(`Chatwoot import messages: ${this.localChatwoot.import_messages}`);
this.localChatwoot.importMessages = data?.importMessages;
this.logger.verbose(`Chatwoot import messages: ${this.localChatwoot.importMessages}`);
this.localChatwoot.days_limit_import_messages = data?.days_limit_import_messages;
this.logger.verbose(`Chatwoot days limit import messages: ${this.localChatwoot.days_limit_import_messages}`);
this.localChatwoot.daysLimitImportMessages = data?.daysLimitImportMessages;
this.logger.verbose(`Chatwoot days limit import messages: ${this.localChatwoot.daysLimitImportMessages}`);
this.logger.verbose('Chatwoot loaded');
}
public async setChatwoot(data: ChatwootRaw) {
public async setChatwoot(data: ChatwootDto) {
this.logger.verbose('Setting chatwoot');
await this.mongodbRepository.chatwoot.create(data, this.instanceName);
this.logger.verbose(`Chatwoot account id: ${data.account_id}`);
await this.prismaRepository.chatwoot.create({
data: {
enabled: data.enabled,
accountId: data.accountId,
token: data.token,
url: data.url,
nameInbox: data.nameInbox,
signMsg: data.signMsg,
number: data.number,
reopenConversation: data.reopenConversation,
conversationPending: data.conversationPending,
mergeBrazilContacts: data.mergeBrazilContacts,
importContacts: data.importContacts,
importMessages: data.importMessages,
daysLimitImportMessages: data.daysLimitImportMessages,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Chatwoot account id: ${data.accountId}`);
this.logger.verbose(`Chatwoot token: ${data.token}`);
this.logger.verbose(`Chatwoot url: ${data.url}`);
this.logger.verbose(`Chatwoot inbox name: ${data.name_inbox}`);
this.logger.verbose(`Chatwoot sign msg: ${data.sign_msg}`);
this.logger.verbose(`Chatwoot sign delimiter: ${data.sign_delimiter}`);
this.logger.verbose(`Chatwoot reopen conversation: ${data.reopen_conversation}`);
this.logger.verbose(`Chatwoot conversation pending: ${data.conversation_pending}`);
this.logger.verbose(`Chatwoot merge brazil contacts: ${data.merge_brazil_contacts}`);
this.logger.verbose(`Chatwoot import contacts: ${data.import_contacts}`);
this.logger.verbose(`Chatwoot import messages: ${data.import_messages}`);
this.logger.verbose(`Chatwoot days limit import messages: ${data.days_limit_import_messages}`);
this.logger.verbose(`Chatwoot inbox name: ${data.nameInbox}`);
this.logger.verbose(`Chatwoot sign msg: ${data.signMsg}`);
this.logger.verbose(`Chatwoot sign delimiter: ${data.signDelimiter}`);
this.logger.verbose(`Chatwoot reopen conversation: ${data.reopenConversation}`);
this.logger.verbose(`Chatwoot conversation pending: ${data.conversationPending}`);
this.logger.verbose(`Chatwoot merge brazil contacts: ${data.mergeBrazilContacts}`);
this.logger.verbose(`Chatwoot import contacts: ${data.importContacts}`);
this.logger.verbose(`Chatwoot import messages: ${data.importMessages}`);
this.logger.verbose(`Chatwoot days limit import messages: ${data.daysLimitImportMessages}`);
Object.assign(this.localChatwoot, { ...data, sign_delimiter: data.sign_msg ? data.sign_delimiter : null });
Object.assign(this.localChatwoot, { ...data, signDelimiter: data.signMsg ? data.signDelimiter : null });
this.clearCacheChatwoot();
@ -353,40 +453,44 @@ export class ChannelStartupService {
public async findChatwoot() {
this.logger.verbose('Finding chatwoot');
const data = await this.mongodbRepository.chatwoot.find(this.instanceName);
const data = await this.prismaRepository.chatwoot.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Chatwoot not found');
return null;
}
this.logger.verbose(`Chatwoot account id: ${data.account_id}`);
this.logger.verbose(`Chatwoot account id: ${data.accountId}`);
this.logger.verbose(`Chatwoot token: ${data.token}`);
this.logger.verbose(`Chatwoot url: ${data.url}`);
this.logger.verbose(`Chatwoot inbox name: ${data.name_inbox}`);
this.logger.verbose(`Chatwoot sign msg: ${data.sign_msg}`);
this.logger.verbose(`Chatwoot sign delimiter: ${data.sign_delimiter}`);
this.logger.verbose(`Chatwoot reopen conversation: ${data.reopen_conversation}`);
this.logger.verbose(`Chatwoot conversation pending: ${data.conversation_pending}`);
this.logger.verbose(`Chatwoot merge brazilian contacts: ${data.merge_brazil_contacts}`);
this.logger.verbose(`Chatwoot import contacts: ${data.import_contacts}`);
this.logger.verbose(`Chatwoot import messages: ${data.import_messages}`);
this.logger.verbose(`Chatwoot days limit import messages: ${data.days_limit_import_messages}`);
this.logger.verbose(`Chatwoot inbox name: ${data.nameInbox}`);
this.logger.verbose(`Chatwoot sign msg: ${data.signMsg}`);
this.logger.verbose(`Chatwoot sign delimiter: ${data.signDelimiter}`);
this.logger.verbose(`Chatwoot reopen conversation: ${data.reopenConversation}`);
this.logger.verbose(`Chatwoot conversation pending: ${data.conversationPending}`);
this.logger.verbose(`Chatwoot merge brazilian contacts: ${data.mergeBrazilContacts}`);
this.logger.verbose(`Chatwoot import contacts: ${data.importContacts}`);
this.logger.verbose(`Chatwoot import messages: ${data.importMessages}`);
this.logger.verbose(`Chatwoot days limit import messages: ${data.daysLimitImportMessages}`);
return {
enabled: data.enabled,
account_id: data.account_id,
accountId: data.accountId,
token: data.token,
url: data.url,
name_inbox: data.name_inbox,
sign_msg: data.sign_msg,
sign_delimiter: data.sign_delimiter || null,
reopen_conversation: data.reopen_conversation,
conversation_pending: data.conversation_pending,
merge_brazil_contacts: data.merge_brazil_contacts,
import_contacts: data.import_contacts,
import_messages: data.import_messages,
days_limit_import_messages: data.days_limit_import_messages,
nameInbox: data.nameInbox,
signMsg: data.signMsg,
signDelimiter: data.signDelimiter || null,
reopenConversation: data.reopenConversation,
conversationPending: data.conversationPending,
mergeBrazilContacts: data.mergeBrazilContacts,
importContacts: data.importContacts,
importMessages: data.importMessages,
daysLimitImportMessages: data.daysLimitImportMessages,
};
}
@ -400,7 +504,11 @@ export class ChannelStartupService {
public async loadWebsocket() {
this.logger.verbose('Loading websocket');
const data = await this.mongodbRepository.websocket.find(this.instanceName);
const data = await this.prismaRepository.websocket.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localWebsocket.enabled = data?.enabled;
this.logger.verbose(`Websocket enabled: ${this.localWebsocket.enabled}`);
@ -411,9 +519,16 @@ export class ChannelStartupService {
this.logger.verbose('Websocket loaded');
}
public async setWebsocket(data: WebsocketRaw) {
public async setWebsocket(data: WebsocketDto) {
this.logger.verbose('Setting websocket');
await this.mongodbRepository.websocket.create(data, this.instanceName);
await this.prismaRepository.websocket.create({
data: {
enabled: data.enabled,
events: data.events,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Websocket events: ${data.events}`);
Object.assign(this.localWebsocket, data);
this.logger.verbose('Websocket set');
@ -421,7 +536,11 @@ export class ChannelStartupService {
public async findWebsocket() {
this.logger.verbose('Finding websocket');
const data = await this.mongodbRepository.websocket.find(this.instanceName);
const data = await this.prismaRepository.websocket.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Websocket not found');
@ -429,15 +548,16 @@ export class ChannelStartupService {
}
this.logger.verbose(`Websocket events: ${data.events}`);
return {
enabled: data.enabled,
events: data.events,
};
return data;
}
public async loadRabbitmq() {
this.logger.verbose('Loading rabbitmq');
const data = await this.mongodbRepository.rabbitmq.find(this.instanceName);
const data = await this.prismaRepository.rabbitmq.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localRabbitmq.enabled = data?.enabled;
this.logger.verbose(`Rabbitmq enabled: ${this.localRabbitmq.enabled}`);
@ -448,9 +568,16 @@ export class ChannelStartupService {
this.logger.verbose('Rabbitmq loaded');
}
public async setRabbitmq(data: RabbitmqRaw) {
public async setRabbitmq(data: RabbitmqDto) {
this.logger.verbose('Setting rabbitmq');
await this.mongodbRepository.rabbitmq.create(data, this.instanceName);
await this.prismaRepository.rabbitmq.create({
data: {
enabled: data.enabled,
events: data.events,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Rabbitmq events: ${data.events}`);
Object.assign(this.localRabbitmq, data);
this.logger.verbose('Rabbitmq set');
@ -458,7 +585,11 @@ export class ChannelStartupService {
public async findRabbitmq() {
this.logger.verbose('Finding rabbitmq');
const data = await this.mongodbRepository.rabbitmq.find(this.instanceName);
const data = await this.prismaRepository.rabbitmq.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Rabbitmq not found');
@ -466,10 +597,7 @@ export class ChannelStartupService {
}
this.logger.verbose(`Rabbitmq events: ${data.events}`);
return {
enabled: data.enabled,
events: data.events,
};
return data;
}
public async removeRabbitmqQueues() {
@ -482,7 +610,11 @@ export class ChannelStartupService {
public async loadSqs() {
this.logger.verbose('Loading sqs');
const data = await this.mongodbRepository.sqs.find(this.instanceName);
const data = await this.prismaRepository.sqs.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localSqs.enabled = data?.enabled;
this.logger.verbose(`Sqs enabled: ${this.localSqs.enabled}`);
@ -493,9 +625,16 @@ export class ChannelStartupService {
this.logger.verbose('Sqs loaded');
}
public async setSqs(data: SqsRaw) {
public async setSqs(data: SqsDto) {
this.logger.verbose('Setting sqs');
await this.mongodbRepository.sqs.create(data, this.instanceName);
await this.prismaRepository.sqs.create({
data: {
enabled: data.enabled,
events: data.events,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Sqs events: ${data.events}`);
Object.assign(this.localSqs, data);
this.logger.verbose('Sqs set');
@ -503,7 +642,11 @@ export class ChannelStartupService {
public async findSqs() {
this.logger.verbose('Finding sqs');
const data = await this.mongodbRepository.sqs.find(this.instanceName);
const data = await this.prismaRepository.sqs.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Sqs not found');
@ -511,10 +654,7 @@ export class ChannelStartupService {
}
this.logger.verbose(`Sqs events: ${data.events}`);
return {
enabled: data.enabled,
events: data.events,
};
return data;
}
public async removeSqsQueues() {
@ -527,7 +667,14 @@ export class ChannelStartupService {
public async loadTypebot() {
this.logger.verbose('Loading typebot');
const data = await this.mongodbRepository.typebot.find(this.instanceName);
const data = await this.prismaRepository.typebot.findUnique({
where: {
instanceId: this.instanceId,
},
include: {
sessions: true,
},
});
this.localTypebot.enabled = data?.enabled;
this.logger.verbose(`Typebot enabled: ${this.localTypebot.enabled}`);
@ -541,39 +688,66 @@ export class ChannelStartupService {
this.localTypebot.expire = data?.expire;
this.logger.verbose(`Typebot expire: ${this.localTypebot.expire}`);
this.localTypebot.keyword_finish = data?.keyword_finish;
this.logger.verbose(`Typebot keyword_finish: ${this.localTypebot.keyword_finish}`);
this.localTypebot.keywordFinish = data?.keywordFinish;
this.logger.verbose(`Typebot keywordFinish: ${this.localTypebot.keywordFinish}`);
this.localTypebot.delay_message = data?.delay_message;
this.logger.verbose(`Typebot delay_message: ${this.localTypebot.delay_message}`);
this.localTypebot.delayMessage = data?.delayMessage;
this.logger.verbose(`Typebot delayMessage: ${this.localTypebot.delayMessage}`);
this.localTypebot.unknown_message = data?.unknown_message;
this.logger.verbose(`Typebot unknown_message: ${this.localTypebot.unknown_message}`);
this.localTypebot.unknownMessage = data?.unknownMessage;
this.logger.verbose(`Typebot unknownMessage: ${this.localTypebot.unknownMessage}`);
this.localTypebot.listening_from_me = data?.listening_from_me;
this.logger.verbose(`Typebot listening_from_me: ${this.localTypebot.listening_from_me}`);
this.localTypebot.listeningFromMe = data?.listeningFromMe;
this.logger.verbose(`Typebot listeningFromMe: ${this.localTypebot.listeningFromMe}`);
this.localTypebot.sessions = data?.sessions;
this.logger.verbose('Typebot loaded');
}
public async setTypebot(data: TypebotRaw) {
public async setTypebot(data: TypebotDto) {
this.logger.verbose('Setting typebot');
await this.mongodbRepository.typebot.create(data, this.instanceName);
const typebot = await this.prismaRepository.typebot.create({
data: {
enabled: data.enabled,
url: data.url,
typebot: data.typebot,
expire: data.expire,
keywordFinish: data.keywordFinish,
delayMessage: data.delayMessage,
unknownMessage: data.unknownMessage,
listeningFromMe: data.listeningFromMe,
instanceId: this.instanceId,
},
});
await this.prismaRepository.typebotSession.deleteMany({
where: {
typebotId: typebot.id,
},
});
this.logger.verbose(`Typebot typebot: ${data.typebot}`);
this.logger.verbose(`Typebot expire: ${data.expire}`);
this.logger.verbose(`Typebot keyword_finish: ${data.keyword_finish}`);
this.logger.verbose(`Typebot delay_message: ${data.delay_message}`);
this.logger.verbose(`Typebot unknown_message: ${data.unknown_message}`);
this.logger.verbose(`Typebot listening_from_me: ${data.listening_from_me}`);
this.logger.verbose(`Typebot keywordFinish: ${data.keywordFinish}`);
this.logger.verbose(`Typebot delayMessage: ${data.delayMessage}`);
this.logger.verbose(`Typebot unknownMessage: ${data.unknownMessage}`);
this.logger.verbose(`Typebot listeningFromMe: ${data.listeningFromMe}`);
Object.assign(this.localTypebot, data);
this.logger.verbose('Typebot set');
}
public async findTypebot() {
this.logger.verbose('Finding typebot');
const data = await this.mongodbRepository.typebot.find(this.instanceName);
const data = await this.prismaRepository.typebot.findUnique({
where: {
instanceId: this.instanceId,
},
include: {
sessions: true,
},
});
if (!data) {
this.logger.verbose('Typebot not found');
@ -585,48 +759,69 @@ export class ChannelStartupService {
url: data.url,
typebot: data.typebot,
expire: data.expire,
keyword_finish: data.keyword_finish,
delay_message: data.delay_message,
unknown_message: data.unknown_message,
listening_from_me: data.listening_from_me,
keywordFinish: data.keywordFinish,
delayMessage: data.delayMessage,
unknownMessage: data.unknownMessage,
listeningFromMe: data.listeningFromMe,
sessions: data.sessions,
};
}
public async loadProxy() {
this.logger.verbose('Loading proxy');
const data = await this.mongodbRepository.proxy.find(this.instanceName);
const data = await this.prismaRepository.proxy.findUnique({
where: {
instanceId: this.instanceId,
},
});
this.localProxy.enabled = data?.enabled;
this.logger.verbose(`Proxy enabled: ${this.localProxy.enabled}`);
this.localProxy.proxy = data?.proxy;
this.localProxy.proxy = {
host: data?.host,
port: `${data?.port}`,
username: data?.username,
password: data?.password,
};
this.logger.verbose(`Proxy proxy: ${this.localProxy.proxy?.host}`);
this.logger.verbose('Proxy loaded');
}
public async setProxy(data: ProxyRaw) {
public async setProxy(data: ProxyDto) {
this.logger.verbose('Setting proxy');
await this.mongodbRepository.proxy.create(data, this.instanceName);
this.logger.verbose(`Proxy proxy: ${data.proxy}`);
await this.prismaRepository.proxy.create({
data: {
enabled: data.enabled,
host: data.host,
port: data.port,
username: data.username,
password: data.password,
instanceId: this.instanceId,
},
});
this.logger.verbose(`Proxy proxy: ${data.host}`);
Object.assign(this.localProxy, data);
this.logger.verbose('Proxy set');
}
public async findProxy() {
this.logger.verbose('Finding proxy');
const data = await this.mongodbRepository.proxy.find(this.instanceName);
const data = await this.prismaRepository.proxy.findUnique({
where: {
instanceId: this.instanceId,
},
});
if (!data) {
this.logger.verbose('Proxy not found');
throw new NotFoundException('Proxy not found');
}
return {
enabled: data.enabled,
proxy: data.proxy,
};
return data;
}
public async sendDataWebhook<T = any>(event: Events, data: T, local = true) {
@ -646,7 +841,11 @@ export class ChannelStartupService {
const now = localISOTime;
const expose = this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES;
const tokenStore = await this.mongodbRepository.auth.find(this.instanceName);
const tokenStore = await this.prismaRepository.auth.findFirst({
where: {
instanceId: this.instanceId,
},
});
const instanceApikey = tokenStore?.apikey || 'Apikey not found';
if (rabbitmqEnabled) {
@ -930,7 +1129,7 @@ export class ChannelStartupService {
this.logger.verbose('Sending data to webhook local');
let baseURL: string;
if (this.localWebhook.webhook_by_events) {
if (this.localWebhook.webhookByEvents) {
baseURL = `${this.localWebhook.url}/${transformedWe}`;
} else {
baseURL = this.localWebhook.url;
@ -1167,61 +1366,63 @@ export class ChannelStartupService {
return `${number}@s.whatsapp.net`;
}
public async fetchContacts(query: ContactQuery) {
public async fetchContacts(query: any) {
this.logger.verbose('Fetching contacts');
if (query?.where) {
query.where.owner = this.instance.name;
if (query.where?.id) {
query.where.id = this.createJid(query.where.id);
query.where.remoteJid = this.instance.name;
if (query.where?.remoteJid) {
query.where.remoteJid = this.createJid(query.where.remoteJid);
}
} else {
query = {
where: {
owner: this.instance.name,
instanceId: this.instanceId,
},
};
}
return await this.mongodbRepository.contact.find(query);
return await this.prismaRepository.contact.findMany({
where: query.where,
});
}
public async fetchMessages(query: MessageQuery) {
public async fetchMessages(query: any) {
this.logger.verbose('Fetching messages');
if (query?.where) {
if (query.where?.key?.remoteJid) {
query.where.key.remoteJid = this.createJid(query.where.key.remoteJid);
}
query.where.owner = this.instance.name;
query.where.instanceId = this.instanceId;
} else {
query = {
where: {
owner: this.instance.name,
instanceId: this.instanceId,
},
limit: query?.limit,
};
}
return await this.mongodbRepository.message.find(query);
return await this.prismaRepository.message.findMany(query);
}
public async fetchStatusMessage(query: MessageUpQuery) {
public async fetchStatusMessage(query: any) {
this.logger.verbose('Fetching status messages');
if (query?.where) {
if (query.where?.remoteJid) {
query.where.remoteJid = this.createJid(query.where.remoteJid);
}
query.where.owner = this.instance.name;
query.where.instanceId = this.instanceId;
} else {
query = {
where: {
owner: this.instance.name,
instanceId: this.instanceId,
},
limit: query?.limit,
};
}
return await this.mongodbRepository.messageUpdate.find(query);
return await this.prismaRepository.messageUpdate.findMany(query);
}
public async fetchChats() {
this.logger.verbose('Fetching chats');
return await this.mongodbRepository.chat.find({ where: { owner: this.instance.name } });
return await this.prismaRepository.chat.findMany({ where: { instanceId: this.instanceId } });
}
}

File diff suppressed because it is too large Load Diff

View File

@ -22,10 +22,8 @@ import {
SendTemplateDto,
SendTextDto,
} from '../../dto/sendMessage.dto';
import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../../models';
import { ProviderFiles } from '../../provider/sessions';
import { MongodbRepository } from '../../repository/mongodb/repository.manager';
import { PrismaRepository } from '../../repository/prisma/repository.service';
import { PrismaRepository } from '../../repository/repository.service';
import { Events, wa } from '../../types/wa.types';
import { CacheService } from './../cache.service';
import { ChannelStartupService } from './../channel.service';
@ -34,14 +32,13 @@ export class BusinessStartupService extends ChannelStartupService {
constructor(
public readonly configService: ConfigService,
public readonly eventEmitter: EventEmitter2,
public readonly mongodbRepository: MongodbRepository,
public readonly prismaRepository: PrismaRepository,
public readonly cache: CacheService,
public readonly chatwootCache: CacheService,
public readonly baileysCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {
super(configService, eventEmitter, mongodbRepository, prismaRepository, chatwootCache);
super(configService, eventEmitter, prismaRepository, chatwootCache);
this.logger.verbose('BusinessStartupService initialized');
this.cleanStore();
}
@ -318,9 +315,9 @@ export class BusinessStartupService extends ChannelStartupService {
return messageType;
}
protected async messageHandle(received: any, database: Database, settings: SettingsRaw) {
protected async messageHandle(received: any, database: Database, settings: any) {
try {
let messageRaw: MessageRaw;
let messageRaw: any;
let pushName: any;
if (received.contacts) pushName = received.contacts[0].profile.name;
@ -398,11 +395,11 @@ export class BusinessStartupService extends ChannelStartupService {
};
}
if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') {
if (this.localSettings.readMessages && received.key.id !== 'status@broadcast') {
// await this.client.readMessages([received.key]);
}
if (this.localSettings.read_status && received.key.id === 'status@broadcast') {
if (this.localSettings.readStatus && received.key.id === 'status@broadcast') {
// await this.client.readMessages([received.key]);
}
@ -433,7 +430,7 @@ export class BusinessStartupService extends ChannelStartupService {
);
if (this.localTypebot.enabled || typebotSessionRemoteJid) {
if (!(this.localTypebot.listening_from_me === false && key.fromMe === true)) {
if (!(this.localTypebot.listeningFromMe === false && key.fromMe === true)) {
if (messageRaw.messageType !== 'reactionMessage')
await this.typebotService.sendTypebot(
{ instanceName: this.instance.name },
@ -444,32 +441,34 @@ export class BusinessStartupService extends ChannelStartupService {
}
this.logger.verbose('Inserting message in database');
await this.mongodbRepository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE);
this.logger.verbose('Verifying contact from message');
const contact = await this.mongodbRepository.contact.find({
where: { owner: this.instance.name, id: key.remoteJid },
await this.prismaRepository.message.create({
data: messageRaw,
});
const contactRaw: ContactRaw = {
id: received.contacts[0].profile.phone,
this.logger.verbose('Verifying contact from message');
const contact = await this.prismaRepository.contact.findFirst({
where: { instanceId: this.instanceId, remoteJid: key.remoteJid },
});
const contactRaw: any = {
remoteJid: received.contacts[0].profile.phone,
pushName,
//profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl,
owner: this.instance.name,
// profilePicUrl: '',
instanceId: this.instanceId,
};
if (contactRaw.id === 'status@broadcast') {
if (contactRaw.remoteJid === 'status@broadcast') {
this.logger.verbose('Contact is status@broadcast');
return;
}
if (contact?.length) {
if (contact) {
this.logger.verbose('Contact found in database');
const contactRaw: ContactRaw = {
id: received.contacts[0].profile.phone,
const contactRaw: any = {
remoteJid: received.contacts[0].profile.phone,
pushName,
//profilePictureUrl: (await this.profilePicture(received.key.remoteJid)).profilePictureUrl,
owner: this.instance.name,
// profilePicUrl: '',
instanceId: this.instanceId,
};
this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE');
@ -484,7 +483,10 @@ export class BusinessStartupService extends ChannelStartupService {
}
this.logger.verbose('Updating contact in database');
await this.mongodbRepository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS);
await this.prismaRepository.contact.updateMany({
where: { remoteJid: contact.remoteJid },
data: contactRaw,
});
return;
}
@ -494,7 +496,9 @@ export class BusinessStartupService extends ChannelStartupService {
this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw);
this.logger.verbose('Inserting contact in database');
this.mongodbRepository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS);
this.prismaRepository.contact.create({
data: contactRaw,
});
}
this.logger.verbose('Event received: messages.update');
if (received.statuses) {
@ -513,27 +517,38 @@ export class BusinessStartupService extends ChannelStartupService {
if (item.status === 'read' && !key.fromMe) return;
const findMessage = await this.prismaRepository.message.findFirst({
where: {
instanceId: this.instanceId,
key: {
path: ['id'],
equals: key.id,
},
},
});
if (item.message === null && item.status === undefined) {
this.logger.verbose('Message deleted');
this.logger.verbose('Sending data to webhook in event MESSAGE_DELETE');
this.sendDataWebhook(Events.MESSAGES_DELETE, key);
const message: MessageUpdateRaw = {
...key,
const message: any = {
messageId: findMessage.id,
keyId: key.id,
remoteJid: key.remoteJid,
fromMe: key.fromMe,
participant: key?.remoteJid,
status: 'DELETED',
datetime: Date.now(),
owner: this.instance.name,
dateTime: Date.now(),
};
this.logger.verbose(message);
this.logger.verbose('Inserting message in database');
await this.mongodbRepository.messageUpdate.insert(
[message],
this.instance.name,
database.SAVE_DATA.MESSAGE_UPDATE,
);
await this.prismaRepository.messageUpdate.create({
data: message,
});
if (this.localChatwoot.enabled) {
this.chatwootService.eventWhatsapp(
@ -546,11 +561,14 @@ export class BusinessStartupService extends ChannelStartupService {
return;
}
const message: MessageUpdateRaw = {
...key,
const message: any = {
messageId: findMessage.id,
keyId: key.id,
remoteJid: key.remoteJid,
fromMe: key.fromMe,
participant: key?.remoteJid,
status: item.status.toUpperCase(),
datetime: Date.now(),
owner: this.instance.name,
dateTime: Date.now(),
};
this.logger.verbose(message);
@ -559,11 +577,9 @@ export class BusinessStartupService extends ChannelStartupService {
this.sendDataWebhook(Events.MESSAGES_UPDATE, message);
this.logger.verbose('Inserting message in database');
this.mongodbRepository.messageUpdate.insert(
[message],
this.instance.name,
database.SAVE_DATA.MESSAGE_UPDATE,
);
await this.prismaRepository.messageUpdate.create({
data: message,
});
}
}
}
@ -848,13 +864,13 @@ export class BusinessStartupService extends ChannelStartupService {
console.log(content);
const messageRaw: MessageRaw = {
const messageRaw: any = {
key: { fromMe: true, id: messageSent?.messages[0]?.id, remoteJid: this.createJid(number) },
//pushName: messageSent.pushName,
message: this.convertMessageToRaw(message, content),
messageType: this.renderMessageType(content.type),
messageTimestamp: (messageSent?.messages[0]?.timestamp as number) || Math.round(new Date().getTime() / 1000),
owner: this.instance.name,
instanceId: this.instanceId,
//ource: getDevice(messageSent.key.id),
};
@ -868,11 +884,9 @@ export class BusinessStartupService extends ChannelStartupService {
}
this.logger.verbose('Inserting message in database');
await this.mongodbRepository.message.insert(
[messageRaw],
this.instance.name,
this.configService.get<Database>('DATABASE').SAVE_DATA.NEW_MESSAGE,
);
await this.prismaRepository.message.create({
data: messageRaw,
});
return messageRaw;
} catch (error) {

View File

@ -1,7 +1,8 @@
import { Integration } from '@prisma/client';
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { IntegrationDto } from '../dto/integration.dto';
import { IntegrationRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class IntegrationService {
@ -16,19 +17,19 @@ export class IntegrationService {
return { integration: { ...instance, integration: data } };
}
public async find(instance: InstanceDto): Promise<IntegrationRaw> {
public async find(instance: InstanceDto): Promise<Integration> {
try {
this.logger.verbose('find integration: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findIntegration();
if (Object.keys(result).length === 0) {
this.create(instance, { integration: 'WHATSAPP-BAILEYS', number: '', token: '' });
return { integration: 'WHATSAPP-BAILEYS', number: '', token: '' };
return null;
}
return result;
} catch (error) {
return { integration: '', number: '', token: '' };
return null;
}
}
}

View File

@ -1,8 +1,6 @@
import { execSync } from 'child_process';
import EventEmitter2 from 'eventemitter2';
import { existsSync, mkdirSync, opendirSync, readdirSync, rmSync, writeFileSync } from 'fs';
import { Db } from 'mongodb';
import { Collection } from 'mongoose';
import { join } from 'path';
import {
@ -17,21 +15,8 @@ import {
import { Logger } from '../../config/logger.config';
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
import { NotFoundException } from '../../exceptions';
import {
AuthModel,
ChatwootModel,
ContactModel,
LabelModel,
ProxyModel,
RabbitmqModel,
SettingsModel,
TypebotModel,
WebhookModel,
WebsocketModel,
} from '../models';
import { ProviderFiles } from '../provider/sessions';
import { MongodbRepository } from '../repository/mongodb/repository.manager';
import { PrismaRepository } from '../repository/prisma/repository.service';
import { PrismaRepository } from '../repository/repository.service';
import { Integration } from '../types/wa.types';
import { CacheService } from './cache.service';
import { BaileysStartupService } from './channels/whatsapp.baileys.service';
@ -41,12 +26,11 @@ export class WAMonitoringService {
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly configService: ConfigService,
private readonly monogodbRepository: MongodbRepository,
private readonly primaRepository: PrismaRepository,
private readonly prismaRepository: PrismaRepository,
private readonly providerFiles: ProviderFiles,
private readonly cache: CacheService,
private readonly chatwootCache: CacheService,
private readonly baileysCache: CacheService,
private readonly providerFiles: ProviderFiles,
) {
this.logger.verbose('instance created');
@ -55,17 +39,11 @@ export class WAMonitoringService {
Object.assign(this.db, configService.get<Database>('DATABASE'));
Object.assign(this.redis, configService.get<CacheConf>('CACHE'));
this.dbInstance = this.db.ENABLED
? this.monogodbRepository.mongodbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
: undefined;
}
private readonly db: Partial<Database> = {};
private readonly redis: Partial<CacheConf> = {};
private dbInstance: Db;
private readonly logger = new Logger(WAMonitoringService.name);
public readonly waInstances: Record<string, BaileysStartupService | BusinessStartupService> = {};
@ -126,7 +104,7 @@ export class WAMonitoringService {
if (findIntegration) {
integration = {
...findIntegration,
webhook_wa_business: `${urlServer}/webhook/whatsapp/${encodeURIComponent(key)}`,
webhookWaBusiness: `${urlServer}/webhook/whatsapp/${encodeURIComponent(key)}`,
};
}
@ -136,7 +114,7 @@ export class WAMonitoringService {
const instanceData = {
instance: {
instanceName: key,
instanceId: (await this.monogodbRepository.auth.find(key))?.instanceId,
instanceId: this.waInstances[key].instanceId,
owner: value.wuid,
profileName: (await value.getProfileName()) || 'not loaded',
profilePictureUrl: value.profilePictureUrl,
@ -148,7 +126,11 @@ export class WAMonitoringService {
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.monogodbRepository.auth.find(key))?.apikey;
instanceData.instance['apikey'] = (
await this.prismaRepository.auth.findFirst({
where: { instanceId: this.waInstances[key].instanceId },
})
)?.apikey;
instanceData.instance['chatwoot'] = chatwoot;
@ -162,7 +144,7 @@ export class WAMonitoringService {
const instanceData = {
instance: {
instanceName: key,
instanceId: (await this.monogodbRepository.auth.find(key))?.instanceId,
instanceId: this.waInstances[key].instanceId,
status: value.connectionStatus.state,
},
};
@ -170,7 +152,11 @@ export class WAMonitoringService {
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.monogodbRepository.auth.find(key))?.apikey;
instanceData.instance['apikey'] = (
await this.prismaRepository.auth.findFirst({
where: { instanceId: this.waInstances[key].instanceId },
})
)?.apikey;
instanceData.instance['chatwoot'] = chatwoot;
@ -194,12 +180,14 @@ export class WAMonitoringService {
this.logger.verbose('get instance info');
let instanceName: string;
if (instanceId) {
instanceName = await this.monogodbRepository.auth.findInstanceNameById(instanceId);
instanceName = await this.prismaRepository.instance.findFirst({ where: { id: instanceId } }).then((r) => r?.name);
if (!instanceName) {
throw new NotFoundException(`Instance "${instanceId}" not found`);
}
} else if (number) {
instanceName = await this.monogodbRepository.auth.findInstanceNameByNumber(number);
const id = await this.prismaRepository.integration.findFirst({ where: { number } }).then((r) => r?.instanceId);
instanceName = await this.prismaRepository.instance.findFirst({ where: { id } }).then((r) => r?.name);
if (!instanceName) {
throw new NotFoundException(`Instance "${number}" not found`);
}
@ -216,50 +204,12 @@ export class WAMonitoringService {
return this.instanceInfo(instanceName);
}
private delInstanceFiles() {
this.logger.verbose('cron to delete instance files started');
setInterval(async () => {
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
const collections = await this.dbInstance.collections();
collections.forEach(async (collection) => {
const name = collection.namespace.replace(/^[\w-]+./, '');
await this.dbInstance.collection(name).deleteMany({
$or: [{ _id: { $regex: /^app.state.*/ } }, { _id: { $regex: /^session-.*/ } }],
});
this.logger.verbose('instance files deleted: ' + name);
});
} else if (!this.redis.REDIS.ENABLED && !this.redis.REDIS.SAVE_INSTANCES) {
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) {
if (dirent.isDirectory()) {
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
encoding: 'utf-8',
});
files.forEach(async (file) => {
if (file.match(/^app.state.*/) || file.match(/^session-.*/)) {
rmSync(join(INSTANCE_DIR, dirent.name, file), {
recursive: true,
force: true,
});
}
});
this.logger.verbose('instance files deleted: ' + dirent.name);
}
}
}
}, 3600 * 1000 * 2);
}
public async cleaningUp(instanceName: string) {
this.logger.verbose('cleaning up instance: ' + instanceName);
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
this.logger.verbose('cleaning up instance in database: ' + instanceName);
await this.monogodbRepository.mongodbServer.connect();
const collections: any[] = await this.dbInstance.collections();
if (collections.length > 0) {
await this.dbInstance.dropCollection(instanceName);
}
// TODO: deleta instancia
return;
}
@ -304,20 +254,7 @@ export class WAMonitoringService {
this.logger.verbose('cleaning store database instance: ' + instanceName);
if (this.db.PROVIDER === 'mongodb') {
await AuthModel.deleteMany({ _id: instanceName });
await WebhookModel.deleteMany({ _id: instanceName });
await ChatwootModel.deleteMany({ _id: instanceName });
await ProxyModel.deleteMany({ _id: instanceName });
await RabbitmqModel.deleteMany({ _id: instanceName });
await TypebotModel.deleteMany({ _id: instanceName });
await WebsocketModel.deleteMany({ _id: instanceName });
await SettingsModel.deleteMany({ _id: instanceName });
await LabelModel.deleteMany({ owner: instanceName });
await ContactModel.deleteMany({ owner: instanceName });
return;
}
// TODO: deleta dados da instancia
}
public async loadInstance() {
@ -329,8 +266,7 @@ export class WAMonitoringService {
} else if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) {
await this.loadInstancesFromRedis();
} else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
if (this.db.PROVIDER === 'mongodb') await this.loadInstancesFromDatabaseMongoDB();
else if (this.db.PROVIDER === 'postgresql') await this.loadInstancesFromDatabasePostgres();
await this.loadInstancesFromDatabasePostgres();
} else {
await this.loadInstancesFromFiles();
}
@ -345,9 +281,22 @@ export class WAMonitoringService {
try {
const msgParsed = JSON.parse(JSON.stringify(data));
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
await this.monogodbRepository.mongodbServer.connect();
await this.dbInstance.collection(data.instanceName).replaceOne({ _id: 'integration' }, msgParsed, {
upsert: true,
await this.prismaRepository.instance.create({
data: {
id: data.instanceId,
name: data.instanceName,
connectionStatus: 'close',
},
});
console.log('saveInstance');
await this.prismaRepository.integration.create({
data: {
instanceId: data.instanceId,
integration: data.integration,
number: data.number,
token: data.token,
},
});
} else {
const path = join(INSTANCE_DIR, data.instanceName);
@ -359,16 +308,18 @@ export class WAMonitoringService {
}
}
private async setInstance(name: string) {
const integration = await this.monogodbRepository.integration.find(name);
private async setInstance(id: string, name: string) {
console.log('setInstance', name);
const integration = await this.prismaRepository.integration.findUnique({
where: { instanceId: id },
});
let instance: BaileysStartupService | BusinessStartupService;
if (integration && integration.integration === Integration.WHATSAPP_BUSINESS) {
instance = new BusinessStartupService(
this.configService,
this.eventEmitter,
this.monogodbRepository,
this.primaRepository,
this.prismaRepository,
this.cache,
this.chatwootCache,
this.baileysCache,
@ -376,12 +327,12 @@ export class WAMonitoringService {
);
instance.instanceName = name;
instance.instanceId = id;
} else {
instance = new BaileysStartupService(
this.configService,
this.eventEmitter,
this.monogodbRepository,
this.primaRepository,
this.prismaRepository,
this.cache,
this.chatwootCache,
this.baileysCache,
@ -389,9 +340,10 @@ export class WAMonitoringService {
);
instance.instanceName = name;
instance.instanceId = id;
if (!integration) {
await instance.setIntegration({ integration: Integration.WHATSAPP_BAILEYS });
await instance.setIntegration({ integration: Integration.WHATSAPP_BAILEYS, number: '', token: '' });
}
}
@ -403,45 +355,34 @@ export class WAMonitoringService {
}
private async loadInstancesFromRedis() {
console.log('loadInstancesFromRedis');
this.logger.verbose('Redis enabled');
const keys = await this.cache.keys();
if (keys?.length > 0) {
this.logger.verbose('Reading instance keys and setting instances');
await Promise.all(keys.map((k) => this.setInstance(k.split(':')[2])));
await Promise.all(keys.map((k) => this.setInstance(k.split(':')[1], k.split(':')[2])));
} else {
this.logger.verbose('No instance keys found');
}
}
private async loadInstancesFromDatabaseMongoDB() {
this.logger.verbose('Database enabled');
await this.monogodbRepository.mongodbServer.connect();
const collections: any[] = await this.dbInstance.collections();
await this.deleteTempInstances(collections);
if (collections.length > 0) {
this.logger.verbose('Reading collections and setting instances');
await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, ''))));
} else {
this.logger.verbose('No collections found');
}
}
private async loadInstancesFromDatabasePostgres() {
console.log('loadInstancesFromDatabasePostgres');
this.logger.verbose('Database enabled');
await this.primaRepository.onModuleInit();
const instances = await this.primaRepository.instance.findMany();
const instances = await this.prismaRepository.instance.findMany();
console.log('instances', instances);
if (instances.length === 0) {
this.logger.verbose('No instances found');
return;
}
await Promise.all(instances.map(async (instance) => this.setInstance(instance.name)));
await Promise.all(instances.map(async (instance) => this.setInstance(instance.id, instance.name)));
}
private async loadInstancesFromProvider() {
console.log('loadInstancesFromProvider');
this.logger.verbose('Provider in files enabled');
const [instances] = await this.providerFiles.allInstances();
@ -450,10 +391,11 @@ export class WAMonitoringService {
return;
}
await Promise.all(instances?.data?.map(async (instanceName: string) => this.setInstance(instanceName)));
await Promise.all(instances?.data?.map(async (instanceName: string) => this.setInstance('', instanceName)));
}
private async loadInstancesFromFiles() {
console.log('loadInstancesFromFiles');
this.logger.verbose('Store in files enabled');
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
const instanceDirs = [];
@ -474,7 +416,7 @@ export class WAMonitoringService {
if (files.length === 0) {
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
} else {
await this.setInstance(instanceName);
await this.setInstance('', instanceName);
}
}),
);
@ -534,25 +476,42 @@ export class WAMonitoringService {
});
}
private async deleteTempInstances(collections: Collection<Document>[]) {
private delInstanceFiles() {
this.logger.verbose('cron to delete instance files started');
setInterval(async () => {
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) {
if (dirent.isDirectory()) {
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
encoding: 'utf-8',
});
files.forEach(async (file) => {
if (file.match(/^app.state.*/) || file.match(/^session-.*/)) {
rmSync(join(INSTANCE_DIR, dirent.name, file), {
recursive: true,
force: true,
});
}
});
this.logger.verbose('instance files deleted: ' + dirent.name);
}
}
}, 3600 * 1000 * 2);
}
private async deleteTempInstances() {
const shouldDelete = this.configService.get<boolean>('DEL_TEMP_INSTANCES');
if (!shouldDelete) {
this.logger.verbose('Temp instances deletion is disabled');
return;
}
this.logger.verbose('Cleaning up temp instances');
const auths = await this.monogodbRepository.auth.list();
if (auths.length === 0) {
this.logger.verbose('No temp instances found');
return;
}
const instancesClosed = await this.prismaRepository.instance.findMany({ where: { connectionStatus: 'close' } });
let tempInstances = 0;
auths.forEach((auth) => {
if (collections.find((coll) => coll.namespace.replace(/^[\w-]+\./, '') === auth._id)) {
return;
}
instancesClosed.forEach((instance) => {
tempInstances++;
this.eventEmitter.emit('remove.instance', auth._id, 'inner');
this.eventEmitter.emit('remove.instance', instance.id, 'inner');
});
this.logger.verbose('Temp instances removed: ' + tempInstances);
}

View File

@ -1,7 +1,8 @@
import { Proxy } from '@prisma/client';
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { ProxyDto } from '../dto/proxy.dto';
import { ProxyRaw } from '../models';
import { WAMonitoringService } from './monitor.service';
export class ProxyService {
@ -16,7 +17,7 @@ export class ProxyService {
return { proxy: { ...instance, proxy: data } };
}
public async find(instance: InstanceDto): Promise<ProxyRaw> {
public async find(instance: InstanceDto): Promise<Proxy> {
try {
this.logger.verbose('find proxy: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findProxy();
@ -27,7 +28,7 @@ export class ProxyService {
return result;
} catch (error) {
return { enabled: false, proxy: null };
return null;
}
}
}

View File

@ -26,7 +26,7 @@ export class SettingsService {
return result;
} catch (error) {
return { reject_call: false, msg_call: '', groups_ignore: true };
return null;
}
}
}

View File

@ -1,3 +1,5 @@
import { Webhook } from '@prisma/client';
import { Logger } from '../../config/logger.config';
import { InstanceDto } from '../dto/instance.dto';
import { WebhookDto } from '../dto/webhook.dto';
@ -15,7 +17,7 @@ export class WebhookService {
return { webhook: { ...instance, webhook: data } };
}
public async find(instance: InstanceDto): Promise<WebhookDto> {
public async find(instance: InstanceDto): Promise<Webhook> {
try {
this.logger.verbose('find webhook: ' + instance.instanceName);
const result = await this.waMonitor.waInstances[instance.instanceName].findWebhook();
@ -26,7 +28,7 @@ export class WebhookService {
return result;
} catch (error) {
return { enabled: false, url: '', events: [], webhook_by_events: false, webhook_base64: false };
return null;
}
}
}

View File

@ -1,4 +1,6 @@
/* eslint-disable @typescript-eslint/no-namespace */
import { TypebotSession } from '@prisma/client';
import { JsonValue } from '@prisma/client/runtime/library';
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
export enum Events {
@ -41,6 +43,7 @@ export declare namespace wa {
code?: string;
};
export type Instance = {
id?: string;
qrcode?: QrCode;
pairingCode?: string;
authState?: { state: AuthenticationState; saveCreds: () => void };
@ -53,50 +56,51 @@ export declare namespace wa {
export type LocalWebHook = {
enabled?: boolean;
url?: string;
events?: string[];
webhook_by_events?: boolean;
webhook_base64?: boolean;
events?: JsonValue;
webhookByEvents?: boolean;
webhookBase64?: boolean;
};
export type LocalChatwoot = {
enabled?: boolean;
account_id?: string;
accountId?: string;
token?: string;
url?: string;
name_inbox?: string;
sign_msg?: boolean;
nameInbox?: string;
signMsg?: boolean;
signDelimiter?: string;
number?: string;
reopen_conversation?: boolean;
conversation_pending?: boolean;
merge_brazil_contacts?: boolean;
import_contacts?: boolean;
import_messages?: boolean;
days_limit_import_messages?: number;
reopenConversation?: boolean;
conversationPending?: boolean;
mergeBrazilContacts?: boolean;
importContacts?: boolean;
importMessages?: boolean;
daysLimitImportMessages?: number;
};
export type LocalSettings = {
reject_call?: boolean;
msg_call?: string;
groups_ignore?: boolean;
always_online?: boolean;
read_messages?: boolean;
read_status?: boolean;
sync_full_history?: boolean;
rejectCall?: boolean;
msgCall?: string;
groupsIgnore?: boolean;
alwaysOnline?: boolean;
readMessages?: boolean;
readStatus?: boolean;
syncFullHistory?: boolean;
};
export type LocalWebsocket = {
enabled?: boolean;
events?: string[];
events?: JsonValue;
};
export type LocalRabbitmq = {
enabled?: boolean;
events?: string[];
events?: JsonValue;
};
export type LocalSqs = {
enabled?: boolean;
events?: string[];
events?: JsonValue;
};
type Session = {
@ -110,11 +114,11 @@ export declare namespace wa {
url?: string;
typebot?: string;
expire?: number;
keyword_finish?: string;
delay_message?: number;
unknown_message?: string;
listening_from_me?: boolean;
sessions?: Session[];
keywordFinish?: string;
delayMessage?: number;
unknownMessage?: string;
listeningFromMe?: boolean;
sessions?: TypebotSession[];
};
type Proxy = {

View File

@ -301,7 +301,7 @@ export class ConfigService {
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution',
},
ENABLED: process.env?.DATABASE_ENABLED === 'true',
PROVIDER: process.env.DATABASE_PROVIDER || 'mongodb',
PROVIDER: process.env.DATABASE_PROVIDER || 'postgresql',
SAVE_DATA: {
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',

View File

@ -1,25 +0,0 @@
import mongoose from 'mongoose';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
const logger = new Logger('MongoDB');
const db = configService.get<Database>('DATABASE');
export const mongodbServer = (() => {
if (db.ENABLED && db.PROVIDER === 'mongodb') {
logger.verbose('connecting');
const dbs = mongoose.createConnection(db.CONNECTION.URI, {
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
});
logger.verbose('connected in ' + db.CONNECTION.URI);
logger.info('ON - dbName: ' + dbs['$dbName']);
process.on('beforeExit', () => {
logger.verbose('instance destroyed');
mongodbServer.destroy(true, (error) => logger.error(error));
});
return dbs;
}
})();

View File

@ -3,11 +3,12 @@ import { PrismaClient } from '@prisma/client';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
const logger = new Logger('MongoDB');
const logger = new Logger('Prisma');
const db = configService.get<Database>('DATABASE');
export const prismaServer = (() => {
if (db.ENABLED && db.PROVIDER !== 'mongodb') {
if (db.ENABLED) {
logger.verbose('connecting');
const db = new PrismaClient();

View File

@ -10,6 +10,7 @@ import { initAMQP, initGlobalQueues } from './api/integrations/rabbitmq/libs/amq
import { initSQS } from './api/integrations/sqs/libs/sqs.server';
import { initIO } from './api/integrations/websocket/libs/socket.server';
import { ProviderFiles } from './api/provider/sessions';
import { PrismaRepository } from './api/repository/repository.service';
import { HttpStatus, router } from './api/routes/index.router';
import { waMonitor } from './api/server.module';
import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from './config/env.config';
@ -29,6 +30,8 @@ async function bootstrap() {
const providerFiles = new ProviderFiles(configService);
await providerFiles.onModuleInit();
logger.info('Provider:Files - ON');
const prismaRepository = new PrismaRepository(configService);
await prismaRepository.onModuleInit();
app.use(
cors({

View File

@ -1,107 +0,0 @@
import {
AuthenticationCreds,
AuthenticationState,
BufferJSON,
initAuthCreds,
proto,
SignalDataTypeMap,
} from '@whiskeysockets/baileys';
import { configService, Database } from '../config/env.config';
import { Logger } from '../config/logger.config';
import { mongodbServer } from '../libs/mongodb.connect';
export async function useMultiFileAuthStateMongoDb(
coll: string,
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
const logger = new Logger(useMultiFileAuthStateMongoDb.name);
const client = mongodbServer.getClient();
const collection = client
.db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
.collection(coll);
const writeData = async (data: any, key: string): Promise<any> => {
try {
await client.connect();
let msgParsed = JSON.parse(JSON.stringify(data, BufferJSON.replacer));
if (Array.isArray(msgParsed)) {
msgParsed = {
_id: key,
content_array: msgParsed,
};
}
return await collection.replaceOne({ _id: key }, msgParsed, {
upsert: true,
});
} catch (error) {
logger.error(error);
}
};
const readData = async (key: string): Promise<any> => {
try {
await client.connect();
let data = (await collection.findOne({ _id: key })) as any;
if (data?.content_array) {
data = data.content_array;
}
const creds = JSON.stringify(data);
return JSON.parse(creds, BufferJSON.reviver);
} catch (error) {
logger.error(error);
}
};
const removeData = async (key: string) => {
try {
await client.connect();
return await collection.deleteOne({ _id: key });
} catch (error) {
logger.error(error);
}
};
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
return {
state: {
creds,
keys: {
get: async (type, ids: string[]) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: { [_: string]: SignalDataTypeMap[type] } = {};
await Promise.all(
ids.map(async (id) => {
let value = await readData(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value);
}
data[id] = value;
}),
);
return data;
},
set: async (data: any) => {
const tasks: Promise<void>[] = [];
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id];
const key = `${category}-${id}`;
tasks.push(value ? writeData(value, key) : removeData(key));
}
}
await Promise.all(tasks);
},
},
},
saveCreds: async () => {
return await writeData(creds, 'creds');
},
};
}

View File

@ -1,9 +1,11 @@
import { PrismaClient } from '@prisma/client';
import { BufferJSON, initAuthCreds, WAProto as proto } from '@whiskeysockets/baileys';
import fs from 'fs/promises';
import path from 'path';
const prisma = new PrismaClient();
import { INSTANCE_DIR } from '../config/path.config';
import { prismaServer } from '../libs/prisma.connect';
const prisma = prismaServer;
const fixFileName = (file) => {
if (!file) {
@ -28,8 +30,16 @@ export async function saveKey(sessionId, keyJson) {
const jaExiste = await keyExists(sessionId);
try {
if (!jaExiste)
return await prisma.session.create({ data: { sessionId: sessionId, creds: JSON.stringify(keyJson) } as any });
await prisma.session.update({ where: { sessionId: sessionId }, data: { creds: JSON.stringify(keyJson) } });
return await prisma.session.create({
data: {
sessionId: sessionId,
creds: JSON.stringify(keyJson),
},
});
await prisma.session.update({
where: { sessionId: sessionId },
data: { creds: JSON.stringify(keyJson) },
});
} catch (error) {
console.log(`${error}`);
return null;
@ -68,7 +78,7 @@ async function fileExists(file) {
}
export default async function useMultiFileAuthStatePrisma(sessionId) {
const localFolder = path.join(process.cwd(), 'sessions', sessionId);
const localFolder = path.join(INSTANCE_DIR, sessionId);
const localFile = (key) => path.join(localFolder, fixFileName(key) + '.json');
await fs.mkdir(localFolder, { recursive: true });