Compare commits

...
19 Commits
Author SHA1 Message Date
devlikepro 92d3e70c1f [core] 2024.8.3
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-08-14 15:49:36 +07:00
devlikepro cb5dfaf066 [core] Remove NEW from dashboard 2024-08-14 15:49:36 +07:00
devlikepro cd75e5e4cc [core] auto reconnection fix - reduce to 28 minutes and check if websocket is not connecting
fixes #483
2024-08-14 15:49:36 +07:00
devlikepro 7bd6ab5ec0 [core] 2024.8.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-08-07 17:29:11 +07:00
devlikepro 92f9c89cf1 [core] add WAHA_HTTP_LOG_LEVEL
fixed #466
2024-08-07 17:29:11 +07:00
devlikepro eb2632b23a [core] Fix noweb autorestart timing 2024-08-07 17:29:10 +07:00
devlikepro 8c90bc6808 [core] logger warning 2024-08-07 17:29:10 +07:00
devlikepro 66146532e1 [core] fix WEBJS constant restart 2024-08-07 17:29:10 +07:00
devlikepro dbf3a41c1d [core] Bump WEBJS 2024-08-07 17:29:10 +07:00
devlikepro 942bbf9c6c [core] Add sleep 2024-08-07 17:29:09 +07:00
devlikepro a9d7cc527c [core] Use SingleDelayedJobRunner in WEBJS 2024-08-07 17:29:09 +07:00
devlikepro 2d362722ed [core] Use SingleDelayedJobRunner 2024-08-07 17:29:09 +07:00
devlikepro f81638e625 [core] Use SinglePeriodicJobRunner to rerun session 2024-08-07 17:29:08 +07:00
devlikepro 8ae77df0ff [core] Add SingleDelayedJobRunner 2024-08-07 17:29:08 +07:00
devlikepro 110bd0e5f7 [core] Add SinglePeriodicJobRunner 2024-08-07 17:29:08 +07:00
devlikepro 1b3a366cb7 [core] Add test.* to ignore 2024-08-07 17:29:07 +07:00
devlikepro 3b5d9a145f [core] Rename WebsocketHeartbeatJob.ts 2024-08-07 17:29:07 +07:00
devlikepro fc37fc067a [core] Install zip and unzip in docker image 2024-08-07 17:29:07 +07:00
devlikepro bb18a28484 [core] Add pretty-bytes 2024-08-07 17:29:06 +07:00
16 changed files with 234 additions and 62 deletions

No files matched your search

