@manybot/manybot 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * pluginLoader.js
3
+ *
4
+ * Responsible for:
5
+ * 1. Reading active plugins (config.js imports this module and give the list)
6
+ * 2. Loading each plugin from ~/.manybot/plugins folder
7
+ * 3. Registering in pluginRegistry with status and public exports
8
+ * 4. Exposing pluginRegistry to kernel and pluginApi
9
+ *
10
+ */
11
+
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import { logger } from "#logger";
15
+ import { t } from "#i18n";
16
+ import { pathToFileURL } from "url";
17
+ import { PATHS } from "#config";
18
+
19
+ const PLUGINS_DIR = path.join(PATHS.HOME, "plugins");
20
+
21
+ /**
22
+ * Each entry in registry:
23
+ * {
24
+ * name: string,
25
+ * status: "active" | "disabled" | "error",
26
+ * run: async function({ msg, chat, api }) — plugin default function
27
+ * exports: any — what plugin exposed via `export const api = { ... }`
28
+ * error: Error | null
29
+ * }
30
+ *
31
+ * @type {Map<string, object>}
32
+ */
33
+ export const pluginRegistry = new Map();
34
+
35
+ /**
36
+ * Load all active plugins listed in `activePlugins`.
37
+ * Called once during bot initialization.
38
+ *
39
+ * @param {string[]} activePlugins — active plugin names (from .conf)
40
+ */
41
+ export async function loadPlugins(activePlugins) {
42
+ if (!fs.existsSync(PLUGINS_DIR)) {
43
+ fs.mkdirSync(PLUGINS_DIR, { recursive: true });
44
+ }
45
+
46
+ for (const name of activePlugins) {
47
+ await loadPlugin(name);
48
+ }
49
+
50
+ const total = pluginRegistry.size;
51
+ const ativos = [...pluginRegistry.values()].filter(p => p.status === "active").length;
52
+ const erros = total - ativos;
53
+
54
+ logger.success(t("system.pluginsLoaded", {
55
+ count: ativos,
56
+ errors: erros ? t("system.pluginsLoadedWithErrors", { count: erros }) : ""
57
+ }));
58
+ }
59
+
60
+ /**
61
+ * Call setup(api) on all plugins that export it.
62
+ * Executed once after bot connects to WhatsApp.
63
+ *
64
+ * @param {object} api — api without message context (only sendTo, log, schedule...)
65
+ */
66
+ export async function setupPlugins(api) {
67
+ for (const plugin of pluginRegistry.values()) {
68
+ if (plugin.status !== "active" || !plugin.setup) continue;
69
+ try {
70
+ await plugin.setup(api);
71
+ } catch (err) {
72
+ logger.error(t("system.pluginSetupFailed", { name: plugin.name, message: err.message }));
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Carrega um único plugin pelo nome.
79
+ * @param {string} name
80
+ */
81
+ async function loadPlugin(name) {
82
+ const pluginPath = path.join(PLUGINS_DIR, name, "index.js");
83
+
84
+ if (!fs.existsSync(pluginPath)) {
85
+ logger.warn(t("system.pluginNotFound", { name, path: pluginPath }));
86
+ pluginRegistry.set(name, { name, status: "disabled", run: null, exports: null, error: null });
87
+ return;
88
+ }
89
+
90
+ try {
91
+ const mod = await import(pathToFileURL(pluginPath).href);
92
+
93
+ // Plugin must export a default function — this is called on every message
94
+ if (typeof mod.default !== "function") {
95
+ throw new Error(`Plugin "${name}" does not export a default function`);
96
+ }
97
+
98
+ pluginRegistry.set(name, {
99
+ name,
100
+ status: "active",
101
+ run: mod.default,
102
+ setup: mod.setup ?? null, // opcional — chamado uma vez na inicialização
103
+ exports: mod.api ?? null,
104
+ error: null,
105
+ });
106
+
107
+ logger.info(t("system.pluginLoaded", { name }));
108
+ } catch (err) {
109
+ logger.error(t("system.pluginLoadFailed", { name, message: err.message }));
110
+ pluginRegistry.set(name, { name, status: "error", run: null, exports: null, error: err });
111
+ }
112
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * pluginState.js
3
+ *
4
+ * Tracks plugin execution state per chat.
5
+ * Used to implement the service vs non-service behavior:
6
+ * - Services (service: true) can run regardless of state
7
+ * - Non-services are blocked when another plugin is running in the same chat
8
+ */
9
+
10
+ import { logger } from "#logger";
11
+
12
+ /**
13
+ * Map<chatId, { pluginName: string, startedAt: Date }>
14
+ * Tracks which plugin is currently "holding the lock" in each chat
15
+ */
16
+ const runningPlugins = new Map();
17
+
18
+ /**
19
+ * Check if any plugin is currently running in a specific chat
20
+ * @param {string} chatId - Chat ID (serialized)
21
+ * @returns {boolean}
22
+ */
23
+ export function isPluginRunning(chatId) {
24
+ return runningPlugins.has(chatId);
25
+ }
26
+
27
+ /**
28
+ * Get info about the plugin running in a chat
29
+ * @param {string} chatId - Chat ID (serialized)
30
+ * @returns {{ pluginName: string, startedAt: Date } | null}
31
+ */
32
+ export function getRunningPlugin(chatId) {
33
+ return runningPlugins.get(chatId) ?? null;
34
+ }
35
+
36
+ /**
37
+ * Mark a plugin as running in a chat
38
+ * @param {string} chatId - Chat ID (serialized)
39
+ * @param {string} pluginName - Name of the plugin taking the lock
40
+ */
41
+ export function startPluginRun(chatId, pluginName) {
42
+ runningPlugins.set(chatId, {
43
+ pluginName,
44
+ startedAt: new Date()
45
+ });
46
+ logger.debug(`Plugin "${pluginName}" started in chat ${chatId}`);
47
+ }
48
+
49
+ /**
50
+ * Mark a plugin as finished in a chat
51
+ * @param {string} chatId - Chat ID (serialized)
52
+ * @param {string} pluginName - Name of the plugin releasing the lock
53
+ */
54
+ export function endPluginRun(chatId, pluginName) {
55
+ const current = runningPlugins.get(chatId);
56
+ if (current && current.pluginName === pluginName) {
57
+ runningPlugins.delete(chatId);
58
+ logger.debug(`Plugin "${pluginName}" ended in chat ${chatId}`);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Force clear the running state for a chat
64
+ * Useful for cleanup or admin commands
65
+ * @param {string} chatId - Chat ID (serialized)
66
+ */
67
+ export function clearPluginRun(chatId) {
68
+ runningPlugins.delete(chatId);
69
+ }
70
+
71
+ /**
72
+ * Get all chats where a specific plugin is running
73
+ * @param {string} pluginName - Plugin name
74
+ * @returns {string[]} Array of chat IDs
75
+ */
76
+ export function getChatsWithPlugin(pluginName) {
77
+ const chats = [];
78
+ for (const [chatId, info] of runningPlugins.entries()) {
79
+ if (info.pluginName === pluginName) {
80
+ chats.push(chatId);
81
+ }
82
+ }
83
+ return chats;
84
+ }
85
+
86
+ /**
87
+ * Get stats about running plugins
88
+ * @returns {{ total: number, byPlugin: Record<string, number> }}
89
+ */
90
+ export function getStats() {
91
+ const byPlugin = {};
92
+ for (const info of runningPlugins.values()) {
93
+ byPlugin[info.pluginName] = (byPlugin[info.pluginName] || 0) + 1;
94
+ }
95
+ return {
96
+ total: runningPlugins.size,
97
+ byPlugin
98
+ };
99
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * scheduler.js
3
+ *
4
+ * Allows plugins to register scheduled tasks via cron.
5
+ * Uses node-cron underneath, but plugins never import node-cron directly —
6
+ * they only call api.schedule(cron, fn).
7
+ *
8
+ * Usage in plugin:
9
+ * import { schedule } from "many";
10
+ * schedule("0 9 * * 1", async () => { await api.send("Good morning!"); });
11
+ */
12
+
13
+ import cron from "node-cron";
14
+ import { logger } from "#logger";
15
+ import { t } from "#i18n";
16
+
17
+ /** List of active tasks (for eventual teardown) */
18
+ const tasks = [];
19
+
20
+ /**
21
+ * Register a cron task.
22
+ * @param {string} expression — cron expression e.g., "0 9 * * 1"
23
+ * @param {Function} fn — async function to execute
24
+ * @param {string} pluginName — plugin name (for logging)
25
+ */
26
+ export function schedule(expression, fn, pluginName = "unknown") {
27
+ if (!cron.validate(expression)) {
28
+ logger.warn(t("system.schedulerInvalidCron", { name: pluginName, expression }));
29
+ return;
30
+ }
31
+
32
+ const task = cron.schedule(expression, async () => {
33
+ try {
34
+ await fn();
35
+ } catch (err) {
36
+ logger.error(t("system.schedulerError", { name: pluginName, message: err.message }));
37
+ }
38
+ });
39
+
40
+ tasks.push({ pluginName, expression, task });
41
+ logger.info(t("system.schedulerRegistered", { name: pluginName, expression }));
42
+ }
43
+
44
+ /** Stop all schedules (useful for shutdown) */
45
+ export function stopAll() {
46
+ tasks.forEach(({ task }) => task.stop());
47
+ tasks.length = 0;
48
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "bot": {
3
+ "starting": "Starting ManyBot...",
4
+ "initialized": "Client initialized. Waiting for WhatsApp connection...",
5
+ "ready": "Bot is ready!",
6
+ "error": {
7
+ "uncaught": "Uncaught exception",
8
+ "unhandled": "Unhandled rejection"
9
+ },
10
+ "signal": {
11
+ "sigterm": "Process interrupted. Shutting down..."
12
+ }
13
+ },
14
+ "log": {
15
+ "info": "INFO",
16
+ "success": "OK",
17
+ "warn": "WARN",
18
+ "error": "ERROR",
19
+ "msg": "MSG",
20
+ "cmd": "CMD",
21
+ "done": "DONE",
22
+ "context": {
23
+ "group": "group",
24
+ "from": "From",
25
+ "type": "Type",
26
+ "replyTo": "replies to"
27
+ }
28
+ },
29
+ "system": {
30
+ "environment": "Environment: {{platform}} — using {{puppeteer}}",
31
+ "environmentTermux": "Environment: Termux — using system Chromium",
32
+ "connected": "WhatsApp connected and ready!",
33
+ "disconnected": "Disconnected — reason: {{reason}}",
34
+ "reconnecting": "Reconnecting in {{seconds}}s...",
35
+ "reinitializing": "Reinitializing client...",
36
+ "qrSaved": "QR Code saved to: {{path}}",
37
+ "qrOpen": "Open with: termux-open qr.png",
38
+ "qrSaveFailed": "Failed to save QR Code:",
39
+ "qrScan": "Scan the QR Code below:",
40
+ "pairingCodeTitle": "Pairing code:",
41
+ "pairingCodeValue": "→ {{code}} ←",
42
+ "pairingCodeInstructions": "Enter this code in WhatsApp: Menu → Linked devices → Link with phone number",
43
+ "phoneNumberInvalid": "Invalid phone number: {{number}}. Expected format: 5511999999999 (country code + area code + number, no spaces or special characters)",
44
+ "phoneNumberChanged": "Phone number changed. Restarting authentication...",
45
+ "clientId": "Client ID: {{id}}",
46
+ "pluginsFolderNotFound": "Plugins folder not found. No plugins loaded.",
47
+ "pluginsLoaded": "Plugins loaded: {{count}} active{{errors}}",
48
+ "pluginsLoadedWithErrors": ", {{count}} with error",
49
+ "pluginSetupFailed": "Plugin \"{{name}}\" setup failed: {{message}}",
50
+ "pluginNotFound": "Plugin \"{{name}}\" not found at {{path}}",
51
+ "pluginLoaded": "Plugin loaded: {{name}}",
52
+ "pluginLoadFailed": "Failed to load plugin \"{{name}}\": {{message}}",
53
+ "pluginDisabledAfterError": "Plugin \"{{name}}\" disabled after error: {{message}}",
54
+ "schedulerInvalidCron": "Plugin \"{{name}}\" registered invalid cron expression: \"{{expression}}\"",
55
+ "schedulerError": "Plugin \"{{name}}\" scheduling error: {{message}}",
56
+ "schedulerRegistered": "Schedule registered — plugin \"{{name}}\" → \"{{expression}}\"",
57
+ "downloadJobFailed": "Download job failed — {{message}}"
58
+ },
59
+ "errors": {
60
+ "pluginLoad": "Failed to load plugin",
61
+ "messageProcess": "Failed to process message",
62
+ "stack": "Stack"
63
+ }
64
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "bot": {
3
+ "starting": "Iniciando ManyBot...",
4
+ "initialized": "Cliente inicializado. Esperando conexión con WhatsApp...",
5
+ "ready": "¡Bot está listo!",
6
+ "error": {
7
+ "uncaught": "Excepción no capturada",
8
+ "unhandled": "Rechazo no manejado"
9
+ },
10
+ "signal": {
11
+ "sigterm": "Proceso interrumpida. Apagando..."
12
+ }
13
+ },
14
+ "log": {
15
+ "info": "INFO",
16
+ "success": "OK",
17
+ "warn": "WARN",
18
+ "error": "ERROR",
19
+ "msg": "MSG",
20
+ "cmd": "CMD",
21
+ "done": "DONE",
22
+ "context": {
23
+ "group": "grupo",
24
+ "from": "De",
25
+ "type": "Tipo",
26
+ "replyTo": "Responde a"
27
+ }
28
+ },
29
+ "system": {
30
+ "environment": "Entorno: {{platform}} — usando {{puppeteer}}",
31
+ "environmentTermux": "Entorno: Termux — usando Chromium del sistema",
32
+ "connected": "¡WhatsApp conectado y listo!",
33
+ "disconnected": "Desconectado — motivo: {{reason}}",
34
+ "reconnecting": "Reconectando en {{seconds}}s...",
35
+ "reinitializing": "Reinicializando cliente...",
36
+ "qrSaved": "Código QR guardado en: {{path}}",
37
+ "qrOpen": "Abrir con: termux-open qr.png",
38
+ "qrSaveFailed": "Error al guardar el Código QR:",
39
+ "qrScan": "Escanea el Código QR abajo:",
40
+ "clientId": "Client ID: {{id}}",
41
+ "pluginsFolderNotFound": "Carpeta de plugins no encontrada. Ningún plugin cargado.",
42
+ "pluginsLoaded": "Plugins cargados: {{count}} activos{{errors}}",
43
+ "pluginsLoadedWithErrors": ", {{count}} con error",
44
+ "pluginSetupFailed": "Error en la configuración del plugin \"{{name}}\": {{message}}",
45
+ "pluginNotFound": "Plugin \"{{name}}\" no encontrado en {{path}}",
46
+ "pluginLoaded": "Plugin cargado: {{name}}",
47
+ "pluginLoadFailed": "Error al cargar el plugin \"{{name}}\": {{message}}",
48
+ "pluginDisabledAfterError": "Plugin \"{{name}}\" desactivado después del error: {{message}}",
49
+ "schedulerInvalidCron": "Plugin \"{{name}}\" registró expresión cron inválida: \"{{expression}}\"",
50
+ "schedulerError": "Error en la programación del plugin \"{{name}}\": {{message}}",
51
+ "schedulerRegistered": "Programación registrada — plugin \"{{name}}\" → \"{{expression}}\"",
52
+ "downloadJobFailed": "Error en el trabajo de descarga — {{message}}"
53
+ },
54
+ "errors": {
55
+ "pluginLoad": "Error al cargar el plugin",
56
+ "messageProcess": "Error al procesar el mensaje",
57
+ "stack": "Stack"
58
+ }
59
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "bot": {
3
+ "starting": "Iniciando ManyBot...",
4
+ "initialized": "Cliente inicializado. Aguardando conexão com WhatsApp...",
5
+ "ready": "Bot está pronto!",
6
+ "error": {
7
+ "uncaught": "Exceção não capturada",
8
+ "unhandled": "Rejeição não tratada"
9
+ },
10
+ "signal": {
11
+ "sigterm": "Processo interrompido. Desligando..."
12
+ }
13
+ },
14
+ "log": {
15
+ "info": "INFO",
16
+ "success": "OK",
17
+ "warn": "WARN",
18
+ "error": "ERRO",
19
+ "msg": "MSG",
20
+ "cmd": "CMD",
21
+ "done": "DONE",
22
+ "context": {
23
+ "group": "grupo",
24
+ "from": "De",
25
+ "type": "Tipo",
26
+ "replyTo": "Responde"
27
+ }
28
+ },
29
+ "system": {
30
+ "environment": "Ambiente: {{platform}} — usando {{puppeteer}}",
31
+ "environmentTermux": "Ambiente: Termux — usando Chromium do sistema",
32
+ "connected": "WhatsApp conectado e pronto!",
33
+ "disconnected": "Desconectado — motivo: {{reason}}",
34
+ "reconnecting": "Reconectando em {{seconds}}s...",
35
+ "reinitializing": "Reinicializando cliente...",
36
+ "qrSaved": "QR Code salvo em: {{path}}",
37
+ "qrOpen": "Abra com: termux-open qr.png",
38
+ "qrSaveFailed": "Falha ao salvar QR Code:",
39
+ "qrScan": "Escaneie o QR Code abaixo:",
40
+ "pairingCodeTitle": "Código de pareamento:",
41
+ "pairingCodeValue": "→ {{code}} ←",
42
+ "pairingCodeInstructions": "Digite este código no WhatsApp: Menu → Dispositivos conectados → Conectar com número de telefone",
43
+ "phoneNumberInvalid": "Número de telefone inválido: {{number}}. Formato esperado: 5511999999999 (código do país + DDD + número, sem espaços ou caracteres especiais)",
44
+ "phoneNumberChanged": "Número de telefone alterado. Reiniciando autenticação...",
45
+ "clientId": "Client ID: {{id}}",
46
+ "pluginsFolderNotFound": "Pasta de plugins não encontrada. Nenhum plugin carregado.",
47
+ "pluginsLoaded": "Plugins carregados: {{count}} ativos{{errors}}",
48
+ "pluginsLoadedWithErrors": ", {{count}} com erro",
49
+ "pluginSetupFailed": "Falha na configuração do plugin \"{{name}}\": {{message}}",
50
+ "pluginNotFound": "Plugin \"{{name}}\" não encontrado em {{path}}",
51
+ "pluginLoaded": "Plugin carregado: {{name}}",
52
+ "pluginLoadFailed": "Falha ao carregar plugin \"{{name}}\": {{message}}",
53
+ "pluginDisabledAfterError": "Plugin \"{{name}}\" desativado após erro: {{message}}",
54
+ "schedulerInvalidCron": "Plugin \"{{name}}\" registrou expressão cron inválida: \"{{expression}}\"",
55
+ "schedulerError": "Erro no agendamento do plugin \"{{name}}\": {{message}}",
56
+ "schedulerRegistered": "Agendamento registrado — plugin \"{{name}}\" → \"{{expression}}\"",
57
+ "downloadJobFailed": "Falha no job de download — {{message}}"
58
+ },
59
+ "errors": {
60
+ "pluginLoad": "Falha ao carregar plugin",
61
+ "messageProcess": "Falha ao processar mensagem",
62
+ "stack": "Stack"
63
+ }
64
+ }
@@ -0,0 +1,31 @@
1
+ const c = {
2
+ reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
3
+ green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m",
4
+ red: "\x1b[31m", gray: "\x1b[90m", white: "\x1b[37m",
5
+ blue: "\x1b[34m", magenta: "\x1b[35m",
6
+ };
7
+
8
+ /**
9
+ * ManyBot central logger.
10
+ * Each method only handles output — no business logic or external I/O.
11
+ */
12
+ export const logger = {
13
+ info: (...a) => console.log(`${c.cyan }INFO ${c.reset}`, ...a),
14
+ success: (...a) => console.log(`${c.green }OK ${c.reset}`, ...a),
15
+ warn: (...a) => console.log(`${c.yellow}WARN ${c.reset}`, ...a),
16
+ error: (...a) => console.log(`${c.red }ERROR ${c.reset}`, ...a),
17
+
18
+ cmd: (cmd, extra = "") =>
19
+ console.log(
20
+ `${c.gray}${now()}${c.reset}${c.yellow}CMD ${c.reset}` +
21
+ `${c.bold}${cmd}${c.reset}` +
22
+ (extra ? ` ${c.dim}${extra}${c.reset}` : "")
23
+ ),
24
+
25
+ done: (cmd, detail = "") =>
26
+ console.log(
27
+ `${c.gray}${now()}${c.reset}${c.green}DONE ${c.reset}` +
28
+ `${c.dim}${cmd}${c.reset}` +
29
+ (detail ? ` — ${detail}` : "")
30
+ ),
31
+ };
package/src/main.js ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * main.js
5
+ *
6
+ * ManyBot entry point.
7
+ * Initializes WhatsApp client and loads plugins.
8
+ */
9
+
10
+ import Module from "module";
11
+ import path from "path";
12
+
13
+ process.env.NODE_PATH = path.resolve(process.cwd(), "node_modules");
14
+ Module._initPaths();
15
+
16
+ import client, { handleQR, handlePairingCode } from "#client/whatsappClient";
17
+ import { handleMessage } from "#kernel/messageHandler";
18
+ import { loadPlugins, setupPlugins } from "#kernel/pluginLoader";
19
+ import { buildSetupApi } from "#manyapi";
20
+ import { logger } from "#logger";
21
+ import { PLUGINS } from "#config";
22
+ import { t } from "#i18n";
23
+ import { printBanner } from "#client/banner";
24
+ import { CLIENT_ID } from "#config";
25
+
26
+ logger.info(t("bot.starting"));
27
+
28
+ // Global safety net — no error should crash the bot
29
+ process.on("uncaughtException", (err) => {
30
+ logger.error(`${t("bot.error.uncaught")} — ${err.message}`,
31
+ `\n ${t("errors.stack")}: ${err.stack?.split("\n")[1]?.trim() ?? ""}`);
32
+ });
33
+
34
+ process.on("unhandledRejection", (reason) => {
35
+ const msg = reason instanceof Error ? reason.message : String(reason);
36
+ logger.error(`${t("bot.error.unhandled")} — ${msg}`);
37
+ });
38
+
39
+ // Clean shutdown
40
+ process.on("SIGTERM", async () => {
41
+ logger.error(t("bot.signal.sigterm"));
42
+ process.exit(0);
43
+ });
44
+
45
+ let state = "BOOT";
46
+ // BOOT → AUTH → SYNC → READY
47
+
48
+ function setState(next) {
49
+ state = next;
50
+ }
51
+
52
+ client.on("loading_screen", (p, msg) => {
53
+ setState("SYNC");
54
+ logger.info(`loading ${p}% ${msg}`);
55
+ });
56
+
57
+ client.on("ready", async () => {
58
+ setState("READY_INIT");
59
+
60
+ logger.success(t("system.connected"));
61
+ logger.info(t("system.clientId", { id: CLIENT_ID }));
62
+
63
+ printBanner();
64
+
65
+ await loadPlugins(PLUGINS);
66
+ await setupPlugins(buildSetupApi(client));
67
+
68
+ // buffer anti-replay / sync ghost messages
69
+ setTimeout(() => {
70
+ setState("READY");
71
+ }, 2000);
72
+ });
73
+
74
+ client.on("message_create", async (msg) => {
75
+ if (state !== "READY") return;
76
+
77
+ if (!msg.body && !msg.hasMedia) return;
78
+
79
+ try {
80
+ await handleMessage(msg);
81
+ } catch (err) {
82
+ logger.error(err);
83
+ }
84
+ });
85
+
86
+ client.on("disconnected", (reason) => {
87
+ logger.warn(t("system.disconnected", { reason }));
88
+ logger.info(t("system.reconnecting", { seconds: 5 }));
89
+ setTimeout(() => {
90
+ logger.info(t("system.reinitializing"));
91
+ client.initialize();
92
+ }, 5000);
93
+ });
94
+
95
+ // -- Events ----------------------------------------------------
96
+ client.on("code", (code) => {
97
+ handlePairingCode(code);
98
+ });
99
+
100
+ client.on("qr", (qr) => {
101
+ handleQR(qr);
102
+ });
103
+
104
+ client.initialize();
105
+ logger.info(t("bot.initialized"));
@@ -0,0 +1,9 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export function emptyFolder(folder) {
5
+ fs.readdirSync(folder).forEach(file => {
6
+ const filePath = path.join(folder, file);
7
+ if (fs.lstatSync(filePath).isFile()) fs.unlinkSync(filePath);
8
+ });
9
+ }
@@ -0,0 +1,3 @@
1
+ export function getChatId(chat) {
2
+ return chat.id._serialized;
3
+ }