@manybot/manybot 5.1.0 → 5.2.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.
Files changed (51) hide show
  1. package/README.md +5 -7
  2. package/dist/client/banner.js +35 -0
  3. package/dist/client/store.js +135 -0
  4. package/dist/config.js +363 -0
  5. package/dist/core/adapter.js +12 -0
  6. package/dist/core/capabilities.js +16 -0
  7. package/dist/core/types.js +6 -0
  8. package/dist/download/queue.js +49 -0
  9. package/dist/drivers/index.js +14 -0
  10. package/dist/drivers/patches/index.js +9 -0
  11. package/dist/drivers/patches/libsignal.js +16 -0
  12. package/dist/drivers/patches/patch.js +1 -0
  13. package/dist/drivers/whatsapp/adapter.js +7 -0
  14. package/dist/drivers/whatsapp/api/index.js +1311 -0
  15. package/dist/drivers/whatsapp/index.js +164 -0
  16. package/dist/drivers/whatsapp/loginPrompt.js +81 -0
  17. package/dist/drivers/whatsapp/messageHandler.js +111 -0
  18. package/dist/drivers/whatsapp/sdk/baileysSock.js +124 -0
  19. package/dist/i18n/index.js +202 -0
  20. package/dist/kernel/pluginApi.js +11 -0
  21. package/dist/kernel/pluginGuard.js +88 -0
  22. package/dist/kernel/pluginLoader.js +322 -0
  23. package/dist/kernel/scheduler.js +110 -0
  24. package/dist/kernel/sendGuard.js +121 -0
  25. package/dist/kernel/settingsDb.js +205 -0
  26. package/{src → dist}/locales/en.json +18 -33
  27. package/dist/locales/es.json +52 -0
  28. package/{src → dist}/locales/pt.json +18 -34
  29. package/dist/logger/logger.js +16 -0
  30. package/dist/main.js +82 -0
  31. package/dist/types.js +16 -0
  32. package/{src → dist}/utils/file.js +3 -3
  33. package/package.json +35 -26
  34. package/src/client/banner.js +0 -57
  35. package/src/client/whatsappClient.js +0 -185
  36. package/src/config.js +0 -288
  37. package/src/download/queue.js +0 -55
  38. package/src/i18n/index.js +0 -235
  39. package/src/kernel/messageHandler.js +0 -88
  40. package/src/kernel/pluginApi.js +0 -630
  41. package/src/kernel/pluginGuard.js +0 -78
  42. package/src/kernel/pluginLoader.js +0 -177
  43. package/src/kernel/pluginState.js +0 -99
  44. package/src/kernel/scheduler.js +0 -48
  45. package/src/kernel/sendGuard.js +0 -166
  46. package/src/locales/es.json +0 -62
  47. package/src/logger/logger.js +0 -32
  48. package/src/main.js +0 -136
  49. package/src/utils/getChatId.js +0 -3
  50. package/src/utils/get_id.js +0 -177
  51. package/src/utils/pluginI18n.js +0 -129