+2
View File
@@ -6,6 +6,8 @@ tokens
files
venv
.wwebjs_auth
src/test.ts
src/test.js
test.js
.wwebjs_cache
src/dashboard
+6
View File
@@ -48,6 +48,9 @@ RUN echo "USE_BROWSER=$USE_BROWSER"
# Install ffmpeg to generate previews for videos
RUN apt-get update && apt-get install -y ffmpeg --no-install-recommends && rm -rf /var/lib/apt/lists/*
# Install zip and unzip
RUN apt-get update && apt-get install -y zip unzip --no-install-recommends && rm -rf /var/lib/apt/lists/*
# Install fonts if using either chromium or chrome
RUN if [ "$USE_BROWSER" = "chromium" ] || [ "$USE_BROWSER" = "chrome" ]; then \
apt-get update \
@@ -88,6 +91,9 @@ COPY --from=dashboard /dashboard ./dist/dashboard
ENV CHOKIDAR_USEPOLLING=1
ENV CHOKIDAR_INTERVAL=5000
# WAHA variables
ENV WAHA_ZIPPER=ZIPUNZIP
# Run command, etc
EXPOSE 3000
CMD yarn start:prod
+1
View File
@@ -60,6 +60,7 @@
"passport-headerapikey": "^1.2.2",
"pino-http": "^10.2.0",
"pino-pretty": "^11.2.1",
"pretty-bytes": "5.6.0",
"puppeteer": "^21.0.0",
"qrcode": "^1.5.1",
"qrcode-terminal": "^0.12.0",
+2 -2
View File
@@ -29,8 +29,8 @@ export class SwaggerConfiguratorCore {
'<li><a href="https://github.com/devlikeapro/waha" target="_blank">GitHub - WAHA Core</a></li>' +
'<li><a href="https://github.com/devlikeapro/waha-plu' + // Separate line to pass pre-commit check
's" target="_blank">GitHub - WAHA Plus</a></li>' +
'<li><b>NEW!</b> - <a href="/dashboard">WAHA Dashboard</a></li>' +
'<li><b>NEW!</b> - <a href="https://waha.devlike.pro/docs/how-to/waha-dashboard">WAHA Dashboard Documentation</a></li>' +
'<li><a href="/dashboard">WAHA Dashboard</a></li>' +
'<li><a href="https://waha.devlike.pro/docs/how-to/waha-dashboard">WAHA Dashboard Documentation</a></li>' +
'</ul>' +
'<p>Support the project and get WAHA Plus version!</p>' +
'<ul>' +
+6 -3
View File
@@ -14,7 +14,7 @@ import {
} from '@nestjs/websockets';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { HeartbeatJob } from '@waha/utils/HeartbeatJob';
import { WebsocketHeartbeatJob } from '@waha/utils/WebsocketHeartbeatJob';
import { WebSocket } from '@waha/utils/ws';
import { IncomingMessage } from 'http';
import * as lodash from 'lodash';
@@ -41,11 +41,14 @@ export class WebsocketGatewayCore
new Map();
private readonly logger: LoggerService;
private heartbeat: HeartbeatJob;
private heartbeat: WebsocketHeartbeatJob;
constructor(private manager: SessionManager) {
this.logger = new Logger('WebsocketGateway');
this.heartbeat = new HeartbeatJob(this.logger, this.HEARTBEAT_INTERVAL);
this.heartbeat = new WebsocketHeartbeatJob(
this.logger,
this.HEARTBEAT_INTERVAL,
);
}
handleConnection(socket: WebSocket, request: IncomingMessage, ...args): any {
+6 -1
View File
@@ -7,7 +7,11 @@ 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 {
getPinoHttpUseLevel,
getPinoLogLevel,
getPinoTransport,
} from '@waha/utils/logging';
import { LoggerModule } from 'nestjs-pino';
import { join } from 'path';
import { Logger } from 'pino';
@@ -43,6 +47,7 @@ export const IMPORTS = [
pinoHttp: {
quietReqLogger: true,
level: getPinoLogLevel(),
useLevel: getPinoHttpUseLevel(),
transport: getPinoTransport(),
autoLogging: {
ignore: (req) => {
+39 -34
View File
@@ -46,6 +46,8 @@ import {
PollVotePayload,
WAMessageAckBody,
} from '@waha/structures/webhooks.dto';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import { SinglePeriodicJobRunner } from '@waha/utils/SinglePeriodicJobRunner';
import * as Buffer from 'buffer';
import { Agent } from 'https';
import * as lodash from 'lodash';
@@ -144,13 +146,13 @@ const ToEnginePresenceStatus = flipObject(PresenceStatuses);
export class WhatsappSessionNoWebCore extends WhatsappSession {
private START_ATTEMPT_DELAY_SECONDS = 2;
private AUTO_RESTART_AFTER_SECONDS = 30 * 60;
private AUTO_RESTART_AFTER_SECONDS = 28 * 60;
engine = WAHAEngine.NOWEB;
authFactory = new NowebAuthFactoryCore();
storageFactory = new NowebStorageFactoryCore();
private startTimeoutId: null | ReturnType<typeof setTimeout> = null;
private autoRestartTimeoutId: null | ReturnType<typeof setTimeout> = null;
private startDelayedJob: SingleDelayedJobRunner;
private autoRestartJob: SinglePeriodicJobRunner;
private msgRetryCounterCache: NodeCache;
protected engineLogger: BaileysLogger;
@@ -175,6 +177,22 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.engineLogger = this.loggerBuilder.child({
name: 'NOWEBEngine',
}) as unknown as BaileysLogger;
// Restart job if session failed
this.startDelayedJob = new SingleDelayedJobRunner(
'start-engine',
this.START_ATTEMPT_DELAY_SECONDS * SECOND,
this.logger,
);
// Enable auto-restart
const shiftSeconds = Math.floor(Math.random() * 30);
const delay = this.AUTO_RESTART_AFTER_SECONDS + shiftSeconds;
this.autoRestartJob = new SinglePeriodicJobRunner(
'auto-restart',
delay * SECOND,
this.logger,
);
}
start() {
@@ -277,20 +295,15 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.enableAutoRestart();
}
protected enableAutoRestart() {
if (this.autoRestartTimeoutId) {
return;
}
// 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.logger.debug(`Auto-restart is enabled, after ${delay} seconds`);
this.autoRestartTimeoutId = setTimeout(() => {
this.autoRestartTimeoutId = null;
private enableAutoRestart() {
this.autoRestartJob.start(async () => {
this.logger.info('Auto-restarting the client connection...');
this.sock?.end(new Error('auto-restart'));
}, delay * SECOND);
if (this.sock?.ws?.isConnecting) {
this.logger.warn('Auto-restart skipped, the client is connecting...');
return;
}
this.sock?.end(undefined);
});
}
protected async getMessage(
@@ -310,19 +323,9 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
private restartClient() {
if (this.startTimeoutId) {
this.logger.info(
'Request to restart is already in progress, ignoring this request',
);
return;
}
this.logger.info(
`Setting up client start in ${this.START_ATTEMPT_DELAY_SECONDS} seconds...`,
);
this.startTimeoutId = setTimeout(() => {
this.startTimeoutId = undefined;
this.buildClient();
}, this.START_ATTEMPT_DELAY_SECONDS * SECOND);
this.startDelayedJob.schedule(async () => {
await this.buildClient();
});
}
protected listenConnectionEvents() {
@@ -344,9 +347,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.qr.save('');
// reconnect if not logged out
if (shouldReconnect) {
this.logger.info(
`Connection closed due to '${lastDisconnect.error}', reconnecting...`,
);
if (lastDisconnect.error) {
this.logger.info(
`Connection closed due to '${lastDisconnect.error}', reconnecting...`,
);
}
this.restartClient();
} else {
this.logger.error(
@@ -433,8 +438,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
private async end() {
clearTimeout(this.autoRestartTimeoutId);
clearTimeout(this.startTimeoutId);
this.autoRestartJob.stop();
this.startDelayedJob.cancel();
// @ts-ignore
this.sock?.ev?.removeAllListeners();
this.sock?.ws?.removeAllListeners();
+20 -16
View File
@@ -55,6 +55,7 @@ import { Label, LabelID } from '@waha/structures/labels.dto';
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
import { MeInfo } from '@waha/structures/sessions.dto';
import { WAMessageRevokedBody } from '@waha/structures/webhooks.dto';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import {
Call,
Channel as WEBJSChannel,
@@ -83,7 +84,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
engine = WAHAEngine.WEBJS;
protected engineConfig?: WebJSConfig;
private startTimeoutId: null | ReturnType<typeof setTimeout> = null;
private startDelayedJob: SingleDelayedJobRunner;
private shouldRestart: boolean;
whatsapp: WebjsClient;
@@ -93,6 +94,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
super(config);
this.qr = new QR();
this.shouldRestart = true;
// Restart job if session failed
this.startDelayedJob = new SingleDelayedJobRunner(
'start-engine',
this.START_ATTEMPT_DELAY_SECONDS * SECOND,
this.logger,
);
}
/**
@@ -132,24 +140,20 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
private restartClient() {
if (!this.shouldRestart) {
this.logger.debug(
"Shouldn't restart the client, ignoring restart request",
'Should not restart the client, ignoring restart request',
);
return;
}
if (this.startTimeoutId) {
const msg =
'Request to restart is already in progress, ignoring restart request';
this.logger.warn(msg);
return;
}
this.logger.info(
`Setting up client start in ${this.START_ATTEMPT_DELAY_SECONDS} seconds...`,
);
this.startTimeoutId = setTimeout(() => {
this.startTimeoutId = undefined;
this.start();
}, this.START_ATTEMPT_DELAY_SECONDS * SECOND);
this.startDelayedJob.schedule(async () => {
if (!this.shouldRestart) {
this.logger.warn(
'Should not restart the client, ignoring restart request',
);
return;
}
await this.start();
});
}
protected addProxyConfig(clientOptions: ClientOptions) {
@@ -229,7 +233,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
private async end() {
try {
this.whatsapp?.removeAllListeners();
clearInterval(this.startTimeoutId);
this.startDelayedJob.cancel();
this.whatsapp?.destroy().catch((error) => {
this.logger.debug('Failed to destroy the client', error);
});
+4 -1
View File
@@ -251,7 +251,10 @@ export class SessionManagerCore extends SessionManager {
try {
engineInfo = await promiseTimeout(10, this.session.getEngineInfo());
} catch (e) {
this.log.warn('Error while getting engine info', e);
this.log.warn(
{ session: this.session },
'Error while getting engine info',
);
}
}
const engine = {
+55
View File
@@ -0,0 +1,55 @@
import { Logger } from 'pino';
type FunctionNoArgs = () => Promise<any>;
/**
* Run a job (function) after a delay,
* but make sure we run exactly ONE job at the same time
*/
export class SingleDelayedJobRunner {
private timeout: NodeJS.Timeout;
private logger: Logger;
constructor(
private name: string,
private timeoutMs: number,
logger: Logger,
private warningOverride: boolean = false,
) {
this.logger = logger.child({
job: name,
class: SingleDelayedJobRunner.name,
});
}
schedule(fn: FunctionNoArgs): boolean {
if (this.timeout) {
const msg = `Job has been started before, do not schedule it again`;
this.log(this.warningOverride, msg);
return false;
}
this.timeout = setTimeout(() => {
this.logger.debug(`Running job...`);
fn().finally(() => {
this.logger.debug(`Job finished`);
this.timeout = null;
});
}, this.timeoutMs);
this.logger.info(`Job scheduled with timeout ${this.timeoutMs} ms`);
return true;
}
cancel() {
clearTimeout(this.timeout);
this.logger.info(`Job cancelled`);
}
private log(warning: boolean, msg: string) {
if (warning) {
this.logger.warn(msg);
} else {
this.logger.info(msg);
}
}
}
+64
View File
@@ -0,0 +1,64 @@
import { Logger } from 'pino';
type FunctionNoArgs = () => Promise<any>;
/**
* Run a job (function) in some interval,
* but make sure we run exactly ONE job at the same time
*/
export class SinglePeriodicJobRunner {
private interval: NodeJS.Timeout;
private isWorking: boolean = false;
private logger: Logger;
constructor(
private name: string,
private intervalMs: number,
logger: Logger,
private warningOverlap: boolean = true,
private warningOverride: boolean = false,
) {
this.logger = logger.child({
job: name,
class: SinglePeriodicJobRunner.name,
});
}
start(fn: FunctionNoArgs): boolean {
if (this.interval) {
const msg = `Job has been started before, do not schedule it again`;
this.log(this.warningOverride, msg);
return false;
}
this.interval = setInterval(() => {
if (this.isWorking) {
const msg = `Job is already running, skipping this run`;
this.log(this.warningOverlap, msg);
return;
}
this.isWorking = true;
this.logger.debug('Running job...');
fn().finally(() => {
this.isWorking = false;
this.logger.debug(`Job finished`);
});
}, this.intervalMs);
this.logger.info(`Job started with interval ${this.intervalMs} ms`);
return true;
}
stop() {
clearInterval(this.interval);
this.logger.info(`Job stopped`);
}
private log(warning: boolean, msg) {
if (warning) {
this.logger.warn(msg);
} else {
this.logger.info(msg);
}
}
}
@@ -2,7 +2,7 @@ import { LoggerService } from '@nestjs/common';
import { WebSocket } from '@waha/utils/ws';
import { WebSocketServer } from 'ws';
export class HeartbeatJob {
export class WebsocketHeartbeatJob {
private interval: ReturnType<typeof setInterval>;
constructor(
+13 -1
View File
@@ -1,5 +1,5 @@
import { LogLevel } from '@nestjs/common';
import { ChildLoggerOptions, Level, Logger } from 'pino';
import { ChildLoggerOptions, Level, LevelWithSilent, Logger } from 'pino';
export interface LoggerBuilder {
child(bindings: Record<string, any>, options?: ChildLoggerOptions): Logger;
@@ -31,6 +31,18 @@ export function getPinoLogLevel(debug: boolean = false): Level {
return getDefaultPinoLogLevel();
}
export function getPinoHttpUseLevel(): LevelWithSilent {
const levels = ['fatal', 'error', 'warn', 'info', 'debug', 'trace'];
const level = (process.env.WAHA_HTTP_LOG_LEVEL || 'info').toLowerCase();
if (!levels.includes(level)) {
console.error(
`Unknown ${process.env.WAHA_HTTP_LOG_LEVEL}' value for WAHA_HTTP_LOG_LEVEL`,
);
return 'info';
}
return level as LevelWithSilent;
}
export function getDefaultPinoLogLevel(): Level {
const logLevel = (process.env.WAHA_LOG_LEVEL || 'info').toLowerCase();
const levels = ['fatal', 'error', 'warn', 'info', 'debug', 'trace'];
+4
View File
@@ -32,3 +32,7 @@ export const promiseTimeout = function (
),
]).finally(() => clearTimeout(timer));
};
export async function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
+1 -1
View File
@@ -33,7 +33,7 @@ export function getEngineName(): string {
}
export const VERSION: WAHAEnvironment = {
version: '2024.8.1',
version: '2024.8.3',
engine: getEngineName(),
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
+10 -2
View File
@@ -10074,6 +10074,13 @@ __metadata:
languageName: node
linkType: hard
"pretty-bytes@npm:5.6.0":
version: 5.6.0
resolution: "pretty-bytes@npm:5.6.0"
checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd
languageName: node
linkType: hard
"pretty-format@npm:^25.2.1, pretty-format@npm:^25.5.0":
version: 25.5.0
resolution: "pretty-format@npm:25.5.0"
@@ -12921,6 +12928,7 @@ __metadata:
pino-http: ^10.2.0
pino-pretty: ^11.2.1
prettier: ^1.19.1
pretty-bytes: 5.6.0
puppeteer: ^21.0.0
qrcode: ^1.5.1
qrcode-terminal: ^0.12.0
@@ -13037,7 +13045,7 @@ __metadata:
"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=ec31dbb7207599b602169c70923f114dbc7c526e"
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=f28dad2d12136192e9ee45370d768323196c10c0"
dependencies:
"@pedroslopez/moduleraid": ^5.0.2
adm-zip: ^0.5.10
@@ -13056,7 +13064,7 @@ __metadata:
optional: true
unzipper:
optional: true
checksum: 65dc78c2e057ef5c366f7e4d2d9940017e5a37eb740c70aaabb1bcf2fc54b8886d41a925cfad87757922c7966bcca328c483438934725958a92e4519e11b6197
checksum: 0bd28b1f332aafaa6f9d467de04c33da99c139208d03bef78c5323c0e3ada6762f31e156d63cc2f6a46bb90e298ad08ddd2db25e13fb045fc2d1a2e69595758b
languageName: node
linkType: hard