mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-13 15:14:49 -06:00
feat: Added audio to mp4 converter in optionally get Base64 From MediaMessage
This commit is contained in:
parent
870968a7c5
commit
f51c3b6519
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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[];
|
||||
}
|
||||
|
@ -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),
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
BIN
temp/audio.mp4
BIN
temp/audio.mp4
Binary file not shown.
Loading…
Reference in New Issue
Block a user