feat: Added audio to mp4 converter in optionally get Base64 From MediaMessage

This commit is contained in:
Davidson Gomes 2023-07-04 17:16:31 -03:00
parent 870968a7c5
commit f51c3b6519
12 changed files with 132 additions and 39 deletions

View File

@ -1,5 +1,10 @@
# 1.1.3 (homolog)
### Features
* Added configuration for Baileys log level in env
* Added audio to mp4 converter in optionally get Base64 From MediaMessage
### Fixed
* Added timestamp internally in urls to avoid caching
@ -8,6 +13,7 @@
* Fixed cash when sending stickers via url
* Improved how Redis works for instances
* Fixed problem when disconnecting the instance it removes the instance
* Fixed problem sending ack when preview is done by me
# 1.1.2 (2023-06-28 13:43)

View File

@ -16,7 +16,7 @@ ENV CORS_ORIGIN="*"
ENV CORS_METHODS="POST,GET,PUT,DELETE"
ENV CORS_CREDENTIALS=true
ENV LOG_LEVEL="ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK"
ENV LOG_LEVEL=$LOG_LEVEL
ENV LOG_COLOR=true
ENV DEL_INSTANCE=$DEL_INSTANCE

View File

@ -14,10 +14,12 @@ services:
- evolution_instances:/evolution/instances
- evolution_store:/evolution/store
environment:
- LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS
- LOG_BAILEYS=error
# Determine how long the instance should be deleted from memory in case of no connection.
# Default time: 5 minutes
# If you don't even want an expiration, enter the value false
- DEL_INSTANCE=5 # or false
- DEL_INSTANCE=false # 5 or false
# Temporary data storage
- STORE_MESSAGES=true
- STORE_MESSAGE_UP=true

View File

