Problema
En entornos de auto‑hosting es frecuente querer ofrecer a los usuarios una forma sencilla de archivar páginas web sin salir de Discord. El patrón típico consiste en:
- Un bot que recibe una URL a través de un mensaje.
- El bot abre la página con un motor sin cabeza, genera un archivo MHTML y lo guarda en un almacenamiento permanente.
Los obstáculos aparecen cuando el bot necesita escribir en un NAS o en otro recurso compartido, cuando las dependencias del motor sin cabeza son incompatibles con la distribución del servidor, o cuando el proceso se cae al cerrar la sesión SSH. En muchos casos el bot funciona en pruebas locales pero falla en producción por problemas de montaje, versiones de Node, o gestión de procesos.
Causa
Los fallos más habituales provienen de tres áreas:
- Montaje del share CIFS/SMB – Si el punto de montaje no está persistente, el bot no encuentra la ruta de destino y aborta. Permisos incorrectos en el archivo de credenciales o en el punto de montaje también provocan “permission denied”.
- Dependencias de Chromium – Puppeteer necesita bibliotecas del sistema (libnss3, libatk‑bridge2.0, etc.). En distribuciones minimalistas esas librerías faltan y el lanzamiento del navegador termina con errores de “cannot open shared object file”.
- Gestión del proceso – Ejecutar
node discord-agent.jsdirectamente funciona mientras la sesión SSH está activa. Al cerrar la terminal el proceso se mata, y al reiniciar el host el bot no vuelve a iniciarse.
A menudo aparecen combinaciones: un NAS montado con la versión 2.0 de SMB que requiere la opción vers=3.0, o una instalación de Node vía apt que deja paquetes rotos y obliga a forzar la instalación.
Solución
Una estrategia reutilizable cubre los tres puntos críticos:
-
Montar el share de forma persistente y segura
- Instalar
cifs-utils. - Crear un archivo de credenciales con permisos 600.
- Añadir la línea adecuada a
/etc/fstabusando la versión SMB que soporte el NAS (generalmente 3.0). - Probar el montaje con
mount -ay validar la escritura contouch.
- Instalar
-
Instalar Node.js y Chromium con sus dependencias
- Usar el script oficial de NodeSource para obtener la última versión LTS (v20 al momento).
- Evitar el paquete
npmde apt; el instalador de Node incluye su propio npm. - Instalar las bibliotecas de Chromium listadas en la documentación de Puppeteer. En Debian/Ubuntu basta con un único
apt-get installque incluye todas.
-
Ejecutar el bot bajo PM2
- PM2 mantiene el proceso activo, lo reinicia tras caídas y genera scripts de inicio automático (
pm2 startup). - Guardar el estado con
pm2 savepara que se recupere tras reboot.
- PM2 mantiene el proceso activo, lo reinicia tras caídas y genera scripts de inicio automático (
Con este enfoque el bot puede recibir URLs, generar MHTML y depositar los archivos directamente en el NAS sin intervención manual. Además, el uso de PM2 permite actualizar el código y aplicar pm2 restart sin perder disponibilidad.
Alternativas prácticas
- Docker – Empaquetar el bot y sus dependencias en un contenedor simplifica la gestión de versiones, pero requiere montar el share dentro del contenedor (
-v /mnt/mhtml_archive:/archive). - VPN/Tunnel – Si el NAS está detrás de una red aislada, crear un túnel SSH o usar WireGuard permite montar el share como si estuviera local.
Cuándo aplicar esta solución
Aplica cuando:
- Necesitas archivar páginas web de forma automática desde Discord o cualquier otro chat bot.
- El destino de los archivos es un NAS, un disco compartido o un volumen montado vía CIFS/SMB.
- El servidor corre una distribución basada en Debian/Ubuntu y se prefiere una instalación nativa de Node.js.
No es la mejor opción si:
- El entorno está completamente contenerizado y prefieres orquestación con Kubernetes.
- El NAS solo soporta NFS y no CIFS; en ese caso se cambia la configuración de montaje.
Código
# 1. Instalación de utilidades CIFS y creación del punto de montaje
sudo apt-get update
sudo apt-get install -y cifs-utils
sudo mkdir -p /mnt/mhtml_archive
sudo nano ~/.smbcredentials # agrega: username=USUARIO password=CONTRASEÑA
chmod 600 ~/.smbcredentials
# 2. Añadir entrada a /etc/fstab (reemplaza los placeholders)
echo "//NAS_IP/ShareName /mnt/mhtml_archive cifs credentials=/home/$USER/.smbcredentials,uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0,noperm 0 0" | sudo tee -a /etc/fstab
# 3. Montar y validar
sudo mount -a
touch /mnt/mhtml_archive/prueba.txt && echo "ok" > /mnt/mhtml_archive/prueba.txt
# 4. Instalar Node.js v20 (NodeSource)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# 5. Instalar dependencias de Chromium
sudo apt-get install -y libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
libgbm1 libasound2
# 6. Preparar proyecto
mkdir -p ~/mhtml-discord-bot && cd ~/mhtml-discord-bot
npm init -y
npm install discord.js puppeteer
# 7. Guardar script (discord-agent.js) – copia el contenido del post original y ajusta el token
# 8. Instalar y lanzar PM2
sudo npm install -g pm2
pm2 start discord-agent.js --name "mhtml-bot"
pm2 save
pm2 startup # sigue las instrucciones que PM2 muestra
Verificación
- Montaje – Ejecuta
df -h | grep mhtml_archive. La salida debe mostrar el share montado y espacio disponible. - Permisos –
ls -l /mnt/mhtml_archivedebe listar el archivoprueba.txtcon tu UID/GID. - Bot activo –
pm2 statusmuestramhtml-boten estadoonline. - Prueba funcional – Envía
!backup https://example.comal canal de Discord. El bot debe responder con una reacción y, tras unos segundos, publicar el archivo MHTML o indicar que se guardó en el NAS. - Persistencia – Reinicia el servidor (
sudo reboot). Tras el arranque, verifica nuevamentepm2 statusy que el share sigue montado.
Notas adicionales
- Captchas y Cloudflare – Algunos sitios lanzan desafíos que Puppeteer no puede resolver sin intervención humana. En esos casos, considera usar un proxy rotativo o ejecutar el bot detrás de una VPN que ya tenga la sesión autenticada.
- Tamaño de archivo – Discord impone un límite de 25 MiB por archivo. El script ya incluye una comprobación; si el MHTML supera ese umbral, el bot solo guarda en el NAS y notifica al usuario.
- Actualizaciones de Puppeteer – Cada versión mayor de Chromium puede requerir nuevas librerías. Si el bot deja de lanzar, revisa los cambios de dependencias en la página de releases de Puppeteer.
- Seguridad – Mantén el archivo
.smbcredentialsfuera del repositorio y usa permisos restrictivos. Si el bot necesita acceso a varios shares, crea un usuario de solo lectura/escritura dedicado en el NAS. - Logs – PM2 guarda logs en
~/.pm2/logs. Configura una rotación conlogrotatesi el bot genera mucho tráfico.