Merge branch 'main' into heroku-button

This commit is contained in:
Gabriel Pastori 2023-10-24 20:32:20 -03:00 committed by GitHub
commit 9e977d45ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 326 additions and 82 deletions

View File

@ -1,4 +1,15 @@
# 1.5.1 (homolog)
# 1.5.2 (2023-09-28 17:56)
### Fixed
* Fix chatwootSchema in chatwoot model to store reopen_conversation and conversation_pending options
* Problem resolved when sending files from minio to typebot
* Improvement in the "startTypebot" method to create persistent session when triggered
* New manager for Evo 1.5.2 - Set Typebot update
* Resolved problems when reading/querying instances
# 1.5.1 (2023-09-17 13:50)
### Feature
@ -7,10 +18,12 @@
* Added webhooks for typebot events
* Added ChamaAI integration
* Added webhook to send errors
* Added support for messaging with ads on chatwoot
### Fixed
* Fix looping connection messages in chatwoot
* Improved performance of fetch instances
# 1.5.0 (2023-08-18 12:47)

View File

@ -1,6 +1,6 @@
FROM node:16.18-alpine
LABEL version="1.5.1" description="Api to control whatsapp features through http requests."
LABEL version="1.5.2" description="Api to control whatsapp features through http requests."
LABEL maintainer="Davidson Gomes" git="https://github.com/DavidsonGomes"
LABEL contact="contato@agenciadgcode.com"

File diff suppressed because one or more lines are too long

View File

@ -1 +1,2 @@
web: node ./dist/src/main.js

View File

