Compare commits

...
11 Commits
Author SHA1 Message Date
devlikepro fb3aa59683 [core] 2024.7.5
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-07-15 16:03:08 +03:00
devlikepro b938792ca8 [core] channels 2024-07-15 16:02:51 +03:00
devlikepro bb74946d54 [core] bump 2024.7.4
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-07-08 15:02:36 +03:00
devlikepro 3e78105509 [core] fix profile picture
closes #422
2024-07-08 15:02:36 +03:00
devlikepro c6adf0ac89 [core] console.log for webjs 2024-07-05 14:04:03 +03:00
devlikepro 219b4dca94 [core] pretty logs by default 2024-07-05 13:40:13 +03:00
devlikepro acec292b0f [core] 2024.7.3 2024-07-05 13:30:47 +03:00
devlikepro 7ba86cbd23 [core] logging 2024-07-05 13:30:08 +03:00
devlikepro 3002eaf479 [core] Init Logger in manager explicitly 2024-07-05 13:30:05 +03:00
devlikepro 15422ca504 [core] 2024.7.2
Release / amd64 - chrome - chrome (push) Waiting to run
Release / amd64 - chromium - latest (push) Waiting to run
Release / linux/arm64 - chromium - arm (push) Waiting to run
Release / amd64 - none - noweb (push) Waiting to run
Release / linux/arm64 - none - noweb-arm (push) Waiting to run
2024-07-04 12:11:43 +03:00
devlikepro 7067f00bee [core] Up WEBJS engine 2024-07-04 12:11:43 +03:00
25 changed files with 972 additions and 184 deletions

No files matched your search

