@manybot/manybot 5.0.0 → 5.2.0

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.
@@ -1,99 +0,0 @@
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
- }
@@ -1,48 +0,0 @@
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
- }
@@ -1,67 +0,0 @@
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
- "chromeNotFound": "Chrome not found. Downloading...",
64
- "couldNotDownloadChrome": "Could not download Chrome: ",
65
- "OSNotSupported": "Unsupported operating system: {{os}}. If you have any questions, please visit: https://manybot.stxerr.dev/docs/getting-started/"
66
- }
67
- }
@@ -1,62 +0,0 @@
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
- "chromeNotFound": "No se ha encontrado Chrome. Descargando...",
59
- "couldNotDownloadChrome": "No se ha podido descargar Chrome: ",
60
- "OSNotSupported": "Sistema operativo no compatible: {{os}}. Si tienes alguna duda, consulta: https://manybot.stxerr.dev/docs/getting-started/"
61
- }
62
- }
@@ -1,68 +0,0 @@
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
- "chromeNotFound": "Chrome não encontrado. Baixando...",
64
- "couldNotDownloadChrome": "Não foi possível baixar o Chrome: ",
65
- "OSNotSupported": "Sistema operacional não suportado: {{os}}. Em caso de dúvidas, consulte: https://manybot.stxerr.dev/docs/getting-started/"
66
-
67
- }
68
- }
@@ -1,31 +0,0 @@
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 DELETED
@@ -1,105 +0,0 @@
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"));
package/src/utils/file.js DELETED
@@ -1,9 +0,0 @@
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
- }
@@ -1,3 +0,0 @@
1
- export function getChatId(chat) {
2
- return chat.id._serialized;
3
- }
@@ -1,177 +0,0 @@
1
- /**
2
- * CLI utility to discover chat/group IDs.
3
- *
4
- * Usage:
5
- * node get_id.js groups|contacts – list all groups or contacts
6
- * node get_id.js <term> [term2] ... – search by name, number or ID fragment
7
- * node get_id.js <term> --json – export results as JSON
8
- * node get_id.js <term> --csv – export results as CSV
9
- *
10
- * Search matches against: display name, phone number, serialized ID.
11
- */
12
-
13
- import pkg from "whatsapp-web.js";
14
- import qrcode from "qrcode-terminal";
15
- import fs from "fs";
16
- import { resolvePuppeteerConfig } from "#client/environment";
17
-
18
- const CLIENT_ID = "getId";
19
- const { Client, LocalAuth } = pkg;
20
-
21
- // ── Parse args ────────────────────────────────────────────────────────────────
22
- const rawArgs = process.argv.slice(2);
23
- const exportJson = rawArgs.includes("--json");
24
- const exportCsv = rawArgs.includes("--csv");
25
- const terms = rawArgs.filter(a => !a.startsWith("--")).map(a => a.toLowerCase());
26
-
27
- if (!terms.length) {
28
- console.log([
29
- "Usage:",
30
- " node get_id.js me – show your own number and ID",
31
- " node get_id.js groups|contacts",
32
- " node get_id.js <term> [term2] ... – search name, number or ID fragment",
33
- " Add --json or --csv to export results",
34
- ].join("\n"));
35
- process.exit(0);
36
- }
37
-
38
- // ── Client ────────────────────────────────────────────────────────────────────
39
- const client = new Client({
40
- authStrategy: new LocalAuth({ clientId: CLIENT_ID }),
41
- puppeteer: {
42
- headless: true,
43
- args: ["--no-sandbox", "--disable-setuid-sandbox", ...(resolvePuppeteerConfig().args || [])],
44
- ...resolvePuppeteerConfig(),
45
- },
46
- });
47
-
48
- client.on("qr", (qr) => {
49
- console.log("[QR] Scan to authenticate:");
50
- qrcode.generate(qr, { small: true });
51
- });
52
-
53
- // ── Helpers ───────────────────────────────────────────────────────────────────
54
- function matches(chat, term) {
55
- const name = (chat.name || "").toLowerCase();
56
- const number = (chat.id?.user || "").toLowerCase();
57
- const serial = (chat.id?._serialized || "").toLowerCase();
58
- return name.includes(term) || number.includes(term) || serial.includes(term);
59
- }
60
-
61
- function buildRow(chat) {
62
- return {
63
- name: chat.name || chat.id?.user || "",
64
- number: chat.id?.user || "",
65
- id: chat.id?._serialized || "",
66
- group: chat.isGroup,
67
- };
68
- }
69
-
70
- function printTable(rows) {
71
- const LINE = "─".repeat(48);
72
- rows.forEach(r => {
73
- console.log(LINE);
74
- console.log("Name: ", r.name);
75
- console.log("Number:", r.number);
76
- console.log("ID: ", r.id);
77
- if (r.phone) console.log("Phone: ", r.phone);
78
- console.log("Group: ", r.group);
79
- });
80
- console.log(LINE);
81
- console.log(`\n${rows.length} result(s) found.`);
82
- }
83
-
84
- function exportResults(rows) {
85
- if (exportJson) {
86
- const file = "get_id_results.json";
87
- fs.writeFileSync(file, JSON.stringify(rows, null, 2));
88
- console.log(`\nExported to ${file}`);
89
- }
90
- if (exportCsv) {
91
- const file = "get_id_results.csv";
92
- const header = "name,number,id,group";
93
- const lines = rows.map(r =>
94
- [r.name, r.number, r.id, r.group].map(v => `"${String(v).replace(/"/g, '""')}"`).join(",")
95
- );
96
- fs.writeFileSync(file, [header, ...lines].join("\n"));
97
- console.log(`Exported to ${file}`);
98
- }
99
- }
100
-
101
- // ── Main ──────────────────────────────────────────────────────────────────────
102
- client.on("ready", async () => {
103
- console.log("[OK] Connected. Searching…\n");
104
-
105
- const [first, ...rest] = terms;
106
-
107
- if (first === "me") {
108
- const info = client.info;
109
-
110
- // Attempt to resolve own LID via internal Store (may be unavailable on older WA versions)
111
- let lid = null;
112
- try {
113
- const serialized = client.info.wid._serialized;
114
- lid = await client.pupPage.evaluate((s) => {
115
- const wid = window.Store.WidFactory.createWid(s);
116
- const resolved = window.Store.LidUtils.getCurrentLid(wid);
117
- return resolved?._serialized ?? null;
118
- }, serialized);
119
- } catch (_) { /* LID unavailable */ }
120
-
121
- const LINE = "─".repeat(48);
122
- console.log(LINE);
123
- console.log("Name: ", info.pushname || "(no name)");
124
- console.log("Number:", info.wid?.user || "");
125
- console.log("ID: ", info.wid?._serialized || "");
126
- if (lid) console.log("LID: ", lid);
127
- console.log(LINE);
128
- await client.destroy();
129
- process.exit(0);
130
- }
131
-
132
- const chats = await client.getChats();
133
-
134
- let filtered;
135
- if (first === "groups") {
136
- filtered = chats.filter(c => c.isGroup);
137
- } else if (first === "contacts") {
138
- filtered = chats.filter(c => !c.isGroup);
139
- } else {
140
- // All terms must match (AND logic across multiple terms)
141
- filtered = chats.filter(c => terms.every(t => matches(c, t)));
142
- }
143
-
144
- if (!filtered.length) {
145
- console.log("No results found.");
146
- } else {
147
- const rows = filtered.map(buildRow);
148
-
149
- // For contacts with @lid, resolve the @c.us phone ID in bulk
150
- const lidRows = rows.filter(r => !r.group && r.id.endsWith("@lid"));
151
- if (lidRows.length) {
152
- try {
153
- const serializedIds = lidRows.map(r => r.id);
154
- const phoneMap = await client.pupPage.evaluate((ids) => {
155
- const result = {};
156
- for (const id of ids) {
157
- try {
158
- const wid = window.Store.WidFactory.createWid(id);
159
- const phone = window.Store.LidUtils.getPhoneNumber(wid);
160
- result[id] = phone?._serialized ?? null;
161
- } catch (_) { result[id] = null; }
162
- }
163
- return result;
164
- }, serializedIds);
165
- for (const row of lidRows) row.phone = phoneMap[row.id] || null;
166
- } catch (_) { /* LID resolution unavailable */ }
167
- }
168
-
169
- printTable(rows);
170
- exportResults(rows);
171
- }
172
-
173
- await client.destroy();
174
- process.exit(0);
175
- });
176
-
177
- client.initialize();