mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-27 02:48:39 -06:00
Merge branch 'develop' into main
This commit is contained in:
commit
f5d49c54e1
@ -7,6 +7,7 @@
|
|||||||
* Join in Group by Invite Code
|
* Join in Group by Invite Code
|
||||||
* Read messages from whatsapp in chatwoot
|
* Read messages from whatsapp in chatwoot
|
||||||
* Add support to use use redis in cacheservice
|
* Add support to use use redis in cacheservice
|
||||||
|
* Add support for labels
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -93,6 +93,8 @@ WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
|||||||
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||||
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||||
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_LABELS_EDIT=true
|
||||||
|
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
|
||||||
WEBHOOK_EVENTS_CALL=true
|
WEBHOOK_EVENTS_CALL=true
|
||||||
# This event fires every time a new token is requested via the refresh route
|
# This event fires every time a new token is requested via the refresh route
|
||||||
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||||
|
@ -73,6 +73,8 @@ WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
|||||||
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||||
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||||
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||||
|
WEBHOOK_EVENTS_LABELS_EDIT=true
|
||||||
|
WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
|
||||||
# This event fires every time a new token is requested via the refresh route
|
# This event fires every time a new token is requested via the refresh route
|
||||||
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||||
|
|
||||||
|
@ -98,6 +98,8 @@ ENV WEBHOOK_EVENTS_GROUPS_UPSERT=true
|
|||||||
ENV WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
ENV WEBHOOK_EVENTS_GROUPS_UPDATE=true
|
||||||
ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
ENV WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true
|
||||||
ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=true
|
||||||
|
ENV WEBHOOK_EVENTS_LABELS_EDIT=true
|
||||||
|
ENV WEBHOOK_EVENTS_LABELS_ASSOCIATION=true
|
||||||
ENV WEBHOOK_EVENTS_CALL=true
|
ENV WEBHOOK_EVENTS_CALL=true
|
||||||
|
|
||||||
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=false
|
||||||
|
@ -36,6 +36,7 @@ export type SaveData = {
|
|||||||
MESSAGE_UPDATE: boolean;
|
MESSAGE_UPDATE: boolean;
|
||||||
CONTACTS: boolean;
|
CONTACTS: boolean;
|
||||||
CHATS: boolean;
|
CHATS: boolean;
|
||||||
|
LABELS: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StoreConf = {
|
export type StoreConf = {
|
||||||
@ -43,6 +44,7 @@ export type StoreConf = {
|
|||||||
MESSAGE_UP: boolean;
|
MESSAGE_UP: boolean;
|
||||||
CONTACTS: boolean;
|
CONTACTS: boolean;
|
||||||
CHATS: boolean;
|
CHATS: boolean;
|
||||||
|
LABELS: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CleanStoreConf = {
|
export type CleanStoreConf = {
|
||||||
@ -106,6 +108,8 @@ export type EventsWebhook = {
|
|||||||
CHATS_DELETE: boolean;
|
CHATS_DELETE: boolean;
|
||||||
CHATS_UPSERT: boolean;
|
CHATS_UPSERT: boolean;
|
||||||
CONNECTION_UPDATE: boolean;
|
CONNECTION_UPDATE: boolean;
|
||||||
|
LABELS_EDIT: boolean;
|
||||||
|
LABELS_ASSOCIATION: boolean;
|
||||||
GROUPS_UPSERT: boolean;
|
GROUPS_UPSERT: boolean;
|
||||||
GROUP_UPDATE: boolean;
|
GROUP_UPDATE: boolean;
|
||||||
GROUP_PARTICIPANTS_UPDATE: boolean;
|
GROUP_PARTICIPANTS_UPDATE: boolean;
|
||||||
@ -242,6 +246,7 @@ export class ConfigService {
|
|||||||
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
|
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
|
||||||
CONTACTS: process.env?.STORE_CONTACTS === 'true',
|
CONTACTS: process.env?.STORE_CONTACTS === 'true',
|
||||||
CHATS: process.env?.STORE_CHATS === 'true',
|
CHATS: process.env?.STORE_CHATS === 'true',
|
||||||
|
LABELS: process.env?.STORE_LABELS === 'true',
|
||||||
},
|
},
|
||||||
CLEAN_STORE: {
|
CLEAN_STORE: {
|
||||||
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
|
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
|
||||||
@ -265,6 +270,7 @@ export class ConfigService {
|
|||||||
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
|
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
|
||||||
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
|
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
|
||||||
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
|
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
|
||||||
|
LABELS: process.env?.DATABASE_SAVE_DATA_LABELS === 'true',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
REDIS: {
|
REDIS: {
|
||||||
@ -331,6 +337,8 @@ export class ConfigService {
|
|||||||
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
|
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
|
||||||
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
|
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
|
||||||
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
|
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
|
||||||
|
LABELS_EDIT: process.env?.WEBHOOK_EVENTS_LABELS_EDIT === 'true',
|
||||||
|
LABELS_ASSOCIATION: process.env?.WEBHOOK_EVENTS_LABELS_ASSOCIATION === 'true',
|
||||||
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
|
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
|
||||||
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
|
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
|
||||||
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
|
GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
|
||||||
|
@ -136,6 +136,8 @@ WEBHOOK:
|
|||||||
GROUP_UPDATE: true
|
GROUP_UPDATE: true
|
||||||
GROUP_PARTICIPANTS_UPDATE: true
|
GROUP_PARTICIPANTS_UPDATE: true
|
||||||
CONNECTION_UPDATE: true
|
CONNECTION_UPDATE: true
|
||||||
|
LABELS_EDIT: true
|
||||||
|
LABELS_ASSOCIATION: true
|
||||||
CALL: true
|
CALL: true
|
||||||
# This event fires every time a new token is requested via the refresh route
|
# This event fires every time a new token is requested via the refresh route
|
||||||
NEW_JWT_TOKEN: false
|
NEW_JWT_TOKEN: false
|
||||||
|
@ -51,6 +51,7 @@ tags:
|
|||||||
- name: Send Message Controller
|
- name: Send Message Controller
|
||||||
- name: Chat Controller
|
- name: Chat Controller
|
||||||
- name: Group Controller
|
- name: Group Controller
|
||||||
|
- name: Label Controller
|
||||||
- name: Profile Settings
|
- name: Profile Settings
|
||||||
- name: JWT
|
- name: JWT
|
||||||
- name: Settings
|
- name: Settings
|
||||||
@ -1856,6 +1857,8 @@ paths:
|
|||||||
"GROUP_UPDATE",
|
"GROUP_UPDATE",
|
||||||
"GROUP_PARTICIPANTS_UPDATE",
|
"GROUP_PARTICIPANTS_UPDATE",
|
||||||
"CONNECTION_UPDATE",
|
"CONNECTION_UPDATE",
|
||||||
|
"LABELS_EDIT",
|
||||||
|
"LABELS_ASSOCIATION",
|
||||||
"CALL",
|
"CALL",
|
||||||
"NEW_JWT_TOKEN",
|
"NEW_JWT_TOKEN",
|
||||||
]
|
]
|
||||||
@ -1932,6 +1935,8 @@ paths:
|
|||||||
"GROUP_UPDATE",
|
"GROUP_UPDATE",
|
||||||
"GROUP_PARTICIPANTS_UPDATE",
|
"GROUP_PARTICIPANTS_UPDATE",
|
||||||
"CONNECTION_UPDATE",
|
"CONNECTION_UPDATE",
|
||||||
|
"LABELS_EDIT",
|
||||||
|
"LABELS_ASSOCIATION",
|
||||||
"CALL",
|
"CALL",
|
||||||
"NEW_JWT_TOKEN",
|
"NEW_JWT_TOKEN",
|
||||||
]
|
]
|
||||||
@ -2008,6 +2013,8 @@ paths:
|
|||||||
"GROUP_UPDATE",
|
"GROUP_UPDATE",
|
||||||
"GROUP_PARTICIPANTS_UPDATE",
|
"GROUP_PARTICIPANTS_UPDATE",
|
||||||
"CONNECTION_UPDATE",
|
"CONNECTION_UPDATE",
|
||||||
|
"LABELS_EDIT",
|
||||||
|
"LABELS_ASSOCIATION",
|
||||||
"CALL",
|
"CALL",
|
||||||
"NEW_JWT_TOKEN",
|
"NEW_JWT_TOKEN",
|
||||||
]
|
]
|
||||||
@ -2046,6 +2053,97 @@ paths:
|
|||||||
content:
|
content:
|
||||||
application/json: {}
|
application/json: {}
|
||||||
|
|
||||||
|
/label/findLabels/{instanceName}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Label Controller
|
||||||
|
summary: List all labels for an instance.
|
||||||
|
parameters:
|
||||||
|
- name: instanceName
|
||||||
|
in: path
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: "- required"
|
||||||
|
example: "evolution"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Successful response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
color:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
predefinedId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- color
|
||||||
|
- name
|
||||||
|
- id
|
||||||
|
/label/handleLabel/{instanceName}:
|
||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- Label Controller
|
||||||
|
summary: Change the label (add or remove) for an specific chat.
|
||||||
|
parameters:
|
||||||
|
- name: instanceName
|
||||||
|
in: path
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: "- required"
|
||||||
|
example: "evolution"
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
type: string
|
||||||
|
labelId:
|
||||||
|
type: string
|
||||||
|
action:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- add
|
||||||
|
- remove
|
||||||
|
required:
|
||||||
|
- number
|
||||||
|
- labelId
|
||||||
|
- action
|
||||||
|
example:
|
||||||
|
number: '553499999999'
|
||||||
|
labelId: '1'
|
||||||
|
action: add
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Successful response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
numberJid:
|
||||||
|
type: string
|
||||||
|
labelId:
|
||||||
|
type: string
|
||||||
|
remove:
|
||||||
|
type: boolean
|
||||||
|
add:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- numberJid
|
||||||
|
- labelId
|
||||||
|
|
||||||
/settings/set/{instanceName}:
|
/settings/set/{instanceName}:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
@ -4,8 +4,6 @@ import path from 'path';
|
|||||||
|
|
||||||
import { ConfigService, Language } from '../config/env.config';
|
import { ConfigService, Language } from '../config/env.config';
|
||||||
|
|
||||||
// export class i18n {
|
|
||||||
// constructor(private readonly configService: ConfigService) {
|
|
||||||
const languages = ['en', 'pt-BR'];
|
const languages = ['en', 'pt-BR'];
|
||||||
const translationsPath = path.join(__dirname, 'translations');
|
const translationsPath = path.join(__dirname, 'translations');
|
||||||
const configService: ConfigService = new ConfigService();
|
const configService: ConfigService = new ConfigService();
|
||||||
@ -31,6 +29,4 @@ i18next.init({
|
|||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// }
|
|
||||||
// }
|
|
||||||
export default i18next;
|
export default i18next;
|
||||||
|
@ -53,6 +53,8 @@ export const instanceNameSchema: JSONSchema7 = {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -938,6 +940,8 @@ export const webhookSchema: JSONSchema7 = {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -1018,6 +1022,8 @@ export const websocketSchema: JSONSchema7 = {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -1061,6 +1067,8 @@ export const rabbitmqSchema: JSONSchema7 = {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -1104,6 +1112,8 @@ export const sqsSchema: JSONSchema7 = {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -1191,3 +1201,14 @@ export const chamaaiSchema: JSONSchema7 = {
|
|||||||
required: ['enabled', 'url', 'token', 'waNumber', 'answerByAudio'],
|
required: ['enabled', 'url', 'token', 'waNumber', 'answerByAudio'],
|
||||||
...isNotEmpty('enabled', 'url', 'token', 'waNumber', 'answerByAudio'),
|
...isNotEmpty('enabled', 'url', 'token', 'waNumber', 'answerByAudio'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleLabelSchema: JSONSchema7 = {
|
||||||
|
$id: v4(),
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
number: { ...numberDefinition },
|
||||||
|
labelId: { type: 'string' },
|
||||||
|
action: { type: 'string', enum: ['add', 'remove'] },
|
||||||
|
},
|
||||||
|
required: ['number', 'labelId', 'action'],
|
||||||
|
};
|
||||||
|
@ -21,7 +21,6 @@ const logger = new Logger('Validate');
|
|||||||
export abstract class RouterBroker {
|
export abstract class RouterBroker {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
public routerPath(path: string, param = true) {
|
public routerPath(path: string, param = true) {
|
||||||
// const route = param ? '/:instanceName/' + path : '/' + path;
|
|
||||||
let route = '/' + path;
|
let route = '/' + path;
|
||||||
param ? (route += '/:instanceName') : null;
|
param ? (route += '/:instanceName') : null;
|
||||||
|
|
||||||
@ -56,10 +55,6 @@ export abstract class RouterBroker {
|
|||||||
message = stack.replace('instance.', '');
|
message = stack.replace('instance.', '');
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
// return {
|
|
||||||
// property: property.replace('instance.', ''),
|
|
||||||
// message,
|
|
||||||
// };
|
|
||||||
});
|
});
|
||||||
logger.error(message);
|
logger.error(message);
|
||||||
throw new BadRequestException(message);
|
throw new BadRequestException(message);
|
||||||
|
@ -155,6 +155,8 @@ export class InstanceController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -205,6 +207,8 @@ export class InstanceController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -252,6 +256,8 @@ export class InstanceController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
@ -299,6 +305,8 @@ export class InstanceController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
|
20
src/whatsapp/controllers/label.controller.ts
Normal file
20
src/whatsapp/controllers/label.controller.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
|
import { HandleLabelDto } from '../dto/label.dto';
|
||||||
|
import { WAMonitoringService } from '../services/monitor.service';
|
||||||
|
|
||||||
|
const logger = new Logger('LabelController');
|
||||||
|
|
||||||
|
export class LabelController {
|
||||||
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||||
|
|
||||||
|
public async fetchLabels({ instanceName }: InstanceDto) {
|
||||||
|
logger.verbose('requested fetchLabels from ' + instanceName + ' instance');
|
||||||
|
return await this.waMonitor.waInstances[instanceName].fetchLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handleLabel({ instanceName }: InstanceDto, data: HandleLabelDto) {
|
||||||
|
logger.verbose('requested chat label change from ' + instanceName + ' instance');
|
||||||
|
return await this.waMonitor.waInstances[instanceName].handleLabel(data);
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,8 @@ export class RabbitmqController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
// import { isURL } from 'class-validator';
|
|
||||||
|
|
||||||
import { Logger } from '../../config/logger.config';
|
import { Logger } from '../../config/logger.config';
|
||||||
// import { BadRequestException } from '../../exceptions';
|
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { SettingsDto } from '../dto/settings.dto';
|
import { SettingsDto } from '../dto/settings.dto';
|
||||||
import { SettingsService } from '../services/settings.service';
|
import { SettingsService } from '../services/settings.service';
|
||||||
|
@ -38,6 +38,8 @@ export class SqsController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
|
@ -54,6 +54,8 @@ export class WebhookController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
|
@ -38,6 +38,8 @@ export class WebsocketController {
|
|||||||
'GROUP_UPDATE',
|
'GROUP_UPDATE',
|
||||||
'GROUP_PARTICIPANTS_UPDATE',
|
'GROUP_PARTICIPANTS_UPDATE',
|
||||||
'CONNECTION_UPDATE',
|
'CONNECTION_UPDATE',
|
||||||
|
'LABELS_EDIT',
|
||||||
|
'LABELS_ASSOCIATION',
|
||||||
'CALL',
|
'CALL',
|
||||||
'NEW_JWT_TOKEN',
|
'NEW_JWT_TOKEN',
|
||||||
'TYPEBOT_START',
|
'TYPEBOT_START',
|
||||||
|
12
src/whatsapp/dto/label.dto.ts
Normal file
12
src/whatsapp/dto/label.dto.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export class LabelDto {
|
||||||
|
id?: string;
|
||||||
|
name: string;
|
||||||
|
color: number;
|
||||||
|
predefinedId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HandleLabelDto {
|
||||||
|
number: string;
|
||||||
|
labelId: string;
|
||||||
|
action: 'add' | 'remove';
|
||||||
|
}
|
@ -7,6 +7,7 @@ export class ChatRaw {
|
|||||||
id?: string;
|
id?: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
lastMsgTimestamp?: number;
|
lastMsgTimestamp?: number;
|
||||||
|
labels?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatRawBoolean<T> = {
|
type ChatRawBoolean<T> = {
|
||||||
@ -18,6 +19,7 @@ const chatSchema = new Schema<ChatRaw>({
|
|||||||
_id: { type: String, _id: true },
|
_id: { type: String, _id: true },
|
||||||
id: { type: String, required: true, minlength: 1 },
|
id: { type: String, required: true, minlength: 1 },
|
||||||
owner: { type: String, required: true, minlength: 1 },
|
owner: { type: String, required: true, minlength: 1 },
|
||||||
|
labels: { type: [String], default: [] },
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats');
|
export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats');
|
||||||
|
@ -3,6 +3,7 @@ export * from './chamaai.model';
|
|||||||
export * from './chat.model';
|
export * from './chat.model';
|
||||||
export * from './chatwoot.model';
|
export * from './chatwoot.model';
|
||||||
export * from './contact.model';
|
export * from './contact.model';
|
||||||
|
export * from './label.model';
|
||||||
export * from './message.model';
|
export * from './message.model';
|
||||||
export * from './proxy.model';
|
export * from './proxy.model';
|
||||||
export * from './rabbitmq.model';
|
export * from './rabbitmq.model';
|
||||||
|
29
src/whatsapp/models/label.model.ts
Normal file
29
src/whatsapp/models/label.model.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Schema } from 'mongoose';
|
||||||
|
|
||||||
|
import { dbserver } from '../../libs/db.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 = dbserver?.model(LabelRaw.name, labelSchema, 'labels');
|
||||||
|
export type ILabelModel = typeof LabelModel;
|
@ -1,4 +1,4 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { opendirSync, readFileSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { Auth, ConfigService } from '../../config/env.config';
|
import { Auth, ConfigService } from '../../config/env.config';
|
||||||
@ -64,6 +64,37 @@ export class AuthRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, this.auth.TYPE), {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
});
|
||||||
|
for await (const dirent of openDir) {
|
||||||
|
if (dirent.isFile()) {
|
||||||
|
auths.push(
|
||||||
|
JSON.parse(
|
||||||
|
readFileSync(join(AUTH_DIR, this.auth.TYPE, dirent.name), {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return auths;
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async findInstanceNameById(instanceId: string): Promise<string | null> {
|
public async findInstanceNameById(instanceId: string): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
this.logger.verbose('finding auth by instanceId');
|
this.logger.verbose('finding auth by instanceId');
|
||||||
|
@ -115,4 +115,63 @@ export class ChatRepository extends Repository {
|
|||||||
return { error: error?.toString() };
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,11 @@ export class ContactQuery {
|
|||||||
where: ContactRaw;
|
where: ContactRaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ContactQueryMany {
|
||||||
|
owner: ContactRaw['owner'];
|
||||||
|
ids: ContactRaw['id'][];
|
||||||
|
}
|
||||||
|
|
||||||
export class ContactRepository extends Repository {
|
export class ContactRepository extends Repository {
|
||||||
constructor(private readonly contactModel: IContactModel, private readonly configService: ConfigService) {
|
constructor(private readonly contactModel: IContactModel, private readonly configService: ConfigService) {
|
||||||
super(configService);
|
super(configService);
|
||||||
@ -169,4 +174,54 @@ export class ContactRepository extends Repository {
|
|||||||
return [];
|
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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
111
src/whatsapp/repository/label.repository.ts
Normal file
111
src/whatsapp/repository/label.repository.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import { ChamaaiRepository } from './chamaai.repository';
|
|||||||
import { ChatRepository } from './chat.repository';
|
import { ChatRepository } from './chat.repository';
|
||||||
import { ChatwootRepository } from './chatwoot.repository';
|
import { ChatwootRepository } from './chatwoot.repository';
|
||||||
import { ContactRepository } from './contact.repository';
|
import { ContactRepository } from './contact.repository';
|
||||||
|
import { LabelRepository } from './label.repository';
|
||||||
import { MessageRepository } from './message.repository';
|
import { MessageRepository } from './message.repository';
|
||||||
import { MessageUpRepository } from './messageUp.repository';
|
import { MessageUpRepository } from './messageUp.repository';
|
||||||
import { ProxyRepository } from './proxy.repository';
|
import { ProxyRepository } from './proxy.repository';
|
||||||
@ -34,6 +35,7 @@ export class RepositoryBroker {
|
|||||||
public readonly proxy: ProxyRepository,
|
public readonly proxy: ProxyRepository,
|
||||||
public readonly chamaai: ChamaaiRepository,
|
public readonly chamaai: ChamaaiRepository,
|
||||||
public readonly auth: AuthRepository,
|
public readonly auth: AuthRepository,
|
||||||
|
public readonly labels: LabelRepository,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
dbServer?: MongoClient,
|
dbServer?: MongoClient,
|
||||||
) {
|
) {
|
||||||
|
@ -62,7 +62,7 @@ export class ChatRouter extends RouterBroker {
|
|||||||
execute: (instance, data) => chatController.whatsappNumber(instance, data),
|
execute: (instance, data) => chatController.whatsappNumber(instance, data),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(HttpStatus.CREATED).json(response);
|
return res.status(HttpStatus.OK).json(response);
|
||||||
})
|
})
|
||||||
.put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => {
|
.put(this.routerPath('markMessageAsRead'), ...guards, async (req, res) => {
|
||||||
logger.verbose('request received in markMessageAsRead');
|
logger.verbose('request received in markMessageAsRead');
|
||||||
|
@ -5,7 +5,6 @@ import { chatwootSchema, instanceNameSchema } from '../../validate/validate.sche
|
|||||||
import { RouterBroker } from '../abstract/abstract.router';
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
import { ChatwootDto } from '../dto/chatwoot.dto';
|
import { ChatwootDto } from '../dto/chatwoot.dto';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
// import { ChatwootService } from '../services/chatwoot.service';
|
|
||||||
import { chatwootController } from '../whatsapp.module';
|
import { chatwootController } from '../whatsapp.module';
|
||||||
import { HttpStatus } from './index.router';
|
import { HttpStatus } from './index.router';
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { ChatRouter } from './chat.router';
|
|||||||
import { ChatwootRouter } from './chatwoot.router';
|
import { ChatwootRouter } from './chatwoot.router';
|
||||||
import { GroupRouter } from './group.router';
|
import { GroupRouter } from './group.router';
|
||||||
import { InstanceRouter } from './instance.router';
|
import { InstanceRouter } from './instance.router';
|
||||||
|
import { LabelRouter } from './label.router';
|
||||||
import { ProxyRouter } from './proxy.router';
|
import { ProxyRouter } from './proxy.router';
|
||||||
import { RabbitmqRouter } from './rabbitmq.router';
|
import { RabbitmqRouter } from './rabbitmq.router';
|
||||||
import { MessageRouter } from './sendMessage.router';
|
import { MessageRouter } from './sendMessage.router';
|
||||||
@ -61,6 +62,7 @@ router
|
|||||||
.use('/sqs', new SqsRouter(...guards).router)
|
.use('/sqs', new SqsRouter(...guards).router)
|
||||||
.use('/typebot', new TypebotRouter(...guards).router)
|
.use('/typebot', new TypebotRouter(...guards).router)
|
||||||
.use('/proxy', new ProxyRouter(...guards).router)
|
.use('/proxy', new ProxyRouter(...guards).router)
|
||||||
.use('/chamaai', new ChamaaiRouter(...guards).router);
|
.use('/chamaai', new ChamaaiRouter(...guards).router)
|
||||||
|
.use('/label', new LabelRouter(...guards).router);
|
||||||
|
|
||||||
export { HttpStatus, router };
|
export { HttpStatus, router };
|
||||||
|
53
src/whatsapp/routers/label.router.ts
Normal file
53
src/whatsapp/routers/label.router.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { RequestHandler, Router } from 'express';
|
||||||
|
|
||||||
|
import { Logger } from '../../config/logger.config';
|
||||||
|
import { handleLabelSchema } from '../../validate/validate.schema';
|
||||||
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
|
import { HandleLabelDto, LabelDto } from '../dto/label.dto';
|
||||||
|
import { labelController } from '../whatsapp.module';
|
||||||
|
import { HttpStatus } from './index.router';
|
||||||
|
|
||||||
|
const logger = new Logger('LabelRouter');
|
||||||
|
|
||||||
|
export class LabelRouter extends RouterBroker {
|
||||||
|
constructor(...guards: RequestHandler[]) {
|
||||||
|
super();
|
||||||
|
this.router
|
||||||
|
.get(this.routerPath('findLabels'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in findLabels');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
|
const response = await this.dataValidate<LabelDto>({
|
||||||
|
request: req,
|
||||||
|
schema: null,
|
||||||
|
ClassRef: LabelDto,
|
||||||
|
execute: (instance) => labelController.fetchLabels(instance),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).json(response);
|
||||||
|
})
|
||||||
|
.put(this.routerPath('handleLabel'), ...guards, async (req, res) => {
|
||||||
|
logger.verbose('request received in handleLabel');
|
||||||
|
logger.verbose('request body: ');
|
||||||
|
logger.verbose(req.body);
|
||||||
|
|
||||||
|
logger.verbose('request query: ');
|
||||||
|
logger.verbose(req.query);
|
||||||
|
|
||||||
|
const response = await this.dataValidate<HandleLabelDto>({
|
||||||
|
request: req,
|
||||||
|
schema: handleLabelSchema,
|
||||||
|
ClassRef: HandleLabelDto,
|
||||||
|
execute: (instance, data) => labelController.handleLabel(instance, data),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(HttpStatus.OK).json(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly router = Router();
|
||||||
|
}
|
@ -5,7 +5,6 @@ import { instanceNameSchema, settingsSchema } from '../../validate/validate.sche
|
|||||||
import { RouterBroker } from '../abstract/abstract.router';
|
import { RouterBroker } from '../abstract/abstract.router';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
import { SettingsDto } from '../dto/settings.dto';
|
import { SettingsDto } from '../dto/settings.dto';
|
||||||
// import { SettingsService } from '../services/settings.service';
|
|
||||||
import { settingsController } from '../whatsapp.module';
|
import { settingsController } from '../whatsapp.module';
|
||||||
import { HttpStatus } from './index.router';
|
import { HttpStatus } from './index.router';
|
||||||
|
|
||||||
|
@ -50,11 +50,6 @@ export class ChatwootService {
|
|||||||
this.cache.set(cacheKey, provider);
|
this.cache.set(cacheKey, provider);
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
// try {
|
|
||||||
// } catch (error) {
|
|
||||||
// this.logger.error('provider not found');
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async clientCw(instance: InstanceDto) {
|
private async clientCw(instance: InstanceDto) {
|
||||||
@ -388,10 +383,6 @@ export class ChatwootService {
|
|||||||
q: query,
|
q: query,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// contact = await client.contacts.filter({
|
|
||||||
// accountId: this.provider.account_id,
|
|
||||||
// payload: this.getFilterPayload(query),
|
|
||||||
// });
|
|
||||||
// hotfix for: https://github.com/EvolutionAPI/evolution-api/pull/382. waiting fix: https://github.com/figurolatam/chatwoot-sdk/pull/7
|
// hotfix for: https://github.com/EvolutionAPI/evolution-api/pull/382. waiting fix: https://github.com/figurolatam/chatwoot-sdk/pull/7
|
||||||
contact = await chatwootRequest(this.getClientCwConfig(), {
|
contact = await chatwootRequest(this.getClientCwConfig(), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -1193,7 +1184,6 @@ export class ChatwootService {
|
|||||||
if (state !== 'open') {
|
if (state !== 'open') {
|
||||||
if (state === 'close') {
|
if (state === 'close') {
|
||||||
this.logger.verbose('request cleaning up instance: ' + instance.instanceName);
|
this.logger.verbose('request cleaning up instance: ' + instance.instanceName);
|
||||||
// await this.waMonitor.cleaningUp(instance.instanceName);
|
|
||||||
}
|
}
|
||||||
this.logger.verbose('connect to whatsapp');
|
this.logger.verbose('connect to whatsapp');
|
||||||
const number = command.split(':')[1];
|
const number = command.split(':')[1];
|
||||||
@ -1204,6 +1194,12 @@ export class ChatwootService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command === 'clearcache') {
|
||||||
|
this.logger.verbose('command clearcache found');
|
||||||
|
waInstance.clearCacheChatwoot();
|
||||||
|
await this.createBotMessage(instance, `✅ ${body.inbox.name} instance cache cleared.`, 'incoming');
|
||||||
|
}
|
||||||
|
|
||||||
if (command === 'status') {
|
if (command === 'status') {
|
||||||
this.logger.verbose('command status found');
|
this.logger.verbose('command status found');
|
||||||
|
|
||||||
@ -2141,7 +2137,7 @@ export class ChatwootService {
|
|||||||
this.logger.verbose('qrcode success');
|
this.logger.verbose('qrcode success');
|
||||||
const fileData = Buffer.from(body?.qrcode.base64.replace('data:image/png;base64,', ''), 'base64');
|
const fileData = Buffer.from(body?.qrcode.base64.replace('data:image/png;base64,', ''), 'base64');
|
||||||
|
|
||||||
const fileName = `${path.join(waInstance?.storePath, 'temp', `${`${instance}.png`}`)}`;
|
const fileName = `${path.join(waInstance?.storePath, 'temp', `${instance.instanceName}.png`)}`;
|
||||||
|
|
||||||
this.logger.verbose('temp file name: ' + fileName);
|
this.logger.verbose('temp file name: ' + fileName);
|
||||||
|
|
||||||
|
@ -2,22 +2,20 @@ import { execSync } from 'child_process';
|
|||||||
import EventEmitter2 from 'eventemitter2';
|
import EventEmitter2 from 'eventemitter2';
|
||||||
import { opendirSync, readdirSync, rmSync, existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
import { opendirSync, readdirSync, rmSync, existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
||||||
import { Db } from 'mongodb';
|
import { Db } from 'mongodb';
|
||||||
|
import { Collection } from 'mongoose';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
|
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
|
||||||
import { Logger } from '../../config/logger.config';
|
import { Logger } from '../../config/logger.config';
|
||||||
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
|
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
|
||||||
import { NotFoundException } from '../../exceptions';
|
import { NotFoundException } from '../../exceptions';
|
||||||
import { dbserver } from '../../libs/db.connect';
|
|
||||||
import { RedisCache } from '../../libs/redis.client';
|
import { RedisCache } from '../../libs/redis.client';
|
||||||
import {
|
import {
|
||||||
AuthModel,
|
AuthModel,
|
||||||
ChamaaiModel,
|
ChamaaiModel,
|
||||||
// ChatModel,
|
|
||||||
ChatwootModel,
|
ChatwootModel,
|
||||||
// ContactModel,
|
ContactModel,
|
||||||
// MessageModel,
|
LabelModel,
|
||||||
// MessageUpModel,
|
|
||||||
ProxyModel,
|
ProxyModel,
|
||||||
RabbitmqModel,
|
RabbitmqModel,
|
||||||
SettingsModel,
|
SettingsModel,
|
||||||
@ -42,7 +40,6 @@ export class WAMonitoringService {
|
|||||||
|
|
||||||
this.removeInstance();
|
this.removeInstance();
|
||||||
this.noConnection();
|
this.noConnection();
|
||||||
// this.delInstanceFiles();
|
|
||||||
|
|
||||||
Object.assign(this.db, configService.get<Database>('DATABASE'));
|
Object.assign(this.db, configService.get<Database>('DATABASE'));
|
||||||
Object.assign(this.redis, configService.get<Redis>('REDIS'));
|
Object.assign(this.redis, configService.get<Redis>('REDIS'));
|
||||||
@ -57,8 +54,6 @@ export class WAMonitoringService {
|
|||||||
|
|
||||||
private dbInstance: Db;
|
private dbInstance: Db;
|
||||||
|
|
||||||
private dbStore = dbserver;
|
|
||||||
|
|
||||||
private readonly logger = new Logger(WAMonitoringService.name);
|
private readonly logger = new Logger(WAMonitoringService.name);
|
||||||
public readonly waInstances: Record<string, WAStartupService> = {};
|
public readonly waInstances: Record<string, WAStartupService> = {};
|
||||||
|
|
||||||
@ -321,17 +316,13 @@ export class WAMonitoringService {
|
|||||||
execSync(`rm -rf ${join(STORE_DIR, 'typebot', instanceName + '*')}`);
|
execSync(`rm -rf ${join(STORE_DIR, 'typebot', instanceName + '*')}`);
|
||||||
execSync(`rm -rf ${join(STORE_DIR, 'websocket', instanceName + '*')}`);
|
execSync(`rm -rf ${join(STORE_DIR, 'websocket', instanceName + '*')}`);
|
||||||
execSync(`rm -rf ${join(STORE_DIR, 'settings', instanceName + '*')}`);
|
execSync(`rm -rf ${join(STORE_DIR, 'settings', instanceName + '*')}`);
|
||||||
|
execSync(`rm -rf ${join(STORE_DIR, 'labels', instanceName + '*')}`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.verbose('cleaning store database instance: ' + instanceName);
|
this.logger.verbose('cleaning store database instance: ' + instanceName);
|
||||||
|
|
||||||
// await ChatModel.deleteMany({ owner: instanceName });
|
|
||||||
// await ContactModel.deleteMany({ owner: instanceName });
|
|
||||||
// await MessageUpModel.deleteMany({ owner: instanceName });
|
|
||||||
// await MessageModel.deleteMany({ owner: instanceName });
|
|
||||||
|
|
||||||
await AuthModel.deleteMany({ _id: instanceName });
|
await AuthModel.deleteMany({ _id: instanceName });
|
||||||
await WebhookModel.deleteMany({ _id: instanceName });
|
await WebhookModel.deleteMany({ _id: instanceName });
|
||||||
await ChatwootModel.deleteMany({ _id: instanceName });
|
await ChatwootModel.deleteMany({ _id: instanceName });
|
||||||
@ -341,6 +332,8 @@ export class WAMonitoringService {
|
|||||||
await TypebotModel.deleteMany({ _id: instanceName });
|
await TypebotModel.deleteMany({ _id: instanceName });
|
||||||
await WebsocketModel.deleteMany({ _id: instanceName });
|
await WebsocketModel.deleteMany({ _id: instanceName });
|
||||||
await SettingsModel.deleteMany({ _id: instanceName });
|
await SettingsModel.deleteMany({ _id: instanceName });
|
||||||
|
await LabelModel.deleteMany({ owner: instanceName });
|
||||||
|
await ContactModel.deleteMany({ owner: instanceName });
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -395,7 +388,7 @@ export class WAMonitoringService {
|
|||||||
this.logger.verbose('Database enabled');
|
this.logger.verbose('Database enabled');
|
||||||
await this.repository.dbServer.connect();
|
await this.repository.dbServer.connect();
|
||||||
const collections: any[] = await this.dbInstance.collections();
|
const collections: any[] = await this.dbInstance.collections();
|
||||||
|
await this.deleteTempInstances(collections);
|
||||||
if (collections.length > 0) {
|
if (collections.length > 0) {
|
||||||
this.logger.verbose('Reading collections and setting instances');
|
this.logger.verbose('Reading collections and setting instances');
|
||||||
await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, ''))));
|
await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, ''))));
|
||||||
@ -507,4 +500,21 @@ export class WAMonitoringService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async deleteTempInstances(collections: Collection<Document>[]) {
|
||||||
|
this.logger.verbose('Cleaning up temp instances');
|
||||||
|
const auths = await this.repository.auth.list();
|
||||||
|
if (auths.length === 0) {
|
||||||
|
this.logger.verbose('No temp instances found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tempInstances = 0;
|
||||||
|
auths.forEach((auth) => {
|
||||||
|
if (collections.find((coll) => coll.namespace.replace(/^[\w-]+\./, '') === auth._id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tempInstances++;
|
||||||
|
this.eventEmitter.emit('remove.instance', auth._id, 'inner');
|
||||||
|
});
|
||||||
|
this.logger.verbose('Temp instances removed: ' + tempInstances);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,41 @@
|
|||||||
|
import ffmpegPath from '@ffmpeg-installer/ffmpeg';
|
||||||
|
import { Boom } from '@hapi/boom';
|
||||||
|
import makeWASocket, {
|
||||||
|
AnyMessageContent,
|
||||||
|
BufferedEventData,
|
||||||
|
BufferJSON,
|
||||||
|
CacheStore,
|
||||||
|
Chat,
|
||||||
|
ConnectionState,
|
||||||
|
Contact,
|
||||||
|
delay,
|
||||||
|
DisconnectReason,
|
||||||
|
downloadMediaMessage,
|
||||||
|
fetchLatestBaileysVersion,
|
||||||
|
generateWAMessageFromContent,
|
||||||
|
getAggregateVotesInPollMessage,
|
||||||
|
getContentType,
|
||||||
|
getDevice,
|
||||||
|
GroupMetadata,
|
||||||
|
isJidBroadcast,
|
||||||
|
isJidGroup,
|
||||||
|
isJidUser,
|
||||||
|
makeCacheableSignalKeyStore,
|
||||||
|
MessageUpsertType,
|
||||||
|
MiscMessageGenerationOptions,
|
||||||
|
ParticipantAction,
|
||||||
|
prepareWAMessageMedia,
|
||||||
|
proto,
|
||||||
|
useMultiFileAuthState,
|
||||||
|
UserFacingSocketConfig,
|
||||||
|
WABrowserDescription,
|
||||||
|
WAMediaUpload,
|
||||||
|
WAMessage,
|
||||||
|
WAMessageUpdate,
|
||||||
|
WASocket,
|
||||||
|
} from '@whiskeysockets/baileys';
|
||||||
|
import { Label } from '@whiskeysockets/baileys/lib/Types/Label';
|
||||||
|
import { LabelAssociation } from '@whiskeysockets/baileys/lib/Types/LabelAssociation';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { isURL } from 'class-validator
|
import { isURL } from 'class-validator
|
||||||
@ -64,6 +102,7 @@ import {
|
|||||||
GroupUpdateSettingDto,
|
GroupUpdateSettingDto,
|
||||||
} from '../dto/group.dto';
|
} from '../dto/group.dto';
|
||||||
import { InstanceDto } from '../dto/instance.dto';
|
import { InstanceDto } from '../dto/instance.dto';
|
||||||
|
import { HandleLabelDto, LabelDto } from '../dto/label.dto';
|
||||||
import {
|
import {
|
||||||
ContactMessage,
|
ContactMessage,
|
||||||
MediaMessage,
|
MediaMessage,
|
||||||
@ -1452,7 +1491,7 @@ export class WAStartupService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.logger.verbose('Verifying if contacts exists in database to insert');
|
this.logger.verbose('Verifying if contacts exists in database to insert');
|
||||||
const contactsRaw: ContactRaw[] = [];
|
let contactsRaw: ContactRaw[] = [];
|
||||||
|
|
||||||
for (const contact of contacts) {
|
for (const contact of contacts) {
|
||||||
if (contactsRepository.has(contact.id)) {
|
if (contactsRepository.has(contact.id)) {
|
||||||
@ -1462,7 +1501,7 @@ export class WAStartupService {
|
|||||||
contactsRaw.push({
|
contactsRaw.push({
|
||||||
id: contact.id,
|
id: contact.id,
|
||||||
pushName: contact?.name || contact?.verifiedName || contact.id.split('@')[0],
|
pushName: contact?.name || contact?.verifiedName || contact.id.split('@')[0],
|
||||||
profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl,
|
profilePictureUrl: null,
|
||||||
owner: this.instance.name,
|
owner: this.instance.name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1477,6 +1516,23 @@ export class WAStartupService {
|
|||||||
this.chatwootService.addHistoryContacts({ instanceName: this.instance.name }, contactsRaw);
|
this.chatwootService.addHistoryContacts({ instanceName: this.instance.name }, contactsRaw);
|
||||||
chatwootImport.importHistoryContacts({ instanceName: this.instance.name }, this.localChatwoot);
|
chatwootImport.importHistoryContacts({ instanceName: this.instance.name }, this.localChatwoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update profile pictures
|
||||||
|
contactsRaw = [];
|
||||||
|
for await (const contact of contacts) {
|
||||||
|
contactsRaw.push({
|
||||||
|
id: contact.id,
|
||||||
|
pushName: contact?.name || contact?.verifiedName || contact.id.split('@')[0],
|
||||||
|
profilePictureUrl: (await this.profilePicture(contact.id)).profilePictureUrl,
|
||||||
|
owner: this.instance.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('Sending data to webhook in event CONTACTS_UPDATE');
|
||||||
|
this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw);
|
||||||
|
|
||||||
|
this.logger.verbose('Updating contacts in database');
|
||||||
|
this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
}
|
}
|
||||||
@ -1942,10 +1998,86 @@ export class WAStartupService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly labelHandle = {
|
||||||
|
[Events.LABELS_EDIT]: async (label: Label, database: Database) => {
|
||||||
|
this.logger.verbose('Event received: labels.edit');
|
||||||
|
this.logger.verbose('Finding labels in database');
|
||||||
|
const labelsRepository = await this.repository.labels.find({
|
||||||
|
where: { owner: this.instance.name },
|
||||||
|
});
|
||||||
|
|
||||||
|
const savedLabel = labelsRepository.find((l) => l.id === label.id);
|
||||||
|
if (label.deleted && savedLabel) {
|
||||||
|
this.logger.verbose('Sending data to webhook in event LABELS_EDIT');
|
||||||
|
await this.repository.labels.delete({
|
||||||
|
where: { owner: this.instance.name, id: label.id },
|
||||||
|
});
|
||||||
|
this.sendDataWebhook(Events.LABELS_EDIT, { ...label, instance: this.instance.name });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const labelName = label.name.replace(/[^\x20-\x7E]/g, '');
|
||||||
|
if (!savedLabel || savedLabel.color !== label.color || savedLabel.name !== labelName) {
|
||||||
|
this.logger.verbose('Sending data to webhook in event LABELS_EDIT');
|
||||||
|
await this.repository.labels.insert(
|
||||||
|
{
|
||||||
|
color: label.color,
|
||||||
|
name: labelName,
|
||||||
|
owner: this.instance.name,
|
||||||
|
id: label.id,
|
||||||
|
predefinedId: label.predefinedId,
|
||||||
|
},
|
||||||
|
this.instance.name,
|
||||||
|
database.SAVE_DATA.LABELS,
|
||||||
|
);
|
||||||
|
this.sendDataWebhook(Events.LABELS_EDIT, { ...label, instance: this.instance.name });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[Events.LABELS_ASSOCIATION]: async (
|
||||||
|
data: { association: LabelAssociation; type: 'remove' | 'add' },
|
||||||
|
database: Database,
|
||||||
|
) => {
|
||||||
|
this.logger.verbose('Sending data to webhook in event LABELS_ASSOCIATION');
|
||||||
|
|
||||||
|
// Atualiza labels nos chats
|
||||||
|
if (database.SAVE_DATA.CHATS) {
|
||||||
|
const chats = await this.repository.chat.find({
|
||||||
|
where: {
|
||||||
|
owner: this.instance.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const chat = chats.find((c) => c.id === data.association.chatId);
|
||||||
|
if (chat) {
|
||||||
|
let labels = [...chat.labels];
|
||||||
|
if (data.type === 'remove') {
|
||||||
|
labels = labels.filter((label) => label !== data.association.labelId);
|
||||||
|
} else if (data.type === 'add') {
|
||||||
|
labels = [...labels, data.association.labelId];
|
||||||
|
}
|
||||||
|
await this.repository.chat.update(
|
||||||
|
[{ id: chat.id, owner: this.instance.name, labels }],
|
||||||
|
this.instance.name,
|
||||||
|
database.SAVE_DATA.CHATS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Envia dados para o webhook
|
||||||
|
this.sendDataWebhook(Events.LABELS_ASSOCIATION, {
|
||||||
|
instance: this.instance.name,
|
||||||
|
type: data.type,
|
||||||
|
chatId: data.association.chatId,
|
||||||
|
labelId: data.association.labelId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
private eventHandler() {
|
private eventHandler() {
|
||||||
this.logger.verbose('Initializing event handler');
|
this.logger.verbose('Initializing event handler');
|
||||||
this.client.ev.process(async (events) => {
|
this.client.ev.process(async (events) => {
|
||||||
if (!this.endSession) {
|
if (!this.endSession) {
|
||||||
|
this.logger.verbose(`Event received: ${Object.keys(events).join(', ')}`);
|
||||||
const database = this.configService.get<Database>('DATABASE');
|
const database = this.configService.get<Database>('DATABASE');
|
||||||
const settings = await this.findSettings();
|
const settings = await this.findSettings();
|
||||||
|
|
||||||
@ -2078,6 +2210,20 @@ export class WAStartupService {
|
|||||||
const payload = events['contacts.update'];
|
const payload = events['contacts.update'];
|
||||||
this.contactHandle['contacts.update'](payload, database);
|
this.contactHandle['contacts.update'](payload, database);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (events[Events.LABELS_ASSOCIATION]) {
|
||||||
|
this.logger.verbose('Listening event: labels.association');
|
||||||
|
const payload = events[Events.LABELS_ASSOCIATION];
|
||||||
|
this.labelHandle[Events.LABELS_ASSOCIATION](payload, database);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (events[Events.LABELS_EDIT]) {
|
||||||
|
this.logger.verbose('Listening event: labels.edit');
|
||||||
|
const payload = events[Events.LABELS_EDIT];
|
||||||
|
this.labelHandle[Events.LABELS_EDIT](payload, database);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -3107,6 +3253,10 @@ export class WAStartupService {
|
|||||||
onWhatsapp.push(...groups);
|
onWhatsapp.push(...groups);
|
||||||
|
|
||||||
// USERS
|
// USERS
|
||||||
|
const contacts: ContactRaw[] = await this.repository.contact.findManyById({
|
||||||
|
owner: this.instance.name,
|
||||||
|
ids: jids.users.map(({ jid }) => (jid.startsWith('+') ? jid.substring(1) : jid)),
|
||||||
|
});
|
||||||
const verify = await this.client.onWhatsApp(
|
const verify = await this.client.onWhatsApp(
|
||||||
...jids.users.map(({ jid }) => (!jid.startsWith('+') ? `+${jid}` : jid)),
|
...jids.users.map(({ jid }) => (!jid.startsWith('+') ? `+${jid}` : jid)),
|
||||||
);
|
);
|
||||||
@ -3116,18 +3266,6 @@ export class WAStartupService {
|
|||||||
const isBrWithDigit = user.jid.startsWith('55') && user.jid.slice(4, 5) === '9' && user.jid.length === 28;
|
const isBrWithDigit = user.jid.startsWith('55') && user.jid.slice(4, 5) === '9' && user.jid.length === 28;
|
||||||
const jid = isBrWithDigit ? user.jid.slice(0, 4) + user.jid.slice(5) : user.jid;
|
const jid = isBrWithDigit ? user.jid.slice(0, 4) + user.jid.slice(5) : user.jid;
|
||||||
|
|
||||||
const query: ContactQuery = {
|
|
||||||
where: {
|
|
||||||
owner: this.instance.name,
|
|
||||||
id: user.jid.startsWith('+') ? user.jid.substring(1) : user.jid,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const contacts: ContactRaw[] = await this.repository.contact.find(query);
|
|
||||||
let firstContactFound;
|
|
||||||
if (contacts.length > 0) {
|
|
||||||
firstContactFound = contacts[0].pushName;
|
|
||||||
}
|
|
||||||
|
|
||||||
const numberVerified = verify.find((v) => {
|
const numberVerified = verify.find((v) => {
|
||||||
const mainJidSimilarity = levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length);
|
const mainJidSimilarity = levenshtein.get(user.jid, v.jid) / Math.max(user.jid.length, v.jid.length);
|
||||||
const jidSimilarity = levenshtein.get(jid, v.jid) / Math.max(jid.length, v.jid.length);
|
const jidSimilarity = levenshtein.get(jid, v.jid) / Math.max(jid.length, v.jid.length);
|
||||||
@ -3136,7 +3274,7 @@ export class WAStartupService {
|
|||||||
return {
|
return {
|
||||||
exists: !!numberVerified?.exists,
|
exists: !!numberVerified?.exists,
|
||||||
jid: numberVerified?.jid || user.jid,
|
jid: numberVerified?.jid || user.jid,
|
||||||
name: firstContactFound,
|
name: contacts.find((c) => c.id === jid)?.pushName,
|
||||||
number: user.number,
|
number: user.number,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
@ -3808,4 +3946,47 @@ export class WAStartupService {
|
|||||||
throw new BadRequestException('Unable to leave the group', error.toString());
|
throw new BadRequestException('Unable to leave the group', error.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async fetchLabels(): Promise<LabelDto[]> {
|
||||||
|
this.logger.verbose('Fetching labels');
|
||||||
|
const labels = await this.repository.labels.find({
|
||||||
|
where: {
|
||||||
|
owner: this.instance.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return labels.map((label) => ({
|
||||||
|
color: label.color,
|
||||||
|
name: label.name,
|
||||||
|
id: label.id,
|
||||||
|
predefinedId: label.predefinedId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handleLabel(data: HandleLabelDto) {
|
||||||
|
this.logger.verbose('Adding label');
|
||||||
|
const whatsappContact = await this.whatsappNumber({ numbers: [data.number] });
|
||||||
|
if (whatsappContact.length === 0) {
|
||||||
|
throw new NotFoundException('Number not found');
|
||||||
|
}
|
||||||
|
const contact = whatsappContact[0];
|
||||||
|
if (!contact.exists) {
|
||||||
|
throw new NotFoundException('Number is not on WhatsApp');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (data.action === 'add') {
|
||||||
|
await this.client.addChatLabel(contact.jid, data.labelId);
|
||||||
|
|
||||||
|
return { numberJid: contact.jid, labelId: data.labelId, add: true };
|
||||||
|
}
|
||||||
|
if (data.action === 'remove') {
|
||||||
|
await this.client.removeChatLabel(contact.jid, data.labelId);
|
||||||
|
|
||||||
|
return { numberJid: contact.jid, labelId: data.labelId, remove: true };
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new BadRequestException(`Unable to ${data.action} label to chat`, error.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ export enum Events {
|
|||||||
TYPEBOT_START = 'typebot.start',
|
TYPEBOT_START = 'typebot.start',
|
||||||
TYPEBOT_CHANGE_STATUS = 'typebot.change-status',
|
TYPEBOT_CHANGE_STATUS = 'typebot.change-status',
|
||||||
CHAMA_AI_ACTION = 'chama-ai.action',
|
CHAMA_AI_ACTION = 'chama-ai.action',
|
||||||
|
LABELS_EDIT = 'labels.edit',
|
||||||
|
LABELS_ASSOCIATION = 'labels.association',
|
||||||
|
CREDS_UPDATE = 'creds.update',
|
||||||
|
MESSAGING_HISTORY_SET = 'messaging-history.set',
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare namespace wa {
|
export declare namespace wa {
|
||||||
|
@ -9,6 +9,7 @@ import { ChatController } from './controllers/chat.controller';
|
|||||||
import { ChatwootController } from './controllers/chatwoot.controller';
|
import { ChatwootController } from './controllers/chatwoot.controller';
|
||||||
import { GroupController } from './controllers/group.controller';
|
import { GroupController } from './controllers/group.controller';
|
||||||
import { InstanceController } from './controllers/instance.controller';
|
import { InstanceController } from './controllers/instance.controller';
|
||||||
|
import { LabelController } from './controllers/label.controller';
|
||||||
import { ProxyController } from './controllers/proxy.controller';
|
import { ProxyController } from './controllers/proxy.controller';
|
||||||
import { RabbitmqController } from './controllers/rabbitmq.controller';
|
import { RabbitmqController } from './controllers/rabbitmq.controller';
|
||||||
import { SendMessageController } from './controllers/sendMessage.controller';
|
import { SendMessageController } from './controllers/sendMessage.controller';
|
||||||
@ -33,11 +34,13 @@ import {
|
|||||||
WebhookModel,
|
WebhookModel,
|
||||||
WebsocketModel,
|
WebsocketModel,
|
||||||
} from './models';
|
} from './models';
|
||||||
|
import { LabelModel } from './models/label.model';
|
||||||
import { AuthRepository } from './repository/auth.repository';
|
import { AuthRepository } from './repository/auth.repository';
|
||||||
import { ChamaaiRepository } from './repository/chamaai.repository';
|
import { ChamaaiRepository } from './repository/chamaai.repository';
|
||||||
import { ChatRepository } from './repository/chat.repository';
|
import { ChatRepository } from './repository/chat.repository';
|
||||||
import { ChatwootRepository } from './repository/chatwoot.repository';
|
import { ChatwootRepository } from './repository/chatwoot.repository';
|
||||||
import { ContactRepository } from './repository/contact.repository';
|
import { ContactRepository } from './repository/contact.repository';
|
||||||
|
import { LabelRepository } from './repository/label.repository';
|
||||||
import { MessageRepository } from './repository/message.repository';
|
import { MessageRepository } from './repository/message.repository';
|
||||||
import { MessageUpRepository } from './repository/messageUp.repository';
|
import { MessageUpRepository } from './repository/messageUp.repository';
|
||||||
import { ProxyRepository } from './repository/proxy.repository';
|
import { ProxyRepository } from './repository/proxy.repository';
|
||||||
@ -82,6 +85,7 @@ const sqsRepository = new SqsRepository(SqsModel, configService);
|
|||||||
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
|
const chatwootRepository = new ChatwootRepository(ChatwootModel, configService);
|
||||||
const settingsRepository = new SettingsRepository(SettingsModel, configService);
|
const settingsRepository = new SettingsRepository(SettingsModel, configService);
|
||||||
const authRepository = new AuthRepository(AuthModel, configService);
|
const authRepository = new AuthRepository(AuthModel, configService);
|
||||||
|
const labelRepository = new LabelRepository(LabelModel, configService);
|
||||||
|
|
||||||
export const repository = new RepositoryBroker(
|
export const repository = new RepositoryBroker(
|
||||||
messageRepository,
|
messageRepository,
|
||||||
@ -98,6 +102,7 @@ export const repository = new RepositoryBroker(
|
|||||||
proxyRepository,
|
proxyRepository,
|
||||||
chamaaiRepository,
|
chamaaiRepository,
|
||||||
authRepository,
|
authRepository,
|
||||||
|
labelRepository,
|
||||||
configService,
|
configService,
|
||||||
dbserver?.getClient(),
|
dbserver?.getClient(),
|
||||||
);
|
);
|
||||||
@ -165,6 +170,7 @@ export const instanceController = new InstanceController(
|
|||||||
export const sendMessageController = new SendMessageController(waMonitor);
|
export const sendMessageController = new SendMessageController(waMonitor);
|
||||||
export const chatController = new ChatController(waMonitor);
|
export const chatController = new ChatController(waMonitor);
|
||||||
export const groupController = new GroupController(waMonitor);
|
export const groupController = new GroupController(waMonitor);
|
||||||
|
export const labelController = new LabelController(waMonitor);
|
||||||
|
|
||||||
|
|
||||||
export const WAStartupClass: {
|
export const WAStartupClass: {
|
||||||
|
Loading…
Reference in New Issue
Block a user