feat: new media download way

This commit is contained in:
Anton Voylenko 2025-03-31 10:55:15 +03:00
commit 5869d86177
4 changed files with 173 additions and 2 deletions

View file

@ -1,6 +1,7 @@
const { MessageMedia, Location, Poll } = require('whatsapp-web.js') const { MessageMedia, Location, Poll } = require('whatsapp-web.js')
const { Readable } = require('stream')
const { sessions } = require('../sessions') 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. * 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 client = sessions.get(req.params.sessionId)
const message = await _getMessageById(client, messageId, chatId) const message = await _getMessageById(client, messageId, chatId)
if (!message) { throw new Error('Message not found') } if (!message) { throw new Error('Message not found') }
if (!message.hasMedia) { throw new Error('Message media not found') }
const messageMedia = await message.downloadMedia() const messageMedia = await message.downloadMedia()
res.json({ success: true, messageMedia }) res.json({ success: true, messageMedia })
} catch (error) { } 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<void>} - 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. * Forwards a message to a destination chat.
* @async * @async
@ -684,6 +734,7 @@ module.exports = {
getClassInfo, getClassInfo,
deleteMessage, deleteMessage,
downloadMedia, downloadMedia,
downloadMediaAsData,
forward, forward,
getInfo, getInfo,
getMentions, getMentions,

View file

@ -170,6 +170,7 @@ routes.use('/message', messageRouter)
messageRouter.post('/getClassInfo/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getClassInfo) messageRouter.post('/getClassInfo/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getClassInfo)
messageRouter.post('/delete/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.deleteMessage) messageRouter.post('/delete/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.deleteMessage)
messageRouter.post('/downloadMedia/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.downloadMedia) 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('/forward/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.forward)
messageRouter.post('/getInfo/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getInfo) messageRouter.post('/getInfo/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getInfo)
messageRouter.post('/getMentions/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getMentions) messageRouter.post('/getMentions/:sessionId', [middleware.sessionNameValidation, middleware.sessionValidation], messageController.getMentions)

View file

@ -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 = { module.exports = {
triggerWebhook, triggerWebhook,
sendErrorResponse, sendErrorResponse,
waitForNestedObject, waitForNestedObject,
checkIfEventisEnabled, checkIfEventisEnabled,
sendMessageSeenStatus sendMessageSeenStatus,
decodeBase64
} }

View file

@ -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}": { "/message/forward/{sessionId}": {
"post": { "post": {
"tags": [ "tags": [