diff --git a/CHANGELOG.md b/CHANGELOG.md index 43ef7281..47b359e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,24 @@ -# 1.5.3 (develop) +# 1.5.5 (develop) + +### Fixed + +* Adjusts in proxy +* Adjusts in start session for Typebot +* Added mimetype field when sending media + +# 1.5.4 (2023-10-09 20:43) + +### Fixed + +* Baileys logger typing issue resolved +* Solved problem with duplicate messages in chatwoot + +# 1.5.3 (2023-10-06 18:55) ### Feature * Swagger documentation +* Added base 64 sending option via webhook ### Fixed @@ -13,6 +29,7 @@ * Adjustment to start typebot, added startSession parameter * Chatwoot now receives messages sent via api and typebot * Fixed problem with starting with an input in typebot +* Added check to ensure variables are not empty before executing foreach in start typebot # 1.5.2 (2023-09-28 17:56) diff --git a/Dockerfile b/Dockerfile index e5c218cc..f60ed48d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:20.7.0-alpine -LABEL version="1.5.3" description="Api to control whatsapp features through http requests." +LABEL version="1.5.5" description="Api to control whatsapp features through http requests." LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes" LABEL contact="contato@agenciadgcode.com" diff --git a/package.json b/package.json index 16c7466d..7997c3fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "1.5.3", + "version": "1.5.5", "description": "Rest api for communication with WhatsApp", "main": "./dist/src/main.js", "scripts": { @@ -46,7 +46,7 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@sentry/node": "^7.59.2", - "@whiskeysockets/baileys": "^6.5.0", + "@whiskeysockets/baileys": "github:WhiskeySockets/Baileys#fix-lids", "amqplib": "^0.10.3", "axios": "^1.3.5", "class-validator": "^0.13.2", diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 77693bcc..589ba8dc 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -23,9 +23,9 @@ info: This project is based on the [evolution](https://github.com/code-chat-br/whatsapp-api). The original project is an implementation of [Baileys](https://github.com/WhiskeySockets/Baileys), serving as a Restful API service that controls WhatsApp functions.
The code allows the creation of multiservice chats, service bots, or any other system that utilizes WhatsApp. The documentation provides instructions on how to set up and use the project, as well as additional information about its features and configuration options. - + [![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442) - version: 1.5.2 + version: 1.5.5 contact: name: DavidsonGomes email: contato@agenciadgcode.com @@ -83,13 +83,13 @@ paths: type: boolean description: QR Code of the instance (optional). example: - instanceName: 'exampleInstance' - token: '87F3F7D0-4B8A-45D0-8618-7399E4AD6469' + instanceName: "exampleInstance" + token: "87F3F7D0-4B8A-45D0-8618-7399E4AD6469" qrcode: true security: - apikeyAuth: [] responses: - '200': + "200": description: Successful response content: application/json: {} @@ -106,15 +106,15 @@ paths: schema: type: string description: Retrieve one or all instances (optional). - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: schema: type: array - items: + items: type: object properties: instance: @@ -148,9 +148,9 @@ paths: schema: type: string description: Connect to your instance. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: @@ -162,6 +162,24 @@ paths: base64: type: string description: The QR Code as a string. + /instance/restart/{instanceName}: + put: + tags: + - Instance Controller + summary: Instance Restart + parameters: + - name: instanceName + in: path + required: true + schema: + type: string + description: Connect to your instance. + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} /instance/connectionState/{instanceName}: get: tags: @@ -174,9 +192,9 @@ paths: schema: type: string description: Check the connection state of your instance. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -192,9 +210,9 @@ paths: schema: type: string description: Logout from your instance. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -210,9 +228,9 @@ paths: schema: type: string description: Delete your instance. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -232,14 +250,14 @@ paths: number: type: string description: The recipient's phone number. - example: '1234567890' + example: "1234567890" textMessage: type: object properties: text: type: string description: The content of the text message. - example: 'Hello, World!' + example: "Hello, World!" options: type: object properties: @@ -248,8 +266,43 @@ paths: description: Delay time before sending the message. presence: type: string - enum: ['composing', 'recording', 'paused'] + enum: ["composing", "recording", "paused"] description: Indicates the sender's action/status. + linkPreview: + type: boolean + description: Indicates whether to enable link preview. + quoted: + type: object + properties: + key: + type: object + properties: + remoteJid: + type: string + description: The ID of the recipient of the original message. + fromMe: + type: boolean + description: Indicates if the message was sent from the user. + id: + type: string + description: The ID of the original message. + message: + type: object + properties: + conversation: + type: string + description: The content of the quoted message. + mentions: + type: object + properties: + everyone: + type: boolean + description: Indicates whether to mention everyone. + mentioned: + type: array + items: + type: string + description: The phone numbers of the users to be mentioned. required: - number - textMessage @@ -260,9 +313,61 @@ paths: schema: type: string description: The name of the instance to which the message should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": + description: Successful response + content: + application/json: {} + /message/sendStatus/{instanceName}: + post: + tags: + - Send Message Controller + summary: Send a status message. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + statusMessage: + type: object + properties: + type: + type: string + enum: ["text", "image", "video", "audio"] + description: Type of the status message. + content: + type: string + description: The content of the status message. + backgroundColor: + type: string + description: The background color of the status message. + font: + type: integer + enum: [1, 2, 3, 4, 5] + description: The font of the status message. + allContacts: + type: boolean + description: Indicates whether to send the status message to all contacts. + statusJidList: + type: array + items: + type: string + description: The phone numbers of the users to whom the status message should be sent. + required: + - type + parameters: + - name: instanceName + in: path + required: true + schema: + type: string + description: The name of the instance to which the status message should be sent. + example: "evolution" + responses: + "200": description: Successful response content: application/json: {} @@ -281,13 +386,13 @@ paths: number: type: string description: The recipient's phone number. - example: '1234567890' + example: "1234567890" mediaMessage: type: object properties: mediatype: type: string - enum: ['image', 'document', 'video', 'audio'] + enum: ["image", "document", "video", "audio"] description: Type of the media content. fileName: type: string @@ -309,8 +414,43 @@ paths: description: Delay time before sending the message. presence: type: string - enum: ['composing', 'recording', 'paused'] + enum: ["composing", "recording", "paused"] description: Indicates the sender's action/status. + linkPreview: + type: boolean + description: Indicates whether to enable link preview. + quoted: + type: object + properties: + key: + type: object + properties: + remoteJid: + type: string + description: The ID of the recipient of the original message. + fromMe: + type: boolean + description: Indicates if the message was sent from the user. + id: + type: string + description: The ID of the original message. + message: + type: object + properties: + conversation: + type: string + description: The content of the quoted message. + mentions: + type: object + properties: + everyone: + type: boolean + description: Indicates whether to mention everyone. + mentioned: + type: array + items: + type: string + description: The phone numbers of the users to be mentioned. parameters: - name: instanceName in: path @@ -318,60 +458,146 @@ paths: schema: type: string description: The name of the instance to which the media message should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} - /message/sendMediaFile/{instanceName}: + /message/sendWhatsAppAudio/{instanceName}: post: tags: - Send Message Controller - summary: Send a media file (image, video, document, audio) directly to a specified instance. + summary: Send an audio message via WhatsApp to a specified instance. + description: This endpoint allows users to share an audio message. requestBody: required: true content: - multipart/form-data: + application/json: schema: type: object properties: number: type: string description: The recipient's phone number. - example: '1234567890' - caption: - type: string - description: Caption to accompany the media (optional). - attachment: - type: string - format: binary - description: The media file to be sent. - mediatype: - type: string - enum: ['image', 'document', 'video', 'audio'] - description: Type of the media content. - presence: - type: string - enum: ['composing', 'recording', 'paused'] - description: Indicates the sender's action/status (optional). - delay: - type: integer - description: Delay time before sending the message (optional). + example: "1234567890" + audioMessage: + type: object + properties: + audio: + type: string + description: URL of the audio file to be sent. + required: + - audio + options: + type: object + properties: + delay: + type: integer + description: Delay time before sending the message. + presence: + type: string + enum: ["composing", "recording", "paused"] + description: Indicates the sender's action/status. + linkPreview: + type: boolean + description: Indicates whether to enable link preview. + quoted: + type: object + properties: + key: + type: object + properties: + remoteJid: + type: string + description: The ID of the recipient of the original message. + fromMe: + type: boolean + description: Indicates if the message was sent from the user. + id: + type: string + description: The ID of the original message. + message: + type: object + properties: + conversation: + type: string + description: The content of the quoted message. + mentions: + type: object + properties: + everyone: + type: boolean + description: Indicates whether to mention everyone. + mentioned: + type: array + items: + type: string + description: The phone numbers of the users to be mentioned. required: - number - - attachment - - mediatype + - audioMessage.audio parameters: - name: instanceName in: path required: true schema: type: string - description: The name of the instance to which the media file should be sent. - example: 'evolution' + description: The name of the instance to which the audio should be sent. + example: "evolution" responses: - '200': + "200": + description: Successful response + content: + application/json: {} + /message/sendSticker/{instanceName}: + post: + tags: + - Send Message Controller + summary: Send an sticker to a specified instance. + description: This endpoint allows users to share an sticker message. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + number: + type: string + description: The recipient's phone number. + example: "1234567890" + stickerMessage: + type: object + properties: + image: + type: string + description: URL of the audio file to be sent. + required: + - image + options: + type: object + properties: + delay: + type: integer + description: Delay time before sending the message. + presence: + type: string + enum: ["composing", "recording", "paused"] + description: Indicates the sender's action/status. + required: + - number + - audioMessage.audio + parameters: + - name: instanceName + in: path + required: true + schema: + type: string + description: The name of the instance to which the audio should be sent. + example: "evolution" + responses: + "200": description: Successful response content: application/json: {} @@ -391,7 +617,7 @@ paths: number: type: string description: The recipient's phone number. - example: '1234567890' + example: "1234567890" locationMessage: type: object properties: @@ -417,8 +643,43 @@ paths: description: Delay time before sending the message. presence: type: string - enum: ['composing', 'recording', 'paused'] + enum: ["composing", "recording", "paused"] description: Indicates the sender's action/status. + linkPreview: + type: boolean + description: Indicates whether to enable link preview. + quoted: + type: object + properties: + key: + type: object + properties: + remoteJid: + type: string + description: The ID of the recipient of the original message. + fromMe: + type: boolean + description: Indicates if the message was sent from the user. + id: + type: string + description: The ID of the original message. + message: + type: object + properties: + conversation: + type: string + description: The content of the quoted message. + mentions: + type: object + properties: + everyone: + type: boolean + description: Indicates whether to mention everyone. + mentioned: + type: array + items: + type: string + description: The phone numbers of the users to be mentioned. required: - number - locationMessage.latitude @@ -430,9 +691,9 @@ paths: schema: type: string description: The name of the instance to which the location should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -452,7 +713,7 @@ paths: number: type: string description: The recipient's phone number. - example: '1234567890' + example: "1234567890" contactMessage: type: array items: @@ -467,6 +728,15 @@ paths: phoneNumber: type: string description: Phone number of the contact. + organization: + type: string + description: Organization of the contact. + email: + type: string + description: Email address of the contact. + url: + type: string + description: Url of the contact. required: - fullName - wuid @@ -476,11 +746,46 @@ paths: properties: delay: type: integer - description: Delay time before sending the contact. + description: Delay time before sending the message. presence: type: string - enum: ['composing', 'recording', 'paused'] + enum: ["composing", "recording", "paused"] description: Indicates the sender's action/status. + linkPreview: + type: boolean + description: Indicates whether to enable link preview. + quoted: + type: object + properties: + key: + type: object + properties: + remoteJid: + type: string + description: The ID of the recipient of the original message. + fromMe: + type: boolean + description: Indicates if the message was sent from the user. + id: + type: string + description: The ID of the original message. + message: + type: object + properties: + conversation: + type: string + description: The content of the quoted message. + mentions: + type: object + properties: + everyone: + type: boolean + description: Indicates whether to mention everyone. + mentioned: + type: array + items: + type: string + description: The phone numbers of the users to be mentioned. required: - number parameters: @@ -490,9 +795,9 @@ paths: schema: type: string description: The name of the instance to which the contacts should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -540,18 +845,18 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} - /message/sendWhatsAppAudio/{instanceName}: + /message/sendPoll/{instanceName}: post: tags: - Send Message Controller - summary: Send an audio message via WhatsApp to a specified instance. - description: This endpoint allows users to share an audio message. + summary: Send a poll to a specified instance. + description: This endpoint allows users to send a poll to a chat. requestBody: required: true content: @@ -562,75 +867,76 @@ paths: number: type: string description: The recipient's phone number. - example: '1234567890' - audioMessage: + example: "1234567890" + pollMessage: type: object properties: - audio: + name: type: string - description: URL of the audio file to be sent. - required: - - audio + description: Name or title of the poll. + selectableCount: + type: integer + description: Number of selectable options. + values: + type: array + items: + type: string + description: The options of the poll. options: type: object properties: delay: type: integer - description: Delay time before sending the audio message. - required: - - number - - audioMessage.audio + description: Delay time before sending the message. + presence: + type: string + enum: ["composing", "recording", "paused"] + description: Indicates the sender's action/status. + linkPreview: + type: boolean + description: Indicates whether to enable link preview. + quoted: + type: object + properties: + key: + type: object + properties: + remoteJid: + type: string + description: The ID of the recipient of the original message. + fromMe: + type: boolean + description: Indicates if the message was sent from the user. + id: + type: string + description: The ID of the original message. + message: + type: object + properties: + conversation: + type: string + description: The content of the quoted message. + mentions: + type: object + properties: + everyone: + type: boolean + description: Indicates whether to mention everyone. + mentioned: + type: array + items: + type: string + description: The phone numbers of the users to be mentioned. parameters: - name: instanceName in: path required: true schema: type: string - description: The name of the instance to which the audio should be sent. - example: 'evolution' + description: The name of the instance to which the poll should be sent. + example: "evolution" responses: - '200': - description: Successful response - content: - application/json: {} - /message/sendWhatsAppAudioFile/{instanceName}: - post: - tags: - - Send Message Controller - summary: Upload and send a WhatsApp audio file to a specified instance. - description: This endpoint allows users to upload and send an audio file via WhatsApp. - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: '1234567890' - attachment: - type: string - description: Audio file to be sent. - format: binary - delay: - type: integer - description: Delay time before sending the audio message. - example: 5 - required: - - number - - attachment - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: 'evolution' - responses: - '200': + "200": description: Successful response content: application/json: {} @@ -654,7 +960,7 @@ paths: type: string description: WhatsApp phone number. example: - - '1234567890' + - "1234567890" required: - numbers parameters: @@ -664,9 +970,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -683,7 +989,7 @@ paths: schema: type: object properties: - readMessages: + read_messages: type: array items: type: object @@ -702,7 +1008,7 @@ paths: - fromMe - id required: - - readMessages + - read_messages parameters: - name: instanceName in: path @@ -710,9 +1016,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -762,9 +1068,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -781,9 +1087,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -812,9 +1118,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -845,9 +1151,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -863,14 +1169,18 @@ paths: schema: type: object properties: - key: + message: type: object properties: - id: - type: string - description: Unique ID of the media message to be converted. - required: - - id + key: + type: object + properties: + id: string + description: Unique ID of the message. + convertToMp4: + type: boolean + description: Indicates whether to convert the media to MP4 format. + example: true parameters: - name: instanceName in: path @@ -878,9 +1188,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -929,9 +1239,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -962,9 +1272,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -981,9 +1291,9 @@ paths: schema: type: string description: The name of the instance to which the reaction should be sent. - example: 'evolution' + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -1002,25 +1312,25 @@ paths: properties: subject: type: string - description: '- required - The name of the group.' + description: "- required - The name of the group." description: type: string - description: '- optional - A brief description or summary of the group.' + description: "- optional - A brief description or summary of the group." participants: type: array items: type: string - description: '- required - List of participant phone numbers.' + description: "- required - List of participant phone numbers." parameters: - name: instanceName in: path schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -1038,23 +1348,198 @@ paths: properties: image: type: string - description: '- required - URL of the new group picture.' + description: "- required - URL of the new group picture." parameters: - name: groupJid in: query schema: type: string - description: '- required - The unique identifier of the group.' - example: '120363046555718472@g.us' + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" - name: instanceName in: path schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": + description: Successful response + content: + application/json: {} + /group/updateGroupSubject/{instanceName}: + put: + tags: + - Group Controller + summary: Update the group's display picture. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + subject: + type: string + description: "- required - The new name of the group." + parameters: + - name: groupJid + in: query + schema: + type: string + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /group/updateGroupDescription/{instanceName}: + put: + tags: + - Group Controller + summary: Update the group's display picture. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + description: + type: string + description: "- required - The new description of the group." + parameters: + - name: groupJid + in: query + schema: + type: string + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /group/inviteCode/{instanceName}: + get: + tags: + - Group Controller + summary: Update the group's display picture. + parameters: + - name: groupJid + in: query + schema: + type: string + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /group/revokeInviteCode/{instanceName}: + put: + tags: + - Group Controller + summary: Update the group's display picture. + parameters: + - name: groupJid + in: query + schema: + type: string + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /group/sendInvite/{instanceName}: + post: + tags: + - Group Controller + summary: Update the group's display picture. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + groupJid: + type: string + description: "The unique identifier of the group." + description: + type: string + description: "The new description of the group." + numbers: + type: array + description: "List of participant phone numbers to be invited." + items: + type: string + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /group/inviteInfo/{instanceName}: + get: + tags: + - Group Controller + summary: Retrieve details about a specific group. + parameters: + - name: inviteCode + in: query + schema: + type: string + description: "- required - The invite code of the group." + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": description: Successful response content: application/json: {} @@ -1068,17 +1553,40 @@ paths: in: query schema: type: string - description: '- required - The unique identifier of the group.' - example: '120363046555718472@g.us' + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" - name: instanceName in: path schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": + description: Successful response + content: + application/json: {} + /group/fetchAllGroups/{instanceName}: + get: + tags: + - Group Controller + summary: Retrieve details about a specific group. + parameters: + - name: getParticipants + in: query + schema: + type: boolean + description: "- required - Indicates whether to retrieve the participants of the group." + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": description: Successful response content: application/json: {} @@ -1092,67 +1600,17 @@ paths: in: query schema: type: string - description: '- required - The unique identifier of the group.' - example: '120363046555718472@g.us' + description: "- required - The unique identifier of the group." + example: "120363046555718472@g.us" - name: instanceName in: path schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': - description: Successful response - content: - application/json: {} - /group/inviteCode/{instanceName}: - get: - tags: - - Group Controller - summary: Retrieve the group's invite link. - parameters: - - name: groupJid - in: query - schema: - type: string - description: '- required - The unique identifier of the group.' - example: '120363046555718472@g.us' - - name: instanceName - in: path - schema: - type: string - required: true - description: '- required' - example: 'evolution' - responses: - '200': - description: Successful response - content: - application/json: {} - /group/revokeInviteCode/{instanceName}: - put: - tags: - - Group Controller - summary: Invalidate the existing group invite link. - requestBody: - content: {} - parameters: - - name: groupJid - in: query - schema: - type: string - description: '- required - The unique identifier of the group.' - example: '120363046555718472@g.us' - - name: instanceName - in: path - schema: - type: string - required: true - description: '- required' - example: 'evolution' - responses: - '200': + "200": description: Successful response content: application/json: {} @@ -1170,23 +1628,81 @@ paths: properties: action: type: string - enum: ['add', 'remove', 'promote', 'demote'] - description: '- required - The action to be taken on the participant.' + enum: ["add", "remove", "promote", "demote"] + description: "- required - The action to be taken on the participant." participants: type: array items: type: string - description: '- required - List of participant phone numbers to be updated.' + description: "- required - List of participant phone numbers to be updated." parameters: - name: instanceName in: path schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": + description: Successful response + content: + application/json: {} + /group/updateSetting/{instanceName}: + put: + tags: + - Group Controller + summary: Update the status or role of a participant in the group. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + action: + type: string + enum: + ["announcement", "not_announcement", "locked", "unlocked"] + description: "- required - The action to be taken on the participant." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /group/toggleEphemeral/{instanceName}: + put: + tags: + - Group Controller + summary: Update the status or role of a participant in the group. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + expiration: + type: number + description: "- required - The action to be taken on the participant." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": description: Successful response content: application/json: {} @@ -1201,10 +1717,10 @@ paths: schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -1223,9 +1739,9 @@ paths: properties: oldToken: type: string - description: '- required - The expired JWT token.' + description: "- required - The expired JWT token." responses: - '200': + "200": description: Successful response content: application/json: {} @@ -1242,23 +1758,55 @@ paths: schema: type: object properties: - enabled: - type: boolean - description: 'Indicates whether the webhook is active.' url: type: string format: uri - description: 'The endpoint URL where the webhook data will be sent.' + description: "The endpoint URL where the webhook data will be sent." + webhook_by_events: + type: boolean + description: "Indicates whether to send the webhook data by events." + webhook_base64: + type: boolean + description: "Indicates whether to send the webhook data in Base64 format." + events: + type: array + enum: + [ + "APPLICATION_STARTUP", + "QRCODE_UPDATED", + "MESSAGES_SET", + "MESSAGES_UPSERT", + "MESSAGES_UPDATE", + "MESSAGES_DELETE", + "SEND_MESSAGE", + "CONTACTS_SET", + "CONTACTS_UPSERT", + "CONTACTS_UPDATE", + "PRESENCE_UPDATE", + "CHATS_SET", + "CHATS_UPSERT", + "CHATS_UPDATE", + "CHATS_DELETE", + "GROUPS_UPSERT", + "GROUP_UPDATE", + "GROUP_PARTICIPANTS_UPDATE", + "CONNECTION_UPDATE", + "CALL", + "NEW_JWT_TOKEN", + ] + items: + type: string + description: "List of events to be sent to the webhook." parameters: - name: instanceName in: path schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": description: Successful response content: application/json: {} @@ -1273,10 +1821,777 @@ paths: schema: type: string required: true - description: '- required' - example: 'evolution' + description: "- required" + example: "evolution" responses: - '200': + "200": description: Successful response content: - application/json: {} \ No newline at end of file + application/json: {} + + /websocket/set/{instanceName}: + post: + tags: + - Websocket + summary: Set up or modify the Websocket for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + description: "Indicates whether to enable the websocket." + events: + type: array + enum: + [ + "APPLICATION_STARTUP", + "QRCODE_UPDATED", + "MESSAGES_SET", + "MESSAGES_UPSERT", + "MESSAGES_UPDATE", + "MESSAGES_DELETE", + "SEND_MESSAGE", + "CONTACTS_SET", + "CONTACTS_UPSERT", + "CONTACTS_UPDATE", + "PRESENCE_UPDATE", + "CHATS_SET", + "CHATS_UPSERT", + "CHATS_UPDATE", + "CHATS_DELETE", + "GROUPS_UPSERT", + "GROUP_UPDATE", + "GROUP_PARTICIPANTS_UPDATE", + "CONNECTION_UPDATE", + "CALL", + "NEW_JWT_TOKEN", + ] + items: + type: string + description: "List of events to be sent to the websocket." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /websocket/find/{instanceName}: + get: + tags: + - Websocket + summary: Retrieve the websocket settings for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /rabbitmq/set/{instanceName}: + post: + tags: + - RabbitMQ + summary: Set up or modify the RabbitMQ for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + description: "Indicates whether to enable the RabbitMQ." + events: + type: array + enum: + [ + "APPLICATION_STARTUP", + "QRCODE_UPDATED", + "MESSAGES_SET", + "MESSAGES_UPSERT", + "MESSAGES_UPDATE", + "MESSAGES_DELETE", + "SEND_MESSAGE", + "CONTACTS_SET", + "CONTACTS_UPSERT", + "CONTACTS_UPDATE", + "PRESENCE_UPDATE", + "CHATS_SET", + "CHATS_UPSERT", + "CHATS_UPDATE", + "CHATS_DELETE", + "GROUPS_UPSERT", + "GROUP_UPDATE", + "GROUP_PARTICIPANTS_UPDATE", + "CONNECTION_UPDATE", + "CALL", + "NEW_JWT_TOKEN", + ] + items: + type: string + description: "List of events to be sent to the RabbitMQ." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /rabbitmq/find/{instanceName}: + get: + tags: + - RabbitMQ + summary: Retrieve the RabbitMQ settings for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /settings/set/{instanceName}: + post: + tags: + - Settings + summary: Set up or modify the Settings for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + reject_call: + type: boolean + description: "Indicates whether to reject incoming calls." + msg_call: + type: string + description: "Message to be sent when rejecting a call." + groups_ignore: + type: boolean + description: "Indicates whether to ignore group messages." + always_online: + type: boolean + description: "Indicates whether to keep the instance always online." + read_messages: + type: boolean + description: "Indicates whether to mark messages as read." + read_status: + type: boolean + description: "Indicates whether to mark status messages as read." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /settings/find/{instanceName}: + get: + tags: + - Settings + summary: Retrieve the Settings for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /chatwoot/set/{instanceName}: + post: + tags: + - Chatwoot + summary: Set up or modify the Chatwoot for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + description: "Indicates whether to enable the Chatwoot integration." + account_id: + type: string + description: "The Chatwoot account ID." + token: + type: string + description: "The Chatwoot token." + url: + type: string + description: "The Chatwoot URL." + sign_msg: + type: boolean + description: "Indicates whether to sign messages." + reopen_conversation: + type: boolean + description: "Indicates whether to reopen conversations." + conversation_pending: + type: boolean + description: "Indicates whether to mark conversations as pending." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chatwoot/find/{instanceName}: + get: + tags: + - Chatwoot + summary: Retrieve the Chatwoot for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /typebot/set/{instanceName}: + post: + tags: + - Typebot + summary: Set up or modify the Typebot for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + description: "Indicates whether to enable the Typebot integration." + url: + type: string + description: "The Chatwoot URL." + typebot: + type: string + description: "The Typebot Name." + expire: + type: number + description: "The Typebot Expire." + keyword_finish: + type: string + description: "The Typebot Keyword Finish." + delay_message: + type: number + description: "The Typebot Delay Message." + unknown_message: + type: string + description: "The Typebot Unknown Message." + listening_from_me: + type: boolean + description: "Indicates whether to listening from me." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /typebot/start/{instanceName}: + post: + tags: + - Typebot + summary: Start the Typebot for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + url: + type: string + description: "The Typebot URL." + typebot: + type: string + description: "The Typebot Name." + remoteJid: + type: string + description: "The Typebot RemoteJid." + startSession: + type: boolean + description: "Indicates whether to start session." + variables: + type: array + description: "List of variables." + items: + type: object + properties: + name: + type: string + description: "The variable name." + value: + type: string + description: "The variable value." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /typebot/find/{instanceName}: + get: + tags: + - Typebot + summary: Retrieve the Typebot for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /typebot/changeStatus/{instanceName}: + post: + tags: + - Typebot + summary: Change the status of the Typebot for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + remoteJid: + type: string + description: "The Typebot RemoteJid." + status: + type: string + description: "The Typebot Status." + enum: ["opened", "paused", "closed"] + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /proxy/set/{instanceName}: + post: + tags: + - Proxy + summary: Set up or modify the Proxy for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + description: "Indicates whether to enable the Proxy integration." + proxy: + type: string + description: "The Proxy URI." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /proxy/find/{instanceName}: + get: + tags: + - Proxy + summary: Retrieve the Proxy for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /chamaai/set/{instanceName}: + post: + tags: + - Chama AI + summary: Set up or modify the Chama AI for an instance. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + description: "Indicates whether to enable the Chamai AI integration." + url: + type: string + description: "The Chamai AI URL." + token: + type: string + description: "The Chamai AI Token." + waNumber: + type: string + description: "The Chamai AI WhatsApp Number." + answerByAudio: + type: boolean + description: "Indicates whether to answer by audio." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chamaai/find/{instanceName}: + get: + tags: + - Chama AI + summary: Retrieve the Chama AI for a specific instance. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + + /chat/fetchBusinessProfile/{instanceName}: + post: + tags: + - Profile Settings + summary: Fetch the business profile of a specific contact. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + number: + type: string + description: "- required - The phone number of the contact." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/fetchProfile/{instanceName}: + post: + tags: + - Profile Settings + summary: Fetch the profile of a specific contact. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + number: + type: string + description: "- required - The phone number of the contact." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/updateProfileName/{instanceName}: + post: + tags: + - Profile Settings + summary: Update the name of a specific contact. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: "- required - The new name of the contact." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/updateProfileStatus/{instanceName}: + post: + tags: + - Profile Settings + summary: Update the status of a specific contact. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + status: + type: string + description: "- required - The new status of the contact." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/updateProfilePicture/{instanceName}: + put: + tags: + - Profile Settings + summary: Update the profile picture of a specific contact. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + picture: + type: string + description: "- required - The new profile picture of the contact." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/removeProfilePicture/{instanceName}: + delete: + tags: + - Profile Settings + summary: Remove the profile picture of a specific contact. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/fetchPrivacySettings/{instanceName}: + get: + tags: + - Profile Settings + summary: Fetch the privacy settings of a specific contact. + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} + /chat/updatePrivacySettings/{instanceName}: + put: + tags: + - Profile Settings + summary: Update the privacy settings of a specific contact. + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + privacySettings: + type: object + description: "- required - The new privacy settings of the contact." + properties: + readreceipts: + type: string + enum: ["all", "none"] + description: "- required - The new read receipts privacy setting of the contact." + profile: + type: string + enum: ["all", "contacts", "contact_blacklist", "none"] + description: "- required - The new profile privacy setting of the contact." + status: + type: string + enum: ["all", "contacts", "contact_blacklist", "none"] + description: "- required - The new status privacy setting of the contact." + online: + type: string + enum: ["all", "match_last_seen"] + description: "- required - The new online privacy setting of the contact." + last: + type: string + enum: ["all", "contacts", "contact_blacklist", "none"] + description: "- required - The new last seen privacy setting of the contact." + groupadd: + type: string + enum: ["all", "contacts", "contact_blacklist", "none"] + description: "- required - The new group add privacy setting of the contact." + parameters: + - name: instanceName + in: path + schema: + type: string + required: true + description: "- required" + example: "evolution" + responses: + "200": + description: Successful response + content: + application/json: {} diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index a2903f59..3ead8690 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -41,6 +41,7 @@ export class InstanceController { instanceName, webhook, webhook_by_events, + webhook_base64, events, qrcode, number, @@ -139,6 +140,7 @@ export class InstanceController { url: webhook, events: newEvents, webhook_by_events, + webhook_base64, }); webhookEvents = (await this.webhookService.find(instance)).events; @@ -297,6 +299,7 @@ export class InstanceController { webhook: { webhook, webhook_by_events, + webhook_base64, events: webhookEvents, }, websocket: { @@ -390,6 +393,7 @@ export class InstanceController { webhook: { webhook, webhook_by_events, + webhook_base64, events: webhookEvents, }, websocket: { diff --git a/src/whatsapp/controllers/views.controller.ts b/src/whatsapp/controllers/views.controller.ts index e775b960..7e15dfe7 100644 --- a/src/whatsapp/controllers/views.controller.ts +++ b/src/whatsapp/controllers/views.controller.ts @@ -1,15 +1,21 @@ import { Request, Response } from 'express'; -import { ConfigService } from '../../config/env.config'; +import { Auth, ConfigService, HttpServer } from '../../config/env.config'; import { HttpStatus } from '../routers/index.router'; import { WAMonitoringService } from '../services/monitor.service'; export class ViewsController { - constructor(private readonly waMonit: WAMonitoringService, private readonly configService: ConfigService) {} + constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {} public async manager(request: Request, response: Response) { try { - return response.status(HttpStatus.OK).render('manager'); + const token = this.configService.get('AUTHENTICATION').API_KEY.KEY; + const port = this.configService.get('SERVER').PORT; + + const instances = await this.waMonitor.instanceInfo(); + + console.log('INSTANCES: ', instances); + return response.status(HttpStatus.OK).render('manager', { token, port, instances }); } catch (error) { console.log('ERROR: ', error); } diff --git a/src/whatsapp/dto/instance.dto.ts b/src/whatsapp/dto/instance.dto.ts index 7807d1a5..700fa099 100644 --- a/src/whatsapp/dto/instance.dto.ts +++ b/src/whatsapp/dto/instance.dto.ts @@ -5,6 +5,7 @@ export class InstanceDto { token?: string; webhook?: string; webhook_by_events?: boolean; + webhook_base64?: boolean; events?: string[]; reject_call?: boolean; msg_call?: string; diff --git a/src/whatsapp/dto/sendMessage.dto.ts b/src/whatsapp/dto/sendMessage.dto.ts index c2ddb3a2..e0d6d8f9 100644 --- a/src/whatsapp/dto/sendMessage.dto.ts +++ b/src/whatsapp/dto/sendMessage.dto.ts @@ -61,6 +61,7 @@ export class SendPollDto extends Metadata { export type MediaType = 'image' | 'document' | 'video' | 'audio'; export class MediaMessage { mediatype: MediaType; + mimetype?: string; caption?: string; // for document fileName?: string; diff --git a/src/whatsapp/dto/webhook.dto.ts b/src/whatsapp/dto/webhook.dto.ts index 5203884d..87a21883 100644 --- a/src/whatsapp/dto/webhook.dto.ts +++ b/src/whatsapp/dto/webhook.dto.ts @@ -3,4 +3,5 @@ export class WebhookDto { url?: string; events?: string[]; webhook_by_events?: boolean; + webhook_base64?: boolean; } diff --git a/src/whatsapp/models/typebot.model.ts b/src/whatsapp/models/typebot.model.ts index c9232bd8..c8ae7105 100644 --- a/src/whatsapp/models/typebot.model.ts +++ b/src/whatsapp/models/typebot.model.ts @@ -48,7 +48,7 @@ const typebotSchema = new Schema({ prefilledVariables: { remoteJid: { type: String, required: false }, pushName: { type: String, required: false }, - additionalData: { type: Schema.Types.Mixed, required: false } + additionalData: { type: Schema.Types.Mixed, required: false }, }, }, ], diff --git a/src/whatsapp/models/webhook.model.ts b/src/whatsapp/models/webhook.model.ts index 05a222e0..9a1bb43d 100644 --- a/src/whatsapp/models/webhook.model.ts +++ b/src/whatsapp/models/webhook.model.ts @@ -8,6 +8,7 @@ export class WebhookRaw { enabled?: boolean; events?: string[]; webhook_by_events?: boolean; + webhook_base64?: boolean; } const webhookSchema = new Schema({ @@ -16,6 +17,7 @@ const webhookSchema = new Schema({ 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 = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook'); diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 6c145ea2..b19fa31f 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -914,7 +914,7 @@ export class ChatwootService { }, }; - await waInstance?.audioWhatsapp(data); + await waInstance?.audioWhatsapp(data, true); this.logger.verbose('audio sent'); return; @@ -939,7 +939,7 @@ export class ChatwootService { data.mediaMessage.caption = caption; } - await waInstance?.mediaMessage(data); + await waInstance?.mediaMessage(data, true); this.logger.verbose('media sent'); return; @@ -1074,7 +1074,7 @@ export class ChatwootService { }, }; - await waInstance?.textMessage(data); + await waInstance?.textMessage(data, true); } } } diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 1decd13c..40147bf8 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -117,7 +117,7 @@ export class WAMonitoringService { if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { instanceData.instance['serverUrl'] = this.configService.get('SERVER').URL; - instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey; + instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; instanceData.instance['chatwoot'] = chatwoot; } @@ -136,7 +136,7 @@ export class WAMonitoringService { if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { instanceData.instance['serverUrl'] = this.configService.get('SERVER').URL; - instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey; + instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; instanceData.instance['chatwoot'] = chatwoot; } diff --git a/src/whatsapp/services/typebot.service.ts b/src/whatsapp/services/typebot.service.ts index c9894f75..fe5cd2c1 100644 --- a/src/whatsapp/services/typebot.service.ts +++ b/src/whatsapp/services/typebot.service.ts @@ -320,6 +320,37 @@ export class TypebotService { return sessions; } + public async clearSessions(instance: InstanceDto, remoteJid: string) { + const findTypebot = await this.find(instance); + const sessions = (findTypebot.sessions as Session[]) ?? []; + + const sessionWithRemoteJid = sessions.filter((session) => session.remoteJid === remoteJid); + + if (sessionWithRemoteJid.length > 0) { + sessionWithRemoteJid.forEach((session) => { + sessions.splice(sessions.indexOf(session), 1); + }); + + const typebotData = { + enabled: findTypebot.enabled, + 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, + sessions, + }; + + this.create(instance, typebotData); + + return sessions; + } + + return sessions; + } + public async sendWAMessage( instance: InstanceDto, remoteJid: string, diff --git a/src/whatsapp/services/webhook.service.ts b/src/whatsapp/services/webhook.service.ts index dd0a88cd..810ce96d 100644 --- a/src/whatsapp/services/webhook.service.ts +++ b/src/whatsapp/services/webhook.service.ts @@ -26,7 +26,7 @@ export class WebhookService { return result; } catch (error) { - return { enabled: false, url: '', events: [], webhook_by_events: false }; + return { enabled: false, url: '', events: [], webhook_by_events: false, webhook_base64: false }; } } } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 567a587e..3321605e 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -128,9 +128,7 @@ import { Events, MessageSubtype, TypeMediaMessage, wa } from '../types/wa.types' import { waMonitor } from '../whatsapp.module'; import { ChamaaiService } from './chamaai.service'; import { ChatwootService } from './chatwoot.service'; -//import { SocksProxyAgent } from './socks-proxy-agent'; import { TypebotService } from './typebot.service'; - export class WAStartupService { constructor( private readonly configService: ConfigService, @@ -276,6 +274,9 @@ export class WAStartupService { this.localWebhook.webhook_by_events = data?.webhook_by_events; this.logger.verbose(`Webhook by events: ${this.localWebhook.webhook_by_events}`); + this.localWebhook.webhook_base64 = data?.webhook_base64; + this.logger.verbose(`Webhook by webhook_base64: ${this.localWebhook.webhook_base64}`); + this.logger.verbose('Webhook loaded'); } @@ -581,7 +582,7 @@ export class WAStartupService { Object.assign(this.localProxy, data); this.logger.verbose('Proxy set'); - this.client?.ws?.close(); + this.reloadConnection(); } public async findProxy() { @@ -1180,17 +1181,28 @@ export class WAStartupService { if (this.localProxy.enabled) { this.logger.verbose('Proxy enabled'); - options = { - agent: new ProxyAgent(this.localProxy.proxy as any), - fetchAgent: new ProxyAgent(this.localProxy.proxy as any), - }; + + if (this.localProxy.proxy.includes('proxyscrape')) { + const response = await axios.get(this.localProxy.proxy); + const text = response.data; + const proxyUrls = text.split('\r\n'); + const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); + const proxyUrl = 'http://' + proxyUrls[rand]; + options = { + agent: new ProxyAgent(proxyUrl as any), + }; + } else { + options = { + agent: new ProxyAgent(this.localProxy.proxy as any), + }; + } } const socketConfig: UserFacingSocketConfig = { ...options, auth: { creds: this.instance.authState.state.creds, - keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' })), + keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' }) as any), }, logger: P({ level: this.logBaileys }), printQRInTerminal: false, @@ -1270,7 +1282,7 @@ export class WAStartupService { ...options, auth: { creds: this.instance.authState.state.creds, - keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' })), + keys: makeCacheableSignalKeyStore(this.instance.authState.state.keys, P({ level: 'error' }) as any), }, logger: P({ level: this.logBaileys }), printQRInTerminal: false, @@ -1508,11 +1520,12 @@ export class WAStartupService { this.logger.verbose('Event received: messages.upsert'); const received = messages[0]; + console.log(received, type); if ( type !== 'notify' || !received?.message || received.message?.protocolMessage || - received.message.senderKeyDistributionMessage || + // received.message.senderKeyDistributionMessage || received.message?.pollUpdateMessage ) { this.logger.verbose('message rejected'); @@ -1528,15 +1541,44 @@ export class WAStartupService { return; } - const messageRaw: MessageRaw = { - key: received.key, - pushName: received.pushName, - message: { ...received.message }, - messageType: getContentType(received.message), - messageTimestamp: received.messageTimestamp as number, - owner: this.instance.name, - source: getDevice(received.key.id), - }; + let messageRaw: MessageRaw; + + if ( + (this.localWebhook.webhook_base64 === true && received?.message.documentMessage) || + received?.message.imageMessage + ) { + const buffer = await downloadMediaMessage( + { key: received.key, message: received?.message }, + 'buffer', + {}, + { + logger: P({ level: 'error' }) as any, + reuploadRequest: this.client.updateMediaMessage, + }, + ); + messageRaw = { + key: received.key, + pushName: received.pushName, + message: { + ...received.message, + base64: buffer ? buffer.toString('base64') : undefined, + }, + messageType: getContentType(received.message), + messageTimestamp: received.messageTimestamp as number, + owner: this.instance.name, + source: getDevice(received.key.id), + }; + } else { + messageRaw = { + key: received.key, + pushName: received.pushName, + message: { ...received.message }, + messageType: getContentType(received.message), + messageTimestamp: received.messageTimestamp as number, + owner: this.instance.name, + source: getDevice(received.key.id), + }; + } if (this.localSettings.read_messages && received.key.id !== 'status@broadcast') { await this.client.readMessages([received.key]); @@ -1559,7 +1601,11 @@ export class WAStartupService { ); } - if (this.localTypebot.enabled) { + const typebotSessionRemoteJid = this.localTypebot.sessions?.find( + (session) => session.remoteJid === received.key.remoteJid, + ); + + if (this.localTypebot.enabled || typebotSessionRemoteJid) { if (!(this.localTypebot.listening_from_me === false && messageRaw.key.fromMe === true)) { await this.typebotService.sendTypebot( { instanceName: this.instance.name }, @@ -1901,8 +1947,8 @@ export class WAStartupService { private createJid(number: string): string { this.logger.verbose('Creating jid with number: ' + number); - if (number.includes('@g.us') || number.includes('@s.whatsapp.net')) { - this.logger.verbose('Number already contains @g.us or @s.whatsapp.net'); + if (number.includes('@g.us') || number.includes('@s.whatsapp.net') || number.includes('@lid')) { + this.logger.verbose('Number already contains @g.us or @s.whatsapp.net or @lid'); return number; } @@ -2028,7 +2074,12 @@ export class WAStartupService { } } - private async sendMessageWithTyping(number: string, message: T, options?: Options) { + private async sendMessageWithTyping( + number: string, + message: T, + options?: Options, + isChatwoot = false, + ) { this.logger.verbose('Sending message with typing'); this.logger.verbose(`Check if number "${number}" is WhatsApp`); @@ -2186,7 +2237,7 @@ export class WAStartupService { this.logger.verbose('Sending data to webhook in event SEND_MESSAGE'); await this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw); - if (this.localChatwoot.enabled) { + if (this.localChatwoot.enabled && !isChatwoot) { this.chatwootService.eventWhatsapp(Events.SEND_MESSAGE, { instanceName: this.instance.name }, messageRaw); } @@ -2211,7 +2262,7 @@ export class WAStartupService { } // Send Message Controller - public async textMessage(data: SendTextDto) { + public async textMessage(data: SendTextDto, isChatwoot = false) { this.logger.verbose('Sending text message'); return await this.sendMessageWithTyping( data.number, @@ -2219,6 +2270,7 @@ export class WAStartupService { conversation: data.textMessage.text, }, data?.options, + isChatwoot, ); } @@ -2384,7 +2436,6 @@ export class WAStartupService { mediaMessage.fileName = arrayMatch[1]; this.logger.verbose('File name: ' + mediaMessage.fileName); } - let mimetype: string; if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) { mediaMessage.fileName = 'image.png'; @@ -2394,10 +2445,16 @@ export class WAStartupService { mediaMessage.fileName = 'video.mp4'; } - if (isURL(mediaMessage.media)) { - mimetype = getMIMEType(mediaMessage.media); + let mimetype: string; + + if (mediaMessage.mimetype) { + mimetype = mediaMessage.mimetype; } else { - mimetype = getMIMEType(mediaMessage.fileName); + if (isURL(mediaMessage.media)) { + mimetype = getMIMEType(mediaMessage.media); + } else { + mimetype = getMIMEType(mediaMessage.fileName); + } } this.logger.verbose('Mimetype: ' + mimetype); @@ -2494,11 +2551,11 @@ export class WAStartupService { return result; } - public async mediaMessage(data: SendMediaDto) { + public async mediaMessage(data: SendMediaDto, isChatwoot = false) { this.logger.verbose('Sending media message'); const generate = await this.prepareMediaMessage(data.mediaMessage); - return await this.sendMessageWithTyping(data.number, { ...generate.message }, data?.options); + return await this.sendMessageWithTyping(data.number, { ...generate.message }, data?.options, isChatwoot); } public async processAudio(audio: string, number: string) { @@ -2555,7 +2612,7 @@ export class WAStartupService { }); } - public async audioWhatsapp(data: SendAudioDto) { + public async audioWhatsapp(data: SendAudioDto, isChatwoot = false) { this.logger.verbose('Sending audio whatsapp'); if (!data.options?.encoding && data.options?.encoding !== false) { @@ -2574,6 +2631,7 @@ export class WAStartupService { mimetype: 'audio/mp4', }, { presence: 'recording', delay: data?.options?.delay }, + isChatwoot, ); fs.unlinkSync(convert); @@ -2595,6 +2653,7 @@ export class WAStartupService { mimetype: 'audio/ogg; codecs=opus', }, { presence: 'recording', delay: data?.options?.delay }, + isChatwoot, ); } @@ -2905,7 +2964,7 @@ export class WAStartupService { 'buffer', {}, { - logger: P({ level: 'error' }), + logger: P({ level: 'error' }) as any, reuploadRequest: this.client.updateMediaMessage, }, ); @@ -3044,7 +3103,7 @@ export class WAStartupService { await this.client.updateGroupsAddPrivacy(settings.privacySettings.groupadd); this.logger.verbose('Groups add privacy updated'); - this.client?.ws?.close(); + this.reloadConnection(); return { update: 'success', diff --git a/src/whatsapp/types/wa.types.ts b/src/whatsapp/types/wa.types.ts index 2025e7f7..9f326c8a 100644 --- a/src/whatsapp/types/wa.types.ts +++ b/src/whatsapp/types/wa.types.ts @@ -50,6 +50,7 @@ export declare namespace wa { url?: string; events?: string[]; webhook_by_events?: boolean; + webhook_base64?: boolean; }; export type LocalChatwoot = { diff --git a/views/manager-wip.hbs b/views/manager-wip.hbs new file mode 100644 index 00000000..59b0cd0f --- /dev/null +++ b/views/manager-wip.hbs @@ -0,0 +1,110 @@ + + + + + + + + + + + Instance Manager + + + +
+ + + + + + + + + + + + + + + + {{#each instances}} + + + + + + + {{/each}} + +
Nome da InstânciaStatusAPI KeyAções
{{this.instance.instanceName}}{{this.instance.status}}{{this.instance.apikey}} + + +
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/views/manager.hbs b/views/manager.hbs index 5414bdf1..57d5fca4 100644 --- a/views/manager.hbs +++ b/views/manager.hbs @@ -11,7 +11,7 @@ - +