+12
View File
@@ -46,3 +46,15 @@ repos:
src/structures/.*|
src/config.service.ts|
)$
- repo: local
hooks:
- id: no-console-log
name: No console.log() calls
language: pygrep
entry: 'console\.log'
exclude: |
(?x)^(
.pre-commit-config.yaml|
^src/core/engines/webjs/session.webjs.core.ts|
)$
+2 -2
View File
@@ -32,8 +32,8 @@ for-swagger:
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
update-dependencies:
. ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master
. ${NVM_DIR}/nvm.sh && nvm exec yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels
. ${NVM_DIR}/nvm.sh && nvm exec yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels
. ${NVM_DIR}/nvm.sh && nvm exec yarn up venom-bot
start-proxy:
+5 -2
View File
@@ -22,7 +22,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master",
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-channels",
"@adiwajshing/keyed-db": "^0.2.4",
"@nestjs/axios": "^3.0.2",
"@nestjs/common": "^9.0.9",
@@ -54,8 +54,11 @@
"lodash": "^4.17.21",
"mime-types": "^2.1.27",
"mongodb": "^6.3.0",
"nestjs-pino": "^4.1.0",
"passport": "^0.6.0",
"passport-headerapikey": "^1.2.2",
"pino-http": "^10.2.0",
"pino-pretty": "^11.2.1",
"puppeteer": "^21.0.0",
"qrcode": "^1.5.1",
"qrcode-terminal": "^0.12.0",
@@ -67,7 +70,7 @@
"sharp": "^0.33.4",
"swagger-ui-express": "^4.1.4",
"venom-bot": "5.0.1",
"whatsapp-web.js": "https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus"
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
+119
View File
@@ -0,0 +1,119 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Query,
} from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import {
Channel,
CreateChannelRequest,
ListChannelsQuery,
NewsletterIdApiParam,
NewsletterIdOrInviteCodeApiParam,
} from '@waha/structures/channels.dto';
import { SessionManager } from '../core/abc/manager.abc';
import { isNewsletter, WhatsappSession } from '../core/abc/session.abc';
import { SessionApiParam, SessionParam } from './helpers';
@ApiSecurity('api_key')
@Controller('api/:session/channels')
@ApiTags('channels')
export class ChannelsController {
constructor(private manager: SessionManager) {}
@Get('')
@SessionApiParam
@ApiOperation({ summary: 'Get list of know channels' })
async list(
@SessionParam session: WhatsappSession,
@Query() query: ListChannelsQuery,
): Promise<Channel[]> {
return session.channelsList(query);
}
@Post('')
@SessionApiParam
@ApiOperation({ summary: 'Create a new channel.' })
create(
@SessionParam session: WhatsappSession,
@Body() request: CreateChannelRequest,
): Promise<Channel> {
return session.channelsCreateChannel(request);
}
@Delete(':id')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Delete the channel.' })
delete(@SessionParam session: WhatsappSession, @Param('id') id: string) {
return session.channelsDeleteChannel(id);
}
@Get(':id')
@SessionApiParam
@NewsletterIdOrInviteCodeApiParam
@ApiOperation({
summary: 'Get the channel info - either by id @newsletter OR invite code.',
})
get(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<Channel> {
if (isNewsletter(id)) {
return session.channelsGetChannel(id);
} else {
// https://www.whatsapp.com/channel/123 => 123
const inviteCode = id.split('/').pop();
return session.channelsGetChannelByInviteCode(inviteCode);
}
}
@Post('follow')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Follow the channel.' })
follow(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<void> {
return session.channelsFollowChannel(id);
}
@Post('unfollow')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Unfollow the channel.' })
unfollow(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<void> {
return session.channelsUnfollowChannel(id);
}
@Post('mute')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Mute the channel.' })
mute(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<void> {
return session.channelsMuteChannel(id);
}
@Post('unmute')
@SessionApiParam
@NewsletterIdApiParam
@ApiOperation({ summary: 'Unmute the channel.' })
unmute(
@SessionParam session: WhatsappSession,
@Param('id') id: string,
): Promise<void> {
return session.channelsUnmuteChannel(id);
}
}
+7 -1
View File
@@ -1,13 +1,18 @@
import { INestApplication } from '@nestjs/common';
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';
import { DECORATORS } from '@nestjs/swagger/dist/constants';
import { Logger } from 'nestjs-pino';
import { WhatsappConfigService } from '../config.service';
import { VERSION } from '../version';
import { SwaggerConfigServiceCore } from './config/SwaggerConfigServiceCore';
export class SwaggerConfiguratorCore {
constructor(protected app: INestApplication) {}
protected logger: any;
constructor(protected app: INestApplication) {
this.logger = app.get(Logger);
}
get title() {
return 'WAHA - WhatsApp HTTP API';
@@ -55,6 +60,7 @@ export class SwaggerConfiguratorCore {
.addTag('auth', 'Authentication')
.addTag('screenshot', 'Get screenshot of WhatsApp and show QR code')
.addTag('chatting', 'Chatting methods')
.addTag('channels', 'Channels (newsletters) methods')
.addTag(
'status',
'Status (aka stories) methods. <b>NOWEB</b> engine only!',
+65 -8
View File
@@ -1,7 +1,13 @@
import { ConsoleLogger } from '@nestjs/common';
import {
Channel,
CreateChannelRequest,
ListChannelsQuery,
} from '@waha/structures/channels.dto';
import { GetChatsQuery } from '@waha/structures/chats.dto';
import { LoggerBuilder } from '@waha/utils/logging';
import { EventEmitter } from 'events';
import * as fs from 'fs';
import { Logger } from 'pino';
import { MessageId } from 'whatsapp-web.js';
import {
@@ -82,7 +88,7 @@ export interface SessionParams {
name: string;
printQR: boolean;
mediaManager: MediaManager;
log: ConsoleLogger;
loggerBuilder: LoggerBuilder;
sessionStore: DataStore;
proxyConfig?: ProxyConfig;
sessionConfig?: SessionConfig;
@@ -95,7 +101,8 @@ export abstract class WhatsappSession {
public name: string;
protected mediaManager: MediaManager;
protected log: ConsoleLogger;
public loggerBuilder: LoggerBuilder;
protected logger: Logger;
protected sessionStore: DataStore;
protected proxyConfig?: ProxyConfig;
public sessionConfig?: SessionConfig;
@@ -107,7 +114,7 @@ export abstract class WhatsappSession {
public constructor({
name,
printQR,
log,
loggerBuilder,
sessionStore,
proxyConfig,
mediaManager,
@@ -117,7 +124,8 @@ export abstract class WhatsappSession {
this.events = new EventEmitter();
this.name = name;
this.proxyConfig = proxyConfig;
this.log = log;
this.loggerBuilder = loggerBuilder;
this.logger = loggerBuilder.child({ name: 'WhatsappSession' });
this.sessionStore = sessionStore;
this.mediaManager = mediaManager;
this.sessionConfig = sessionConfig;
@@ -170,7 +178,7 @@ export abstract class WhatsappSession {
}
protected isDebugEnabled() {
return this.log.isLevelEnabled('debug');
return this.logger.isLevelEnabled('debug');
}
/** Start the session */
@@ -434,6 +442,47 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
/**
* Channels methods
*/
public channelsList(query: ListChannelsQuery): Promise<Channel[]> {
throw new NotImplementedByEngineError();
}
public channelsCreateChannel(
request: CreateChannelRequest,
): Promise<Channel> {
throw new NotImplementedByEngineError();
}
public channelsGetChannel(id: string): Promise<Channel> {
throw new NotImplementedByEngineError();
}
public channelsGetChannelByInviteCode(inviteCode: string): Promise<Channel> {
throw new NotImplementedByEngineError();
}
public channelsDeleteChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsFollowChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsUnfollowChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsMuteChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsUnmuteChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
/**
* Status methods
*/
@@ -488,14 +537,22 @@ export abstract class WhatsappSession {
return;
}
if (!qr.raw) {
this.log.error(
this.logger.error(
'QR.raw is not available, can not print it in the console',
);
return;
}
this.log.log(
this.logger.info(
"You can disable QR in console by setting 'WAHA_PRINT_QR=false' in your environment variables.",
);
qrcode.generate(qr.raw, { small: true });
}
}
export function isNewsletter(jid: string) {
return jid.endsWith('@newsletter');
}
export function getChannelInviteLink(code: string) {
return `https://whatsapp.com/channel/${code}`;
}
+6 -2
View File
@@ -1,15 +1,19 @@
import { ConsoleLogger } from '@nestjs/common';
import { LoggerBuilder } from '@waha/utils/logging';
import { Logger } from 'pino';
import { WebhookConfig } from '../../structures/webhooks.config.dto';
import { WhatsappSession } from './session.abc';
export abstract class WebhookSender {
protected url: string;
protected logger: Logger;
constructor(
protected log: ConsoleLogger,
loggerBuilder: LoggerBuilder,
protected webhookConfig: WebhookConfig,
) {
this.url = webhookConfig.url;
this.logger = loggerBuilder.child({ name: WebhookSender.name });
}
abstract send(json);
+21 -18
View File
@@ -1,4 +1,9 @@
import { BeforeApplicationShutdown, ConsoleLogger } from '@nestjs/common';
import {
BeforeApplicationShutdown,
ConsoleLogger,
Logger,
LoggerService,
} from '@nestjs/common';
import { sleep } from '@nestjs/terminus/dist/utils';
import {
OnGatewayConnection,
@@ -8,7 +13,6 @@ import {
WebSocketServer,
} from '@nestjs/websockets';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { getLogLevels } from '@waha/helpers';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { HeartbeatJob } from '@waha/utils/HeartbeatJob';
import { WebSocket } from '@waha/utils/ws';
@@ -36,26 +40,25 @@ export class WebsocketGatewayCore
private listeners: Map<WebSocket, { session: string; events: string[] }> =
new Map();
private log: ConsoleLogger;
private readonly logger: LoggerService;
private heartbeat: HeartbeatJob;
constructor(private manager: SessionManager) {
const levels = getLogLevels(false);
this.log = new ConsoleLogger('WebsocketGateway', { logLevels: levels });
this.heartbeat = new HeartbeatJob(this.log, this.HEARTBEAT_INTERVAL);
this.logger = new Logger('WebsocketGateway');
this.heartbeat = new HeartbeatJob(this.logger, this.HEARTBEAT_INTERVAL);
}
handleConnection(socket: WebSocket, request: IncomingMessage, ...args): any {
const random = Math.random().toString(36).substring(7);
const id = `wsc_${random}`;
socket.id = id;
this.log.debug(`New client connected: ${request.url}`);
this.logger.debug(`New client connected: ${request.url}`);
const params = this.getParams(id, request, socket);
if (!params) {
return;
}
const { session, events } = params;
this.log.debug(
this.logger.debug(
`Client connected to session: '${session}', events: ${events}, ${id}`,
);
this.listeners.set(socket, { session, events });
@@ -65,7 +68,7 @@ export class WebsocketGatewayCore
const query = url.parse(request.url, true).query;
const session = (query.session as string) || '*';
if (session !== '*') {
this.log.warn(
this.logger.warn(
`Only connecting to all sessions is allowed for now, use session=*, ${id}`,
);
const error =
@@ -79,7 +82,7 @@ export class WebsocketGatewayCore
!lodash.isEqual(events, ['*']) &&
!lodash.isEqual(events, [WAHAEvents.SESSION_STATUS])
) {
this.log.warn(
this.logger.warn(
`Only \'session.status\' event is allowed for now, use events=session.status or events=*, ${id}`,
);
const error =
@@ -91,12 +94,12 @@ export class WebsocketGatewayCore
}
handleDisconnect(socket: WebSocket): any {
this.log.debug(`Client disconnected - ${socket.id}`);
this.logger.debug(`Client disconnected - ${socket.id}`);
this.listeners.delete(socket);
}
async beforeApplicationShutdown(signal?: string) {
this.log.log('Shutting down websocket server');
this.logger.log('Shutting down websocket server');
// Allow pending messages to be sent, it can be even 1ms, just to release the event loop
await sleep(100);
this.server.clients.forEach((options, client) => {
@@ -104,20 +107,20 @@ export class WebsocketGatewayCore
});
// Do not turn off heartbeat service here,
// it's responsible for terminating the connection that is not alive
this.log.log('Websocket server is down');
this.logger.log('Websocket server is down');
}
afterInit(server: Server) {
this.log.debug('Websocket server initialized');
this.logger.debug('Websocket server initialized');
this.manager.events.on(
WAHAEvents.SESSION_STATUS,
this.sendToAll.bind(this),
);
this.log.debug('Subscribed to manager events');
this.logger.debug('Subscribed to manager events');
this.log.debug('Starting heartbeat service...');
this.logger.debug('Starting heartbeat service...');
this.heartbeat.start(server);
this.log.debug('Heartbeat service started');
this.logger.debug('Heartbeat service started');
}
sendToAll(data: any) {
@@ -125,7 +128,7 @@ export class WebsocketGatewayCore
if (options.events.length === 0) {
return;
}
this.log.debug('Sending data to client', data);
this.logger.debug('Sending data to client', data);
client.send(JSON.stringify(data));
});
}
+15 -2
View File
@@ -5,8 +5,12 @@ import { PassportModule } from '@nestjs/passport';
import { ServeStaticModule } from '@nestjs/serve-static';
import { TerminusModule } from '@nestjs/terminus';
import { BufferJsonReplacerInterceptor } from '@waha/api/BufferJsonReplacerInterceptor';
import { ChannelsController } from '@waha/api/channels.controller';
import { WebsocketGatewayCore } from '@waha/core/api/websocket.gateway.core';
import { getPinoLogLevel, getPinoTransport } from '@waha/utils/logging';
import { LoggerModule } from 'nestjs-pino';
import { join } from 'path';
import { Logger } from 'pino';
import { AuthController } from '../api/auth.controller';
import { ChatsController } from '../api/chats.controller';
@@ -32,6 +36,14 @@ import { WAHAHealthCheckServiceCore } from './health/WAHAHealthCheckServiceCore'
import { SessionManagerCore } from './manager.core';
export const IMPORTS = [
LoggerModule.forRoot({
renameContext: 'name',
pinoHttp: {
quietReqLogger: true,
level: getPinoLogLevel(),
transport: getPinoTransport(),
},
}),
ConfigModule.forRoot({
isGlobal: true,
}),
@@ -68,6 +80,7 @@ export const CONTROLLERS = [
SessionController,
ChattingController,
ChatsController,
ChannelsController,
StatusController,
ContactsController,
GroupsController,
@@ -105,11 +118,11 @@ const PROVIDERS = [
export class AppModuleCore {
constructor(protected config: WhatsappConfigService) {}
static getHttpsOptions() {
static getHttpsOptions(logger: Logger) {
return undefined;
}
static appReady(app: INestApplication) {
static appReady(app: INestApplication, logger: Logger) {
return;
}
}
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
import { parseBool } from '../../helpers';
@@ -7,7 +8,11 @@ import { parseBool } from '../../helpers';
export class DashboardConfigServiceCore {
public dashboardUri = '/dashboard';
constructor(protected configService: ConfigService) {}
constructor(
protected configService: ConfigService,
@InjectPinoLogger('DashboardConfigService')
protected logger: PinoLogger,
) {}
get enabled(): boolean {
const value = this.configService.get('WAHA_DASHBOARD_ENABLED', 'true');
+7 -3
View File
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { parseBool } from '../../helpers';
@@ -7,14 +7,18 @@ import { getEngineName } from '../../version';
@Injectable()
export class EngineConfigService {
constructor(protected configService: ConfigService) {}
private logger: Logger;
constructor(protected configService: ConfigService) {
this.logger = new Logger('EngineConfigService');
}
getDefaultEngineName(): WAHAEngine {
const value = getEngineName();
if (value in WAHAEngine) {
return WAHAEngine[value];
}
console.log(
this.logger.warn(
`Unknown WhatsApp default engine WHATSAPP_DEFAULT_ENGINE=${value}. Using WEBJS`,
);
return WAHAEngine.WEBJS;
+6 -1
View File
@@ -1,11 +1,16 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
import { parseBool } from '../../helpers';
@Injectable()
export class SwaggerConfigServiceCore {
constructor(protected configService: ConfigService) {}
constructor(
protected configService: ConfigService,
@InjectPinoLogger('SwaggerConfigService')
protected logger: PinoLogger,
) {}
get advancedConfigEnabled(): boolean {
const value = this.configService.get(
+131 -42
View File
@@ -5,11 +5,13 @@ import makeWASocket, {
extractMessageContent,
getAggregateVotesInPollMessage,
getKeyAuthor,
getUrlFromDirectPath,
isJidGroup,
isJidStatusBroadcast,
isRealMessage,
jidNormalizedUser,
makeCacheableSignalKeyStore,
NewsletterMetadata,
normalizeMessageContent,
PresenceData,
proto,
@@ -17,10 +19,17 @@ import makeWASocket, {
WAMessageKey,
} from '@adiwajshing/baileys';
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import { Logger as BaileysLogger } from '@adiwajshing/baileys/node_modules/pino';
import { UnprocessableEntityException } from '@nestjs/common';
import { NowebInMemoryStore } from '@waha/core/engines/noweb/store/NowebInMemoryStore';
import { flipObject, getLogLevels, parseBool, splitAt } from '@waha/helpers';
import { flipObject, parseBool, splitAt } from '@waha/helpers';
import { PairingCodeResponse } from '@waha/structures/auth.dto';
import {
Channel,
ChannelRole,
CreateChannelRequest,
ListChannelsQuery,
} from '@waha/structures/channels.dto';
import { GetChatsQuery } from '@waha/structures/chats.dto';
import { ContactQuery, ContactRequest } from '@waha/structures/contacts.dto';
import {
@@ -29,12 +38,10 @@ import {
WAMessageAckBody,
} from '@waha/structures/webhooks.dto';
import * as Buffer from 'buffer';
import { request } from 'express';
import { Agent } from 'https';
import * as lodash from 'lodash';
import { toNumber } from 'lodash';
import * as NodeCache from 'node-cache';
import type { Logger } from 'pino';
import {
ChatRequest,
@@ -86,6 +93,8 @@ import {
import { IEngineMediaProcessor } from '../../abc/media.abc';
import {
ensureSuffix,
getChannelInviteLink,
isNewsletter,
WAHAInternalEvent,
WhatsappSession,
} from '../../abc/session.abc';
@@ -95,7 +104,6 @@ import {
} from '../../exceptions';
import { toVcard } from '../../helpers';
import { createAgentProxy } from '../../helpers.proxy';
import { buildLogger } from '../../manager.core';
import { QR } from '../../QR';
import { NowebAuthFactoryCore } from './NowebAuthFactoryCore';
import { INowebStore } from './store/INowebStore';
@@ -105,8 +113,6 @@ import { extractMediaContent } from './utils';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pino = require('pino');
export const BaileysEvents = {
CONNECTION_UPDATE: 'connection.update',
@@ -136,8 +142,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
storageFactory = new NowebStorageFactoryCore();
private startTimeoutId: null | ReturnType<typeof setTimeout> = null;
private autoRestartTimeoutId: null | ReturnType<typeof setTimeout> = null;
public logger: Logger;
private msgRetryCounterCache: NodeCache;
protected engineLogger: BaileysLogger;
get listenConnectionEventsFromTheStart() {
return true;
@@ -150,18 +156,16 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
public constructor(config) {
super(config);
this.qr = new QR();
const levels = getLogLevels(this.sessionConfig?.debug);
const debug = levels.includes('debug');
const logger = pino({ level: debug ? 'debug' : 'info' });
this.logger = logger.child({
session: this.name,
});
// external map to store retry counts of messages when decryption/encryption fails
// keep this out of the socket itself, to prevent a message decryption/encryption loop across socket restarts
this.msgRetryCounterCache = new NodeCache({
stdTTL: 60 * 60, // 1 hour
useClones: false,
});
this.engineLogger = this.loggerBuilder.child({
name: 'NOWEBEngine',
}) as unknown as BaileysLogger;
}
start() {
@@ -169,7 +173,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.buildClient();
}
getSocketConfig(agent, state) {
getSocketConfig(agent, state): any {
const fullSyncEnabled = this.sessionConfig?.noweb?.store?.fullSync || false;
const browser = fullSyncEnabled
? Browsers.ubuntu('Desktop')
@@ -180,11 +184,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
auth: {
creds: state.creds,
/** caching makes the store faster to send/recv messages */
keys: makeCacheableSignalKeyStore(state.keys, this.logger),
keys: makeCacheableSignalKeyStore(state.keys, this.engineLogger),
},
printQRInTerminal: false,
browser: browser,
logger: this.logger,
logger: this.engineLogger,
mobile: false,
defaultQueryTimeoutMs: 120_000,
keepAliveIntervalMs: 30_000,
@@ -194,7 +198,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
};
}
async makeSocket() {
async makeSocket(): Promise<any> {
const { state, saveCreds } = await this.authFactory.buildAuth(
this.sessionStore,
this.name,
@@ -214,31 +218,32 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
async connectStore() {
this.log.debug(`Connecting store...`);
this.logger.debug(`Connecting store...`);
if (!this.store) {
this.log.debug(`Making a new store...`);
const levels = getLogLevels(this.sessionConfig?.debug);
const log = buildLogger(`NowebStore - ${this.name}`, levels);
this.logger.debug(`Making a new store...`);
const storeEnabled = this.sessionConfig?.noweb?.store?.enabled || false;
if (storeEnabled) {
this.log.debug('Using NowebPersistentStore');
this.logger.debug('Using NowebPersistentStore');
const storage = this.storageFactory.createStorage(
this.sessionStore,
this.name,
);
this.store = new NowebPersistentStore(log, storage);
this.store = new NowebPersistentStore(
this.loggerBuilder.child({ name: NowebPersistentStore.name }),
storage,
);
await this.store.init().catch((err) => {
this.log.error(`Failed to initialize storage or store: ${err}`);
this.logger.error(`Failed to initialize storage or store: ${err}`);
this.status = WAHASessionStatus.FAILED;
this.end();
throw err;
});
} else {
this.log.debug('Using NowebInMemoryStore');
this.logger.debug('Using NowebInMemoryStore');
this.store = new NowebInMemoryStore();
}
}
this.log.debug(`Binding store to socket...`);
this.logger.debug(`Binding store to socket...`);
this.store.bind(this.sock.ev, this.sock);
}
@@ -271,10 +276,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
// add random delay to avoid multiple restarts at the same time
const shiftSeconds = Math.floor(Math.random() * 30);
const delay = this.AUTO_RESTART_AFTER_SECONDS + shiftSeconds;
this.log.debug(`Auto-restart is enabled, after ${delay} seconds`);
this.logger.debug(`Auto-restart is enabled, after ${delay} seconds`);
this.autoRestartTimeoutId = setTimeout(() => {
this.autoRestartTimeoutId = null;
this.log.log('Auto-restarting the client connection...');
this.logger.info('Auto-restarting the client connection...');
this.sock?.end(new Error('auto-restart'));
}, delay * SECOND);
}
@@ -291,18 +296,18 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
protected listenEngineEventsInDebugMode() {
this.sock.ev.process((events) => {
this.log.debug(`NOWEB events: ${JSON.stringify(events)}`);
this.logger.debug({ events: events }, `NOWEB events`);
});
}
private restartClient() {
if (this.startTimeoutId) {
this.log.log(
this.logger.info(
'Request to restart is already in progress, ignoring this request',
);
return;
}
this.log.log(
this.logger.info(
`Setting up client start in ${this.START_ATTEMPT_DELAY_SECONDS} seconds...`,
);
this.startTimeoutId = setTimeout(() => {
@@ -312,7 +317,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
protected listenConnectionEvents() {
this.log.debug(`Start listening ${BaileysEvents.CONNECTION_UPDATE}...`);
this.logger.debug(`Start listening ${BaileysEvents.CONNECTION_UPDATE}...`);
this.sock.ev.on('connection.update', async (update) => {
const { connection, lastDisconnect, qr, isNewLogin } = update;
if (isNewLogin) {
@@ -330,12 +335,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.qr.save('');
// reconnect if not logged out
if (shouldReconnect) {
this.log.log(
this.logger.info(
`Connection closed due to '${lastDisconnect.error}', reconnecting...`,
);
this.restartClient();
} else {
this.log.error(
this.logger.error(
`Connection closed due to '${lastDisconnect.error}', do not reconnect the session.`,
);
await this.end();
@@ -405,7 +410,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
const jidPresences = this.store?.presences?.[jid];
const participantPresence = jidPresences?.[participant];
if (participantPresence?.lastKnownPresence === 'composing') {
this.log.debug(
this.logger.debug(
`Fixing presence for '${participant}' in '${jid} since it's typing`,
);
const presence: PresenceData = { lastKnownPresence: 'available' };
@@ -462,7 +467,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
if (this.status == WAHASessionStatus.STARTING) {
this.log.debug('Waiting for connection update...');
this.logger.debug('Waiting for connection update...');
await this.sock.waitForConnectionUpdate((update) => !!update.qr);
}
@@ -471,12 +476,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
throw new UnprocessableEntityException(err);
}
this.log.log(`Requesting pairing code for '${phoneNumber}'...`);
this.logger.info(`Requesting pairing code for '${phoneNumber}'...`);
const code: string = await this.sock.requestPairingCode(phoneNumber);
// show it as ABCD-ABCD
const parts = splitAt(code, 4);
const codeRepr = parts.join('-');
this.log.log(`Your code: ${codeRepr}`);
this.logger.info(`Your code: ${codeRepr}`);
return { code: codeRepr };
}
@@ -870,6 +875,81 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
}
/**
* Channels methods
*/
protected toChannel(newsletter: NewsletterMetadata): Channel {
const role =
// @ts-ignore
newsletter.viewer_metadata?.role ||
(newsletter.viewer_metadata?.view_role as ChannelRole) ||
ChannelRole.GUEST;
const preview = newsletter.preview
? getUrlFromDirectPath(newsletter.preview)
: null;
const picture = newsletter.picture
? getUrlFromDirectPath(newsletter.picture)
: null;
return {
id: newsletter.id,
name: newsletter.name,
description: newsletter.description,
invite: getChannelInviteLink(newsletter.invite),
preview: preview,
picture: picture,
verified: newsletter.verification === 'VERIFIED',
role: role,
};
}
public async channelsList(query: ListChannelsQuery): Promise<Channel[]> {
const newsletters = await this.sock.newsletterSubscribed();
let channels = newsletters.map(this.toChannel);
if (query.role) {
// @ts-ignore
channels = channels.filter((channel) => channel.role === query.role);
}
return channels;
}
public async channelsCreateChannel(request: CreateChannelRequest) {
const newsletter = await this.sock.newsletterCreate(
request.name,
request.description,
);
return this.toChannel(newsletter);
}
public async channelsGetChannel(id: string) {
const newsletter = await this.sock.newsletterMetadata('jid', id);
return this.toChannel(newsletter);
}
public async channelsGetChannelByInviteCode(inviteCode: string) {
const newsletter = await this.sock.newsletterMetadata('invite', inviteCode);
return this.toChannel(newsletter);
}
public async channelsDeleteChannel(id: string) {
return await this.sock.newsletterDelete(id);
}
public async channelsFollowChannel(id: string): Promise<void> {
return await this.sock.newsletterFollow(id);
}
public async channelsUnfollowChannel(id: string): Promise<void> {
return await this.sock.newsletterUnfollow(id);
}
public async channelsMuteChannel(id: string): Promise<void> {
return await this.sock.newsletterMute(id);
}
public async channelsUnmuteChannel(id: string): Promise<void> {
return await this.sock.newsletterUnmute(id);
}
/**
* END - Methods for API
*/
@@ -994,16 +1074,16 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
try {
message = await this.downloadMedia(message);
} catch (e) {
this.log.error('Failed when tried to download media for a message');
this.log.error(e, e.stack);
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
}
try {
return await this.toWAMessage(message);
} catch (error) {
this.log.error('Failed to process incoming message');
this.log.error(error);
this.logger.error('Failed to process incoming message');
this.logger.error(error);
console.trace(error);
return null;
}
@@ -1229,6 +1309,9 @@ export class EngineMediaProcessor implements IEngineMediaProcessor<any> {
* Convert from 11111111111@s.whatsapp.net to 11111111111@c.us
*/
function toCusFormat(remoteJid) {
if (!remoteJid) {
return remoteJid;
}
if (isJidGroup(remoteJid)) {
return remoteJid;
}
@@ -1238,6 +1321,9 @@ function toCusFormat(remoteJid) {
if (isLidUser(remoteJid)) {
return remoteJid;
}
if (isNewsletter(remoteJid)) {
return remoteJid;
}
if (!remoteJid) {
return;
}
@@ -1259,6 +1345,9 @@ export function toJID(chatId) {
if (isJidStatusBroadcast(chatId)) {
return chatId;
}
if (isNewsletter(chatId)) {
return chatId;
}
const number = chatId.split('@')[0];
return number + '@s.whatsapp.net';
}
@@ -11,6 +11,7 @@ import {
} from '@adiwajshing/baileys';
import { ConsoleLogger } from '@nestjs/common';
import { toNumber } from 'lodash';
import { Logger } from 'pino';
import { toJID } from '../session.noweb.core';
import { IChatRepository } from './IChatRepository';
@@ -31,7 +32,7 @@ export class NowebPersistentStore implements INowebStore {
private lock: any;
constructor(
private log: ConsoleLogger,
private logger: Logger,
public storage: INowebStorage,
) {
this.socket = null;
@@ -89,7 +90,7 @@ export class NowebPersistentStore implements INowebStore {
async close(): Promise<void> {
await this.storage?.close().catch((error) => {
this.log.warn(`Failed to close storage: ${error}`);
this.logger.warn(`Failed to close storage: ${error}`);
});
return;
}
@@ -97,7 +98,7 @@ export class NowebPersistentStore implements INowebStore {
private async onMessagingHistorySet(history) {
const { contacts, chats, messages, isLatest } = history;
if (isLatest) {
this.log.debug(
this.logger.debug(
'history sync - clearing all entities, got latest history',
);
await Promise.all([
@@ -110,7 +111,7 @@ export class NowebPersistentStore implements INowebStore {
await Promise.all([
this.withLock('contacts', async () => {
await this.onContactsUpsert(contacts);
this.log.log(`history sync - '${contacts.length}' synced contacts`);
this.logger.info(`history sync - '${contacts.length}' synced contacts`);
}),
this.withLock('chats', () => this.onChatUpsert(chats)),
this.withLock('messages', () => this.syncMessagesHistory(messages)),
@@ -120,7 +121,7 @@ export class NowebPersistentStore implements INowebStore {
private async syncMessagesHistory(messages) {
const realMessages = messages.filter(isRealMessage);
await this.messagesRepo.upsert(realMessages);
this.log.log(
this.logger.info(
`history sync - '${messages.length}' got messages, '${realMessages.length}' real messages`,
);
}
@@ -128,12 +129,12 @@ export class NowebPersistentStore implements INowebStore {
private async onMessagesUpsert(update) {
const { messages, type } = update;
if (type !== 'notify' && type !== 'append') {
this.log.debug(`unexpected type for messages.upsert: '${type}'`);
this.logger.debug(`unexpected type for messages.upsert: '${type}'`);
return;
}
const realMessages = messages.filter(isRealMessage);
await this.messagesRepo.upsert(realMessages);
this.log.debug(
this.logger.debug(
`messages.upsert - ${messages.length} got messages, ${realMessages.length} real messages`,
);
}
@@ -178,7 +179,7 @@ export class NowebPersistentStore implements INowebStore {
chat.conversationTimestamp = toNumber(chat.conversationTimestamp);
await this.chatRepo.save(chat);
}
this.log.log(`history sync - '${chats.length}' synced chats`);
this.logger.info(`history sync - '${chats.length}' synced chats`);
}
private async onChatUpdate(updates: ChatUpdate[]) {
@@ -219,7 +220,7 @@ export class NowebPersistentStore implements INowebStore {
const contact = await this.contactRepo.getById(update.id);
if (!contact) {
this.log.warn(
this.logger.warn(
`got update for non-existent contact. update: '${JSON.stringify(
update,
)}'`,
@@ -246,7 +247,7 @@ export class NowebPersistentStore implements INowebStore {
for (const { key, reaction } of reactions) {
const msg = await this.messagesRepo.getByJidById(key.remoteJid, key.id);
if (!msg) {
this.log.warn(
this.logger.warn(
`got reaction update for non-existent message. key: '${JSON.stringify(
key,
)}'`,
@@ -262,7 +263,7 @@ export class NowebPersistentStore implements INowebStore {
for (const { key, receipt } of updates) {
const msg = await this.messagesRepo.getByJidById(key.remoteJid, key.id);
if (!msg) {
this.log.warn(
this.logger.warn(
`got receipt update for non-existent message. key: '${JSON.stringify(
key,
)}'`,
+2 -2
View File
@@ -69,7 +69,7 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
this.qr.save(base64Qrimg, urlCode);
this.printQR(this.qr);
this.status = WAHASessionStatus.SCAN_QR_CODE;
this.log.debug('Number of attempts to read the qrcode: ', attempts);
this.logger.debug('Number of attempts to read the qrcode: ', attempts);
};
}
@@ -79,7 +79,7 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
this.whatsapp = await this.buildClient();
} catch (error) {
this.status = WAHASessionStatus.FAILED;
this.log.error(error);
this.logger.error(error);
this.qr.save('');
return;
}
+135 -19
View File
@@ -1,6 +1,10 @@
import { UnprocessableEntityException } from '@nestjs/common/exceptions/unprocessable-entity.exception';
import { IEngineMediaProcessor } from '@waha/core/abc/media.abc';
import { WAHAInternalEvent, WhatsappSession } from '@waha/core/abc/session.abc';
import {
getChannelInviteLink,
WAHAInternalEvent,
WhatsappSession,
} from '@waha/core/abc/session.abc';
import { WebjsClient } from '@waha/core/engines/webjs/WebjsClient';
import {
AvailableInPlusVersion,
@@ -8,6 +12,12 @@ import {
} from '@waha/core/exceptions';
import { QR } from '@waha/core/QR';
import { parseBool } from '@waha/helpers';
import {
Channel,
ChannelRole,
CreateChannelRequest,
ListChannelsQuery,
} from '@waha/structures/channels.dto';
import { GetChatsQuery } from '@waha/structures/chats.dto';
import {
ChatRequest,
@@ -44,6 +54,7 @@ import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
import { MeInfo } from '@waha/structures/sessions.dto';
import { WAMessageRevokedBody } from '@waha/structures/webhooks.dto';
import {
Channel as WEBJSChannel,
Chat,
ClientOptions,
Contact,
@@ -116,17 +127,19 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
private restartClient() {
if (!this.shouldRestart) {
this.log.debug("Shouldn't restart the client, ignoring restart request");
this.logger.debug(
"Shouldn't restart the client, ignoring restart request",
);
return;
}
if (this.startTimeoutId) {
const msg =
'Request to restart is already in progress, ignoring restart request';
this.log.warn(msg);
this.logger.warn(msg);
return;
}
this.log.log(
this.logger.info(
`Setting up client start in ${this.START_ATTEMPT_DELAY_SECONDS} seconds...`,
);
this.startTimeoutId = setTimeout(() => {
@@ -162,21 +175,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
// Listen for browser disconnected event
this.whatsapp.pupBrowser.on('disconnected', () => {
this.status = WAHASessionStatus.FAILED;
this.log.error('The browser has been disconnected');
this.logger.error('The browser has been disconnected');
this.restartClient();
});
// Listen for page close event
this.whatsapp.pupPage.on('close', () => {
this.status = WAHASessionStatus.FAILED;
this.log.error('The WhatsApp Web page has been closed');
this.logger.error('The WhatsApp Web page has been closed');
this.restartClient();
});
// Listen for page error event
if (this.isDebugEnabled()) {
this.log.debug("Logging 'console' event for web page");
this.logger.debug("Logging 'console' event for web page");
this.whatsapp.pupPage.on('console', (msg) =>
this.log.debug(`WEBJS page log: ${msg.text()}`),
this.logger.debug(`WEBJS page log: ${msg.text()}`),
);
this.whatsapp.pupPage.evaluate(() =>
console.log(`url is ${location.href}`),
@@ -185,7 +198,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
})
.catch((error) => {
this.status = WAHASessionStatus.FAILED;
this.log.error(error);
this.logger.error(error);
this.restartClient();
return;
});
@@ -214,10 +227,10 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
this.whatsapp?.removeAllListeners();
clearInterval(this.startTimeoutId);
this.whatsapp?.destroy().catch((error) => {
this.log.debug('Failed to destroy the client', error);
this.logger.debug('Failed to destroy the client', error);
});
} catch (error) {
this.log.error(error);
this.logger.error(error);
}
}
@@ -240,14 +253,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
const event = Events[key];
this.whatsapp.on(event, (...data: any[]) => {
const log = { event: event, data: data };
this.log.debug(`WEBJS event: ${JSON.stringify(log)}`);
this.logger.debug({ event: log }, `WEBJS event`);
});
}
}
protected listenConnectionEvents() {
this.whatsapp.on(Events.QR_RECEIVED, async (qr) => {
this.log.debug('QR received');
this.logger.debug('QR received');
// Convert to image and save
const url = await QRCode.toDataURL(qr);
this.qr.save(url, qr);
@@ -258,25 +271,25 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
this.whatsapp.on(Events.READY, () => {
this.status = WAHASessionStatus.WORKING;
this.qr.save('');
this.log.log(`Session '${this.name}' has been authenticated!`);
this.logger.info(`Session '${this.name}' is ready!`);
});
this.whatsapp.on(Events.AUTHENTICATED, () => {
this.status = WAHASessionStatus.WORKING;
this.qr.save('');
this.log.log(`Session '${this.name}' has been authenticated!`);
this.logger.info(`Session '${this.name}' has been authenticated!`);
});
this.whatsapp.on(Events.AUTHENTICATION_FAILURE, () => {
this.status = WAHASessionStatus.FAILED;
this.qr.save('');
this.log.log(`Session '${this.name}' has been disconnected!`);
this.logger.info(`Session '${this.name}' has been disconnected!`);
});
this.whatsapp.on(Events.DISCONNECTED, () => {
this.status = WAHASessionStatus.FAILED;
this.qr.save('');
this.log.log(`Session '${this.name}' has been disconnected!`);
this.logger.info(`Session '${this.name}' has been disconnected!`);
});
}
@@ -615,6 +628,109 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return groupChat.demoteParticipants(participantIds);
}
/**
* Channels methods
*/
protected ChatToChannel(chat: WEBJSChannel): Channel {
// @ts-ignore
const metadata = chat.channelMetadata;
let role = metadata.membershipType.toUpperCase();
if (role === 'VIEWER') {
role = ChannelRole.GUEST;
}
return {
id: chat.id._serialized,
name: chat.name,
description: chat.description,
invite: getChannelInviteLink(metadata.inviteCode),
preview: null,
picture: null,
verified: metadata.verified,
role: role,
};
}
protected ChannelMetadataToChannel(metadata: any): Channel {
let role = metadata.membershipType.toUpperCase();
if (role === 'VIEWER') {
role = ChannelRole.GUEST;
}
return {
id: metadata.id,
name: metadata.titleMetadata.title,
description: metadata.descriptionMetadata.description,
invite: metadata.inviteLink,
preview: metadata.pictureUrl,
picture: metadata.pictureUrl,
verified: metadata.isVerified,
role: role,
};
}
public async channelsList(query: ListChannelsQuery): Promise<Channel[]> {
const data = await this.whatsapp.getChannels();
let channels = data.map(this.ChatToChannel);
if (query.role) {
// @ts-ignore
channels = channels.filter((channel) => channel.role === query.role);
}
const promises = channels.map(async (channel) =>
this.whatsapp.getProfilePicUrl(channel.id),
);
const pictures = await Promise.all(promises);
channels = channels.map((channel, index) => {
channel.picture = pictures[index] || null;
channel.preview = channel.picture;
return channel;
});
return channels;
}
public channelsCreateChannel(
request: CreateChannelRequest,
): Promise<Channel> {
throw new NotImplementedByEngineError();
}
public async channelsGetChannel(id: string): Promise<Channel> {
return await this.channelsGetChannelByInviteCode(id);
}
public async channelsGetChannelByInviteCode(
inviteCode: string,
): Promise<Channel> {
const metadata = await this.whatsapp.getChannelByInviteCode(inviteCode);
const channel = this.ChannelMetadataToChannel(metadata);
channel.preview =
(await this.whatsapp.getProfilePicUrl(channel.id)) || null;
channel.picture = channel.preview;
return channel;
}
public channelsDeleteChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsFollowChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsUnfollowChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsMuteChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
public channelsUnmuteChannel(id: string): Promise<void> {
throw new NotImplementedByEngineError();
}
/**
* Presences methods
*/
public async setPresence(presence: WAHAPresenceStatus, chatId?: string) {
let chat: Chat;
switch (presence) {
@@ -705,8 +821,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
try {
message = await this.downloadMedia(message);
} catch (e) {
this.log.error('Failed when tried to download media for a message');
this.log.error(e, e.stack);
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
}
return await this.toWAMessage(message);
+14 -16
View File
@@ -5,11 +5,12 @@ import {
NotFoundException,
UnprocessableEntityException,
} from '@nestjs/common';
import { getPinoLogLevel, LoggerBuilder } from '@waha/utils/logging';
import { promiseTimeout } from '@waha/utils/promiseTimeout';
import { EventEmitter } from 'events';
import { PinoLogger } from 'nestjs-pino';
import { WhatsappConfigService } from '../config.service';
import { getLogLevels } from '../helpers';
import {
WAHAEngine,
WAHAEvents,
@@ -46,10 +47,6 @@ export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
}
}
export function buildLogger(name: string, levels: LogLevel[]) {
return new ConsoleLogger(name, { logLevels: levels });
}
@Injectable()
export class SessionManagerCore extends SessionManager {
private session?: WhatsappSession;
@@ -61,12 +58,12 @@ export class SessionManagerCore extends SessionManager {
constructor(
private config: WhatsappConfigService,
private log: ConsoleLogger,
private engineConfigService: EngineConfigService,
private log: PinoLogger,
) {
super();
this.events = new EventEmitter();
this.log.setContext('SessionManager');
this.log.setContext(SessionManagerCore.name);
this.session = undefined;
const engineName = this.engineConfigService.getDefaultEngineName();
this.EngineClass = this.getEngine(engineName);
@@ -120,20 +117,21 @@ export class SessionManagerCore extends SessionManager {
}
const name = request.name;
this.log.log(`'${name}' - starting session...`);
const levels = getLogLevels(request.config?.debug);
const log = buildLogger(`WhatsappSession - ${name}`, levels);
this.log.info(`'${name}' - starting session...`);
const mediaManager = new CoreMediaManager(
new MediaStorageCore(),
this.config.mimetypes,
);
const webhookLog = buildLogger(`Webhook - ${name}`, levels);
const webhook = new this.WebhookConductorClass(webhookLog);
const logger = this.log.logger.child({ session: name });
logger.level = getPinoLogLevel(request.config?.debug);
const loggerBuilder: LoggerBuilder = logger;
const webhook = new this.WebhookConductorClass(loggerBuilder);
const proxyConfig = this.getProxyConfig(request);
const sessionConfig: SessionParams = {
name,
mediaManager,
log,
loggerBuilder,
printQR: this.engineConfigService.shouldPrintQR,
sessionStore: this.store,
proxyConfig: proxyConfig,
@@ -198,10 +196,10 @@ export class SessionManagerCore extends SessionManager {
this.onlyDefault(request.name);
const name = request.name;
this.log.log(`Stopping ${name} session...`);
this.log.info(`Stopping ${name} session...`);
const session = this.getSession(name);
await session.stop();
this.log.log(`"${name}" has been stopped.`);
this.log.info(`"${name}" has been stopped.`);
this.session = undefined;
}
@@ -210,7 +208,7 @@ export class SessionManagerCore extends SessionManager {
this.onlyDefault(request.name);
this.stop({ name: name, logout: false })
.then(() => {
this.log.log(`Session '${name}' has been stopped.`);
this.log.info(`Session '${name}' has been stopped.`);
})
.catch((err) => {
this.log.error(
+19 -13
View File
@@ -7,30 +7,36 @@ import { VERSION } from '../version';
import { WAHAInternalEvent, WhatsappSession } from './abc/session.abc';
import { WebhookConductor, WebhookSender } from './abc/webhooks.abc';
import request = require('request');
import { LoggerBuilder } from '@waha/utils/logging';
import { Logger } from 'pino';
export class WebhookSenderCore extends WebhookSender {
send(json) {
this.log.log(`Sending POST to ${this.url}...`);
this.log.verbose(`POST DATA: ${JSON.stringify(json)}`);
this.logger.info({ url: this.url }, `Sending POST ...`);
this.logger.debug({ data: json }, `POST DATA`);
request.post(this.url, { json: json }, (error, res, body) => {
if (error) {
this.log.error(error);
this.logger.error(error);
return;
}
this.log.log(`POST request was sent with status code: ${res.statusCode}`);
this.log.debug(`Response: ${body}`);
this.logger.info(
`POST request was sent with status code: ${res.statusCode}`,
);
this.logger.debug({ body: body }, `Response`);
});
}
}
export class WebhookConductorCore implements WebhookConductor {
constructor(protected log: ConsoleLogger) {
this.log = log;
private logger: Logger;
constructor(protected loggerBuilder: LoggerBuilder) {
this.logger = loggerBuilder.child({ name: WebhookConductor.name });
}
protected buildSender(webhookConfig: WebhookConfig): WebhookSender {
return new WebhookSenderCore(this.log, webhookConfig);
return new WebhookSenderCore(this.loggerBuilder, webhookConfig);
}
private getSuitableEvents(events: WAHAEvents[] | string[]): WAHAEvents[] {
@@ -47,7 +53,7 @@ export class WebhookConductorCore implements WebhookConductor {
for (const event of events) {
// @ts-ignore
if (!allEvents.includes(event)) {
this.log.error(`Unknown event for webhook: '${event}'`);
this.logger.error(`Unknown event for webhook: '${event}'`);
continue;
}
rightEvents.push(event);
@@ -70,7 +76,7 @@ export class WebhookConductorCore implements WebhookConductor {
}
const url = webhook.url;
this.log.log(`Configuring webhooks for ${url}...`);
this.logger.info(`Configuring webhooks for ${url}...`);
const events = this.getSuitableEvents(webhook.events);
const sender = this.buildSender(webhook);
for (const event of events) {
@@ -93,14 +99,14 @@ export class WebhookConductorCore implements WebhookConductor {
url,
);
if (!found) {
this.log.error(
this.logger.error(
`Engine does not support webhook event: '${event}' for url '${url}'`,
);
}
});
}
}
this.log.log(`Webhooks were configured for ${url}.`);
this.logger.info(`Webhooks were configured for ${url}.`);
}
private configureSingleEvent(
@@ -117,7 +123,7 @@ export class WebhookConductorCore implements WebhookConductor {
if (!found) {
return false;
}
this.log.log(`Event '${event}' is enabled for url: ${url}`);
this.logger.info(`Event '${event}' is enabled for url: ${url}`);
return true;
}
+1 -10
View File
@@ -1,5 +1,3 @@
import { LogLevel } from '@nestjs/common';
function parseBool(value: any): boolean {
if (typeof value === 'boolean') {
return value;
@@ -37,11 +35,4 @@ function splitAt(str: string, index) {
return [fst.join(''), snd.join('')];
}
function getLogLevels(debug: boolean): LogLevel[] {
const enableDebug = process.env.DEBUG != undefined || debug;
return enableDebug
? ['log', 'error', 'warn', 'debug', 'verbose']
: ['log', 'error', 'warn'];
}
export { flipObject, getLogLevels, parseBool, splitAt };
export { flipObject, parseBool, splitAt };
+24 -11
View File
@@ -1,27 +1,39 @@
import { NestFactory } from '@nestjs/core';
import { WsAdapter } from '@nestjs/platform-ws';
import {
getNestJSLogLevels,
getPinoLogLevel,
getPinoTransport,
} from '@waha/utils/logging';
import { json, urlencoded } from 'express';
import { Logger as NestJSPinoLogger } from 'nestjs-pino';
import { Logger } from 'pino';
import pino from 'pino';
import { AllExceptionsFilter } from './api/exception.filter';
import { WhatsappConfigService } from './config.service';
import { AppModuleCore } from './core/app.module.core';
import { SwaggerConfiguratorCore } from './core/SwaggerConfiguratorCore';
import { getLogLevels } from './helpers';
import { WAHA_WEBHOOKS } from './structures/webhooks.dto';
import { getWAHAVersion, VERSION, WAHAVersion } from './version';
console.log('NODE - Catching unhandled exceptions enabled');
const logger: Logger = pino({
level: getPinoLogLevel(),
transport: getPinoTransport(),
}).child({ name: 'Bootstrap' });
logger.info('NODE - Catching unhandled exceptions enabled');
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
// @ts-ignore
console.error(reason.stack);
logger.error(reason.stack);
});
async function loadModules(): Promise<
[typeof AppModuleCore, typeof SwaggerConfiguratorCore]
> {
const version = getWAHAVersion();
console.log(`WAHA (WhatsApp HTTP API) - Running ${version} version...`);
logger.info(`WAHA (WhatsApp HTTP API) - Running ${version} version...`);
if (version === WAHAVersion.CORE) {
const { AppModuleCore } = await import('./core/app.module.core');
@@ -41,12 +53,13 @@ async function loadModules(): Promise<
async function bootstrap() {
const [AppModule, SwaggerModule] = await loadModules();
const httpsOptions = AppModule.getHttpsOptions();
const httpsOptions = AppModule.getHttpsOptions(logger);
const app = await NestFactory.create(AppModule, {
logger: getLogLevels(false),
logger: getNestJSLogLevels(),
httpsOptions: httpsOptions,
bufferLogs: true,
});
app.useLogger(app.get(NestJSPinoLogger));
app.enableShutdownHooks();
app.useGlobalFilters(new AllExceptionsFilter());
app.enableCors();
@@ -63,11 +76,11 @@ async function bootstrap() {
const swaggerConfigurator = new SwaggerModule(app);
swaggerConfigurator.configure(WAHA_WEBHOOKS);
AppModule.appReady(app);
AppModule.appReady(app, logger);
const config = app.get(WhatsappConfigService);
await app.listen(config.port);
console.log(`WhatsApp HTTP API is running on: ${await app.getUrl()}`);
console.log(`Environment:`, VERSION);
logger.info(`WhatsApp HTTP API is running on: ${await app.getUrl()}`);
logger.info(VERSION, 'Environment');
}
bootstrap();
+94
View File
@@ -0,0 +1,94 @@
import { ApiParam, ApiProperty, getSchemaPath } from '@nestjs/swagger';
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
export class CreateChannelRequest {
@ApiProperty({
example: 'Channel Name',
})
name: string;
@ApiProperty({
example: 'Channel Description',
})
description?: string;
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(RemoteFile) },
{ $ref: getSchemaPath(BinaryFile) },
],
})
picture?: RemoteFile | BinaryFile;
}
export enum ChannelRole {
OWNER = 'OWNER',
ADMIN = 'ADMIN',
SUBSCRIBER = 'SUBSCRIBER',
GUEST = 'GUEST',
}
export enum ChannelRoleFilter {
OWNER = ChannelRole.OWNER,
ADMIN = ChannelRole.ADMIN,
SUBSCRIBER = ChannelRole.SUBSCRIBER,
}
export class ListChannelsQuery {
role?: ChannelRoleFilter;
}
export class Channel {
@ApiProperty({
description: 'Newsletter id',
example: '123123123123@newsletter',
})
id: string;
name: string;
description?: string;
@ApiProperty({
description: 'Invite link',
example: 'https://www.whatsapp.com/channel/111111111111111111111111',
})
invite: string;
@ApiProperty({
description: "Preview for channel's picture",
example: 'https://mmg.whatsapp.net/m1/v/t24/An&_nc_cat=10',
})
preview?: string;
@ApiProperty({
description: "Channel's picture",
example: 'https://mmg.whatsapp.net/m1/v/t24/An&_nc_cat=10',
})
picture?: string;
verified: boolean;
role: ChannelRole;
}
export const NewsletterIdApiParam = ApiParam({
name: 'id',
required: true,
type: 'string',
schema: {
default: '123123123@newsletter',
},
description: 'WhatsApp Channel ID',
});
export const NewsletterIdOrInviteCodeApiParam = ApiParam({
name: 'id',
required: true,
type: 'string',
schema: {
default: '123123123@newsletter',
},
description:
'WhatsApp Channel ID or invite code from invite link https://www.whatsapp.com/channel/11111',
});
+5 -5
View File
@@ -1,4 +1,4 @@
import { ConsoleLogger } from '@nestjs/common';
import { LoggerService } from '@nestjs/common';
import { WebSocket } from '@waha/utils/ws';
import { WebSocketServer } from 'ws';
@@ -6,7 +6,7 @@ export class HeartbeatJob {
private interval: ReturnType<typeof setInterval>;
constructor(
private log: ConsoleLogger,
private logger: LoggerService,
private intervalTime: number = 10_000,
) {}
@@ -19,14 +19,14 @@ export class HeartbeatJob {
this.interval = setInterval(() => {
server.clients.forEach((client: WebSocket) => {
if (client.isAlive === false) {
this.log.debug(
this.logger.debug(
`Terminating client connection due to heartbeat timeout, ${client.id}`,
);
return client.terminate();
}
client.isAlive = false;
this.log.debug(`Sending heartbeat (ping) to ${client.id}`);
this.logger.debug(`Sending heartbeat (ping) to ${client.id}`);
client.ping();
});
}, this.intervalTime);
@@ -39,7 +39,7 @@ export class HeartbeatJob {
private onPong(ws: WebSocket) {
return (event: any) => {
ws.isAlive = true;
this.log.debug(`Heartbeat (pong) received from ${ws.id}`);
this.logger.debug(`Heartbeat (pong) received from ${ws.id}`);
};
}
}
+61
View File
@@ -0,0 +1,61 @@
import { LogLevel } from '@nestjs/common';
import { ChildLoggerOptions, Level, Logger } from 'pino';
export interface LoggerBuilder {
child(bindings: Record<string, any>, options?: ChildLoggerOptions): Logger;
}
function getNestJSLogLevels(): LogLevel[] {
const level = getPinoLogLevel();
switch (level) {
case 'trace':
return ['error', 'warn', 'log', 'debug', 'verbose'];
case 'debug':
return ['error', 'warn', 'log', 'debug'];
case 'info':
return ['error', 'warn', 'log'];
case 'warn':
return ['error', 'warn'];
case 'error':
return ['error', 'warn'];
default:
return ['error', 'warn', 'log'];
}
}
export function getPinoLogLevel(debug: boolean = false): Level {
const enableDebug = process.env.DEBUG != undefined || debug;
if (enableDebug) {
return 'debug';
}
return getDefaultPinoLogLevel();
}
export function getDefaultPinoLogLevel(): Level {
const logLevel = (process.env.WAHA_LOG_LEVEL || 'info').toLowerCase();
const levels = ['fatal', 'error', 'warn', 'info', 'debug', 'trace'];
if (!levels.includes(logLevel)) {
console.error(
`Unknown ${process.env.WAHA_LOG_LEVEL}' value for WAHA_LOG_LEVEL`,
);
return 'info';
}
return logLevel as Level;
}
export function getPinoTransport() {
const logFormat = (process.env.WAHA_LOG_FORMAT || 'PRETTY').toUpperCase();
if (logFormat == 'JSON') {
return undefined;
}
return {
target: 'pino-pretty',
options: {
singleLine: true,
colorize: true,
messageFormat: '{if session} session:{session} - {end}{msg}',
},
};
}
export { getNestJSLogLevels };
+1 -1
View File
@@ -33,7 +33,7 @@ export function getEngineName(): string {
}
export const VERSION: WAHAEnvironment = {
version: '2024.7.1',
version: '2024.7.5',
engine: getEngineName(),
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
+201 -13
View File
@@ -5,9 +5,9 @@ __metadata:
version: 6
cacheKey: 8
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master":
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels":
version: 6.7.5
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=f46437cefa0e28d1b31add54daf4f257c57e5a42"
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=66fde3f3e5df76b52c6a01b75c301d63e2b6a75f"
dependencies:
"@adiwajshing/keyed-db": ^0.2.4
"@hapi/boom": ^9.1.3
@@ -39,7 +39,7 @@ __metadata:
optional: true
sharp:
optional: true
checksum: be470d6332aa0478705aa0514596daf2609d11ebd64bdfa895dc052be384a5e5ce2e474c8974682961fafffdf99533e7387c7e030c2b7c85968af81a42cc810c
checksum: b4f3bc47c559641800e220bddfd06f86848c74dd0c5ea85aba4d1033606a3f90a2d2e488df33da191de75be7357b10df041b2b6d76ee5b7bda1bd37d2586b45c
languageName: node
linkType: hard
@@ -4215,6 +4215,13 @@ __metadata:
languageName: node
linkType: hard
"colorette@npm:^2.0.7":
version: 2.0.20
resolution: "colorette@npm:2.0.20"
checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d
languageName: node
linkType: hard
"colorspace@npm:1.1.x":
version: 1.1.4
resolution: "colorspace@npm:1.1.4"
@@ -4641,6 +4648,13 @@ __metadata:
languageName: node
linkType: hard
"dateformat@npm:^4.6.3":
version: 4.6.3
resolution: "dateformat@npm:4.6.3"
checksum: c3aa0617c0a5b30595122bc8d1bee6276a9221e4d392087b41cbbdf175d9662ae0e50d0d6dcdf45caeac5153c4b5b0844265f8cd2b2245451e3da19e39e3b65d
languageName: node
linkType: hard
"debug@npm:2.6.9, debug@npm:^2.6.9":
version: 2.6.9
resolution: "debug@npm:2.6.9"
@@ -5621,7 +5635,7 @@ __metadata:
languageName: node
linkType: hard
"events@npm:^3.2.0":
"events@npm:^3.2.0, events@npm:^3.3.0":
version: 3.3.0
resolution: "events@npm:3.3.0"
checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780
@@ -5802,6 +5816,13 @@ __metadata:
languageName: node
linkType: hard
"fast-copy@npm:^3.0.2":
version: 3.0.2
resolution: "fast-copy@npm:3.0.2"
checksum: 47f584bcede08ab3198559d3e0e093a547d567715b86be2198da6e3366c3c73eed550d97b86f9fb90dae179982b89c15d68187def960f522cdce14bacdfc6184
languageName: node
linkType: hard
"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
version: 3.1.3
resolution: "fast-deep-equal@npm:3.1.3"
@@ -5843,14 +5864,14 @@ __metadata:
languageName: node
linkType: hard
"fast-redact@npm:^3.0.0":
"fast-redact@npm:^3.0.0, fast-redact@npm:^3.1.1":
version: 3.5.0
resolution: "fast-redact@npm:3.5.0"
checksum: ef03f0d1849da074a520a531ad299bf346417b790a643931ab4e01cb72275c8d55b60dc8512fb1f1818647b696790edefaa96704228db9f012da935faa1940af
languageName: node
linkType: hard
"fast-safe-stringify@npm:2.1.1":
"fast-safe-stringify@npm:2.1.1, fast-safe-stringify@npm:^2.1.1":
version: 2.1.1
resolution: "fast-safe-stringify@npm:2.1.1"
checksum: a851cbddc451745662f8f00ddb622d6766f9bd97642dabfd9a405fb0d646d69fc0b9a1243cbf67f5f18a39f40f6fa821737651ff1bceeba06c9992ca2dc5bd3d
@@ -6695,6 +6716,13 @@ __metadata:
languageName: node
linkType: hard
"help-me@npm:^5.0.0":
version: 5.0.0
resolution: "help-me@npm:5.0.0"
checksum: 474436627b6c7d2f406a2768453895889eb2712c8ded4c47658d5c6dd46c2ff3f742be4e4e8dedd57b7f1ac6b28803896a2e026a32a977f507222c16f23ab2e1
languageName: node
linkType: hard
"homedir-polyfill@npm:^1.0.1":
version: 1.0.3
resolution: "homedir-polyfill@npm:1.0.3"
@@ -7965,6 +7993,13 @@ __metadata:
languageName: node
linkType: hard
"joycon@npm:^3.1.1":
version: 3.1.1
resolution: "joycon@npm:3.1.1"
checksum: 8003c9c3fc79c5c7602b1c7e9f7a2df2e9916f046b0dbad862aa589be78c15734d11beb9fe846f5e06138df22cb2ad29961b6a986ba81c4920ce2b15a7f11067
languageName: node
linkType: hard
"js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
@@ -9151,6 +9186,16 @@ __metadata:
languageName: node
linkType: hard
"nestjs-pino@npm:^4.1.0":
version: 4.1.0
resolution: "nestjs-pino@npm:4.1.0"
peerDependencies:
"@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0
pino-http: ^6.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
checksum: 83e4bee5934447edfb60ec4eeed5fb5529f0e4a12a4498920ff7de6277152ceb0b37127637f447e60faaaf8c2ef9e3a2f9f6f51c1e936e50246e485d36972639
languageName: node
linkType: hard
"netmask@npm:^2.0.2":
version: 2.0.2
resolution: "netmask@npm:2.0.2"
@@ -9419,6 +9464,13 @@ __metadata:
languageName: node
linkType: hard
"on-exit-leak-free@npm:^2.1.0":
version: 2.1.2
resolution: "on-exit-leak-free@npm:2.1.2"
checksum: 6ce7acdc7b9ceb51cf029b5239cbf41937ee4c8dcd9d4e475e1777b41702564d46caa1150a744e00da0ac6d923ab83471646a39a4470f97481cf6e2d8d253c3f
languageName: node
linkType: hard
"on-finished@npm:2.4.1":
version: 2.4.1
resolution: "on-finished@npm:2.4.1"
@@ -9814,6 +9866,16 @@ __metadata:
languageName: node
linkType: hard
"pino-abstract-transport@npm:^1.0.0, pino-abstract-transport@npm:^1.2.0":
version: 1.2.0
resolution: "pino-abstract-transport@npm:1.2.0"
dependencies:
readable-stream: ^4.0.0
split2: ^4.0.0
checksum: 3336c51fb91ced5ef8a4bfd70a96e41eb6deb905698e83350dc71eedffb34795db1286d2d992ce1da2f6cd330a68be3f7e2748775a6b8a2ee3416796070238d6
languageName: node
linkType: hard
"pino-abstract-transport@npm:v0.5.0":
version: 0.5.0
resolution: "pino-abstract-transport@npm:0.5.0"
@@ -9824,6 +9886,42 @@ __metadata:
languageName: node
linkType: hard
"pino-http@npm:^10.2.0":
version: 10.2.0
resolution: "pino-http@npm:10.2.0"
dependencies:
get-caller-file: ^2.0.5
pino: ^9.0.0
pino-std-serializers: ^7.0.0
process-warning: ^3.0.0
checksum: 1d7cc08409872f5dbb8405b92ce3252c82ff37edbb1988d65d2aab049df410203040f8bd236853103b27f88f48c9167e6a55fc45d88bf96952cbcb901359fb1f
languageName: node
linkType: hard
"pino-pretty@npm:^11.2.1":
version: 11.2.1
resolution: "pino-pretty@npm:11.2.1"
dependencies:
colorette: ^2.0.7
dateformat: ^4.6.3
fast-copy: ^3.0.2
fast-safe-stringify: ^2.1.1
help-me: ^5.0.0
joycon: ^3.1.1
minimist: ^1.2.6
on-exit-leak-free: ^2.1.0
pino-abstract-transport: ^1.0.0
pump: ^3.0.0
readable-stream: ^4.0.0
secure-json-parse: ^2.4.0
sonic-boom: ^4.0.1
strip-json-comments: ^3.1.1
bin:
pino-pretty: bin.js
checksum: ae9754484809d4485733a9badca339bad1f9e07d106549c4977e2557bf9183d775496866c786aee701d260abadf16f0bf201084943ebe16fa93b9ad80ba3a410
languageName: node
linkType: hard
"pino-std-serializers@npm:^4.0.0":
version: 4.0.0
resolution: "pino-std-serializers@npm:4.0.0"
@@ -9831,6 +9929,13 @@ __metadata:
languageName: node
linkType: hard
"pino-std-serializers@npm:^7.0.0":
version: 7.0.0
resolution: "pino-std-serializers@npm:7.0.0"
checksum: 08cd1d7b7adc4cfca39e42c2d5fd21bcf4513153734e7b8fa278b0e9e9f62df78c4c202886343fe882a462539c931cb8110b661775ad7f7217c96856795b5a86
languageName: node
linkType: hard
"pino@npm:^7.0.0":
version: 7.11.0
resolution: "pino@npm:7.11.0"
@@ -9852,6 +9957,27 @@ __metadata:
languageName: node
linkType: hard
"pino@npm:^9.0.0":
version: 9.2.0
resolution: "pino@npm:9.2.0"
dependencies:
atomic-sleep: ^1.0.0
fast-redact: ^3.1.1
on-exit-leak-free: ^2.1.0
pino-abstract-transport: ^1.2.0
pino-std-serializers: ^7.0.0
process-warning: ^3.0.0
quick-format-unescaped: ^4.0.3
real-require: ^0.2.0
safe-stable-stringify: ^2.3.1
sonic-boom: ^4.0.1
thread-stream: ^3.0.0
bin:
pino: bin.js
checksum: d702a3f6ff031afc4b165fc3b514e35bd1864d691f27789225a165cd5d344acf08f21ceee4b7d55d522ede57ac9bcb661ce161d8e1510acbc1bfd32b0f56fe39
languageName: node
linkType: hard
"pirates@npm:^4.0.4":
version: 4.0.6
resolution: "pirates@npm:4.0.6"
@@ -9992,6 +10118,20 @@ __metadata:
languageName: node
linkType: hard
"process-warning@npm:^3.0.0":
version: 3.0.0
resolution: "process-warning@npm:3.0.0"
checksum: 1fc2eb4524041de3c18423334cc8b4e36bec5ad5472640ca1a936122c6e01da0864c1a4025858ef89aea93eabe7e77db93ccea225b10858617821cb6a8719efe
languageName: node
linkType: hard
"process@npm:^0.11.10":
version: 0.11.10
resolution: "process@npm:0.11.10"
checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3
languageName: node
linkType: hard
"progress@npm:2.0.3, progress@npm:^2.0.0":
version: 2.0.3
resolution: "progress@npm:2.0.3"
@@ -10523,6 +10663,19 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:^4.0.0":
version: 4.5.2
resolution: "readable-stream@npm:4.5.2"
dependencies:
abort-controller: ^3.0.0
buffer: ^6.0.3
events: ^3.3.0
process: ^0.11.10
string_decoder: ^1.3.0
checksum: c4030ccff010b83e4f33289c535f7830190773e274b3fcb6e2541475070bdfd69c98001c3b0cb78763fc00c8b62f514d96c2b10a8bd35d5ce45203a25fa1d33a
languageName: node
linkType: hard
"readable-web-to-node-stream@npm:^3.0.0":
version: 3.0.2
resolution: "readable-web-to-node-stream@npm:3.0.2"
@@ -10557,6 +10710,13 @@ __metadata:
languageName: node
linkType: hard
"real-require@npm:^0.2.0":
version: 0.2.0
resolution: "real-require@npm:0.2.0"
checksum: fa060f19f2f447adf678d1376928c76379dce5f72bd334da301685ca6cdcb7b11356813332cc243c88470796bc2e2b1e2917fc10df9143dd93c2ea608694971d
languageName: node
linkType: hard
"rechoir@npm:^0.6.2":
version: 0.6.2
resolution: "rechoir@npm:0.6.2"
@@ -10930,6 +11090,13 @@ __metadata:
languageName: node
linkType: hard
"secure-json-parse@npm:^2.4.0":
version: 2.7.0
resolution: "secure-json-parse@npm:2.7.0"
checksum: d9d7d5a01fc6db6115744ba23cf9e67ecfe8c524d771537c062ee05ad5c11b64c730bc58c7f33f60bd6877f96b86f0ceb9ea29644e4040cb757f6912d4dd6737
languageName: node
linkType: hard
"semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.0, semver@npm:^6.3.1":
version: 6.3.1
resolution: "semver@npm:6.3.1"
@@ -11308,6 +11475,15 @@ __metadata:
languageName: node
linkType: hard
"sonic-boom@npm:^4.0.1":
version: 4.0.1
resolution: "sonic-boom@npm:4.0.1"
dependencies:
atomic-sleep: ^1.0.0
checksum: 451b0f09bc0a0abfa6bfed0e2d7d36a6fa245be8a444a7ef1e3c8abb006e9994cb7530b1da39c8aee9033598d1ce187e244a6194c92a81790a2e2633c60cd63d
languageName: node
linkType: hard
"source-map-support@npm:0.5.13":
version: 0.5.13
resolution: "source-map-support@npm:0.5.13"
@@ -11551,7 +11727,7 @@ __metadata:
languageName: node
linkType: hard
"string_decoder@npm:^1.1.1":
"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0":
version: 1.3.0
resolution: "string_decoder@npm:1.3.0"
dependencies:
@@ -11908,6 +12084,15 @@ __metadata:
languageName: node
linkType: hard
"thread-stream@npm:^3.0.0":
version: 3.1.0
resolution: "thread-stream@npm:3.1.0"
dependencies:
real-require: ^0.2.0
checksum: 3c5b494ce776f832dfd696792cc865f78c1e850db93e07979349bbc1a5845857cd447aea95808892906cc0178a2fd3233907329f3376e7fc9951e2833f5b7896
languageName: node
linkType: hard
"through@npm:^2.3.6, through@npm:^2.3.8":
version: 2.3.8
resolution: "through@npm:2.3.8"
@@ -12676,7 +12861,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "waha@workspace:."
dependencies:
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master"
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-channels"
"@adiwajshing/keyed-db": ^0.2.4
"@nestjs/axios": ^3.0.2
"@nestjs/cli": ^9.0.0
@@ -12722,8 +12907,11 @@ __metadata:
lodash: ^4.17.21
mime-types: ^2.1.27
mongodb: ^6.3.0
nestjs-pino: ^4.1.0
passport: ^0.6.0
passport-headerapikey: ^1.2.2
pino-http: ^10.2.0
pino-pretty: ^11.2.1
prettier: ^1.19.1
puppeteer: ^21.0.0
qrcode: ^1.5.1
@@ -12742,7 +12930,7 @@ __metadata:
tsconfig-paths: ^4.1.0
typescript: ^4.8.4
venom-bot: 5.0.1
whatsapp-web.js: "https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus"
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels"
languageName: unknown
linkType: soft
@@ -12839,9 +13027,9 @@ __metadata:
languageName: node
linkType: hard
"whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js#fork-webpack-exodus":
version: 1.23.1-alpha.5
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=ec4f08fd0d47d5ec6637ec8d6377c0f3b425da9b"
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-webpack-exodus-channels":
version: 1.24.0
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=035245d3216ff98f231468d9be87253600194d42"
dependencies:
"@pedroslopez/moduleraid": ^5.0.2
adm-zip: ^0.5.10
@@ -12860,7 +13048,7 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: 4ffb53ccd3cb98efc852f885b2a5d82da6a9cfe1b75b865bc45524d70c3c75cb6d54250173d40fe5ddbbe22979881ec6d6469a0a5c28286507aac1c1fdef4c5e
checksum: 32048fdf11284ca516e2a09801f74ba1e5676cbdfbe4ac7aee7b167e26173aa250cd2593288573e490b474b3c5ebdc20933bd57b3b0b42c1c9299d9fda563923
languageName: node
linkType: hard