@@ -0,0 +1,205 @@
1
+ /**
2
+ * settingsDb.ts
3
+ *
4
+ * Persistent per-chat settings backed by SQLite.
5
+ * Exposed to plugins via ctx.settings.
6
+ *
7
+ * Two concerns live here:
8
+ * 1. Plugin settings — arbitrary key/value per (plugin, chat)
9
+ * 2. Community links — grouping chats under a shared community ID
10
+ *
11
+ * Plugins never touch the DB directly — only through buildSettingsApi().
12
+ */
13
+ import Database from "better-sqlite3";
14
+ import path from "path";
15
+ import { mkdirSync } from "fs";
16
+ import { CONFIG_DIR } from "#config";
17
+ // ── DB init ───────────────────────────────────────────────────────────────────
18
+ const DB_PATH = path.join(CONFIG_DIR, "settings.db");
19
+ mkdirSync(path.dirname(DB_PATH), { recursive: true });
20
+ const db = new Database(DB_PATH);
21
+ db.pragma("journal_mode = WAL");
22
+ db.pragma("foreign_keys = ON");
23
+ db.exec(`
24
+ CREATE TABLE IF NOT EXISTS plugin_settings (
25
+ plugin_name TEXT NOT NULL,
26
+ chat_id TEXT NOT NULL,
27
+ key TEXT NOT NULL,
28
+ value TEXT NOT NULL,
29
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
30
+ PRIMARY KEY (plugin_name, chat_id, key)
31
+ );
32
+
33
+ CREATE TABLE IF NOT EXISTS community_links (
34
+ chat_id TEXT PRIMARY KEY,
35
+ community_id TEXT NOT NULL,
36
+ linked_at INTEGER NOT NULL DEFAULT (unixepoch())
37
+ );
38
+
39
+ CREATE INDEX IF NOT EXISTS idx_community
40
+ ON community_links (community_id);
41
+ `);
42
+ // ── Prepared statements ───────────────────────────────────────────────────────
43
+ const stmts = {
44
+ get: db.prepare("SELECT value FROM plugin_settings WHERE plugin_name = ? AND chat_id = ? AND key = ?"),
45
+ getAll: db.prepare("SELECT key, value FROM plugin_settings WHERE plugin_name = ? AND chat_id = ?"),
46
+ set: db.prepare(`
47
+ INSERT INTO plugin_settings (plugin_name, chat_id, key, value, updated_at)
48
+ VALUES (?, ?, ?, ?, unixepoch())
49
+ ON CONFLICT (plugin_name, chat_id, key)
50
+ DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
51
+ `),
52
+ delete: db.prepare("DELETE FROM plugin_settings WHERE plugin_name = ? AND chat_id = ? AND key = ?"),
53
+ deleteAll: db.prepare("DELETE FROM plugin_settings WHERE plugin_name = ? AND chat_id = ?"),
54
+ getCommunityId: db.prepare("SELECT community_id FROM community_links WHERE chat_id = ?"),
55
+ getCommunityChats: db.prepare("SELECT chat_id FROM community_links WHERE community_id = ?"),
56
+ link: db.prepare(`
57
+ INSERT INTO community_links (chat_id, community_id, linked_at)
58
+ VALUES (?, ?, unixepoch())
59
+ ON CONFLICT (chat_id)
60
+ DO UPDATE SET community_id = excluded.community_id, linked_at = excluded.linked_at
61
+ `),
62
+ unlink: db.prepare("DELETE FROM community_links WHERE chat_id = ?"),
63
+ };
64
+ // ── Core helpers ──────────────────────────────────────────────────────────────
65
+ function dbGet(pluginName, chatId, key) {
66
+ const row = stmts.get.get(pluginName, chatId, key);
67
+ if (!row)
68
+ return undefined;
69
+ try {
70
+ return JSON.parse(row.value);
71
+ }
72
+ catch {
73
+ return row.value;
74
+ }
75
+ }
76
+ function dbGetAll(pluginName, chatId) {
77
+ const rows = stmts.getAll.all(pluginName, chatId);
78
+ return Object.fromEntries(rows.map(({ key, value }) => {
79
+ try {
80
+ return [key, JSON.parse(value)];
81
+ }
82
+ catch {
83
+ return [key, value];
84
+ }
85
+ }));
86
+ }
87
+ function dbSet(pluginName, chatId, key, value) {
88
+ stmts.set.run(pluginName, chatId, key, JSON.stringify(value));
89
+ }
90
+ function dbDelete(pluginName, chatId, key) {
91
+ stmts.delete.run(pluginName, chatId, key);
92
+ }
93
+ function dbDeleteAll(pluginName, chatId) {
94
+ stmts.deleteAll.run(pluginName, chatId);
95
+ }
96
+ // ── Scoped accessor factory ───────────────────────────────────────────────────
97
+ /**
98
+ * Returns a settings accessor for a specific (pluginName, chatId) pair.
99
+ * Used both for the current chat and for forChat(id) cross-chat reads.
100
+ */
101
+ function scopedAccessor(pluginName, chatId) {
102
+ return {
103
+ /**
104
+ * Get a setting value. Returns `defaultValue` (default: undefined) if not set.
105
+ * @param {string} key
106
+ * @param {*} [defaultValue]
107
+ */
108
+ get(key, defaultValue = undefined) {
109
+ const val = dbGet(pluginName, chatId, key);
110
+ return val !== undefined ? val : defaultValue;
111
+ },
112
+ /**
113
+ * Get all settings for this chat as a plain object.
114
+ * @returns {Record<string, *>}
115
+ */
116
+ getAll() {
117
+ return dbGetAll(pluginName, chatId);
118
+ },
119
+ /**
120
+ * Set a setting value. Any JSON-serializable value is accepted.
121
+ * @param {string} key
122
+ * @param {*} value
123
+ */
124
+ set(key, value) {
125
+ dbSet(pluginName, chatId, key, value);
126
+ },
127
+ /**
128
+ * Delete a single setting key.
129
+ * @param {string} key
130
+ */
131
+ delete(key) {
132
+ dbDelete(pluginName, chatId, key);
133
+ },
134
+ /**
135
+ * Delete all settings for this chat under this plugin.
136
+ */
137
+ deleteAll() {
138
+ dbDeleteAll(pluginName, chatId);
139
+ },
140
+ };
141
+ }
142
+ // ── Public API builder ────────────────────────────────────────────────────────
143
+ /**
144
+ * Builds ctx.settings for a given runtime context.
145
+ *
146
+ * ctx.settings — scoped to current chat
147
+ * ctx.settings.global — scoped to a synthetic "_global" chat ID
148
+ * ctx.settings.forChat — cross-chat read (any chatId)
149
+ * ctx.settings.link — community membership
150
+ *
151
+ * @param {string} pluginName
152
+ * @param {string} chatId — current chat's serialized ID
153
+ */
154
+ export function buildSettingsApi(pluginName, chatId) {
155
+ const current = scopedAccessor(pluginName, chatId);
156
+ return {
157
+ ...current,
158
+ /**
159
+ * Bot-wide settings, not tied to any specific chat.
160
+ * Stored under the synthetic key "_global".
161
+ */
162
+ global: scopedAccessor(pluginName, "_global"),
163
+ /**
164
+ * Access settings of any other chat by ID.
165
+ * Useful for cross-chat features like community-wide XP.
166
+ * @param {string} targetChatId
167
+ */
168
+ forChat(targetChatId) {
169
+ return scopedAccessor(pluginName, targetChatId);
170
+ },
171
+ /**
172
+ * Link the current chat to a community by ID.
173
+ * The community ID is any string you choose — typically a name or UUID.
174
+ * Replaces any existing link for this chat.
175
+ * @param {string} communityId
176
+ */
177
+ link(communityId) {
178
+ stmts.link.run(chatId, communityId);
179
+ },
180
+ /**
181
+ * Unlink the current chat from its community.
182
+ */
183
+ unlink() {
184
+ stmts.unlink.run(chatId);
185
+ },
186
+ /**
187
+ * Returns the community ID this chat belongs to, or null.
188
+ * @returns {string | null}
189
+ */
190
+ getCommunityId() {
191
+ return stmts.getCommunityId.get(chatId)?.community_id ?? null;
192
+ },
193
+ /**
194
+ * Returns all chat IDs that belong to the same community as the current chat.
195
+ * Returns [] if this chat has no community link.
196
+ * @returns {string[]}
197
+ */
198
+ getCommunityChats() {
199
+ const row = stmts.getCommunityId.get(chatId);
200
+ if (!row)
201
+ return [];
202
+ return stmts.getCommunityChats.all(row.community_id).map((r) => r.chat_id);
203
+ },
204
+ };
205
+ }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "bot": {
3
- "starting": "Starting ManyBot...",
4
3
  "initialized": "Client initialized. Waiting for WhatsApp connection...",
5
4
  "ready": "Bot is ready!",
6
5
  "error": {
@@ -11,57 +10,43 @@
11
10
  "sigterm": "Process interrupted. Shutting down..."
12
11
  }
13
12
  },
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
13
  "system": {
30
- "environment": "Environment: {{platform}} — using {{puppeteer}}",
31
- "environmentTermux": "Environment: Termux — using system Chromium",
32
14
  "connected": "WhatsApp connected and ready!",
33
15
  "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
16
  "qrScan": "Scan the QR Code below:",
40
17
  "pairingCodeTitle": "Pairing code:",
41
18
  "pairingCodeValue": "→ {{code}} ←",
42
19
  "pairingCodeInstructions": "Enter this code in WhatsApp: Menu → Linked devices → Link with phone number",
43
20
  "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
21
  "clientId": "Client ID: {{id}}",
46
- "pluginsFolderNotFound": "Plugins folder not found. No plugins loaded.",
47
22
  "pluginsLoaded": "Plugins loaded: {{count}} active{{errors}}",
48
23
  "pluginsLoadedWithErrors": ", {{count}} with error",
49
24
  "pluginSetupFailed": "Plugin \"{{name}}\" setup failed: {{message}}",
50
25
  "pluginNotFound": "Plugin \"{{name}}\" not found at {{path}}",
51
26
  "pluginLoaded": "Plugin loaded: {{name}}",
27
+ "pluginReloaded": "Plugin reloaded: {{name}}",
52
28
  "pluginLoadFailed": "Failed to load plugin \"{{name}}\": {{message}}",
53
- "pluginDisabledAfterError": "Plugin \"{{name}}\" disabled after error: {{message}}",
54
29
  "schedulerInvalidCron": "Plugin \"{{name}}\" registered invalid cron expression: \"{{expression}}\"",
55
30
  "schedulerError": "Plugin \"{{name}}\" scheduling error: {{message}}",
56
31
  "schedulerRegistered": "Schedule registered — plugin \"{{name}}\" → \"{{expression}}\"",
57
- "downloadJobFailed": "Download job failed — {{message}}"
32
+ "downloadJobFailed": "Download job failed — {{message}}",
33
+ "reconnecting": "Reconnecting in {{secs}}s...",
34
+ "sessionExpired": "Session expired or logged out — removing local session and restarting..."
58
35
  },
