primer commit
This commit is contained in:
185
nucleo-bot/indexv1.js
Normal file
185
nucleo-bot/indexv1.js
Normal file
@@ -0,0 +1,185 @@
|
||||
import express from 'express';
|
||||
import axios from 'axios';
|
||||
import morgan from 'morgan';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc.js'; // ⬅ OJO: “.js” si usás ESM puro
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
// ───────────────────────────────────────────
|
||||
// ⚙️ Variables de entorno (con defaults)
|
||||
const VERSION = '0.3.21';
|
||||
const API_URL = process.env.BOT_API_URL ?? 'http://whatsapp-bot:8002';
|
||||
const GROUP_ID = process.env.GROUP_ID ?? '120363203056794284@g.us';
|
||||
const REPLY_MSG = process.env.REPLY_MSG ?? 'que pedos';
|
||||
const PORT = +(process.env.PORT ?? 4000);
|
||||
const LOG_LEVEL = process.env.LOG_LEVEL ?? 'debug';
|
||||
const RETRY_MS = +(process.env.RETRY_MS ?? 5_000); // 5 s
|
||||
const MAX_ATTEMPTS = +(process.env.MAX_ATTEMPTS ?? 60);// 5 min máx
|
||||
// ───────────────────────────────────────────
|
||||
|
||||
// ► Logger interno
|
||||
function log(level, ...args) {
|
||||
const levels = ['debug', 'info', 'warn', 'error'];
|
||||
if (levels.indexOf(level) >= levels.indexOf(LOG_LEVEL)) {
|
||||
console[level === 'debug' ? 'log' : level](
|
||||
`[${dayjs().utc().format()}]`, level.toUpperCase(), ...args
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────
|
||||
const app = express();
|
||||
app.use(express.json({ limit: '1mb' }));
|
||||
app.use(morgan('[:date[iso]] :method :url :status - :response-time ms'));
|
||||
|
||||
// ► Util para enviar texto
|
||||
async function sendText(to, content) {
|
||||
log('info', `Enviando mensaje a ${to}: "${content}"`);
|
||||
const { data } = await axios.post(`${API_URL}/sendText`, { args: { to, content } });
|
||||
log('debug', 'Respuesta de /sendText →', data);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ► Procesar mensajes entrantes
|
||||
async function processIncoming(msg) {
|
||||
const text = msg.body ?? msg.text ?? '';
|
||||
|
||||
// 🧠 Extraemos info útil
|
||||
const info = {
|
||||
idMensaje: msg.id ?? msg.mId ?? null,
|
||||
texto: text,
|
||||
esDeGrupo: msg.isGroupMsg ?? false,
|
||||
grupoId: msg.chatId ?? null,
|
||||
nombreGrupo: msg.chat?.name ?? msg.chat?.formattedTitle ?? null,
|
||||
participantesGrupo: msg.chat?.participantsCount ?? null,
|
||||
esPrivado: !(msg.isGroupMsg ?? false),
|
||||
autorId: msg.sender?.id ?? msg.author ?? null,
|
||||
autorNombre: msg.sender?.name ?? null,
|
||||
autorPushName: msg.sender?.pushname ?? null,
|
||||
autorEsContacto: msg.sender?.isMyContact ?? false,
|
||||
reenviado: msg.isForwarded ?? false,
|
||||
menciones: msg.mentionedJidList ?? [],
|
||||
citandoMensaje: msg.isQuotedMsgAvailable ?? false,
|
||||
tieneReaccion: msg.hasReaction ?? false,
|
||||
tipo: msg.type ?? 'chat',
|
||||
timestamp: msg.timestamp ?? null,
|
||||
fecha: msg.timestamp ? dayjs.unix(msg.timestamp).format('YYYY-MM-DD HH:mm:ss') : null,
|
||||
};
|
||||
|
||||
// 📋 Logueamos limpio
|
||||
log('debug', '↪︎ Mensaje IN procesado', info);
|
||||
|
||||
// 🚀 Acción si menciona al bot
|
||||
if (/@nucleo/i.test(info.texto)) {
|
||||
await sendText(GROUP_ID, REPLY_MSG);
|
||||
}
|
||||
|
||||
// 🚀 Acción si pide repetir
|
||||
if (/@nucleoRepeti/i.test(info.texto)) {
|
||||
const partes = info.texto.split(/@nucleoRepeti/i);
|
||||
const contenido = (partes[1]?.trim() || 'vacio');
|
||||
log('info', `📢 Reenviando: "${contenido}" al chat ${info.grupoId}`);
|
||||
await sendText(info.grupoId, contenido);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ──────── ENDPOINTS ─────────────────────────
|
||||
app.post('/webhook', async (req, res) => {
|
||||
const { event, data } = req.body;
|
||||
log('debug', `📩 Webhook event "${event}"`);
|
||||
if (event === 'onMessage' || event === 'onAnyMessage') await processIncoming(data);
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
app.get('/debug/scan', async (_req, res) => {
|
||||
const { data } = await axios.post(`${API_URL}/loadAndGetAllMessagesInChat`, {
|
||||
args: { chatId: GROUP_ID, includeMe: 'true', includeNotifications: 'false' }
|
||||
});
|
||||
const msgs = (data?.response ?? []).slice(-20);
|
||||
log('info', `Escaneando ${msgs.length} mensajes recientes…`);
|
||||
for (const m of msgs) await processIncoming(m);
|
||||
res.json({ ok: true, scanned: msgs.length });
|
||||
});
|
||||
|
||||
app.get('/debug/send', async (req, res) => {
|
||||
const text = req.query.msg ?? REPLY_MSG;
|
||||
const resp = await sendText(GROUP_ID, text);
|
||||
res.json({ ok: true, resp });
|
||||
});
|
||||
|
||||
app.get('/debug/version', (_req, res) => {
|
||||
res.json({ version: VERSION });
|
||||
});
|
||||
|
||||
// ────────── INICIALIZACIÓN ──────────────────
|
||||
async function waitForGateway() {
|
||||
for (let i = 1; i <= MAX_ATTEMPTS; i++) {
|
||||
try {
|
||||
await axios.get(`${API_URL}/api-docs`); // endpoint de salud de open-wa
|
||||
log('info', '🟢 whatsapp-gateway listo');
|
||||
return;
|
||||
} catch {
|
||||
log('warn', `Gateway no responde (intento ${i}/${MAX_ATTEMPTS})…`);
|
||||
await new Promise(r => setTimeout(r, RETRY_MS));
|
||||
}
|
||||
}
|
||||
throw new Error('whatsapp-gateway no respondió a tiempo');
|
||||
}
|
||||
|
||||
|
||||
async function clearWebhooks() {
|
||||
try {
|
||||
const { data } = await axios.post(`${API_URL}/listWebhooks`);
|
||||
const hooks = data?.response ?? [];
|
||||
if (!hooks.length) {
|
||||
log('info', 'Sin webhooks previos que limpiar');
|
||||
return;
|
||||
}
|
||||
|
||||
log('info', `Eliminando ${hooks.length} webhooks…`);
|
||||
|
||||
const results = await Promise.allSettled(
|
||||
hooks.map(h => axios.post(`${API_URL}/removeWebhook`, { args: { webhookId: h.id } }))
|
||||
// ⬆️ NOTA: ahora es "webhookId", no "id"
|
||||
);
|
||||
|
||||
results.forEach((result, idx) => {
|
||||
const id = hooks[idx].id;
|
||||
if (result.status === 'fulfilled' && result.value?.data?.response === true) {
|
||||
log('debug', `✔️ Eliminado webhook ${id}`);
|
||||
} else {
|
||||
log('warn', `⚠️ Falló eliminar webhook ${id}`);
|
||||
}
|
||||
});
|
||||
|
||||
const okCount = results.filter(r => r.status === 'fulfilled' && r.value?.data?.response === true).length;
|
||||
log('info', `Limpieza OK (${okCount}/${hooks.length} eliminados)`);
|
||||
} catch (e) {
|
||||
log('error', 'Fallo limpiando webhooks:', e.response?.data ?? e.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function registerWebhook() {
|
||||
const url = `http://nucleo-bot:${PORT}/webhook`;
|
||||
const { data } = await axios.post(`${API_URL}/registerWebhook`, {
|
||||
args: { url, events: ['onAnyMessage'], id: 'nucleo-bot' }
|
||||
});
|
||||
log('info', '✔️ Webhook registrado:', data);
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
await waitForGateway();
|
||||
await clearWebhooks();
|
||||
await registerWebhook();
|
||||
}
|
||||
|
||||
app.listen(PORT, () => {
|
||||
log('info', `🪪 Versión del bot: ${VERSION}`);
|
||||
log('info', `🚀 nucleo‑bot escuchando en :${PORT}`);
|
||||
bootstrap().catch(err => log('error', 'Error en bootstrap:', err.message));
|
||||
});
|
||||
Reference in New Issue
Block a user