Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
163b11aa36 | ||
|
|
f4171a9086 | ||
|
|
dfbae9c71e | ||
|
|
91caa7ba25 | ||
|
|
2ac57bb6c3 | ||
|
|
e01647eb48 | ||
|
|
90bc05ecf3 | ||
|
|
21998cbfae | ||
|
|
87c49a8766 | ||
|
|
4e78d559a8 | ||
|
|
18ce902c60 | ||
|
|
5784db8055 | ||
|
|
6862063654 | ||
|
|
4aea282356 | ||
|
|
82ff15ae56 | ||
|
|
6cbf8cded2 | ||
|
|
35e9418988 | ||
|
|
a58be84e56 | ||
|
|
2994e0a54d | ||
|
|
f16100e1d9 | ||
|
|
e57be58d48 | ||
|
|
7909910dc6 | ||
|
|
7f1778ceb0 | ||
|
|
aba90574d8 | ||
|
|
33c945bf95 | ||
|
|
d40e82eb02 | ||
|
|
6e82d6f40b | ||
|
|
e193eb5696 | ||
|
|
8c63c4b097 | ||
|
|
4127d609cf |
No files matched your search
@@ -1,3 +1,4 @@
|
||||
tmp/*
|
||||
Dockerfile
|
||||
docker-compose
|
||||
src/core/engines/gows/proto
|
||||
|
||||
@@ -32,6 +32,12 @@ WAHA_BASE_URL=http://localhost:3000
|
||||
# WHATSAPP_API_HOSTNAME=localhost
|
||||
|
||||
|
||||
# ===================
|
||||
# ====== GOWS =======
|
||||
# ===================
|
||||
# By default, GOWS uses cgo (libc) DNS resolver
|
||||
# You can ask to use golang one, but it's less reliable
|
||||
# GODEBUG=netdns=go
|
||||
|
||||
# ===================
|
||||
# ===== LOGGING =====
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
tmp/*
|
||||
src/core/engines/gows/proto
|
||||
.env
|
||||
.envs/*
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
ARG NODE_IMAGE_TAG=22.16-bookworm-slim
|
||||
ARG GOLANG_IMAGE_TAG=1.23-bookworm
|
||||
ARG NODE_IMAGE_TAG=24.11-bookworm-slim
|
||||
ARG GOLANG_IMAGE_TAG=1.24-bookworm
|
||||
|
||||
#
|
||||
# Build
|
||||
@@ -229,6 +229,9 @@ ENV CHOKIDAR_INTERVAL=5000
|
||||
# WAHA variables
|
||||
ENV WAHA_ZIPPER=ZIPUNZIP
|
||||
|
||||
# GOWS - use libc DNS resolver
|
||||
ENV GODEBUG netdns=cgo
|
||||
|
||||
# Run command, etc
|
||||
EXPOSE 3000
|
||||
# Use tini as init system to handle zombie processes properly
|
||||
|
||||
@@ -32,10 +32,10 @@ push:
|
||||
docker push devlikeapro/waha
|
||||
|
||||
for-swagger:
|
||||
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
|
||||
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true WHATSAPP_SWAGGER_PASSWORD=666 . ${NVM_DIR}/nvm.sh && nvm exec yarn start
|
||||
|
||||
up-noweb:
|
||||
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-07
|
||||
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-21
|
||||
|
||||
up-noweb-libsignal:
|
||||
yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
|
||||
|
||||
+4
-3
@@ -26,7 +26,7 @@
|
||||
"gows:proto": "yarn gows:proto:fetch && yarn gows:proto:build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-07",
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-21",
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@aws-sdk/client-s3": "^3.633.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.633.0",
|
||||
@@ -106,6 +106,7 @@
|
||||
"swagger-ui-express": "^4.1.4",
|
||||
"ulid": "^2.3.0",
|
||||
"undici": "^7.16.0",
|
||||
"uniqid": "^5.4.0",
|
||||
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05",
|
||||
"write-file-atomic": "^6.0.0",
|
||||
"yaml": "^2.7.1"
|
||||
@@ -121,8 +122,8 @@
|
||||
"axios": "^1.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grpc/grpc-js": "^1.13.4",
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@grpc/grpc-js": "^1.14.1",
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
"@nestjs/cli": "^11.0.0",
|
||||
"@nestjs/schematics": "^11.0.0",
|
||||
"@nestjs/testing": "^11.0.0",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import * as inspector from 'node:inspector';
|
||||
import * as v8 from 'node:v8';
|
||||
import { Readable } from 'node:stream';
|
||||
|
||||
import {
|
||||
Controller,
|
||||
@@ -18,7 +20,10 @@ import {
|
||||
WorkingSessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
import { WAHAValidationPipe } from '@waha/nestjs/pipes/WAHAValidationPipe';
|
||||
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
|
||||
import {
|
||||
BrowserTraceQuery,
|
||||
CpuProfileQuery,
|
||||
} from '@waha/structures/server.debug.dto';
|
||||
import { createReadStream } from 'fs';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@@ -36,6 +41,56 @@ export class ServerDebugController {
|
||||
this.enabled = this.config.debugModeEnabled;
|
||||
}
|
||||
|
||||
@Get('cpu')
|
||||
@ApiOperation({
|
||||
summary: 'Collect and return a CPU profile for the current nodejs process',
|
||||
description: 'Uses the Node.js inspector profiler to capture a .cpuprofile',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async cpuProfile(@Query() query: CpuProfileQuery) {
|
||||
if (!this.enabled) {
|
||||
throw new NotFoundException('WAHA_DEBUG_MODE is disabled');
|
||||
}
|
||||
const { seconds } = query;
|
||||
this.logger.log(`Collecting CPU profile for ${seconds}s...`);
|
||||
|
||||
const session = new inspector.Session();
|
||||
session.connect();
|
||||
|
||||
const profile = await new Promise<inspector.Profiler.Profile>(
|
||||
(resolve, reject) => {
|
||||
session.post('Profiler.enable', (enableError) => {
|
||||
if (enableError) {
|
||||
session.disconnect();
|
||||
return reject(enableError);
|
||||
}
|
||||
session.post('Profiler.start', (startError) => {
|
||||
if (startError) {
|
||||
session.disconnect();
|
||||
return reject(startError);
|
||||
}
|
||||
setTimeout(() => {
|
||||
session.post('Profiler.stop', (stopError, params) => {
|
||||
session.disconnect();
|
||||
if (stopError) {
|
||||
return reject(stopError);
|
||||
}
|
||||
resolve(params.profile);
|
||||
});
|
||||
}, seconds * 1000);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const filename = `CPU.${Date.now()}.${process.pid}.cpuprofile`;
|
||||
const stream = Readable.from([JSON.stringify(profile)]);
|
||||
return new StreamableFile(stream, {
|
||||
type: 'application/json',
|
||||
disposition: `attachment; filename=${filename}`,
|
||||
});
|
||||
}
|
||||
|
||||
@Get('heapsnapshot')
|
||||
@ApiOperation({
|
||||
summary: 'Return a heapsnapshot for the current nodejs process',
|
||||
|
||||
@@ -29,13 +29,22 @@ export class AxiosLogging {
|
||||
const { method, url } = response.config;
|
||||
const status = response.status;
|
||||
const methodStr = method?.toUpperCase() ?? 'GET';
|
||||
const responseData = JSON.stringify(response.data);
|
||||
|
||||
if (status >= 200 && status < 400) {
|
||||
this.logger.debug(`${methodStr} ${status}:OK ${url}`);
|
||||
this.logger.trace(`${methodStr} ${status}:OK ${url} ${responseData}`);
|
||||
const type: any = response.headers['content-type'];
|
||||
// Log warnings for non-2xx/3xx responses
|
||||
if (status < 200 || status >= 400) {
|
||||
const data = JSON.stringify(response.data);
|
||||
this.logger.warn(`${methodStr} ${status} ${url} ${data}`);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Log debug for 2xx/3xx responses
|
||||
this.logger.debug(`${methodStr} ${status}:OK ${url}`);
|
||||
if (type.startsWith('application/json')) {
|
||||
const data = JSON.stringify(response.data);
|
||||
this.logger.trace(`${methodStr} ${status}:OK ${url} ${data}`);
|
||||
} else {
|
||||
this.logger.warn(`${methodStr} ${status} ${url} ${responseData}`);
|
||||
this.logger.trace(`${methodStr} ${status}:OK ${url} [${type}]`);
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
|
||||
import {
|
||||
ContactIds,
|
||||
ConversationResult,
|
||||
ConversationService,
|
||||
} from '@waha/apps/chatwoot/client/ConversationService';
|
||||
import { ChatWootAPIConfig } from '@waha/apps/chatwoot/client/interfaces';
|
||||
@@ -43,9 +44,10 @@ export class ContactConversationService {
|
||||
this.cache = CacheForConfig(config);
|
||||
}
|
||||
|
||||
public async upsertByContactInfo(
|
||||
private async getByContactInfo(
|
||||
contactInfo: ContactInfo,
|
||||
): Promise<ContactIds> {
|
||||
upsert: boolean,
|
||||
): Promise<ContactIds | null> {
|
||||
const chatId = contactInfo.ChatId();
|
||||
|
||||
// Check cache for chat id
|
||||
@@ -77,10 +79,21 @@ export class ContactConversationService {
|
||||
//
|
||||
// Get or create a conversation for this inbox
|
||||
//
|
||||
const conversation = await this.conversationService.upsert({
|
||||
const contactIds = {
|
||||
id: cwContact.data.id,
|
||||
sourceId: cwContact.sourceId,
|
||||
});
|
||||
};
|
||||
let conversation: ConversationResult;
|
||||
if (upsert) {
|
||||
conversation = await this.conversationService.upsert(contactIds);
|
||||
} else {
|
||||
conversation = await this.conversationService.find(contactIds);
|
||||
if (!conversation) {
|
||||
// Nothing found, do not save it in cache
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug(
|
||||
`Using conversation for chat.id: ${chatId}, conversation.id: ${conversation.id}, contact.id: ${cwContact.sourceId}`,
|
||||
);
|
||||
@@ -94,11 +107,15 @@ export class ContactConversationService {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public async ConversationByContact(
|
||||
private async conversationByContact(
|
||||
contactInfo: ContactInfo,
|
||||
): Promise<Conversation> {
|
||||
upsert: boolean,
|
||||
): Promise<Conversation | null> {
|
||||
const chatId = contactInfo.ChatId();
|
||||
const ids = await this.upsertByContactInfo(contactInfo);
|
||||
const ids = await this.getByContactInfo(contactInfo, upsert);
|
||||
if (!ids) {
|
||||
return null;
|
||||
}
|
||||
const conversation = new Conversation(
|
||||
this.accountAPI,
|
||||
this.config.accountId,
|
||||
@@ -118,6 +135,26 @@ export class ContactConversationService {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create if not exists conversation for the contact
|
||||
*/
|
||||
public async ConversationByContact(contactInfo: ContactInfo) {
|
||||
const conversation = await this.conversationByContact(contactInfo, true);
|
||||
if (!conversation) {
|
||||
// Shouldn't happen, but for type safety
|
||||
throw new Error('Conversation could not be created or found');
|
||||
}
|
||||
return conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find suitable conversation for the contact (based on filters)
|
||||
* If nothing found - it doesn't create a new one
|
||||
*/
|
||||
public async FindConversationByContact(contactInfo: ContactInfo) {
|
||||
return this.conversationByContact(contactInfo, false);
|
||||
}
|
||||
|
||||
public ConversationById(conversationId: number): Conversation {
|
||||
return new Conversation(
|
||||
this.accountAPI,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { sanitizeName } from '@waha/apps/chatwoot/client/ContactService';
|
||||
|
||||
describe('sanitizeName', () => {
|
||||
it('should return the same name', () => {
|
||||
const name = 'John Doe';
|
||||
expect(sanitizeName(name)).toBe(name);
|
||||
});
|
||||
it('should return 254 symbols max', () => {
|
||||
const name = 'a'.repeat(300);
|
||||
expect(sanitizeName(name)).toBe('a'.repeat(255));
|
||||
});
|
||||
|
||||
it('should remove bidi characters and trim the name', () => {
|
||||
const name =
|
||||
'+123123';
|
||||
expect(sanitizeName(name)).toBe('+123123');
|
||||
});
|
||||
|
||||
it('should not remove bidi characters if within limit', () => {
|
||||
const name = 'محمد +123';
|
||||
expect(sanitizeName(name)).toBe(name);
|
||||
});
|
||||
});
|
||||
@@ -26,6 +26,20 @@ export enum AvatarUpdateMode {
|
||||
ALWAYS,
|
||||
}
|
||||
|
||||
export function sanitizeName(name: string) {
|
||||
// 255 chars max
|
||||
const limit = 255;
|
||||
if (!name) {
|
||||
return name;
|
||||
}
|
||||
if (name.length < limit) {
|
||||
return name;
|
||||
}
|
||||
// remove only bidi controls
|
||||
const clean = name.replace(/[\u200E\u200F\u202A-\u202E\u2066-\u2069]/g, '');
|
||||
return clean.slice(0, 255).trim();
|
||||
}
|
||||
|
||||
export class ContactService {
|
||||
constructor(
|
||||
private config: ChatWootAPIConfig,
|
||||
@@ -139,6 +153,7 @@ export class ContactService {
|
||||
chatId: string,
|
||||
payload: public_contact_create_update_payload,
|
||||
): Promise<ContactResponse> {
|
||||
payload.name = sanitizeName(payload.name);
|
||||
const contact = await this.inboxAPI.contacts.create({
|
||||
inboxIdentifier: this.config.inboxIdentifier,
|
||||
data: payload,
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
} from '@waha/apps/chatwoot/client/interfaces';
|
||||
import type { conversation } from '@figuro/chatwoot-sdk/dist/models/conversation';
|
||||
import { ConversationSelector } from '@waha/apps/chatwoot/services/ConversationSelector';
|
||||
import axios from 'axios';
|
||||
import { ChatWootInboxNewAPI } from '@waha/apps/chatwoot/client/ChatWootInboxNewAPI';
|
||||
|
||||
export type ConversationResult = Pick<conversation, 'id' | 'account_id'>;
|
||||
@@ -25,14 +24,20 @@ export class ConversationService {
|
||||
private logger: ILogger,
|
||||
) {}
|
||||
|
||||
private async find(contact: ContactIds): Promise<ConversationResult | null> {
|
||||
public async find(contact: ContactIds): Promise<ConversationResult | null> {
|
||||
const result: { payload: contact_conversations } =
|
||||
(await this.accountAPI.contacts.listConversations({
|
||||
accountId: this.config.accountId,
|
||||
id: contact.id,
|
||||
})) as any;
|
||||
const conversations = result.payload;
|
||||
return this.selector.select(conversations);
|
||||
const conversation = this.selector.select(conversations);
|
||||
if (conversation) {
|
||||
this.logger.debug(
|
||||
`Found existing conversation.id: ${conversation.id} for contact.id: ${contact.id}, contact.sourceId: ${contact.sourceId}`,
|
||||
);
|
||||
}
|
||||
return conversation;
|
||||
}
|
||||
|
||||
private async create(contact: ContactIds): Promise<ConversationResult> {
|
||||
@@ -51,9 +56,6 @@ export class ConversationService {
|
||||
if (!conversation) {
|
||||
conversation = await this.create(contact);
|
||||
}
|
||||
this.logger.debug(
|
||||
`Using conversation.id: ${conversation.id} for contact.id: ${contact.id}, contact.sourceId: ${contact.sourceId}`,
|
||||
);
|
||||
return conversation;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import { WAHAWebhookMessageAck } from '@waha/structures/webhooks.dto';
|
||||
import { Job } from 'bullmq';
|
||||
import { PinoLogger } from 'nestjs-pino';
|
||||
import { ShouldMarkAsReadInChatWoot } from '@waha/apps/chatwoot/consumers/waha/message.ack.utils';
|
||||
import { toCusFormat } from '@waha/core/utils/jids';
|
||||
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
|
||||
import { MessageMappingService } from '@waha/apps/chatwoot/storage';
|
||||
|
||||
@@ -95,7 +94,16 @@ class MessageAckHandler {
|
||||
this.locale,
|
||||
);
|
||||
const conversation =
|
||||
await this.contactConversationService.ConversationByContact(contactInfo);
|
||||
await this.contactConversationService.FindConversationByContact(
|
||||
contactInfo,
|
||||
);
|
||||
|
||||
if (!conversation) {
|
||||
this.logger.debug(
|
||||
`No suitable conversation found to mark as read for chat.id: ${payload.from}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.info.onConversationId(conversation.conversationId);
|
||||
|
||||
const sourceId = conversation.sourceId;
|
||||
|
||||
@@ -181,7 +181,7 @@ export abstract class WhatsappSession {
|
||||
public name: string;
|
||||
protected mediaManager: IMediaManager;
|
||||
public loggerBuilder: LoggerBuilder;
|
||||
protected logger: Logger;
|
||||
public logger: Logger;
|
||||
protected sessionStore: DataStore;
|
||||
protected proxyConfig?: ProxyConfig;
|
||||
public sessionConfig?: SessionConfig;
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface SValue {
|
||||
generated: boolean;
|
||||
}
|
||||
|
||||
function rand() {
|
||||
export function rand() {
|
||||
return crypto.randomUUID().toString().replace(/-/g, '');
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { EnginePayload } from '@waha/structures/webhooks.dto';
|
||||
import { sleep } from '@waha/utils/promiseTimeout';
|
||||
import { Logger } from 'pino';
|
||||
import { Observable } from 'rxjs';
|
||||
import { rand } from '@waha/core/auth/config';
|
||||
|
||||
/**
|
||||
* Observable that listens to a gRPC stream and emits EnginePayload objects.
|
||||
@@ -14,17 +15,35 @@ export class GowsEventStreamObservable extends Observable<EnginePayload> {
|
||||
CLIENT_CLOSE_TIMEOUT = 1_000;
|
||||
|
||||
constructor(
|
||||
private logger: Logger,
|
||||
logger: Logger,
|
||||
factory: () => {
|
||||
client: grpc.Client;
|
||||
stream: grpc.ClientReadableStream<messages.EventJson>;
|
||||
},
|
||||
) {
|
||||
super((subscriber) => {
|
||||
this.logger.debug('Creating grpc client and stream...');
|
||||
logger.debug('Creating grpc stream...');
|
||||
logger.setBindings({ id: rand() });
|
||||
const { client, stream } = factory();
|
||||
this._client = client;
|
||||
|
||||
let closed = false;
|
||||
const cleanup = async (reason: string) => {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
|
||||
logger.debug({ reason }, 'Cancelling gRPC stream...');
|
||||
try {
|
||||
stream.cancel();
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'Failed to cancel gRPC stream');
|
||||
}
|
||||
|
||||
await sleep(this.CLIENT_CLOSE_TIMEOUT);
|
||||
};
|
||||
|
||||
stream.on('data', (raw) => {
|
||||
const obj = raw.toObject();
|
||||
obj.data = JSON.parse(obj.data);
|
||||
@@ -32,18 +51,21 @@ export class GowsEventStreamObservable extends Observable<EnginePayload> {
|
||||
});
|
||||
|
||||
stream.on('end', (...args) => {
|
||||
this.logger.debug('Stream ended', args);
|
||||
logger.debug('Stream ended', args);
|
||||
subscriber?.complete();
|
||||
subscriber = null;
|
||||
void cleanup('end');
|
||||
});
|
||||
|
||||
stream.on('error', async (err: any) => {
|
||||
const CLIENT_CANCELLED_CODE = grpc.status.CANCELLED;
|
||||
if (err.code === CLIENT_CANCELLED_CODE) {
|
||||
this.logger.debug('Stream cancelled by client');
|
||||
logger.debug('Stream cancelled by client');
|
||||
await cleanup('cancelled');
|
||||
return;
|
||||
}
|
||||
this.logger.error(err, 'Stream error');
|
||||
logger.error(err, 'Stream error');
|
||||
await cleanup('error');
|
||||
// Give some time to node event loop to process the error
|
||||
await sleep(100);
|
||||
subscriber?.error(err);
|
||||
@@ -51,14 +73,7 @@ export class GowsEventStreamObservable extends Observable<EnginePayload> {
|
||||
});
|
||||
|
||||
return async () => {
|
||||
this.logger.debug('Closing stream client...');
|
||||
client.close();
|
||||
await sleep(this.CLIENT_CLOSE_TIMEOUT);
|
||||
this.logger.debug('Stream client closed');
|
||||
|
||||
this.logger.debug('Cancelling stream...');
|
||||
stream.cancel();
|
||||
this.logger.debug('Stream cancelled');
|
||||
await cleanup('teardown');
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import { messages } from '@waha/core/engines/gows/grpc/gows';
|
||||
|
||||
let messageServiceClient: messages.MessageServiceClient | null = null;
|
||||
let eventStreamClient: messages.EventStreamClient | null = null;
|
||||
|
||||
export const MessageServiceClientSingleton = (
|
||||
address: string,
|
||||
credentials: grpc.ChannelCredentials,
|
||||
): messages.MessageServiceClient => {
|
||||
if (!messageServiceClient) {
|
||||
messageServiceClient = new messages.MessageServiceClient(
|
||||
address,
|
||||
credentials,
|
||||
{
|
||||
'grpc.max_send_message_length': 128 * 1024 * 1024,
|
||||
'grpc.max_receive_message_length': 128 * 1024 * 1024,
|
||||
},
|
||||
);
|
||||
}
|
||||
return messageServiceClient;
|
||||
};
|
||||
|
||||
export const EventStreamClientSingleton = (
|
||||
address: string,
|
||||
credentials: grpc.ChannelCredentials,
|
||||
): messages.EventStreamClient => {
|
||||
if (!eventStreamClient) {
|
||||
eventStreamClient = new messages.EventStreamClient(address, credentials, {
|
||||
'grpc.max_send_message_length': 128 * 1024 * 1024,
|
||||
'grpc.max_receive_message_length': 128 * 1024 * 1024,
|
||||
});
|
||||
}
|
||||
return eventStreamClient;
|
||||
};
|
||||
@@ -2687,6 +2687,7 @@ export namespace messages {
|
||||
mimetype?: string;
|
||||
audio?: AudioInfo;
|
||||
filename?: string;
|
||||
contentPath?: string;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||
@@ -2706,6 +2707,9 @@ export namespace messages {
|
||||
if ("filename" in data && data.filename != undefined) {
|
||||
this.filename = data.filename;
|
||||
}
|
||||
if ("contentPath" in data && data.contentPath != undefined) {
|
||||
this.contentPath = data.contentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
get content() {
|
||||
@@ -2741,12 +2745,19 @@ export namespace messages {
|
||||
set filename(value: string) {
|
||||
pb_1.Message.setField(this, 5, value);
|
||||
}
|
||||
get contentPath() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 6, "") as string;
|
||||
}
|
||||
set contentPath(value: string) {
|
||||
pb_1.Message.setField(this, 6, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
content?: Uint8Array;
|
||||
type?: MediaType;
|
||||
mimetype?: string;
|
||||
audio?: ReturnType<typeof AudioInfo.prototype.toObject>;
|
||||
filename?: string;
|
||||
contentPath?: string;
|
||||
}): Media {
|
||||
const message = new Media({});
|
||||
if (data.content != null) {
|
||||
@@ -2764,6 +2775,9 @@ export namespace messages {
|
||||
if (data.filename != null) {
|
||||
message.filename = data.filename;
|
||||
}
|
||||
if (data.contentPath != null) {
|
||||
message.contentPath = data.contentPath;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
@@ -2773,6 +2787,7 @@ export namespace messages {
|
||||
mimetype?: string;
|
||||
audio?: ReturnType<typeof AudioInfo.prototype.toObject>;
|
||||
filename?: string;
|
||||
contentPath?: string;
|
||||
} = {};
|
||||
if (this.content != null) {
|
||||
data.content = this.content;
|
||||
@@ -2789,6 +2804,9 @@ export namespace messages {
|
||||
if (this.filename != null) {
|
||||
data.filename = this.filename;
|
||||
}
|
||||
if (this.contentPath != null) {
|
||||
data.contentPath = this.contentPath;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -2805,6 +2823,8 @@ export namespace messages {
|
||||
writer.writeMessage(4, this.audio, () => this.audio.serialize(writer));
|
||||
if (this.filename.length)
|
||||
writer.writeString(5, this.filename);
|
||||
if (this.contentPath.length)
|
||||
writer.writeString(6, this.contentPath);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -2829,6 +2849,9 @@ export namespace messages {
|
||||
case 5:
|
||||
message.filename = reader.readString();
|
||||
break;
|
||||
case 6:
|
||||
message.contentPath = reader.readString();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
@@ -7949,6 +7972,7 @@ export namespace messages {
|
||||
message?: string;
|
||||
jid?: string;
|
||||
messageId?: string;
|
||||
contentPath?: string;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||
@@ -7965,6 +7989,9 @@ export namespace messages {
|
||||
if ("messageId" in data && data.messageId != undefined) {
|
||||
this.messageId = data.messageId;
|
||||
}
|
||||
if ("contentPath" in data && data.contentPath != undefined) {
|
||||
this.contentPath = data.contentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
get session() {
|
||||
@@ -7994,11 +8021,18 @@ export namespace messages {
|
||||
set messageId(value: string) {
|
||||
pb_1.Message.setField(this, 4, value);
|
||||
}
|
||||
get contentPath() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 5, "") as string;
|
||||
}
|
||||
set contentPath(value: string) {
|
||||
pb_1.Message.setField(this, 5, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
message?: string;
|
||||
jid?: string;
|
||||
messageId?: string;
|
||||
contentPath?: string;
|
||||
}): DownloadMediaRequest {
|
||||
const message = new DownloadMediaRequest({});
|
||||
if (data.session != null) {
|
||||
@@ -8013,6 +8047,9 @@ export namespace messages {
|
||||
if (data.messageId != null) {
|
||||
message.messageId = data.messageId;
|
||||
}
|
||||
if (data.contentPath != null) {
|
||||
message.contentPath = data.contentPath;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
@@ -8021,6 +8058,7 @@ export namespace messages {
|
||||
message?: string;
|
||||
jid?: string;
|
||||
messageId?: string;
|
||||
contentPath?: string;
|
||||
} = {};
|
||||
if (this.session != null) {
|
||||
data.session = this.session.toObject();
|
||||
@@ -8034,6 +8072,9 @@ export namespace messages {
|
||||
if (this.messageId != null) {
|
||||
data.messageId = this.messageId;
|
||||
}
|
||||
if (this.contentPath != null) {
|
||||
data.contentPath = this.contentPath;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -8048,6 +8089,8 @@ export namespace messages {
|
||||
writer.writeString(3, this.jid);
|
||||
if (this.messageId.length)
|
||||
writer.writeString(4, this.messageId);
|
||||
if (this.contentPath.length)
|
||||
writer.writeString(5, this.contentPath);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -8069,6 +8112,9 @@ export namespace messages {
|
||||
case 4:
|
||||
message.messageId = reader.readString();
|
||||
break;
|
||||
case 5:
|
||||
message.contentPath = reader.readString();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
@@ -8085,6 +8131,7 @@ export namespace messages {
|
||||
#one_of_decls: number[][] = [];
|
||||
constructor(data?: any[] | {
|
||||
content?: Uint8Array;
|
||||
contentPath?: string;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||
@@ -8092,6 +8139,9 @@ export namespace messages {
|
||||
if ("content" in data && data.content != undefined) {
|
||||
this.content = data.content;
|
||||
}
|
||||
if ("contentPath" in data && data.contentPath != undefined) {
|
||||
this.contentPath = data.contentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
get content() {
|
||||
@@ -8100,22 +8150,36 @@ export namespace messages {
|
||||
set content(value: Uint8Array) {
|
||||
pb_1.Message.setField(this, 1, value);
|
||||
}
|
||||
get contentPath() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 5, "") as string;
|
||||
}
|
||||
set contentPath(value: string) {
|
||||
pb_1.Message.setField(this, 5, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
content?: Uint8Array;
|
||||
contentPath?: string;
|
||||
}): DownloadMediaResponse {
|
||||
const message = new DownloadMediaResponse({});
|
||||
if (data.content != null) {
|
||||
message.content = data.content;
|
||||
}
|
||||
if (data.contentPath != null) {
|
||||
message.contentPath = data.contentPath;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
const data: {
|
||||
content?: Uint8Array;
|
||||
contentPath?: string;
|
||||
} = {};
|
||||
if (this.content != null) {
|
||||
data.content = this.content;
|
||||
}
|
||||
if (this.contentPath != null) {
|
||||
data.contentPath = this.contentPath;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -8124,6 +8188,8 @@ export namespace messages {
|
||||
const writer = w || new pb_1.BinaryWriter();
|
||||
if (this.content.length)
|
||||
writer.writeBytes(1, this.content);
|
||||
if (this.contentPath.length)
|
||||
writer.writeString(5, this.contentPath);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -8136,6 +8202,9 @@ export namespace messages {
|
||||
case 1:
|
||||
message.content = reader.readBytes();
|
||||
break;
|
||||
case 5:
|
||||
message.contentPath = reader.readString();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7115,7 +7115,8 @@ proto.messages.Media.toObject = function(includeInstance, msg) {
|
||||
type: jspb.Message.getFieldWithDefault(msg, 2, 0),
|
||||
mimetype: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
audio: (f = msg.getAudio()) && proto.messages.AudioInfo.toObject(includeInstance, f),
|
||||
filename: jspb.Message.getFieldWithDefault(msg, 5, "")
|
||||
filename: jspb.Message.getFieldWithDefault(msg, 5, ""),
|
||||
contentpath: jspb.Message.getFieldWithDefault(msg, 6, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -7173,6 +7174,10 @@ proto.messages.Media.deserializeBinaryFromReader = function(msg, reader) {
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setFilename(value);
|
||||
break;
|
||||
case 6:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setContentpath(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -7238,6 +7243,13 @@ proto.messages.Media.serializeBinaryToWriter = function(message, writer) {
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getContentpath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
6,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7374,6 +7386,24 @@ proto.messages.Media.prototype.setFilename = function(value) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string contentPath = 6;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.messages.Media.prototype.getContentpath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.messages.Media} returns this
|
||||
*/
|
||||
proto.messages.Media.prototype.setContentpath = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 6, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16673,7 +16703,8 @@ proto.messages.DownloadMediaRequest.toObject = function(includeInstance, msg) {
|
||||
session: (f = msg.getSession()) && proto.messages.Session.toObject(includeInstance, f),
|
||||
message: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
jid: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
messageid: jspb.Message.getFieldWithDefault(msg, 4, "")
|
||||
messageid: jspb.Message.getFieldWithDefault(msg, 4, ""),
|
||||
contentpath: jspb.Message.getFieldWithDefault(msg, 5, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -16727,6 +16758,10 @@ proto.messages.DownloadMediaRequest.deserializeBinaryFromReader = function(msg,
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setMessageid(value);
|
||||
break;
|
||||
case 5:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setContentpath(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -16785,6 +16820,13 @@ proto.messages.DownloadMediaRequest.serializeBinaryToWriter = function(message,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getContentpath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
5,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -16879,6 +16921,24 @@ proto.messages.DownloadMediaRequest.prototype.setMessageid = function(value) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string contentPath = 5;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.messages.DownloadMediaRequest.prototype.getContentpath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.messages.DownloadMediaRequest} returns this
|
||||
*/
|
||||
proto.messages.DownloadMediaRequest.prototype.setContentpath = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 5, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16911,7 +16971,8 @@ proto.messages.DownloadMediaResponse.prototype.toObject = function(opt_includeIn
|
||||
*/
|
||||
proto.messages.DownloadMediaResponse.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
content: msg.getContent_asB64()
|
||||
content: msg.getContent_asB64(),
|
||||
contentpath: jspb.Message.getFieldWithDefault(msg, 5, "")
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -16952,6 +17013,10 @@ proto.messages.DownloadMediaResponse.deserializeBinaryFromReader = function(msg,
|
||||
var value = /** @type {!Uint8Array} */ (reader.readBytes());
|
||||
msg.setContent(value);
|
||||
break;
|
||||
case 5:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setContentpath(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -16988,6 +17053,13 @@ proto.messages.DownloadMediaResponse.serializeBinaryToWriter = function(message,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getContentpath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
5,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17033,6 +17105,24 @@ proto.messages.DownloadMediaResponse.prototype.setContent = function(value) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string contentPath = 5;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.messages.DownloadMediaResponse.prototype.getContentpath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {!proto.messages.DownloadMediaResponse} returns this
|
||||
*/
|
||||
proto.messages.DownloadMediaResponse.prototype.setContentpath = function(value) {
|
||||
return jspb.Message.setProto3StringField(this, 5, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -171,13 +171,20 @@ import {
|
||||
isLabelChatAddedEvent,
|
||||
isLabelUpsertEvent,
|
||||
} from './labels.gows';
|
||||
import {
|
||||
EventStreamClientSingleton,
|
||||
MessageServiceClientSingleton,
|
||||
} from '@waha/core/engines/gows/clients';
|
||||
import esm from '@waha/vendor/esm';
|
||||
import { IsEditedMessage } from '@waha/core/utils/pwa';
|
||||
import MessageServiceClient = messages.MessageServiceClient;
|
||||
import { GoToJSWAProto } from '@waha/core/engines/gows/waproto';
|
||||
import { extractWALocation } from '@waha/core/engines/waproto/locaiton';
|
||||
import { extractVCards } from '@waha/core/engines/waproto/vcards';
|
||||
import { Activity } from '@waha/core/abc/activity';
|
||||
import { TmpDir } from '@waha/utils/tmpdir';
|
||||
import * as path from 'path';
|
||||
import MessageServiceClient = messages.MessageServiceClient;
|
||||
import * as fsp from 'fs/promises';
|
||||
|
||||
enum WhatsMeowEvent {
|
||||
CONNECTED = 'gows.ConnectedEventData',
|
||||
@@ -203,11 +210,6 @@ enum WhatsMeowEvent {
|
||||
POLL_VOTE_EVENT = 'gows.PollVoteEvent',
|
||||
}
|
||||
|
||||
const gRPCClientConfig = {
|
||||
'grpc.max_send_message_length': 512 * 1024 * 1024,
|
||||
'grpc.max_receive_message_length': 512 * 1024 * 1024,
|
||||
};
|
||||
|
||||
export interface GowsConfig {
|
||||
connection: string;
|
||||
}
|
||||
@@ -272,10 +274,9 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
}),
|
||||
});
|
||||
|
||||
this.client = new MessageServiceClient(
|
||||
this.client = MessageServiceClientSingleton(
|
||||
this.engineConfig.connection,
|
||||
grpc.credentials.createInsecure(),
|
||||
gRPCClientConfig,
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -302,10 +303,9 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
this.stream$ = new GowsEventStreamObservable(
|
||||
this.loggerBuilder.child({ grpc: 'stream' }),
|
||||
() => {
|
||||
const client = new messages.EventStreamClient(
|
||||
const client = EventStreamClientSingleton(
|
||||
this.engineConfig.connection,
|
||||
grpc.credentials.createInsecure(),
|
||||
gRPCClientConfig,
|
||||
);
|
||||
const stream = client.StreamEvents(this.session);
|
||||
return { client, stream };
|
||||
@@ -701,7 +701,6 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
this.status = WAHASessionStatus.STOPPED;
|
||||
this.events?.stop();
|
||||
this.stopEvents();
|
||||
this.client?.close();
|
||||
this.mediaManager.close();
|
||||
}
|
||||
|
||||
@@ -2369,19 +2368,47 @@ export class GOWSEngineMediaProcessor implements IMediaEngineProcessor<any> {
|
||||
}
|
||||
|
||||
async getMediaBuffer(message: any): Promise<Buffer | null> {
|
||||
const mediaDownloadTimeoutMs = 600_000; // 10 minutes
|
||||
|
||||
const data = JSON.stringify(message.Message);
|
||||
const request = new messages.DownloadMediaRequest({
|
||||
// double "session" it's not a mistake here
|
||||
session: this.session.session,
|
||||
message: data,
|
||||
jid: message.Info.Chat,
|
||||
messageId: message.Info.ID,
|
||||
});
|
||||
const response = await promisify(this.session.client.DownloadMedia)(
|
||||
request,
|
||||
const tmpdir = new TmpDir(
|
||||
this.session.logger,
|
||||
`waha-media-${this.session.name}-`,
|
||||
);
|
||||
const obj = response.toObject();
|
||||
return Buffer.from(obj.content);
|
||||
return await tmpdir.use(async (dir) => {
|
||||
const file = path.join(dir, 'content.tmp');
|
||||
const request = new messages.DownloadMediaRequest({
|
||||
// double "session" it's not a mistake here
|
||||
session: this.session.session,
|
||||
message: data,
|
||||
jid: message.Info.Chat,
|
||||
messageId: message.Info.ID,
|
||||
contentPath: file,
|
||||
});
|
||||
|
||||
const opts = {
|
||||
deadline: new Date(Date.now() + mediaDownloadTimeoutMs),
|
||||
};
|
||||
const call = promisify(
|
||||
this.session.client.DownloadMedia.bind(this.session.client),
|
||||
);
|
||||
try {
|
||||
const response = await call(request, opts);
|
||||
const obj = response.toObject();
|
||||
if (!obj.contentPath) {
|
||||
// Read directly from grpc response
|
||||
return Buffer.from(obj.content);
|
||||
} else {
|
||||
// Read from the file
|
||||
return await fsp.readFile(obj.contentPath);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err?.code === grpc.status.DEADLINE_EXCEEDED) {
|
||||
err.message = `DownloadMedia timed out after ${mediaDownloadTimeoutMs}ms for message '${message?.Info?.ID}'`;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getFilename(message: any): string | null {
|
||||
|
||||
@@ -9,8 +9,9 @@ import axios, { AxiosInstance } from 'axios';
|
||||
import axiosRetry, { retryAfter } from 'axios-retry';
|
||||
import * as crypto from 'crypto';
|
||||
import { Logger } from 'pino';
|
||||
import { ulid } from 'ulid';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const uniqid = require('uniqid');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const HttpAgent = require('agentkeepalive');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
@@ -71,6 +72,7 @@ export class WebhookSender {
|
||||
const ctx = {
|
||||
id: headers['X-Webhook-Request-Id'],
|
||||
['event.id']: json.id,
|
||||
event: json.event,
|
||||
url: this.url,
|
||||
};
|
||||
this.logger.info(ctx, `Sending POST...`);
|
||||
@@ -133,7 +135,9 @@ export class WebhookSender {
|
||||
retryCondition: (error) => true,
|
||||
onRetry: (retryCount, error, requestConfig) => {
|
||||
this.logger.warn(
|
||||
{ id: requestConfig.headers['X-Webhook-Request-Id'] },
|
||||
{
|
||||
id: requestConfig.headers['X-Webhook-Request-Id'],
|
||||
},
|
||||
`Error sending POST request: '${error.message}'. Retrying ${retryCount}/${attempts}...`,
|
||||
);
|
||||
},
|
||||
@@ -157,7 +161,7 @@ export class WebhookSender {
|
||||
const timestamp = json.timestamp?.toString() || Date.now().toString();
|
||||
return {
|
||||
// UUID, no '-' in it
|
||||
'X-Webhook-Request-Id': ulid(),
|
||||
'X-Webhook-Request-Id': uniqid(),
|
||||
// unix timestamp with ms
|
||||
'X-Webhook-Timestamp': timestamp,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
import { IsArray, IsNumber, IsOptional, IsString, Min } from 'class-validator';
|
||||
|
||||
export class CpuProfileQuery {
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@ApiProperty({
|
||||
description: 'How many seconds to sample CPU',
|
||||
example: 30,
|
||||
required: false,
|
||||
default: 30,
|
||||
})
|
||||
seconds: number = 30;
|
||||
}
|
||||
|
||||
export class BrowserTraceQuery {
|
||||
@Type(() => Number)
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ export function getWAHAVersion(): WAHAVersion {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2025.11.2',
|
||||
version: '2025.11.4',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser:
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.21"
|
||||
"ref": "v1.0.24"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
"ref": "3861e0854f4dba106e3e223e6f5df15d58b61949"
|
||||
"ref": "d982212d20191adab7065216cad45ff064b483d8"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ __metadata:
|
||||
version: 6
|
||||
cacheKey: 8
|
||||
|
||||
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-07":
|
||||
version: 7.0.0-rc.6
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=e828866d7936785b054e16f360c32115f118f291"
|
||||
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-21":
|
||||
version: 7.0.0-rc.9
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=4fb1d817fbc9e25c8011dcd0bd0836b11a340c5a"
|
||||
dependencies:
|
||||
"@cacheable/node-cache": ^1.4.0
|
||||
"@hapi/boom": ^9.1.3
|
||||
@@ -31,7 +31,7 @@ __metadata:
|
||||
optional: true
|
||||
link-preview-js:
|
||||
optional: true
|
||||
checksum: 0a02bc71fe13e998047f10e205126cca978ffde85a50fabc8ccf25c79a3ff6baecc857a395956e6c37b10abbeb9e390e4fcbc870ea11e3a22224cbc8c562c41e
|
||||
checksum: b355cd7012afb96ae79d4d7debd3a888ed844d3ddef0ba83b9cd965f3d901bf2d42630be93064e0abcb1abd842f62d89c09cc6f7545ed3086d14422f789a80ec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1290,27 +1290,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grpc/grpc-js@npm:^1.13.4":
|
||||
version: 1.13.4
|
||||
resolution: "@grpc/grpc-js@npm:1.13.4"
|
||||
"@grpc/grpc-js@npm:^1.14.1":
|
||||
version: 1.14.1
|
||||
resolution: "@grpc/grpc-js@npm:1.14.1"
|
||||
dependencies:
|
||||
"@grpc/proto-loader": ^0.7.13
|
||||
"@grpc/proto-loader": ^0.8.0
|
||||
"@js-sdsl/ordered-map": ^4.4.2
|
||||
checksum: fe5db84bbbcd07cc1b68d1683b7fbe9cfcc5c3a60655ecc17fb3e1cd2adc4c1ce891b15e6e9a9c2140f6891def6f93b509a60d2bce253d13b317f9136e968451
|
||||
checksum: 3f208c23cc985789934d806045599ad3bfa1b6fd9877151cf9af423b4d708cbb3b0cb1347a147061c6ed36470958fd522fbefbf6ae4ca8246f0860bd647f0e5e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grpc/proto-loader@npm:^0.7.13":
|
||||
version: 0.7.15
|
||||
resolution: "@grpc/proto-loader@npm:0.7.15"
|
||||
"@grpc/proto-loader@npm:^0.8.0":
|
||||
version: 0.8.0
|
||||
resolution: "@grpc/proto-loader@npm:0.8.0"
|
||||
dependencies:
|
||||
lodash.camelcase: ^4.3.0
|
||||
long: ^5.0.0
|
||||
protobufjs: ^7.2.5
|
||||
protobufjs: ^7.5.3
|
||||
yargs: ^17.7.2
|
||||
bin:
|
||||
proto-loader-gen-types: build/bin/proto-loader-gen-types.js
|
||||
checksum: 9f19f4c611a17cd33aec0d6e3686a76696495f40593f7c284933c4b7877f58dfa5a225ddc20705860a632311f4dc0d143cb6a0da7b51b6f5ffd7de26938df308
|
||||
checksum: 97570e2e8c29a35b999fa929acad506765e6752d3219764825fecc0ab632492b1ac2e4dd4c0a96cc2e01754d99bfa838dff0729c6608a02f7b027fd19bc1d1e4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10456,7 +10456,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5":
|
||||
"protobufjs@npm:^7.2.4":
|
||||
version: 7.5.3
|
||||
resolution: "protobufjs@npm:7.5.3"
|
||||
dependencies:
|
||||
@@ -10476,6 +10476,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"protobufjs@npm:^7.5.3":
|
||||
version: 7.5.4
|
||||
resolution: "protobufjs@npm:7.5.4"
|
||||
dependencies:
|
||||
"@protobufjs/aspromise": ^1.1.2
|
||||
"@protobufjs/base64": ^1.1.2
|
||||
"@protobufjs/codegen": ^2.0.4
|
||||
"@protobufjs/eventemitter": ^1.1.0
|
||||
"@protobufjs/fetch": ^1.1.0
|
||||
"@protobufjs/float": ^1.0.2
|
||||
"@protobufjs/inquire": ^1.1.0
|
||||
"@protobufjs/path": ^1.1.2
|
||||
"@protobufjs/pool": ^1.1.0
|
||||
"@protobufjs/utf8": ^1.1.0
|
||||
"@types/node": ">=13.7.0"
|
||||
long: ^5.0.0
|
||||
checksum: 53bf83b9a726b05d43da35bb990dba7536759787dccea9a67b8f31be9df470ba17f1f1b982ca19956cfc7726f3ec7e0e883ca4ad93b5ec753cc025a637fc704f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"protoc-gen-ts@npm:^0.8.7":
|
||||
version: 0.8.7
|
||||
resolution: "protoc-gen-ts@npm:0.8.7"
|
||||
@@ -12372,6 +12392,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"uniqid@npm:^5.4.0":
|
||||
version: 5.4.0
|
||||
resolution: "uniqid@npm:5.4.0"
|
||||
checksum: 69fc28e7b2b5b24227b4295e51aa7c1d4085a60655ad071db8fc350876cc9044d68ee0781c0d27c2c98691380aa418970e0d1b02a1e4564480f9d19b0dc1707b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-filename@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "unique-filename@npm:1.1.1"
|
||||
@@ -12532,7 +12559,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "waha@workspace:."
|
||||
dependencies:
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-07"
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-21"
|
||||
"@adiwajshing/keyed-db": ^0.2.4
|
||||
"@aws-sdk/client-s3": ^3.633.0
|
||||
"@aws-sdk/s3-request-presigner": ^3.633.0
|
||||
@@ -12540,8 +12567,8 @@ __metadata:
|
||||
"@bull-board/express": ^6.9.1
|
||||
"@bull-board/nestjs": ^6.9.1
|
||||
"@figuro/chatwoot-sdk": ^1.1.17
|
||||
"@grpc/grpc-js": ^1.13.4
|
||||
"@grpc/proto-loader": ^0.7.13
|
||||
"@grpc/grpc-js": ^1.14.1
|
||||
"@grpc/proto-loader": ^0.8.0
|
||||
"@liaoliaots/nestjs-redis": ^9
|
||||
"@nestjs/axios": ^3.0.2
|
||||
"@nestjs/bullmq": ^11.0.2
|
||||
@@ -12636,6 +12663,7 @@ __metadata:
|
||||
typescript: 5.9.2
|
||||
ulid: ^2.3.0
|
||||
undici: ^7.16.0
|
||||
uniqid: ^5.4.0
|
||||
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05"
|
||||
write-file-atomic: ^6.0.0
|
||||
yaml: ^2.7.1
|
||||
@@ -12739,7 +12767,7 @@ __metadata:
|
||||
|
||||
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05":
|
||||
version: 1.34.2
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=c84b4af4dfd01f326d37f4de6acd7bebc06496d0"
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=59a904a9a1f2869d39754117289267e0a925a7af"
|
||||
dependencies:
|
||||
"@pedroslopez/moduleraid": ^5.0.2
|
||||
archiver: ^5.3.1
|
||||
@@ -12757,7 +12785,7 @@ __metadata:
|
||||
optional: true
|
||||
unzipper:
|
||||
optional: true
|
||||
checksum: d9d615d29ba14f6113080a19d8f7e1396ea7dde3cbe4983db04e8df512483198c3f73236e9e8c366afb035ee700bd5718d72bc18ee01caeb7cd86bcadfdbb056
|
||||
checksum: a59e4cee66a41f2a59d0340c4a765848f9fe76bdf4cfdd749c50deb5246f61bb8f9974fe4b35a7cc13d3adde6244975cea02caf72ca6e746b18b5d9c5c425b69
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user