59
36
  "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/"
37
+ "stack": "Stack"
38
+ },
39
+ "onboarding": {
40
+ "intro": "ManyBot first login",
41
+ "methodPrompt": "How do you want to connect your WhatsApp account?",
42
+ "methodPhone": "Enter phone number",
43
+ "methodPhoneHint": "pairing code",
44
+ "methodQr": "Show QR code",
45
+ "methodQrHint": "scan with your phone",
46
+ "phonePrompt": "Phone number (with country code, digits only)",
47
+ "phoneValidation": "Digits only, with country code. E.g.: 5511999999999",
48
+ "outroQr": "Scan the QR code that will appear next.",
49
+ "outroPhone": "Wait for the pairing code that will appear next.",
50
+ "cancelled": "Login cancelled."
66
51
  }
67
52
  }
@@ -0,0 +1,52 @@
1
+ {
2
+ "bot": {
3
+ "initialized": "Cliente inicializado. Esperando conexión con WhatsApp...",
4
+ "ready": "¡Bot está listo!",
5
+ "error": {
6
+ "uncaught": "Excepción no capturada",
7
+ "unhandled": "Rechazo no manejado"
8
+ },
9
+ "signal": {
10
+ "sigterm": "Proceso interrumpida. Apagando..."
11
+ }
12
+ },
13
+ "system": {
14
+ "connected": "¡WhatsApp conectado y listo!",
15
+ "disconnected": "Desconectado — motivo: {{reason}}",
16
+ "qrScan": "Escanea el Código QR abajo:",
17
+ "pairingCodeTitle": "Código de emparejamiento:",
18
+ "pairingCodeValue": "→ {{code}} ←",
19
+ "pairingCodeInstructions": "Ingresa este código en WhatsApp: Menú → Dispositivos vinculados → Vincular con número de teléfono",
20
+ "phoneNumberInvalid": "Número de teléfono inválido: {{number}}. Formato esperado: 5511999999999 (código de país + área + número, sin espacios ni caracteres especiales)",
21
+ "clientId": "Client ID: {{id}}",
22
+ "pluginsLoaded": "Plugins cargados: {{count}} activos{{errors}}",
23
+ "pluginsLoadedWithErrors": ", {{count}} con error",
24
+ "pluginSetupFailed": "Error en la configuración del plugin \"{{name}}\": {{message}}",
25
+ "pluginNotFound": "Plugin \"{{name}}\" no encontrado en {{path}}",
26
+ "pluginLoaded": "Plugin cargado: {{name}}",
27
+ "pluginReloaded": "Plugin recargado: {{name}}",
28
+ "pluginLoadFailed": "Error al cargar el plugin \"{{name}}\": {{message}}",
29
+ "schedulerInvalidCron": "Plugin \"{{name}}\" registró expresión cron inválida: \"{{expression}}\"",
30
+ "schedulerError": "Error en la programación del plugin \"{{name}}\": {{message}}",
31
+ "schedulerRegistered": "Programación registrada — plugin \"{{name}}\" → \"{{expression}}\"",
32
+ "downloadJobFailed": "Error en el trabajo de descarga — {{message}}",
33
+ "reconnecting": "Reconectando en {{secs}}s...",
34
+ "sessionExpired": "Sesión expirada o cerrada — eliminando sesión local y reiniciando..."
35
+ },
36
+ "errors": {
37
+ "stack": "Stack"
38
+ },
39
+ "onboarding": {
40
+ "intro": "ManyBot — primer inicio de sesión",
41
+ "methodPrompt": "¿Cómo quieres conectar tu cuenta de WhatsApp?",
42
+ "methodPhone": "Ingresar número de teléfono",
43
+ "methodPhoneHint": "código de emparejamiento",
44
+ "methodQr": "Mostrar código QR",
45
+ "methodQrHint": "escanea con tu teléfono",
46
+ "phonePrompt": "Número de teléfono (con código de país, solo dígitos)",
47
+ "phoneValidation": "Solo dígitos, con código de país. Ej.: 5511999999999",
48
+ "outroQr": "Escanea el código QR que aparecerá a continuación.",
49
+ "outroPhone": "Espera el código de emparejamiento que aparecerá a continuación.",
50
+ "cancelled": "Inicio de sesión cancelado."
51
+ }
52
+ }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "bot": {
3
- "starting": "Iniciando ManyBot...",
4
3
  "initialized": "Cliente inicializado. Aguardando conexão com WhatsApp...",
5
4
  "ready": "Bot está pronto!",
6
5
  "error": {
@@ -11,58 +10,43 @@
11
10
  "sigterm": "Processo interrompido. Desligando..."
12
11
  }
13
12
  },
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
13
  "system": {
30
- "environment": "Ambiente: {{platform}} — usando {{puppeteer}}",
31
- "environmentTermux": "Ambiente: Termux — usando Chromium do sistema",
32
14
  "connected": "WhatsApp conectado e pronto!",
33
15
  "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
16
  "qrScan": "Escaneie o QR Code abaixo:",
40
17
  "pairingCodeTitle": "Código de pareamento:",
41
18
  "pairingCodeValue": "→ {{code}} ←",
42
19
  "pairingCodeInstructions": "Digite este código no WhatsApp: Menu → Dispositivos conectados → Conectar com número de telefone",
43
20
  "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
21
  "clientId": "Client ID: {{id}}",
46
- "pluginsFolderNotFound": "Pasta de plugins não encontrada. Nenhum plugin carregado.",
47
22
  "pluginsLoaded": "Plugins carregados: {{count}} ativos{{errors}}",
48
23
  "pluginsLoadedWithErrors": ", {{count}} com erro",
49
24
  "pluginSetupFailed": "Falha na configuração do plugin \"{{name}}\": {{message}}",
50
25
  "pluginNotFound": "Plugin \"{{name}}\" não encontrado em {{path}}",
51
26
  "pluginLoaded": "Plugin carregado: {{name}}",
27
+ "pluginReloaded": "Plugin recarregado: {{name}}",
52
28
  "pluginLoadFailed": "Falha ao carregar plugin \"{{name}}\": {{message}}",
53
- "pluginDisabledAfterError": "Plugin \"{{name}}\" desativado após erro: {{message}}",
54
29
  "schedulerInvalidCron": "Plugin \"{{name}}\" registrou expressão cron inválida: \"{{expression}}\"",
55
30
  "schedulerError": "Erro no agendamento do plugin \"{{name}}\": {{message}}",
56
31
  "schedulerRegistered": "Agendamento registrado — plugin \"{{name}}\" → \"{{expression}}\"",
57
- "downloadJobFailed": "Falha no job de download — {{message}}"
32
+ "downloadJobFailed": "Falha no job de download — {{message}}",
33
+ "reconnecting": "Reconectando em {{secs}}s...",
34
+ "sessionExpired": "Sessão expirada ou desconectada — removendo sessão local e reiniciando..."
58
35
  },
