diff --git a/.env.example b/.env.example index 73a3b40d35..acba680779 100644 --- a/.env.example +++ b/.env.example @@ -45,6 +45,13 @@ LOG_BAILEYS=error # Set the maximum number of listeners that can be registered for an event EVENT_EMITTER_MAX_LISTENERS=50 +# Keep-alive - Periodically ping URLs to prevent PaaS services (Render, etc.) from sleeping. +# KEEP_ALIVE_URLS accepts a comma-separated list of external URLs to ping (e.g. your BotWave URL). +# SERVER_URL is always pinged automatically when keep-alive is enabled. +KEEP_ALIVE_ENABLED=false +KEEP_ALIVE_INTERVAL=10 +KEEP_ALIVE_URLS= + # Determine how long the instance should be deleted from memory in case of no connection. # Default time: 5 minutes # If you don't even want an expiration, enter the value false diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 7c4e382e7e..83b7952b49 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -389,6 +389,12 @@ export type EventEmitter = { MAX_LISTENERS: number; }; +export type KeepAlive = { + ENABLED: boolean; + INTERVAL: number; + URLS: string[]; +}; + export type Production = boolean; export interface Env { @@ -428,6 +434,7 @@ export interface Env { FACEBOOK: Facebook; SENTRY: Sentry; EVENT_EMITTER: EventEmitter; + KEEP_ALIVE: KeepAlive; PRODUCTION?: Production; } @@ -903,6 +910,14 @@ export class ConfigService { EVENT_EMITTER: { MAX_LISTENERS: Number.parseInt(process.env?.EVENT_EMITTER_MAX_LISTENERS) || 50, }, + KEEP_ALIVE: { + ENABLED: process.env?.KEEP_ALIVE_ENABLED === 'true', + INTERVAL: Number.parseInt(process.env?.KEEP_ALIVE_INTERVAL) || 10, + URLS: + process.env?.KEEP_ALIVE_URLS?.split(',') + .map((url) => url.trim()) + .filter(Boolean) || [], + }, }; } } diff --git a/src/main.ts b/src/main.ts index f1f00ba9ae..c805f58094 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,7 @@ import { configService, Cors, HttpServer, + KeepAlive, ProviderSession, Sentry as SentryConfig, Webhook, @@ -159,6 +160,29 @@ async function bootstrap() { server.listen(httpServer.PORT, () => logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT)); + const keepAlive = configService.get('KEEP_ALIVE'); + if (keepAlive.ENABLED) { + const intervalMs = keepAlive.INTERVAL * 60 * 1000; + const urls = [...keepAlive.URLS]; + + if (httpServer.URL) { + urls.unshift(httpServer.URL); + } + + if (urls.length > 0) { + logger.info(`Keep-alive pinging ${urls.length} URL(s) every ${keepAlive.INTERVAL}m`); + + setInterval(() => { + for (const url of urls) { + axios + .get(url, { timeout: 15000 }) + .then(() => logger.verbose(`Keep-alive OK: ${url}`)) + .catch((err) => logger.warn(`Keep-alive failed: ${url} - ${err.message}`)); + } + }, intervalMs); + } + } + initWA().catch((error) => { logger.error('Error loading instances: ' + error); });