[core] Media improvements

- Disable downloading files
- Add 'media' field
This commit is contained in:
allburov committed 2023-11-25 17:25:19 +07:00
1 parent 7904516c97
commit 7b7bd02503
20 files changed
+335 -132

No files matched your search

@@ -72,14 +72,34 @@ Keep in mind that session's proxy configuration takes precedence over proxy conf
The following environment variables can be used to configure the file storage options for the WAHA:
- `WHATSAPP_FILES_MIMETYPES`: This variable can be used to download only specific mimetypes from messages. By default,
all files are downloaded. The mimetypes must be separated by a comma, without spaces. For
example: `audio,image/png,image/gif`. To choose a specific type, use a prefix (like `audio,image`).
- `WHATSAPP_FILES_MIMETYPES`: This variable can be used to download only specific mimetypes from messages.
By default, all files are downloaded. The mimetypes must be separated by a comma, without spaces.
For example: `audio,image/png,image/gif`. To choose a specific type, use a prefix (like `audio,image`). See usage below.
- `WHATSAPP_DOWNLOAD_MEDIA=true` - this variable can be used to **completely** disable downloading media files. By default, all files are downloaded.
Set this variable to `WHATSAPP_DOWNLOAD_MEDIA=false` to disable downloading media files.
- Under the hood, it sets `WHATSAPP_FILES_MIMETYPES=mimetype/ignore-all-media` to ignore all media files.
- `WHATSAPP_FILES_LIFETIME`: This variable can be used to set the time (in seconds) after which files will be removed to
free up space. The default value is `180`.
- `WHATSAPP_FILES_FOLDER`: This variable can be used to set the folder where files from chats (images, voice messages)
will be stored. The default value is `/tmp/whatsapp-files`.
💡 Even if WAHA doesn't process the message media because of `WHATSAPP_FILES_MIMETYPES` or `WHATSAPP_DOWNLOAD_MEDIA`
you'll get a webhook event with `hasMedia: True` field, but with no `media.url`.
```json
{
"event": "message",
"session": "default",
"payload": {
"hasMedia": true,
"media": {
"url": null,
"mimetype": "video/mp4",
"filename": null
}
}
}
```
## Examples
#### Debug Mode
@@ -138,6 +158,13 @@ comma-separated list of mimetypes:
WHATSAPP_FILES_MIMETYPES=audio,image/png,image/gif
```
#### Disable Downloading Media Files
To disable downloading media files, set the `WHATSAPP_DOWNLOAD_MEDIA` environment variable to `false`:
```
WHATSAPP_DOWNLOAD_MEDIA=false
```
#### Setting the File Lifetime
To set the time (in seconds) after which files will be removed to free up space, set the `WHATSAPP_FILES_LIFETIME`
@@ -155,9 +155,9 @@ Receive events when server or recipient gets the message, read or played it.
## Files ![](/images/versions/plus.png)
When people send you media - images, voice messages, and documents - WAHA saves it in the file storage.
In your application you must download it and use it as you want to. You can find the URL in `mediaUrl` field.
In your application you must download it and use it as you want to. You can find the URL in `media.url` field.
For example, you can get the webhook like this with `mediaUrl` value (we've skipped other fields):
For example, you can get the webhook like this with `media` value (we've skipped other fields):
```json
{
@@ -167,7 +167,12 @@ For example, you can get the webhook like this with `mediaUrl` value (we've skip
"id": "true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"timestamp": 1667561485,
"from": "11111111111@c.us",
"mediaUrl": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.jpg"
"hasMedia": true,
"media": {
"url": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.jpg",
"mimetype": "image/jpeg",
"filename": null
}
}
}
```
@@ -182,8 +187,11 @@ For documents (file attachments) there's also `filename` field with the original
"id": "true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"timestamp": 1667561485,
"from": "11111111111@c.us",
"mediaUrl": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.pdf",
"filename": "some-file.pdf"
"media": {
"url": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.pdf",
"filename": "some-file.pdf",
"mimetype": "application/pdf"
}
}
}
```
@@ -21,6 +21,25 @@ You even can **subscribe to get new updates** there!
## 2023.11
November 2023
- Add dedicated `media` field in webhook payload - now you can know which media is attached to the message.
```json
{
"event": "message",
"session": "default",
"payload": {
"hasMedia": true,
"media": {
"url": "http://localhost:3000/api/files/true_11111111111@c.us_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.pdf",
"mimetype": "application/pdf",
"filename": "file.pdf"
}
}
}
```
- Add `WHATSAPP_DOWNLOAD_MEDIA` environment variable to control media download. Available only in Plus version.
- `WHATSAPP_DOWNLOAD_MEDIA=True` - download media
- `WHATSAPP_DOWNLOAD_MEDIA=False` - do not download media at all.
- Add `filename` field for document (file attachments) - the original filename of the document.
- Add `/api/sendVideo` endpoint to **NOWEB** and **WEBJS** engines. Available only in Plus version.
- Add security settings for edit group info `GET /api/{session}/groups/{groupId}/settings/security/info-admin-only` - updates the group settings to only allow admins to edit group info (title, description, photo).
+3 -6
View File
@@ -1,5 +1,4 @@
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
import {
ApiExtraModels,
ApiOperation,
@@ -8,6 +7,7 @@ import {
} from '@nestjs/swagger';
import { SessionManager } from '../core/abc/manager.abc';
import { WhatsappSession } from '../core/abc/session.abc';
import { parseBool } from '../helpers';
import {
ListSessionsQuery,
@@ -19,9 +19,6 @@ import {
SessionStopRequest,
} from '../structures/sessions.dto';
import { SessionApiParam, SessionParam } from './helpers';
import { WhatsappSession } from '../core/abc/session.abc';
import { WAHAChatPresences } from '../structures/presence.dto';
import { WAHAWebhook } from '../structures/webhooks.dto';
@ApiSecurity('api_key')
@Controller('api/sessions')
@@ -34,7 +31,7 @@ class SessionsController {
const result = await this.manager.start(request);
await this.manager.sessionStorage.configRepository.save(
request.name,
request.config,
request.config || null,
);
return result;
}
@@ -71,7 +68,7 @@ class SessionController {
@Get('me')
@SessionApiParam
@ApiOperation({ summary: 'Get information about the authenticated account' })
getMe(@SessionParam session: WhatsappSession): Promise<MeInfo> {
getMe(@SessionParam session: WhatsappSession): Promise<MeInfo | null> {
return session.getSessionMeInfo();
}
}
+11 -3
View File
@@ -36,9 +36,17 @@ export class WhatsappConfigService {
return this.configService.get<number>('WHATSAPP_FILES_LIFETIME', 180);
}
get mimetypes(): string[] | null {
get mimetypes(): string[] {
if (!this.shouldDownloadMedia) {
return ['mimetype/ignore-all-media'];
}
const types = this.configService.get('WHATSAPP_FILES_MIMETYPES', '');
return types ? types.split(',') : null;
return types ? types.split(',') : [];
}
get shouldDownloadMedia(): boolean {
const value = this.configService.get('WHATSAPP_DOWNLOAD_MEDIA', 'true');
return parseBool(value);
}
get startSessions(): string[] {
@@ -111,7 +119,7 @@ export class WhatsappConfigService {
return WAHAEngine.WEBJS;
}
get(name: string, defaultValue = undefined): any {
get(name: string, defaultValue: any = undefined): any {
return this.configService.get(name, defaultValue);
}
+2 -4
View File
@@ -9,7 +9,7 @@ import {
SessionStopRequest,
} from '../../structures/sessions.dto';
import { WhatsappSession } from './session.abc';
import { LocalSessionStorage, MediaStorage } from './storage.abc';
import { LocalSessionStorage } from './storage.abc';
import { WebhookConductor } from './webhooks.abc';
export abstract class SessionManager implements OnApplicationShutdown {
@@ -21,8 +21,6 @@ export abstract class SessionManager implements OnApplicationShutdown {
protected abstract get WebhookConductorClass(): typeof WebhookConductor;
protected abstract get MediaStorageClass(): typeof MediaStorage;
abstract onApplicationShutdown(signal?: string);
//
@@ -34,7 +32,7 @@ export abstract class SessionManager implements OnApplicationShutdown {
abstract logout(request: SessionLogoutRequest): Promise<void>;
abstract getSession(name: string, error?: boolean): WhatsappSession;
abstract getSession(name: string): WhatsappSession;
abstract getSessions(all: boolean): Promise<SessionInfo[]>;
}
+28
View File
@@ -0,0 +1,28 @@
abstract class MediaStorage {
abstract save(
messageId: string,
mimetype: string,
buffer: Buffer,
): Promise<string>;
}
interface IEngineMediaProcessor<Message> {
hasMedia(message: Message): boolean;
getFilename(message: Message): string | null;
getMimetype(message: Message): string;
getMessageId(message: Message): string;
getMediaBuffer(message: Message): Promise<Buffer | null>;
}
interface MediaManager {
processMedia<Message>(
processor: IEngineMediaProcessor<Message>,
message: Message,
): Promise<Message>;
}
export { IEngineMediaProcessor, MediaManager, MediaStorage };
+10 -9
View File
@@ -3,6 +3,7 @@ import { EventEmitter } from 'events';
import * as fs from 'fs';
import { MessageId } from 'whatsapp-web.js';
import { OTPRequest, RequestCodeRequest } from '../../structures/auth.dto';
import {
ChatRequest,
CheckNumberStatusQuery,
@@ -37,9 +38,6 @@ import {
ProxyConfig,
SessionConfig,
} from '../../structures/sessions.dto';
import { NotImplementedByEngineError } from '../exceptions';
import { LocalSessionStorage, MediaStorage } from './storage.abc';
import { OTPRequest, RequestCodeRequest } from '../../structures/auth.dto';
import {
ImageStatus,
TextStatus,
@@ -47,6 +45,9 @@ import {
VoiceStatus,
} from '../../structures/status.dto';
import { WASessionStatusBody } from '../../structures/webhooks.dto';
import { NotImplementedByEngineError } from '../exceptions';
import { MediaManager } from './media.abc';
import { LocalSessionStorage } from './storage.abc';
const CHROME_PATH = '/usr/bin/google-chrome-stable';
const CHROMIUM_PATH = '/usr/bin/chromium';
@@ -73,11 +74,11 @@ export enum WAHAInternalEvent {
export interface SessionParams {
name: string;
storage: MediaStorage;
mediaManager: MediaManager;
log: ConsoleLogger;
sessionStorage: LocalSessionStorage;
proxyConfig?: ProxyConfig;
sessionConfig: SessionConfig;
sessionConfig?: SessionConfig;
}
export abstract class WhatsappSession {
@@ -85,11 +86,11 @@ export abstract class WhatsappSession {
public engine: WAHAEngine;
public name: string;
protected storage: MediaStorage;
protected mediaManager: MediaManager;
protected log: ConsoleLogger;
protected sessionStorage: LocalSessionStorage;
protected proxyConfig?: ProxyConfig;
public sessionConfig: SessionConfig;
public sessionConfig?: SessionConfig;
private _status: WAHASessionStatus;
@@ -98,7 +99,7 @@ export abstract class WhatsappSession {
log,
sessionStorage,
proxyConfig,
storage,
mediaManager,
sessionConfig,
}: SessionParams) {
this.events = new EventEmitter();
@@ -106,7 +107,7 @@ export abstract class WhatsappSession {
this.proxyConfig = proxyConfig;
this.log = log;
this.sessionStorage = sessionStorage;
this.storage = storage;
this.mediaManager = mediaManager;
this.sessionConfig = sessionConfig;
}
+6 -11
View File
@@ -1,17 +1,12 @@
import { SessionConfig } from '../../structures/sessions.dto';
abstract class MediaStorage {
abstract save(
messageId: string,
mimetype: string,
buffer: Buffer,
): Promise<string>;
}
abstract class SessionConfigRepository {
abstract save(sessionName: string, config: SessionConfig): Promise<void>;
abstract save(
sessionName: string,
config: SessionConfig | null,
): Promise<void>;
abstract get(sessionName: string): Promise<SessionConfig>;
abstract get(sessionName: string): Promise<SessionConfig | null>;
}
abstract class SessionStorage {
@@ -37,4 +32,4 @@ abstract class LocalSessionStorage extends SessionStorage {
abstract getFolderPath(sessionName: string): string;
}
export { LocalSessionStorage, MediaStorage, SessionConfigRepository };
export { LocalSessionStorage, SessionConfigRepository };
+4 -4
View File
@@ -9,7 +9,7 @@ import { WhatsappSession } from './abc/session.abc';
export function getProxyConfig(
config: WhatsappConfigService,
sessions: Record<string, WhatsappSession>,
sessionName?: string,
sessionName: string,
): ProxyConfig | undefined {
// Single proxy server configuration
if (typeof config.proxyServer === 'string') {
@@ -23,7 +23,7 @@ export function getProxyConfig(
// Multiple proxy server configuration
if (Array.isArray(config.proxyServer)) {
const prefix = config.proxyServerIndexPrefix;
let indexToUse: number = undefined;
let indexToUse: number | undefined = undefined;
if (prefix && sessionName.includes(prefix)) {
// match numbers at the end of the string
const matches = sessionName.match(/\d+$/);
@@ -66,8 +66,8 @@ function getAuthenticatedUrl(
export function createAgentProxy(proxyConfig: ProxyConfig): Agent | undefined {
const url = getAuthenticatedUrl(
proxyConfig.server,
proxyConfig.username,
proxyConfig.password,
proxyConfig.username || '',
proxyConfig.password || '',
);
return new HttpsProxyAgent(url);
}
+19 -19
View File
@@ -18,17 +18,14 @@ import {
} from '../structures/sessions.dto';
import { WebhookConfig } from '../structures/webhooks.config.dto';
import { SessionManager } from './abc/manager.abc';
import {
SessionParams,
WAHAInternalEvent,
WhatsappSession,
} from './abc/session.abc';
import { SessionParams, WhatsappSession } from './abc/session.abc';
import { DOCS_URL } from './exceptions';
import { getProxyConfig } from './helpers.proxy';
import { CoreMediaManager, MediaStorageCore } from './media.core';
import { WhatsappSessionNoWebCore } from './session.noweb.core';
import { WhatsappSessionVenomCore } from './session.venom.core';
import { WhatsappSessionWebJSCore } from './session.webjs.core';
import { MediaStorageCore, SessionStorageCore } from './storage.core';
import { SessionStorageCore } from './storage.core';
import { WebhookConductorCore } from './webhooks.core';
export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
@@ -45,11 +42,9 @@ export function buildLogger(name) {
@Injectable()
export class SessionManagerCore extends SessionManager {
private session: WhatsappSession;
private session?: WhatsappSession;
DEFAULT = 'default';
// @ts-ignore
protected MediaStorageClass = MediaStorageCore;
// @ts-ignore
protected WebhookConductorClass = WebhookConductorCore;
protected readonly EngineClass: typeof WhatsappSession;
@@ -110,13 +105,16 @@ export class SessionManagerCore extends SessionManager {
const name = request.name;
this.log.log(`'${name}' - starting session...`);
const log = buildLogger(`WhatsappSession - ${name}`);
const storage = new this.MediaStorageClass();
const mediaManager = new CoreMediaManager(
new MediaStorageCore(),
this.config.mimetypes,
);
const webhookLog = buildLogger(`Webhook - ${name}`);
const webhook = new this.WebhookConductorClass(webhookLog);
const proxyConfig = this.getProxyConfig(request);
const sessionConfig: SessionParams = {
name,
storage,
mediaManager,
log,
sessionStorage: this.sessionStorage,
proxyConfig: proxyConfig,
@@ -149,7 +147,9 @@ export class SessionManagerCore extends SessionManager {
webhooks = webhooks.concat(request.config.webhooks);
}
const globalWebhookConfig = this.config.getWebhookConfig();
webhooks.push(globalWebhookConfig);
if (globalWebhookConfig) {
webhooks.push(globalWebhookConfig);
}
return webhooks;
}
@@ -162,6 +162,9 @@ export class SessionManagerCore extends SessionManager {
if (request.config?.proxy) {
return request.config.proxy;
}
if (!this.session) {
return undefined;
}
const sessions = { [request.name]: this.session };
return getProxyConfig(this.config, sessions, request.name);
}
@@ -181,16 +184,13 @@ export class SessionManagerCore extends SessionManager {
await this.sessionStorage.clean(request.name);
}
getSession(name: string, error = true): WhatsappSession {
getSession(name: string): WhatsappSession {
this.onlyDefault(name);
const session = this.session;
if (!session) {
if (error) {
throw new NotFoundException(
`We didn't find a session with name '${name}'. Please start it first by using POST /sessions/start request`,
);
}
return;
throw new NotFoundException(
`We didn't find a session with name '${name}'. Please start it first by using POST /sessions/start request`,
);
}
return session;
}
+45
View File
@@ -0,0 +1,45 @@
import {
IEngineMediaProcessor,
MediaManager,
MediaStorage,
} from './abc/media.abc';
import { DOCS_URL } from './exceptions';
import { WAMedia } from '../structures/responses.dto';
import * as process from 'process';
export class MediaStorageCore implements MediaStorage {
async save(
messageId: string,
mimetype: string,
buffer: Buffer,
): Promise<string> {
return Promise.resolve(
`Media attachment's available only in WAHA Plus version. ${DOCS_URL}`,
);
}
}
export class CoreMediaManager implements MediaManager {
constructor(private storage: MediaStorage, private mimetypes: string[]) {}
async processMedia<Message>(
processor: IEngineMediaProcessor<Message>,
message: Message,
): Promise<Message> {
if (!processor.hasMedia(message)) {
return message;
}
const mimetype = '';
const filename = processor.getFilename(message);
const url = await this.storage.save('', mimetype, Buffer.from(''));
const media: WAMedia = {
mimetype: mimetype,
filename: filename,
url: url,
};
// @ts-ignore
message.media = media;
return message;
}
}
+39 -14
View File
@@ -17,7 +17,6 @@ import * as fs from 'fs/promises';
import { Agent } from 'https';
import * as lodash from 'lodash';
import { PairingCodeResponse } from 'src/structures/auth.dto';
import { Message } from 'whatsapp-web.js';
import { flipObject, splitAt } from '../helpers';
import {
@@ -55,12 +54,14 @@ import {
WAHAPresenceData,
} from '../structures/presence.dto';
import { WAMessage } from '../structures/responses.dto';
import { MeInfo } from '../structures/sessions.dto';
import { BROADCAST_ID, TextStatus } from '../structures/status.dto';
import {
PollVote,
PollVotePayload,
WAMessageAckBody,
} from '../structures/webhooks.dto';
import { IEngineMediaProcessor } from './abc/media.abc';
import {
ensureSuffix,
WAHAInternalEvent,
@@ -72,7 +73,7 @@ import {
} from './exceptions';
import { createAgentProxy } from './helpers.proxy';
import { QR } from './QR';
import { MeInfo } from '../structures/sessions.dto';
import * as Buffer from 'buffer';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
@@ -659,11 +660,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
body: body,
to: toCusFormat(fromToParticipant.to),
participant: toCusFormat(fromToParticipant.participant),
// @ts-ignore
hasMedia: Boolean(message.mediaUrl),
// @ts-ignore
mediaUrl: message.mediaUrl,
filename: message.message?.documentMessage?.fileName,
// Media
hasMedia: Boolean(message.media),
media: message.media,
mediaUrl: message.media?.url,
// @ts-ignore
ack: message.ack,
// @ts-ignore
@@ -812,14 +812,39 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return { id: chatId, presences: presences };
}
protected async downloadMedia(message: Message) {
if (!message.hasMedia) {
return message;
}
protected downloadMedia(message) {
const processor = new EngineMediaProcessor(this);
return this.mediaManager.processMedia(processor, message);
}
}
// @ts-ignore
message.mediaUrl = await this.storage.save(message.key.id, '', undefined);
return message;
export class EngineMediaProcessor implements IEngineMediaProcessor<any> {
constructor(public session: WhatsappSessionNoWebCore) {}
hasMedia(message: any): boolean {
const messageType = Object.keys(message.message)[0];
const hasMedia =
messageType === 'imageMessage' ||
messageType == 'audioMessage' ||
messageType == 'documentMessage' ||
messageType == 'videoMessage';
return hasMedia;
}
getMessageId(message: any): string {
return '';
}
getMimetype(message: any): string {
return '';
}
getMediaBuffer(message: any): Promise<Buffer | null> {
return Promise.resolve(undefined);
}
getFilename(message: any): string | null {
return message.message?.documentMessage?.fileName || null;
}
}
+37 -10
View File
@@ -21,9 +21,11 @@ import {
WAHASessionStatus,
} from '../structures/enums.dto';
import { WAMessage } from '../structures/responses.dto';
import { IEngineMediaProcessor } from './abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
import { NotImplementedByEngineError } from './exceptions';
import { QR } from './QR';
import * as Buffer from 'buffer';
export class WhatsappSessionVenomCore extends WhatsappSession {
engine = WAHAEngine.VENOM;
@@ -246,17 +248,13 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
* END - Methods for API
*/
protected async downloadAndDecryptMedia(message: Message) {
if (!message.isMMS || !message.isMedia) {
return message;
}
// @ts-ignore
message.mediaUrl = await this.storage.save(message.id, '', undefined);
return message;
protected downloadMedia(message: Message) {
const processor = new EngineMediaProcessor(this);
return this.mediaManager.processMedia(processor, message);
}
private processIncomingMessage(message: Message) {
return this.downloadAndDecryptMedia(message).then(this.toWAMessage);
return this.downloadMedia(message).then(this.toWAMessage);
}
protected toWAMessage(message: Message): Promise<WAMessage> {
@@ -268,10 +266,13 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
fromMe: message.fromMe,
to: message.to,
body: message.body,
// Media
// @ts-ignore
hasMedia: Boolean(message.mediaUrl),
hasMedia: Boolean(message.media),
// @ts-ignore
mediaUrl: message.mediaUrl,
media: message.media,
// @ts-ignore
mediaUrl: message.media?.url,
// @ts-ignore
ack: message.ack,
location: undefined,
@@ -280,3 +281,29 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
});
}
}
export class EngineMediaProcessor implements IEngineMediaProcessor<Message> {
constructor(public session: WhatsappSessionVenomCore) {}
hasMedia(message: any): boolean {
if (!message.isMMS || !message.isMedia) {
return message;
}
}
getMessageId(message: any): string {
return '';
}
getMimetype(message: any): string {
return '';
}
getMediaBuffer(message: any): Promise<Buffer | null> {
return Promise.resolve(undefined);
}
getFilename(message: Message): string | null {
return null;
}
}
+34 -13
View File
@@ -40,14 +40,15 @@ import {
SettingsSecurityChangeInfo,
} from '../structures/groups.dto';
import { WAMessage } from '../structures/responses.dto';
import { MeInfo } from '../structures/sessions.dto';
import { WAMessageRevokedBody } from '../structures/webhooks.dto';
import { IEngineMediaProcessor } from './abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
import {
AvailableInPlusVersion,
NotImplementedByEngineError,
} from './exceptions';
import { QR } from './QR';
import { MeInfo } from '../structures/sessions.dto';
import { WAMessageRevokedBody } from '../structures/webhooks.dto';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
@@ -541,12 +542,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
fromMe: message.fromMe,
to: message.to,
body: message.body,
// Media
// @ts-ignore
hasMedia: Boolean(message.mediaUrl),
hasMedia: Boolean(message.media),
// @ts-ignore
mediaUrl: message.mediaUrl,
media: message.media,
// @ts-ignore
mediaUrl: message.media?.url,
// @ts-ignore
filename: message.rawData?.filename,
ack: message.ack,
ackName: WAMessageAck[message.ack] || ACK_UNKNOWN,
location: message.location,
@@ -561,17 +564,35 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return contact;
}
protected async downloadMedia(message: Message) {
protected downloadMedia(message: Message) {
const processor = new EngineMediaProcessor();
return this.mediaManager.processMedia(processor, message);
}
}
export class EngineMediaProcessor implements IEngineMediaProcessor<Message> {
hasMedia(message: Message): boolean {
if (!message.hasMedia) {
return message;
return false;
}
// Can't get media for revoked messages
return message.type !== 'revoked';
}
getMessageId(message: Message): string {
return '';
}
getMimetype(message: Message): string {
return '';
}
getMediaBuffer(message: Message): Promise<Buffer | null> {
return Promise.resolve(undefined);
}
getFilename(message: Message): string | null {
// @ts-ignore
message.mediaUrl = await this.storage.save(
message.id._serialized,
'',
undefined,
);
return message;
return message.rawData?.filename || null;
}
}
+4 -18
View File
@@ -6,22 +6,8 @@ import * as path from 'path';
import { SessionConfig } from '../structures/sessions.dto';
import {
LocalSessionStorage,
MediaStorage,
SessionConfigRepository,
} from './abc/storage.abc';
import { DOCS_URL } from './exceptions';
export class MediaStorageCore implements MediaStorage {
async save(
messageId: string,
mimetype: string,
buffer: Buffer,
): Promise<string> {
return Promise.resolve(
`Media attachment's available only in WAHA Plus version. ${DOCS_URL}`,
);
}
}
class LocalSessionConfigRepository extends SessionConfigRepository {
FILENAME = '.waha.session.config.json';
@@ -30,13 +16,13 @@ class LocalSessionConfigRepository extends SessionConfigRepository {
super();
}
async get(sessionName: string): Promise<SessionConfig> {
async get(sessionName: string): Promise<SessionConfig | null> {
const filepath = this.getFilePath(sessionName);
// Check file exists
try {
await fs.access(filepath, fs.constants.F_OK);
} catch (error) {
return undefined;
return null;
}
// Try to load config
@@ -44,13 +30,13 @@ class LocalSessionConfigRepository extends SessionConfigRepository {
try {
content = await fs.readFile(filepath, 'utf-8');
} catch (error) {
return undefined;
return null;
}
return JSON.parse(content);
}
async save(sessionName: string, config: SessionConfig) {
async save(sessionName: string, config: SessionConfig | null) {
// Create folder if not exist
const folder = this.storage.getFolderPath(sessionName);
await fs.mkdir(folder, { recursive: true });
+2 -2
View File
@@ -131,7 +131,7 @@ export class SwaggerModuleCore {
return document;
}
protected setUpAuth(app: INestApplication) {
return undefined;
protected setUpAuth(app: INestApplication): void {
return;
}
}
+2 -3
View File
@@ -1,7 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { WAHAEvents } from './enums.dto';
import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger';
function ChatIdProperty(options = undefined) {
function ChatIdProperty(options: ApiPropertyOptions | null = null) {
options = options || {};
if (!options.example) {
options.example = '11111111111@c.us';
+26 -7
View File
@@ -9,6 +9,27 @@ export class WALocation {
longitude: string;
}
export class WAMedia {
@ApiProperty({
description: 'The URL for the media in the message if any',
example:
'http://localhost:3000/api/files/false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA.oga',
})
url: string;
@ApiProperty({
description: 'mimetype for the media in the message if any',
example: 'audio/jpeg',
})
mimetype: string | null;
@ApiProperty({
description: 'The original filename in mediaUrl in the message if any',
example: 'example.pdf',
})
filename: string | null;
}
export class WAMessage {
@MessageIdProperty()
id: string;
@@ -55,19 +76,17 @@ export class WAMessage {
})
hasMedia: boolean;
media: WAMedia | null = null;
@ApiProperty({
description: 'The URL for the media in the message if any',
description:
'Use `media.url` instead! The URL for the media in the message if any',
deprecated: true,
example:
'http://localhost:3000/api/files/false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA.oga',
})
mediaUrl: string;
@ApiProperty({
description: 'The original filename in mediaUrl in the message if any',
example: 'example.pdf',
})
filename: string;
@ApiProperty({
description: 'ACK status for the message',
})
+1 -1
View File
@@ -65,7 +65,7 @@ export class SessionLogoutRequest {
export class SessionDTO {
name = 'default';
status: WAHASessionStatus;
config: SessionConfig;
config?: SessionConfig;
}
export class MeInfo {