59
36
  "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
-
37
+ "stack": "Stack"
38
+ },
39
+ "onboarding": {
40
+ "intro": "ManyBot primeiro login",
41
+ "methodPrompt": "Como você quer conectar sua conta do WhatsApp?",
42
+ "methodPhone": "Digitar número de telefone",
43
+ "methodPhoneHint": "código de pareamento",
44
+ "methodQr": "Mostrar QR code",
45
+ "methodQrHint": "escaneie com seu celular",
46
+ "phonePrompt": "Número de telefone (com código do país, somente dígitos)",
47
+ "phoneValidation": "Somente dígitos, com código do país. Ex.: 5511999999999",
48
+ "outroQr": "Escaneie o QR code que vai aparecer a seguir.",
49
+ "outroPhone": "Aguarde o código de pareamento que vai aparecer a seguir.",
50
+ "cancelled": "Login cancelado."
67
51
  }
68
52
  }
@@ -0,0 +1,16 @@
1
+ const c = {
2
+ reset: "\x1b[0m",
3
+ green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m",
4
+ red: "\x1b[31m", blue: "\x1b[34m",
5
+ };
6
+ /**
7
+ * ManyBot central logger.
8
+ * Each method only handles output — no business logic or external I/O.
9
+ */
10
+ export const logger = {
11
+ info: (...a) => console.log(`${c.cyan}INFO ${c.reset}`, ...a),
12
+ success: (...a) => console.log(`${c.green}OK ${c.reset}`, ...a),
13
+ warn: (...a) => console.log(`${c.yellow}WARN ${c.reset}`, ...a),
14
+ error: (...a) => console.log(`${c.red}ERROR ${c.reset}`, ...a),
15
+ debug: (...a) => console.log(`${c.blue}DEBUG ${c.reset}`, ...a),
16
+ };
package/dist/main.js ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * main.ts
4
+ *
5
+ * ManyBot entry point.
6
+ * Orchestrates process lifecycle, error handling, and driver startup.
7
+ */
8
+ import Module from "module";
9
+ import path from "path";
10
+ process.env.NODE_PATH = path.resolve(process.cwd(), "node_modules");
11
+ Module._initPaths();
12
+ import { initializeSelectedDriver } from "#drivers/index.js";
13
+ import { cleanupPlugins } from "#kernel/pluginLoader.js";
14
+ import { stopAll as stopScheduler } from "#kernel/scheduler.js";
15
+ import { logger } from "#logger";
16
+ import { t } from "#i18n";
17
+ let shuttingDown = false;
18
+ const activeDriver = initializeSelectedDriver();
19
+ async function shutdown(reason, isError = false) {
20
+ if (shuttingDown)
21
+ return;
22
+ shuttingDown = true;
23
+ if (isError) {
24
+ logger.error(`${t("bot.error.uncaught")}: ${reason}`);
25
+ }
26
+ else {
27
+ logger.warn(t("bot.signal.sigterm", { signal: reason }));
28
+ }
29
+ try {
30
+ await cleanupPlugins();
31
+ }
32
+ catch (err) {
33
+ logger.error(`Error cleaning up plugins: ${err.message}`);
34
+ }
35
+ stopScheduler();
36
+ try {
37
+ await activeDriver.disconnect();
38
+ }
39
+ catch (err) {
40
+ logger.error(`Error disconnecting driver: ${err.message}`);
41
+ }
42
+ process.exit(isError ? 1 : 0);
43
+ }
44
+ // Global error listeners
45
+ process.on("uncaughtException", (err) => {
46
+ const stackFrame = err.stack?.split("\n")[1]?.trim() ?? "";
47
+ shutdown(`${err.message}\n ${t("errors.stack")}: ${stackFrame}`, true);
48
+ });
49
+ process.on("unhandledRejection", (reason) => {
50
+ const msg = reason instanceof Error ? reason.message : String(reason);
51
+ shutdown(`${t("bot.error.unhandled")}: ${msg}`, true);
52
+ });
53
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
54
+ process.on("SIGINT", () => shutdown("SIGINT"));
55
+ // ── --getid mode (provisional) ───────────────────────────────────────────
56
+ // Usage: npm run start -- --getid
57
+ // Connects, waits for the next message to arrive from any chat, and prints
58
+ // the JID to the console, to paste into CHATS/TEST_CHAT in manybot.toml.
59
+ // Does not enter the normal bot flow (plugins are not loaded).
60
+ if (process.argv.includes("--getid")) {
61
+ if (!activeDriver.getId) {
62
+ logger.error(`Current driver does not support --getid.`);
63
+ process.exit(1);
64
+ }
65
+ activeDriver.getId()
66
+ .then(() => process.exit(0))
67
+ .catch((err) => {
68
+ logger.error(`--getid mode failed: ${err.message}`);
69
+ process.exit(1);
70
+ });
71
+ }
72
+ else {
73
+ // Start bot
74
+ logger.info(t("bot.initialized"));
75
+ activeDriver.connect()
76
+ .then(() => {
77
+ logger.success(t("bot.ready"));
78
+ })
79
+ .catch((err) => {
80
+ shutdown(`Failed to connect driver: ${err.message}`, true);
81
+ });
82
+ }
package/dist/types.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * src/types.ts
3
+ *
4
+ * Shared WhatsApp-facing types, imported everywhere as "#types" (see
5
+ * tsconfig.json paths / package.json imports). Kept as a single module so
6
+ * call sites don't need to know whether a type comes straight from
7
+ * Baileys or from ManyBot's own store/adapter layer.
8
+ *
9
+ * Types that are only meaningful within the plugin-API builder itself
10
+ * (the shape of `ctx`, `ctx.msg`, the chainable sender returned by
11
+ * ctx.send/ctx.msg.reply, etc.) are NOT defined here — they're inferred
12
+ * with `ReturnType<typeof ...>` right next to the functions that build
13
+ * them, in drivers/whatsapp/api/index.ts, to avoid circular type
14
+ * definitions.
15
+ */
16
+ export {};
@@ -1,9 +1,9 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
-
4
3
  export function emptyFolder(folder) {
5
4
  fs.readdirSync(folder).forEach(file => {
6
5
  const filePath = path.join(folder, file);
7
- if (fs.lstatSync(filePath).isFile()) fs.unlinkSync(filePath);
6
+ if (fs.lstatSync(filePath).isFile())
7
+ fs.unlinkSync(filePath);
8
8
  });
9
- }
9
+ }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "SyntaxError!",
6
6
  "email": "me@stxerr.dev"
