Creando un bot de feedback MVP para Max.ru con polling largo y SQLite
Max.ru ofrece a los desarrolladores acceso a su API de bots para crear herramientas interactivas. Para este bot de feedback MVP, utilizamos Node.js con la biblioteca @maxhub/max-bot-api y SQLite. El polling largo permite el desarrollo local sin necesidad de webhooks, servidores ni HTTPS.
El mayor desafío consiste en identificar al cliente cuando el administrador responde. Los mensajes de respuesta incluyen un enlace con el mid del mensaje original, lo que nos permite mapear las respuestas de vuelta a los usuarios mediante la base de datos.
Elección de la pila tecnológica y configuración del proyecto
La biblioteca @maxhub/max-bot-api soporta polling largo de forma nativa, eliminando la necesidad de ngrok o Cloudflare Tunnel durante el desarrollo.
SQLite es ideal para un MVP: es una base de datos basada en archivos, sin servidor separado, con latencia mínima y manejo consistente de solicitudes.
Instala las dependencias:
npm init -y
npm install @maxhub/max-bot-api sqlite sqlite3 dotenv
Crea un archivo .env:
BOT_TOKEN=your_token_here
OWNER_ID=12345678
El token se genera en business.max.ru, y OWNER_ID es tu ID de usuario personal en Max.
Inicializando SQLite y estructura de la base de datos
Usa conexión asíncrona y crea la tabla reply_map para el mapeo:
import { open } from 'sqlite';
import sqlite3 from 'sqlite3';
let db;
(async () => {
db = await open({ filename: './database.sqlite', driver: sqlite3.Database });
await db.exec(`
CREATE TABLE IF NOT EXISTS reply_map (
owner_msg_mid TEXT PRIMARY KEY,
client_user_id INTEGER
)
`);
console.log('Base de datos lista');
})();
Esta tabla almacena el mapeo desde owner_msg_mid hasta client_user_id.
Manejo de mensajes de los clientes
El evento message_created verifica al remitente. Los mensajes de clientes se reenvían al administrador con formato Markdown:
const ownerId = Number(process.env.OWNER_ID);
bot.on('message_created', async (ctx) => {
const msg = ctx.message;
const senderId = msg.sender.user_id;
const text = msg.body.text;
if (senderId !== ownerId) {
const forwardText = `📩 **Mensaje de ${msg.sender.first_name}** (ID: ${senderId}):
${text}`;
const sentMsg = await bot.api.sendMessageToUser(ownerId, forwardText, { format: 'markdown' });
if (sentMsg && sentMsg.body && sentMsg.body.mid) {
await db.run('INSERT OR REPLACE INTO reply_map (owner_msg_mid, client_user_id) VALUES (?, ?)',
[sentMsg.body.mid, senderId]);
}
return;
}
// lógica de respuesta
});
El mid del mensaje enviado se almacena en reply_map para su posterior enrutamiento.
Lógica de respuesta del administrador
Los mensajes de respuesta contienen link.type = 'reply' y link.message.mid — el ID del mensaje original. Para encontrar al cliente:
- Si
link.type === 'reply': extraerepliedMsgMid, consulta la DB. - Envía la respuesta al cliente usando
client_user_id. - Confirma la recepción al administrador.
Estructura JSON de ejemplo para la respuesta:
{
"body": { "text": "respuesta", ... },
"sender": { ... },
"link": {
"type": "reply",
"message": {
"mid": "mid.mensaje_original..."
}
}
}
Beneficios de esta arquitectura
- Desarrollo local: Ejecuta con
npm start— no necesitas puertos ni configuración SSL. - Interfaz de respuesta: Los administradores usan la función estándar de respuesta sin problemas.
- Base de datos sencilla: Un solo archivo (
database.sqlite) para copias de seguridad fáciles. - Escalabilidad: Migración sencilla a PostgreSQL conforme crece el tráfico.
Conclusiones clave
- El polling largo simplifica el desarrollo de MVP al eliminar la necesidad de webhooks.
- La tabla
reply_mapde SQLite asegura un enrutamiento preciso de las respuestas. - El uso de Markdown en los mensajes reenviados añade contexto (nombre del cliente, ID).
- Respuesta de respaldo: si no hay coincidencia, se usa al último cliente activo.
— Editorial Team
Aún no hay comentarios.