@manybot/manybot 5.2.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.
- package/dist/client/banner.js +35 -0
- package/dist/client/store.js +135 -0
- package/dist/config.js +363 -0
- package/dist/core/adapter.js +12 -0
- package/dist/core/capabilities.js +16 -0
- package/dist/core/types.js +6 -0
- package/dist/download/queue.js +49 -0
- package/dist/drivers/index.js +14 -0
- package/dist/drivers/patches/index.js +9 -0
- package/dist/drivers/patches/libsignal.js +16 -0
- package/dist/drivers/patches/patch.js +1 -0
- package/dist/drivers/whatsapp/adapter.js +7 -0
- package/dist/drivers/whatsapp/api/index.js +1311 -0
- package/dist/drivers/whatsapp/index.js +164 -0
- package/dist/drivers/whatsapp/loginPrompt.js +81 -0
- package/dist/drivers/whatsapp/messageHandler.js +111 -0
- package/dist/drivers/whatsapp/sdk/baileysSock.js +124 -0
- package/dist/i18n/index.js +202 -0
- package/dist/kernel/pluginApi.js +11 -0
- package/dist/kernel/pluginGuard.js +88 -0
- package/dist/kernel/pluginLoader.js +322 -0
- package/dist/kernel/scheduler.js +110 -0
- package/dist/kernel/sendGuard.js +121 -0
- package/dist/kernel/settingsDb.js +205 -0
- package/dist/locales/en.json +52 -0
- package/dist/locales/es.json +52 -0
- package/dist/locales/pt.json +52 -0
- package/dist/logger/logger.js +16 -0
- package/dist/main.js +82 -0
- package/dist/types.js +16 -0
- package/dist/utils/file.js +9 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bot": {
|
|
3
|
+
"initialized": "Client initialized. Waiting for WhatsApp connection...",
|
|
4
|
+
"ready": "Bot is ready!",
|
|
5
|
+
"error": {
|
|
6
|
+
"uncaught": "Uncaught exception",
|
|
7
|
+
"unhandled": "Unhandled rejection"
|
|
8
|
+
},
|
|
9
|
+
"signal": {
|
|
10
|
+
"sigterm": "Process interrupted. Shutting down..."
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"system": {
|
|
14
|
+
"connected": "WhatsApp connected and ready!",
|
|
15
|
+
"disconnected": "Disconnected — reason: {{reason}}",
|
|
16
|
+
"qrScan": "Scan the QR Code below:",
|
|
17
|
+
"pairingCodeTitle": "Pairing code:",
|
|
18
|
+
"pairingCodeValue": "→ {{code}} ←",
|
|
19
|
+
"pairingCodeInstructions": "Enter this code in WhatsApp: Menu → Linked devices → Link with phone number",
|
|
20
|
+
"phoneNumberInvalid": "Invalid phone number: {{number}}. Expected format: 5511999999999 (country code + area code + number, no spaces or special characters)",
|
|
21
|
+
"clientId": "Client ID: {{id}}",
|
|
22
|
+
"pluginsLoaded": "Plugins loaded: {{count}} active{{errors}}",
|
|
23
|
+
"pluginsLoadedWithErrors": ", {{count}} with error",
|
|
24
|
+
"pluginSetupFailed": "Plugin \"{{name}}\" setup failed: {{message}}",
|
|
25
|
+
"pluginNotFound": "Plugin \"{{name}}\" not found at {{path}}",
|
|
26
|
+
"pluginLoaded": "Plugin loaded: {{name}}",
|
|
27
|
+
"pluginReloaded": "Plugin reloaded: {{name}}",
|
|
28
|
+
"pluginLoadFailed": "Failed to load plugin \"{{name}}\": {{message}}",
|
|
29
|
+
"schedulerInvalidCron": "Plugin \"{{name}}\" registered invalid cron expression: \"{{expression}}\"",
|
|
30
|
+
"schedulerError": "Plugin \"{{name}}\" scheduling error: {{message}}",
|
|
31
|
+
"schedulerRegistered": "Schedule registered — plugin \"{{name}}\" → \"{{expression}}\"",
|
|
32
|
+
"downloadJobFailed": "Download job failed — {{message}}",
|
|
33
|
+
"reconnecting": "Reconnecting in {{secs}}s...",
|
|
34
|
+
"sessionExpired": "Session expired or logged out — removing local session and restarting..."
|
|
35
|
+
},
|
|
36
|
+
"errors": {
|
|
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."
|
|
51
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bot": {
|
|
3
|
+
"initialized": "Cliente inicializado. Aguardando conexão com WhatsApp...",
|
|
4
|
+
"ready": "Bot está pronto!",
|
|
5
|
+
"error": {
|
|
6
|
+
"uncaught": "Exceção não capturada",
|
|
7
|
+
"unhandled": "Rejeição não tratada"
|
|
8
|
+
},
|
|
9
|
+
"signal": {
|
|
10
|
+
"sigterm": "Processo interrompido. Desligando..."
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"system": {
|
|
14
|
+
"connected": "WhatsApp conectado e pronto!",
|
|
15
|
+
"disconnected": "Desconectado — motivo: {{reason}}",
|
|
16
|
+
"qrScan": "Escaneie o QR Code abaixo:",
|
|
17
|
+
"pairingCodeTitle": "Código de pareamento:",
|
|
18
|
+
"pairingCodeValue": "→ {{code}} ←",
|
|
19
|
+
"pairingCodeInstructions": "Digite este código no WhatsApp: Menu → Dispositivos conectados → Conectar com número de telefone",
|
|
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)",
|
|
21
|
+
"clientId": "Client ID: {{id}}",
|
|
22
|
+
"pluginsLoaded": "Plugins carregados: {{count}} ativos{{errors}}",
|
|
23
|
+
"pluginsLoadedWithErrors": ", {{count}} com erro",
|
|
24
|
+
"pluginSetupFailed": "Falha na configuração do plugin \"{{name}}\": {{message}}",
|
|
25
|
+
"pluginNotFound": "Plugin \"{{name}}\" não encontrado em {{path}}",
|
|
26
|
+
"pluginLoaded": "Plugin carregado: {{name}}",
|
|
27
|
+
"pluginReloaded": "Plugin recarregado: {{name}}",
|
|
28
|
+
"pluginLoadFailed": "Falha ao carregar plugin \"{{name}}\": {{message}}",
|
|
29
|
+
"schedulerInvalidCron": "Plugin \"{{name}}\" registrou expressão cron inválida: \"{{expression}}\"",
|
|
30
|
+
"schedulerError": "Erro no agendamento do plugin \"{{name}}\": {{message}}",
|
|
31
|
+
"schedulerRegistered": "Agendamento registrado — plugin \"{{name}}\" → \"{{expression}}\"",
|
|
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..."
|
|
35
|
+
},
|
|
36
|
+
"errors": {
|
|
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."
|
|
51
|
+
}
|
|
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 {};
|