7
7
  },
8
- "version": "5.1.0",
8
+ "version": "5.2.1",
9
9
  "license": "GPL-3.0-only",
10
10
  "private": false,
11
11
  "engines": {
@@ -17,40 +17,49 @@
17
17
  },
18
18
  "type": "module",
19
19
  "bin": {
20
- "manybot": "./src/main.js"
20
+ "manybot": "dist/main.js"
21
21
  },
22
22
  "files": [
23
- "src/client",
24
- "src/config.js",
25
- "src/download",
26
- "src/i18n",
27
- "src/kernel",
28
- "src/locales",
29
- "src/logger",
30
- "src/main.js",
31
- "src/utils",
23
+ "dist/",
32
24
  "README.md",
33
- "README_EN.md",
34
- "latest",
35
25
  "LICENSE"
36
26
  ],
27
+ "scripts": {
28
+ "build": "tsc && node -e \"fs.mkdirSync('dist/locales', { recursive: true }); fs.cpSync('src/locales', 'dist/locales', { recursive: true });\"",
29
+ "start": "node dist/main.js",
30
+ "typecheck": "tsc --noEmit"
31
+ },
32
+ "devDependencies": {
33
+ "@types/better-sqlite3": "^7.6.12",
34
+ "@types/node": "^22.0.0",
35
+ "tsx": "^4.19.2",
36
+ "typescript": "^5.7.3"
37
+ },
37
38
  "dependencies": {
39
+ "@clack/prompts": "^0.10.0",
40
+ "@hapi/boom": "^10.0.1",
41
+ "@whiskeysockets/baileys": "^6.7.23",
42
+ "better-sqlite3": "^12.11.1",
43
+ "node-cron": "^4.6.0",
38
44
  "node-webpmux": "^3.2.1",
45
+ "pino": "^10.3.1",
39
46
  "qrcode-terminal": "^0.12.0",
40
- "smol-toml": "^1.7.0",
41
- "tar": "^7.5.16",
42
- "whatsapp-web.js": "^1.24.0"
47
+ "smol-toml": "^1.7.0"
43
48
  },