@ -13,10 +13,22 @@ export type Cors = {
CREDENTIALS: boolean;
};
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK';
export type LogBaileys = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';
export type LogLevel =
| 'ERROR'
| 'WARN'
| 'DEBUG'
| 'INFO'
| 'LOG'
| 'VERBOSE'
| 'DARK'
| 'WEBHOOKS';
export type Log = {
LEVEL: LogLevel[];
COLOR: boolean;
BAILEYS: LogBaileys;
};
export type SaveData = {
@ -204,6 +216,7 @@ export class ConfigService {
LOG: {
LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[],
COLOR: process.env?.LOG_COLOR === 'true',
BAILEYS: (process.env?.LOG_BAILEYS as LogBaileys) || 'error',
},
DEL_INSTANCE: isBooleanString(process.env?.DEL_INSTANCE)
? process.env.DEL_INSTANCE === 'true'

View File

@ -36,12 +36,14 @@ LOG:
- LOG
- VERBOSE
- DARK
- WEBHOOKS
COLOR: true
BAILEYS: error # "fatal" | "error" | "warn" | "info" | "debug" | "trace"
# Determine how long the instance should be deleted from memory in case of no connection.
# Default time: 5 minutes
# If you don't even want an expiration, enter the value false
DEL_INSTANCE: 5 # or false
DEL_INSTANCE: false # or false
# Temporary data storage
STORE:

View File

@ -9,6 +9,7 @@ import {
ProfileStatusDto,
ReadMessageDto,
WhatsAppNumberDto,
getBase64FromMediaMessageDto,
} from '../dto/chat.dto';
import { InstanceDto } from '../dto/instance.dto';
import { ContactQuery } from '../repository/contact.repository';
@ -45,11 +46,9 @@ export class ChatController {
public async getBase64FromMediaMessage(
{ instanceName }: InstanceDto,
message: proto.IWebMessageInfo,
data: getBase64FromMediaMessageDto,
) {
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(
message,
);
return await this.waMonitor.waInstances[instanceName].getBase64FromMediaMessage(data);
}
public async fetchMessages({ instanceName }: InstanceDto, query: MessageQuery) {

View File

@ -207,7 +207,6 @@ export class InstanceController {
);
this.waMonitor.waInstances[instanceName]?.client?.ws?.close();
this.waMonitor.waInstances[instanceName]?.client?.end(undefined);
return { error: false, message: 'Instance logged out' };
} catch (error) {

View File

@ -2,6 +2,7 @@ import {
WAPrivacyOnlineValue,
WAPrivacyValue,
WAReadReceiptsValue,
proto,
} from '@whiskeysockets/baileys';
export class OnWhatsAppDto {
@ -12,6 +13,11 @@ export class OnWhatsAppDto {
) {}
}
export class getBase64FromMediaMessageDto {
message: proto.WebMessageInfo;
convertToMp4?: boolean;
}
export class WhatsAppNumberDto {
numbers: string[];
}

View File

@ -22,6 +22,7 @@ import {
ProfileStatusDto,
ReadMessageDto,
WhatsAppNumberDto,
getBase64FromMediaMessageDto,
} from '../dto/chat.dto';
import { ContactQuery } from '../repository/contact.repository';
import { MessageQuery } from '../repository/message.repository';
@ -101,10 +102,10 @@ export class ChatRouter extends RouterBroker {
return res.status(HttpStatus.OK).json(response);
})
.post(this.routerPath('getBase64FromMediaMessage'), ...guards, async (req, res) => {
const response = await this.dataValidate<proto.IWebMessageInfo>({
const response = await this.dataValidate<getBase64FromMediaMessageDto>({
request: req,
schema: null,
ClassRef: Object,
ClassRef: getBase64FromMediaMessageDto,
execute: (instance, data) =>
chatController.getBase64FromMediaMessage(instance, data),
});

View File

@ -253,6 +253,13 @@ export class WAMonitoringService {
this.logger.warn(`Instance "${instanceName}" - REMOVED`);
}
});
this.eventEmitter.on('logout.instance', async (instanceName: string) => {
try {
this.cleaningUp(instanceName);
} finally {
this.logger.warn(`Instance "${instanceName}" - LOGOUT`);
}
});
}
private noConnection() {

View File

@ -87,6 +87,7 @@ import {
PrivacySettingDto,
ReadMessageDto,
WhatsAppNumberDto,
getBase64FromMediaMessageDto,
} from '../dto/chat.dto';
import { MessageQuery } from '../repository/message.repository';
import { ContactQuery } from '../repository/contact.repository';
@ -116,6 +117,7 @@ import NodeCache from 'node-cache';
import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db';
import sharp from 'sharp';
import { RedisCache } from '../../db/redis.client';
import { Log } from '../../config/env.config';
export class WAStartupService {
constructor(
@ -137,7 +139,8 @@ export class WAStartupService {
private readonly msgRetryCounterCache: CacheStore = new NodeCache();
private readonly userDevicesCache: CacheStore = new NodeCache();
private endSession = false;
private store = makeInMemoryStore({ logger: P({ level: 'error' }) });
private logBaileys = this.configService.get<Log>('LOG').BAILEYS;
private store = makeInMemoryStore({ logger: P({ level: this.logBaileys }) });
public set instanceName(name: string) {
if (!name) {
@ -154,7 +157,7 @@ export class WAStartupService {
public get instanceName() {
return this.instance.name;
}
s;
public get wuid() {
return this.instance.wuid;
}
@ -238,14 +241,16 @@ export class WAStartupService {
baseURL = this.localWebhook.url;
}
this.logger.log({
local: WAStartupService.name + '.sendDataWebhook-local',
url: baseURL,
event,
instance: this.instance.name,
data,
destination: this.localWebhook.url,
});
if (this.configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS')) {
this.logger.log({
local: WAStartupService.name + '.sendDataWebhook-local',
url: baseURL,
event,
instance: this.instance.name,
data,
destination: this.localWebhook.url,
});
}
try {
if (this.localWebhook.enabled && isURL(this.localWebhook.url)) {
@ -293,14 +298,16 @@ export class WAStartupService {
localUrl = this.localWebhook.url;
}
this.logger.log({
local: WAStartupService.name + '.sendDataWebhook-global',
url: globalURL,
event,
instance: this.instance.name,
data,
destination: localUrl,
});
if (this.configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS')) {
this.logger.log({
local: WAStartupService.name + '.sendDataWebhook-global',
url: globalURL,
event,
instance: this.instance.name,
data,
destination: localUrl,
});
}
try {
if (globalWebhook && globalWebhook?.ENABLED && isURL(globalURL)) {
@ -409,6 +416,7 @@ export class WAStartupService {
instance: this.instance.name,
status: 'removed',
});
this.eventEmitter.emit('logout.instance', this.instance.name, 'inner');
this.client?.ws?.close();
this.client.end(new Error('Close connection'));
}
@ -444,7 +452,7 @@ export class WAStartupService {
private async getMessageStore(key: proto.IMessageKey) {
if (this.store) {
const msg = await this.store.loadMessage(key.remoteJid!, key.id!);
const msg = await this.store.loadMessage(key.remoteJid, key.id);
return msg?.message || undefined;
}
@ -466,7 +474,6 @@ export class WAStartupService {
this.instance.wuid,
)}/*.json`,
);
this.store?.writeToFile(`${this.storePath}/baileys_store_multi.json`);
}
}
} catch (error) {}
@ -500,7 +507,11 @@ export class WAStartupService {
const session = this.configService.get<ConfigSessionPhone>('CONFIG_SESSION_PHONE');
const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()];
this.store?.readFromFile(`${this.storePath}/baileys_store_multi.json`);
this.store?.readFromFile(`${this.storePath}/baileys/store.json`);
setInterval(() => {
this.store?.writeToFile(`${this.storePath}/baileys/store.json`);
}, 10_000);
const socketConfig: UserFacingSocketConfig = {
auth: {
@ -510,7 +521,7 @@ export class WAStartupService {
P({ level: 'error' }),
),
},
logger: P({ level: 'error' }),
logger: P({ level: this.logBaileys }),
printQRInTerminal: false,
browser,
version,
@ -724,7 +735,11 @@ export class WAStartupService {
) => {
const received = messages[0];
if (type !== 'notify' || received.message?.protocolMessage) {
if (
type !== 'notify' ||
received.message?.protocolMessage ||
received.message?.pollUpdateMessage
) {
return;
}
@ -742,7 +757,7 @@ export class WAStartupService {
source: getDevice(received.key.id),
};
this.logger.log(received);
this.logger.log(messageRaw);
await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);
await this.repository.message.insert([messageRaw], database.SAVE_DATA.NEW_MESSAGE);
@ -758,7 +773,11 @@ export class WAStartupService {
5: 'PLAYED',
};
for await (const { key, update } of args) {
if (key.remoteJid !== 'status@broadcast' && !key?.remoteJid?.match(/(:\d+)/)) {
if (
key.remoteJid !== 'status@broadcast' &&
!key?.remoteJid?.match(/(:\d+)/) &&
!key.fromMe
) {
let pollUpdates: any;
if (update.pollUpdates) {
const pollCreation = await this.getMessageStore(key);
@ -1053,10 +1072,19 @@ export class WAStartupService {
);
})();
messageSent['messageType'] = getContentType(messageSent.message);
const messageRaw: proto.IWebMessageInfo = {
key: messageSent.key,
messageTimestamp: Long.isLong(messageSent.messageTimestamp)
? messageSent.messageTimestamp?.toNumber()
: messageSent.messageTimestamp,
pushName: messageSent.pushName,
broadcast: messageSent.broadcast,
status: 2,
message: { ...messageSent.message },
};
this.client.ev.emit('messages.upsert', {
messages: [messageSent],
messages: [messageRaw],
type: 'notify',
});
@ -1471,12 +1499,19 @@ export class WAStartupService {
}
}
public async getBase64FromMediaMessage(m: proto.IWebMessageInfo) {
public async getBase64FromMediaMessage(data: getBase64FromMediaMessageDto) {
try {
const m = data?.message;
const convertToMp4 = data?.convertToMp4 ?? false;
const msg = m?.message
? m
: ((await this.getMessage(m.key, true)) as proto.IWebMessageInfo);
if (!msg) {
throw 'Message not found';
}
for (const subtype of MessageSubtype) {
if (msg.message[subtype]) {
msg.message = msg.message[subtype].message;
@ -1511,6 +1546,29 @@ export class WAStartupService {
reuploadRequest: this.client.updateMediaMessage,
},
);
const typeMessage = getContentType(msg.message);
// if for audioMessage converte para mp3
if (convertToMp4 && typeMessage === 'audioMessage') {
const convert = await this.processAudio(buffer.toString('base64'));
if (typeof convert === 'string') {
const audio = fs.readFileSync(convert).toString('base64');
return {
mediaType,
fileName: mediaMessage['fileName'],
caption: mediaMessage['caption'],
size: {
fileLength: mediaMessage['fileLength'],
height: mediaMessage['height'],
width: mediaMessage['width'],
},
mimetype: 'audio/mp4',
base64: Buffer.from(audio, 'base64').toString('base64'),
};
}
}
return {
mediaType,

Binary file not shown.