@ -1,6 +1,6 @@
{
"name": "evolution-api",
"version": "1.5.1",
"version": "1.5.2",
"description": "Rest api for communication with WhatsApp",
"main": "./dist/src/main.js",
"scripts": {
@ -43,10 +43,10 @@
"dependencies": {
"@adiwajshing/keyed-db": "^0.2.4",
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@figuro/chatwoot-sdk": "^1.1.14",
"@figuro/chatwoot-sdk": "^1.1.16",
"@hapi/boom": "^10.0.1",
"@sentry/node": "^7.59.2",
"@whiskeysockets/baileys": "^6.4.1",
"@whiskeysockets/baileys": "^6.5.0",
"amqplib": "^0.10.3",
"axios": "^1.3.5",
"class-validator": "^0.13.2",

View File

@ -76,6 +76,10 @@ export type Websocket = {
ENABLED: boolean;
};
export type Chatwoot = {
USE_REPLY_ID: boolean;
};
export type EventsWebhook = {
APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean;
@ -145,6 +149,7 @@ export interface Env {
QRCODE: QrCode;
AUTHENTICATION: Auth;
PRODUCTION?: Production;
CHATWOOT?: Chatwoot;
}
export type Key = keyof Env;
@ -166,11 +171,10 @@ export class ConfigService {
? this.envYaml()
: this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
if ((process.env?.DOCKER_ENV === 'true', process.env?.HEROKU_ENV === 'true')) {
this.env.SERVER.TYPE = 'http';
this.env.SERVER.PORT = parseInt(process.env.PORT) || 8080;
}
// if ((process.env?.DOCKER_ENV === 'true', process.env?.HEROKU_ENV === 'true')) {
// this.env.SERVER.TYPE = 'http';
// this.env.SERVER.PORT = parseInt(process.env.PORT) || 8080;
// }
}
private envYaml(): Env {
@ -250,9 +254,7 @@ export class ConfigService {
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'
: Number.parseInt(process.env.DEL_INSTANCE) || false,
DEL_INSTANCE: process.env?.DEL_INSTANCE === 'true' ? 5 : Number.parseInt(process.env.DEL_INSTANCE) || false,
WEBHOOK: {
GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL || '',
@ -309,6 +311,9 @@ export class ConfigService {
SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`',
},
},
CHATWOOT: {
USE_REPLY_ID: process.env?.USE_REPLY_ID === 'true',
},
};
}
}

View File

@ -156,3 +156,7 @@ AUTHENTICATION:
JWT:
EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires
SECRET: L=0YWt]b2w[WF>#>:&E`
# Configure to chatwoot
CHATWOOT:
USE_REPLY_ID: false

View File

@ -11,7 +11,7 @@ let io: SocketIO;
const cors = configService.get<Cors>('CORS').ORIGIN;
export const initIO = (httpServer: Server) => {
if (configService.get<Websocket>('WEBSOCKET').ENABLED) {
if (configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
io = new SocketIO(httpServer, {
cors: {
origin: cors,

View File

@ -6,7 +6,7 @@ import cors from 'cors';
import express, { json, NextFunction, Request, Response, urlencoded } from 'express';
import { join } from 'path';
import { configService, Cors, HttpServer, Rabbitmq, Webhook } from './config/env.config';
import { Auth, configService, Cors, HttpServer, Rabbitmq, Webhook } from './config/env.config';
import { onUnexpectedError } from './config/error.config';
import { Logger } from './config/logger.config';
import { ROOT_DIR } from './config/path.config';
@ -61,6 +61,8 @@ function bootstrap() {
const tzoffset = new Date().getTimezoneOffset() * 60000; //offset in milliseconds
const localISOTime = new Date(Date.now() - tzoffset).toISOString();
const now = localISOTime;
const globalApiKey = configService.get<Auth>('AUTHENTICATION').API_KEY.KEY;
const serverUrl = configService.get<HttpServer>('SERVER').URL;
const errorData = {
event: 'error',
@ -73,6 +75,8 @@ function bootstrap() {
},
},
date_time: now,
api_key: globalApiKey,
server_url: serverUrl,
};
logger.error(errorData);
@ -83,11 +87,6 @@ function bootstrap() {
httpService.post('', errorData);
}
if (err['message'].includes('No sessions')) {
console.log(err['message']);
process.exit(1);
}
return res.status(err['status'] || 500).json({
status: err['status'] || 500,
error: err['error'] || 'Internal Server Error',
@ -125,7 +124,7 @@ function bootstrap() {
initIO(server);
if (configService.get<Rabbitmq>('RABBITMQ').ENABLED) initAMQP();
if (configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) initAMQP();
onUnexpectedError();
}

View File

@ -4,6 +4,13 @@ export class Session {
status?: string;
createdAt?: number;
updateAt?: number;
prefilledVariables?: PrefilledVariables;
}
export class PrefilledVariables {
remoteJid?: string;
pushName?: string;
additionalData?: { [key: string]: any };
}
export class TypebotDto {

View File

@ -24,6 +24,8 @@ const chatwootSchema = new Schema<ChatwootRaw>({
name_inbox: { type: String, required: true },
sign_msg: { type: Boolean, required: true },
number: { type: String, required: true },
reopen_conversation: { type: Boolean, required: true },
conversation_pending: { type: Boolean, required: true },
});
export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot');

View File

@ -20,6 +20,8 @@ export class MessageRaw {
messageTimestamp?: number | Long.Long;
owner: string;
source?: 'android' | 'web' | 'ios';
source_id?: string;
source_reply_id?: string;
}
const messageSchema = new Schema<MessageRaw>({

View File

@ -8,6 +8,11 @@ class Session {
status?: string;
createdAt?: number;
updateAt?: number;
prefilledVariables?: {
remoteJid?: string;
pushName?: string;
additionalData?: { [key: string]: any };
};
}
export class TypebotRaw {
@ -40,6 +45,11 @@ const typebotSchema = new Schema<TypebotRaw>({
status: { type: String, required: true },
createdAt: { type: Number, required: true },
updateAt: { type: Number, required: true },
prefilledVariables: {
remoteJid: { type: String, required: false },
pushName: { type: String, required: false },
additionalData: { type: Schema.Types.Mixed, required: false }
},
},
],
});

View File

@ -36,8 +36,6 @@ const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]];
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
// Hide index if needed
if (!httpServer.HIDE_INDEX)
router.get('/', (req, res) => {
res.status(HttpStatus.OK).json({
@ -46,13 +44,10 @@ if (!httpServer.HIDE_INDEX)
version: packageJson.version,
});
});
// Hide manager if needed
if (!httpServer.HIDE_MANAGER) router.use('/manager', new ViewsRouter().router);
router
.use('/instance', new InstanceRouter(configService, ...guards).router)
.use('/manager', new ViewsRouter().router)
.use('/message', new MessageRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router)

View File

@ -2,6 +2,7 @@ import ChatwootClient from '@figuro/chatwoot-sdk';
import axios from 'axios';
import FormData from 'form-data';
import { createReadStream, readFileSync, unlinkSync, writeFileSync } from 'fs';
import Jimp from 'jimp';
import mimeTypes from 'mime-types';
import path from 'path';
@ -949,7 +950,6 @@ export class ChatwootService {
public async receiveWebhook(instance: InstanceDto, body: any) {
try {
// espera 500ms para evitar duplicidade de mensagens
await new Promise((resolve) => setTimeout(resolve, 500));
this.logger.verbose('receive webhook to chatwoot instance: ' + instance.instanceName);
@ -1125,6 +1125,20 @@ export class ChatwootService {
return result;
}
private getAdsMessage(msg: any) {
interface AdsMessage {
title: string;
body: string;
thumbnailUrl: string;
sourceUrl: string;
}
const adsMessage: AdsMessage | undefined = msg.extendedTextMessage?.contextInfo.externalAdReply;
this.logger.verbose('Get ads message if it exist');
adsMessage && this.logger.verbose('Ads message: ' + adsMessage);
return adsMessage;
}
private getTypeMessage(msg: any) {
this.logger.verbose('get type message');
@ -1278,15 +1292,17 @@ export class ChatwootService {
const isMedia = this.isMediaMessage(body.message);
const adsMessage = this.getAdsMessage(body.message);
if (!bodyMessage && !isMedia) {
this.logger.warn('no body message found');
return;
}
this.logger.verbose('get conversation in chatwoot');
const getConversion = await this.createConversation(instance, body);
const getConversation = await this.createConversation(instance, body);
if (!getConversion) {
if (!getConversation) {
this.logger.warn('conversation not found');
return;
}
@ -1337,7 +1353,7 @@ export class ChatwootService {
}
this.logger.verbose('send data to chatwoot');
const send = await this.sendData(getConversion, fileName, messageType, content);
const send = await this.sendData(getConversation, fileName, messageType, content);
if (!send) {
this.logger.warn('message not sent');
@ -1358,7 +1374,7 @@ export class ChatwootService {
this.logger.verbose('message is not group');
this.logger.verbose('send data to chatwoot');
const send = await this.sendData(getConversion, fileName, messageType, bodyMessage);
const send = await this.sendData(getConversation, fileName, messageType, bodyMessage);
if (!send) {
this.logger.warn('message not sent');
@ -1378,6 +1394,67 @@ export class ChatwootService {
}
}
this.logger.verbose('check if has Ads Message');
if (adsMessage) {
this.logger.verbose('message is from Ads');
this.logger.verbose('get base64 from media ads message');
const imgBuffer = await axios.get(adsMessage.thumbnailUrl, { responseType: 'arraybuffer' });
const extension = mimeTypes.extension(imgBuffer.headers['content-type']);
const mimeType = extension && mimeTypes.lookup(extension);
if (!mimeType) {
this.logger.warn('mimetype of Ads message not found');
return;
}
const random = Math.random().toString(36).substring(7);
const nameFile = `${random}.${mimeTypes.extension(mimeType)}`;
const fileData = Buffer.from(imgBuffer.data, 'binary');
const fileName = `${path.join(waInstance?.storePath, 'temp', `${nameFile}`)}`;
this.logger.verbose('temp file name: ' + nameFile);
this.logger.verbose('create temp file');
await Jimp.read(fileData)
.then(async (img) => {
await img.cover(320, 180).writeAsync(fileName);
})
.catch((err) => {
this.logger.error(`image is not write: ${err}`);
});
const truncStr = (str: string, len: number) => {
return str.length > len ? str.substring(0, len) + '...' : str;
};
const title = truncStr(adsMessage.title, 40);
const description = truncStr(adsMessage.body, 75);
this.logger.verbose('send data to chatwoot');
const send = await this.sendData(
getConversation,
fileName,
messageType,
`${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`,
);
if (!send) {
this.logger.warn('message not sent');
return;
}
this.messageCacheFile = path.join(ROOT_DIR, 'store', 'chatwoot', `${instance.instanceName}_cache.txt`);
this.messageCache = this.loadMessageCache();
this.messageCache.add(send.id.toString());
this.logger.verbose('save message cache');
this.saveMessageCache();
return send;
}
this.logger.verbose('check if is group');
if (body.key.remoteJid.includes('@g.us')) {
this.logger.verbose('message is group');
@ -1394,7 +1471,7 @@ export class ChatwootService {
}
this.logger.verbose('send data to chatwoot');
const send = await this.createMessage(instance, getConversion, content, messageType);
const send = await this.createMessage(instance, getConversation, content, messageType);
if (!send) {
this.logger.warn('message not sent');
@ -1415,7 +1492,7 @@ export class ChatwootService {
this.logger.verbose('message is not group');
this.logger.verbose('send data to chatwoot');
const send = await this.createMessage(instance, getConversion, bodyMessage, messageType);
const send = await this.createMessage(instance, getConversation, bodyMessage, messageType);
if (!send) {
this.logger.warn('message not sent');

View File

@ -7,6 +7,9 @@ import { join } from 'path';
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
import { Logger } from '../../config/logger.config';
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
// inserido por francis inicio
import { NotFoundException } from '../../exceptions';
// inserido por francis fim
import { dbserver } from '../../libs/db.connect';
import { RedisCache } from '../../libs/redis.client';
import {
@ -72,7 +75,7 @@ export class WAMonitoringService {
}, 1000 * 60 * time);
}
}
/* ocultado por francis inicio
public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info');
@ -128,6 +131,96 @@ export class WAMonitoringService {
return instances;
}
ocultado por francis fim */
// inserido por francis inicio
public async instanceInfo(instanceName?: string) {
this.logger.verbose('get instance info');
if (instanceName && !this.waInstances[instanceName]) {
throw new NotFoundException(`Instance "${instanceName}" not found`);
}
const instances: any[] = [];
for await (const [key, value] of Object.entries(this.waInstances)) {
if (value) {
this.logger.verbose('get instance info: ' + key);
let chatwoot: any;
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
const findChatwoot = await this.waInstances[key].findChatwoot();
if (findChatwoot && findChatwoot.enabled) {
chatwoot = {
...findChatwoot,
webhook_url: `${urlServer}/chatwoot/webhook/${encodeURIComponent(key)}`,
};
}
if (value.connectionStatus.state === 'open') {
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
const instanceData = {
instance: {
instanceName: key,
owner: value.wuid,
profileName: (await value.getProfileName()) || 'not loaded',
profilePictureUrl: value.profilePictureUrl,
profileStatus: (await value.getProfileStatus()) || '',
status: value.connectionStatus.state,
},
};
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey;
instanceData.instance['chatwoot'] = chatwoot;
}
instances.push(instanceData);
} else {
this.logger.verbose('instance: ' + key + ' - connectionStatus: ' + value.connectionStatus.state);
const instanceData = {
instance: {
instanceName: key,
status: value.connectionStatus.state,
},
};
if (this.configService.get<Auth>('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) {
instanceData.instance['serverUrl'] = this.configService.get<HttpServer>('SERVER').URL;
instanceData.instance['apikey'] = (await this.repository.auth.find(key)).apikey;
instanceData.instance['chatwoot'] = chatwoot;
}
instances.push(instanceData);
}
}
}
this.logger.verbose('return instance info: ' + instances.length);
return instances.find((i) => i.instance.instanceName === instanceName) ?? instances;
}
// inserido por francis fim
private delInstanceFiles() {
this.logger.verbose('cron to delete instance files started');
setInterval(async () => {

View File

@ -100,6 +100,13 @@ export class TypebotService {
const url = data.url;
const typebot = data.typebot;
const variables = data.variables;
const findTypebot = await this.find(instance);
const sessions = (findTypebot.sessions as Session[]) ?? [];
const expire = findTypebot.expire;
const keyword_finish = findTypebot.keyword_finish;
const delay_message = findTypebot.delay_message;
const unknown_message = findTypebot.unknown_message;
const listening_from_me = findTypebot.listening_from_me;
const prefilledVariables = {
remoteJid: remoteJid,
@ -109,33 +116,38 @@ export class TypebotService {
prefilledVariables[variable.name] = variable.value;
});
const id = Math.floor(Math.random() * 10000000000).toString();
const reqData = {
sessionId: id,
startParams: {
typebot: data.typebot,
const response = await this.createNewSession(instance, {
url: url,
typebot: typebot,
remoteJid: remoteJid,
expire: expire,
keyword_finish: keyword_finish,
delay_message: delay_message,
unknown_message: unknown_message,
listening_from_me: listening_from_me,
sessions: sessions,
prefilledVariables: prefilledVariables,
},
};
const request = await axios.post(data.url + '/api/v1/sendMessage', reqData);
});
if (response.sessionId) {
await this.sendWAMessage(
instance,
remoteJid,
request.data.messages,
request.data.input,
request.data.clientSideActions,
response.messages,
response.input,
response.clientSideActions,
);
this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.TYPEBOT_START, {
remoteJid: remoteJid,
url: url,
typebot: typebot,
variables: variables,
sessionId: id,
prefilledVariables: prefilledVariables,
sessionId: `${response.sessionId}`,
});
} else {
throw new Error("Session ID not found in response");
}
return {
typebot: {
@ -144,7 +156,7 @@ export class TypebotService {
url: url,
remoteJid: remoteJid,
typebot: typebot,
variables: variables,
prefilledVariables: prefilledVariables,
},
},
};
@ -193,8 +205,9 @@ export class TypebotService {
startParams: {
typebot: data.typebot,
prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid,
pushName: data.pushName,
pushName: data.pushName || 'Default Name',
instanceName: instance.instanceName,
},
},
@ -209,6 +222,12 @@ export class TypebotService {
status: 'opened',
createdAt: Date.now(),
updateAt: Date.now(),
prefilledVariables: {
...data.prefilledVariables,
remoteJid: data.remoteJid,
pushName: data.pushName || 'Default Name',
instanceName: instance.instanceName,
}
});
const typebotData = {

View File

@ -654,15 +654,17 @@ export class WAStartupService {
const amqp = getAMQP();
if (amqp) {
this.logger.verbose('Sending data to rabbitMQ on channel: ' + this.instance.name);
if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) {
const exchangeName = this.instanceName ?? 'evolution_exchange';
this.logger.verbose('Sending data to rabbitMQ on event: ' + event);
const exchangeName = this.instance.name ?? 'evolution_exchange';
amqp.assertExchange(exchangeName, 'topic', {
durable: true,
autoDelete: false,
});
const queueName = `${this.instanceName}.${event}`;
const queueName = `${this.instance.name}.${event}`;
amqp.assertQueue(queueName, {
durable: true,
@ -711,7 +713,7 @@ export class WAStartupService {
}
}
if (this.configService.get<Websocket>('WEBSOCKET').ENABLED && this.localWebsocket.enabled) {
if (this.configService.get<Websocket>('WEBSOCKET')?.ENABLED && this.localWebsocket.enabled) {
this.logger.verbose('Sending data to websocket on channel: ' + this.instance.name);
if (Array.isArray(websocketLocal) && websocketLocal.includes(we)) {
this.logger.verbose('Sending data to websocket on event: ' + event);
@ -1434,8 +1436,9 @@ export class WAStartupService {
if (
type !== 'notify' ||
!received.message ||
!received?.message ||
received.message?.protocolMessage ||
received.message.senderKeyDistributionMessage ||
received.message?.pollUpdateMessage
) {
this.logger.verbose('message rejected');
@ -1954,9 +1957,10 @@ export class WAStartupService {
private async sendMessageWithTyping<T = proto.IMessage>(number: string, message: T, options?: Options) {
this.logger.verbose('Sending message with typing');
const numberWA = await this.whatsappNumber({ numbers: [number] });
const isWA = numberWA[0];
this.logger.verbose(`Check if number "${number}" is WhatsApp`);
const isWA = (await this.whatsappNumber({ numbers: [number] }))?.shift();
this.logger.verbose(`Exists: "${isWA.exists}" | jid: ${isWA.jid}`);
if (!isWA.exists && !isJidGroup(isWA.jid) && !isWA.jid.includes('@broadcast')) {
throw new BadRequestException(isWA);
}
@ -1980,7 +1984,7 @@ export class WAStartupService {
this.logger.verbose('Sending presence update: paused');
}
const linkPreview = options?.linkPreview != false ? undefined : false;
const linkPreview = options?.linkPreview == true ? undefined : options?.linkPreview || false;
let quoted: WAMessage;
@ -2000,9 +2004,9 @@ export class WAStartupService {
let mentions: string[];
if (isJidGroup(sender)) {
try {
const groupMetadata = await this.client.groupMetadata(sender);
const group = await this.findGroup({ groupJid: sender }, 'inner');
if (!groupMetadata) {
if (!group) {
throw new NotFoundException('Group not found');
}
@ -2013,7 +2017,7 @@ export class WAStartupService {
this.logger.verbose('Mentions everyone');
this.logger.verbose('Getting group metadata');
mentions = groupMetadata.participants.map((participant) => participant.id);
mentions = group.participants.map((participant) => participant.id);
this.logger.verbose('Getting group metadata for mentions');
} else if (options.mentions?.mentioned?.length) {
this.logger.verbose('Mentions manually defined');
@ -2021,7 +2025,6 @@ export class WAStartupService {
const jid = this.createJid(mention);
if (isJidGroup(jid)) {
return null;
// throw new BadRequestException('Mentions must be a number');
}
return jid;
});
@ -2307,22 +2310,36 @@ export class WAStartupService {
mediaMessage.fileName = arrayMatch[1];
this.logger.verbose('File name: ' + mediaMessage.fileName);
}
// *inserido francis inicio
let mimetype: string;
// *inserido francis final
if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
mediaMessage.fileName = 'image.png';
// inserido francis inicio
mimetype = 'image/png';
// inserido francis inicio
}
if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
mediaMessage.fileName = 'video.mp4';
// inserido francis inicio
mimetype = 'video/mp4';
// inserido francis final
}
let mimetype: string;
// ocultado francis inicio
// let mimetype: string;
if (isURL(mediaMessage.media)) {
mimetype = getMIMEType(mediaMessage.media);
} else {
mimetype = getMIMEType(mediaMessage.fileName);
}
// if (isURL(mediaMessage.media)) {
// mimetype = getMIMEType(mediaMessage.media);
// } else {
// mimetype = getMIMEType(mediaMessage.fileName);
// }
// ocultado francis final
this.logger.verbose('Mimetype: ' + mimetype);