44
49
  "imports": {
45
- "#client/*": "./src/client/*.js",
46
- "#kernel/*": "./src/kernel/*.js",
47
- "#manyapi": "./src/kernel/pluginApi.js",
48
- "#sendguard": "./src/kernel/sendGuard.js",
49
- "#logger": "./src/logger/logger.js",
50
- "#utils/*": "./src/utils/*.js",
51
- "#i18n": "./src/i18n/index.js",
52
- "#download": "./src/download/queue.js",
53
- "#config": "./src/config.js",
54
- "#main": "./src/main.js"
50
+ "#drivers/*": "./dist/drivers/*",
51
+ "#core/*": "./dist/core/*",
52
+ "#client/*": "./dist/client/*",
53
+ "#kernel/*": "./dist/kernel/*",
54
+ "#manyapi": "./dist/kernel/pluginApi.js",
55
+ "#settingsdb": "./dist/kernel/settingsDb.js",
56
+ "#sendguard": "./dist/kernel/sendGuard.js",
57
+ "#logger": "./dist/logger/logger.js",
58
+ "#utils/*": "./dist/utils/*",
59
+ "#i18n": "./dist/i18n/index.js",
60
+ "#download": "./dist/download/queue.js",
61
+ "#config": "./dist/config.js",
62
+ "#main": "./dist/main.js",
63
+ "#types": "./dist/types.js"
55
64
  }
56
65
  }