refactor(whatsapp.business.service): enhance media handling and audio processing

- Updated media message preparation to conditionally include filename and caption based on media type.
- Improved error handling in media ID retrieval and audio processing methods.
- Refactored audio processing to support file uploads and URL handling more effectively.
- Enhanced logging for better error tracking during media operations.
This commit is contained in:
Davidson Gomes 2025-06-12 17:28:02 -03:00
parent bc451e8493
commit a02ecc88f5
2 changed files with 115 additions and 54 deletions

View File

@ -28,7 +28,6 @@ import axios from 'axios';
import { arrayUnique, isURL } from 'class-validator'; import { arrayUnique, isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2'; import EventEmitter2 from 'eventemitter2';
import FormData from 'form-data'; import FormData from 'form-data';
import { createReadStream } from 'fs';
import mimeTypes from 'mime-types'; import mimeTypes from 'mime-types';
import { join } from 'path'; import { join } from 'path';
@ -997,9 +996,10 @@ export class BusinessStartupService extends ChannelStartupService {
to: number.replace(/\D/g, ''), to: number.replace(/\D/g, ''),
[message['mediaType']]: { [message['mediaType']]: {
[message['type']]: message['id'], [message['type']]: message['id'],
preview_url: Boolean(options?.linkPreview), ...(message['mediaType'] !== 'audio' &&
...(message['fileName'] && !isImage && { filename: message['fileName'] }), message['fileName'] &&
caption: message['caption'], !isImage && { filename: message['fileName'] }),
...(message['mediaType'] !== 'audio' && message['caption'] && { caption: message['caption'] }),
}, },
}; };
quoted ? (content.context = { message_id: quoted.id }) : content; quoted ? (content.context = { message_id: quoted.id }) : content;
@ -1097,7 +1097,7 @@ export class BusinessStartupService extends ChannelStartupService {
} }
})(); })();
if (messageSent?.error_data) { if (messageSent?.error_data || messageSent.message) {
this.logger.error(messageSent); this.logger.error(messageSent);
return messageSent; return messageSent;
} }
@ -1164,28 +1164,50 @@ export class BusinessStartupService extends ChannelStartupService {
return res; return res;
} }
private async getIdMedia(mediaMessage: any) { private async getIdMedia(mediaMessage: any, isFile = false) {
const formData = new FormData(); try {
const fileStream = createReadStream(mediaMessage.media); const formData = new FormData();
formData.append('file', fileStream, { filename: 'media', contentType: mediaMessage.mimetype }); if (isFile === false) {
formData.append('typeFile', mediaMessage.mimetype); if (isURL(mediaMessage.media)) {
formData.append('messaging_product', 'whatsapp'); const response = await axios.get(mediaMessage.media, { responseType: 'arraybuffer' });
const buffer = Buffer.from(response.data, 'base64');
formData.append('file', buffer, {
filename: mediaMessage.fileName || 'media',
contentType: mediaMessage.mimetype,
});
} else {
const buffer = Buffer.from(mediaMessage.media, 'base64');
formData.append('file', buffer, {
filename: mediaMessage.fileName || 'media',
contentType: mediaMessage.mimetype,
});
}
} else {
formData.append('file', mediaMessage.media.buffer, {
filename: mediaMessage.media.originalname,
contentType: mediaMessage.media.mimetype,
});
}
// const fileBuffer = await fs.readFile(mediaMessage.media); const mimetype = mediaMessage.mimetype || mediaMessage.media.mimetype;
// const fileBlob = new Blob([fileBuffer], { type: mediaMessage.mimetype }); formData.append('typeFile', mimetype);
// formData.append('file', fileBlob); formData.append('messaging_product', 'whatsapp');
// formData.append('typeFile', mediaMessage.mimetype);
// formData.append('messaging_product', 'whatsapp');
const headers = { Authorization: `Bearer ${this.token}` }; const token = process.env.FACEBOOK_USER_TOKEN;
const res = await axios.post(
process.env.API_URL + '/' + process.env.VERSION + '/' + this.number + '/media', const headers = { Authorization: `Bearer ${token}` };
formData, const url = `${this.configService.get<WaBusiness>('WA_BUSINESS').URL}/${
{ headers }, this.configService.get<WaBusiness>('WA_BUSINESS').VERSION
); }/${this.number}/media`;
return res.data.id;
const res = await axios.post(url, formData, { headers });
return res.data.id;
} catch (error) {
this.logger.error(error.response.data);
throw new InternalServerErrorException(error?.toString() || error);
}
} }
protected async prepareMediaMessage(mediaMessage: MediaMessage) { protected async prepareMediaMessage(mediaMessage: MediaMessage) {
@ -1258,48 +1280,87 @@ export class BusinessStartupService extends ChannelStartupService {
return mediaSent; return mediaSent;
} }
public async processAudio(audio: string, number: string) { public async processAudio(audio: string, number: string, file: any) {
number = number.replace(/\D/g, ''); number = number.replace(/\D/g, '');
const hash = `${number}-${new Date().getTime()}`; const hash = `${number}-${new Date().getTime()}`;
let mimetype: string | false; if (process.env.API_AUDIO_CONVERTER) {
this.logger.verbose('Using audio converter API');
const formData = new FormData();
const prepareMedia: any = { if (file) {
fileName: `${hash}.mp3`, formData.append('file', file.buffer, {
mediaType: 'audio', filename: file.originalname,
media: audio, contentType: file.mimetype,
}; });
} else if (isURL(audio)) {
formData.append('url', audio);
} else {
formData.append('base64', audio);
}
formData.append('format', 'mp3');
const response = await axios.post(process.env.API_AUDIO_CONVERTER, formData, {
headers: {
...formData.getHeaders(),
apikey: process.env.API_AUDIO_CONVERTER_KEY,
},
});
const audioConverter = response?.data?.audio || response?.data?.url;
if (!audioConverter) {
throw new InternalServerErrorException('Failed to convert audio');
}
const prepareMedia: any = {
fileName: `${hash}.mp3`,
mediaType: 'audio',
media: audioConverter,
mimetype: 'audio/mpeg',
};
if (isURL(audio)) {
mimetype = mimeTypes.lookup(audio);
prepareMedia.id = audio;
prepareMedia.type = 'link';
} else {
mimetype = mimeTypes.lookup(prepareMedia.fileName);
const id = await this.getIdMedia(prepareMedia); const id = await this.getIdMedia(prepareMedia);
prepareMedia.id = id; prepareMedia.id = id;
prepareMedia.type = 'id'; prepareMedia.type = 'id';
this.logger.verbose('Audio converted');
return prepareMedia;
} else {
let mimetype: string | false;
const prepareMedia: any = {
fileName: `${hash}.mp3`,
mediaType: 'audio',
media: audio,
};
if (isURL(audio)) {
mimetype = mimeTypes.lookup(audio);
prepareMedia.id = audio;
prepareMedia.type = 'link';
} else if (audio && !file) {
mimetype = mimeTypes.lookup(prepareMedia.fileName);
const id = await this.getIdMedia(prepareMedia);
prepareMedia.id = id;
prepareMedia.type = 'id';
} else if (file) {
prepareMedia.media = file;
const id = await this.getIdMedia(prepareMedia, true);
prepareMedia.id = id;
prepareMedia.type = 'id';
mimetype = file.mimetype;
}
prepareMedia.mimetype = mimetype;
return prepareMedia;
} }
prepareMedia.mimetype = mimetype;
return prepareMedia;
} }
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) { public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
const mediaData: SendAudioDto = { ...data }; const message = await this.processAudio(data.audio, data.number, file);
if (file?.buffer) {
mediaData.audio = file.buffer.toString('base64');
} else if (isURL(mediaData.audio)) {
// DO NOTHING
// mediaData.audio = mediaData.audio;
} else {
console.error('El archivo no tiene buffer o file es undefined');
throw new Error('File or buffer is undefined');
}
const message = await this.processAudio(mediaData.audio, data.number);
const audioSent = await this.sendMessageWithTyping( const audioSent = await this.sendMessageWithTyping(
data.number, data.number,

View File

@ -1819,7 +1819,7 @@ export class BaileysStartupService extends ChannelStartupService {
// setTimeout(() => this.client.terminateCall(call.id, call.to), callDuration * 1000); // setTimeout(() => this.client.terminateCall(call.id, call.to), callDuration * 1000);
// return call; // return call;
return { id: '123' }; return { id: '123', jid, isVideo, callDuration };
} catch (error) { } catch (error) {
return error; return error;
} }