diff --git a/src/controllers/messageController.js b/src/controllers/messageController.js index 43eaec4..f422d61 100644 --- a/src/controllers/messageController.js +++ b/src/controllers/messageController.js @@ -1,6 +1,7 @@ const { MessageMedia, Location, Poll } = require('whatsapp-web.js') +const { Readable } = require('stream') const { sessions } = require('../sessions') -const { sendErrorResponse } = require('../utils') +const { sendErrorResponse, decodeBase64 } = require('../utils') /** * Get message by its ID from a given chat using the provided client. @@ -116,6 +117,7 @@ const downloadMedia = async (req, res) => { const client = sessions.get(req.params.sessionId) const message = await _getMessageById(client, messageId, chatId) if (!message) { throw new Error('Message not found') } + if (!message.hasMedia) { throw new Error('Message media not found') } const messageMedia = await message.downloadMedia() res.json({ success: true, messageMedia }) } catch (error) { @@ -123,6 +125,54 @@ const downloadMedia = async (req, res) => { } } +/** + * Downloads media from a message and sends it as binary data. + * @async + * @function + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @param {string} req.params.sessionId - The session ID. + * @param {string} req.body.messageId - The message ID. + * @param {string} req.body.chatId - The chat ID. + * @returns {Promise} - A Promise that resolves with no value when the function completes. + */ +const downloadMediaAsData = async (req, res) => { + /* + #swagger.summary = 'Download attached message media as binary data' + */ + try { + const { messageId, chatId } = req.body + const client = sessions.get(req.params.sessionId) + const message = await _getMessageById(client, messageId, chatId) + if (!message) { throw new Error('Message not found') } + if (!message.hasMedia) { throw new Error('Message media not found') } + const { data, mimetype, filename, filesize } = await message.downloadMedia() + /* #swagger.responses[200] = { + description: 'Binary data.' + } + */ + res.writeHead(200, { + ...(mimetype && { 'Content-Type': mimetype }), + ...(filesize && { 'Content-Length': filesize }), + ...(filename && { 'Content-Disposition': `attachment; filename=${encodeURIComponent(filename)}` }) + }) + const readableStream = new Readable({ + read () { + for (const chunk of decodeBase64(data)) { + this.push(chunk) + } + this.push(null) + } + }) + readableStream.on('end', () => { + res.end() + }) + readableStream.pipe(res) + } catch (error) { + sendErrorResponse(res, 500, error.message) + } +} + /** * Forwards a message to a destination chat. * @async @@ -684,6 +734,7 @@ module.exports = { getClassInfo, deleteMessage, downloadMedia, + downloadMediaAsData, forward, getInfo, getMentions, diff --git a/src/routes.js b/src/routes.js index ab58957..496dbda 100644 --- a/src/routes.js +++ b/src/routes.js @@ -170,6 +170,7 @@ routes.use('/message', messageRouter) messageRouter.post('/getClassInfo/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getClassInfo) messageRouter.post('/delete/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.deleteMessage) messageRouter.post('/downloadMedia/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.downloadMedia) +messageRouter.post('/downloadMediaAsData/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.downloadMediaAsData) messageRouter.post('/forward/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.forward) messageRouter.post('/getInfo/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getInfo) messageRouter.post('/getMentions/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getMentions) diff --git a/src/utils.js b/src/utils.js index 48e3b11..85b0504 100644 --- a/src/utils.js +++ b/src/utils.js @@ -51,10 +51,19 @@ const sendMessageSeenStatus = async (message) => { } } +const decodeBase64 = function * (base64String) { + const chunkSize = 1024 + for (let i = 0; i < base64String.length; i += chunkSize) { + const chunk = base64String.slice(i, i + chunkSize) + yield Buffer.from(chunk, 'base64') + } +} + module.exports = { triggerWebhook, sendErrorResponse, waitForNestedObject, checkIfEventisEnabled, - sendMessageSeenStatus + sendMessageSeenStatus, + decodeBase64 } diff --git a/swagger.json b/swagger.json index 8545632..9c17c28 100644 --- a/swagger.json +++ b/swagger.json @@ -8664,6 +8664,116 @@ } } }, + "/message/downloadMediaAsData/{sessionId}": { + "post": { + "tags": [ + "Message" + ], + "summary": "Download attached message media as binary data", + "description": "", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier for the session (alphanumeric and - allowed)", + "example": "f8377d8d-a589-4242-9ba6-9486a04ef80c" + } + ], + "responses": { + "200": { + "description": "Binary data." + }, + "403": { + "description": "Forbidden.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenResponse" + } + } + } + }, + "404": { + "description": "Not Found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Entity.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Server failure.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "apiKeyAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "chatId": { + "type": "string", + "description": "The chat id which contains the message", + "example": "6281288888888@c.us" + }, + "messageId": { + "type": "string", + "description": "Unique WhatsApp ID for the message", + "example": "ABCDEF999999999" + } + } + } + }, + "application/xml": { + "schema": { + "type": "object", + "properties": { + "chatId": { + "type": "string", + "description": "The chat id which contains the message", + "example": "6281288888888@c.us" + }, + "messageId": { + "type": "string", + "description": "Unique WhatsApp ID for the message", + "example": "ABCDEF999999999" + } + } + } + } + } + } + } + }, "/message/forward/{sessionId}": { "post": { "tags": [