DEV Community

Cover image for WPPCONNECT Server, Docker Deploy
Carlos Sanchez
Carlos Sanchez

Posted on

WPPCONNECT Server, Docker Deploy

Hola developers, hoy les comparto mi experiencia y frustración en la implementación de la librería wwpconnect a través de su API REST y todo el desafío a la hora de desplegar en producción.
¿Quién no se ha enfrentado con el típico problema donde vamos iniciando en la empresa o en el producto y queremos ahorrar recursos?

Todo un reto encontrarme con el requerimiento de querer usar una librería no oficial de WhatasApp web y no tener experiencia en ello.

Image description

Hoy les comparto mis primeros pasos para iniciar con el despliegue a producción de la librería.
Iniciamos descargando el repositorio desde GitHub , nos encontraremos con un proyecto completo, con poca necesidad de cambios para realizar un despliegue completo y funcional.

Una vez clonado, iniciamos la configuración de Docker.

  1. Creamos un fichero Dockerfile donde vamos a especificar el directorio y los comandos para prepararlo a producción.
FROM node:lts-alpine3.18 as base
WORKDIR /app
ENV NODE_ENV=production PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
COPY package.json ./
RUN yarn install --production --pure-lockfile && \
    yarn cache clean
Enter fullscreen mode Exit fullscreen mode

Especificamos que base de node usaremos, además especificamos el directorio donde enviaremos todos los archivos necesarios para la ejecución, exportaremos una variable de puppeteer skip chromium donde le decimos que no genere cada vez que compile, una descarga nueva de chromiun, por último copias el package al directorio raíz y corremos la instalación de dependencias.

  1. Pasamos a la generación de los archivos build para ejecutar nuestro proyecto compilado.
FROM base as build
WORKDIR /app
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
COPY package.json  ./
RUN yarn install --production=false --pure-lockfile && \
    yarn cache clean
COPY . .
RUN yarn build
Enter fullscreen mode Exit fullscreen mode
  1. Agregamos chromiun y la ejecución de nuestro proyecto.
FROM base
WORKDIR /app/
RUN apk add --no-cache chromium
RUN yarn cache clean
COPY . .
COPY --from=build /app/ /app/
EXPOSE 21465
ENTRYPOINT ["node", "dist/server.js"]
Enter fullscreen mode Exit fullscreen mode
  1. Creamos nuestro archivo compose de docker y para poder correr nuestra API en el demonio de docker.
version: '3.9'
services:
  wppconnect:
    build:
      context: .
    volumes:
      - ./config.ts:/app/config.ts
      - ./wppconnect_tokens:/app/tokens
    ports:
      - '21465:21465'
  mongo:
    image: mongo:latest
    restart: always
    container_name: mongo
    environment:
      MONGO_INITDB_ROOT_USERNAME: test-app
      MONGO_INITDB_ROOT_PASSWORD: 12345678a
    ports:
      - '27017:27017'
volumes:
  mongodb_data_container:
  wppconnect_tokens: {}

Enter fullscreen mode Exit fullscreen mode
  1. Por último corremos en nuestro cloud el comando de docker-compose up -d y disfrutamos nuestro servicio de WhatsApp Web.

Conclusión:
WppConnect API un proyecto donde nos encontramos con una solución no tan directa con WhatsApp Web, pero con gran facilidad de implementar para poder cumplir con un requerimiento y de fácil uso.

Image description

Carlos Sanchez, developer Full-Stack JavaScript/TypeScript

Top comments (1)

Collapse
 
manuel_aimeida_ece04f57b5 profile image
Manuel AImeida

Saludos Carlos Sanchez, yo hice un previo con Selenium y me facilito mucho la conexion de multiples cuentas whatsaap, aunque aun no he medido la posibilidad de bloqueos de la ip de parte de whatsapp.

Entre articulos me encontre con WPPCONNECT, y es frustrante no poder pasar del paso del token. Ya con el docker en ejecucion intento interactuar con el pero me dice que no puede generar el token. Si esta en tus posibilidades puedes darme una luz en esta situacion. Te agradezco

import requests
import json
import time
from urllib.parse import quote

=================================================================

CONFIGURACIÓN DE PARÁMETROS

=================================================================

SERVER_URL = "localhost:8081"
SESSION_NAME = "python_client_session"
SECRET_KEY = "tucontraseñasecreta"

TARGET_NUMBER = "584514555999"
MESSAGE_TEXT = "Hola Mundo"

=================================================================

FUNCIONES DE LA API

=================================================================

def generate_token():
"""
Función de última instancia: Genera el token enviando la clave secreta y la sesión
en el cuerpo (body) a la ruta simple /api/generate-token.
"""

# RUTA MÁS MODERNA: POST /api/generate-token (con body)
token_endpoint = f"{SERVER_URL}/api/generate-token"

# Parámetros en el body
payload = {
    "secret": SECRET_KEY,
    "sessionName": SESSION_NAME 
}

headers = {"Content-Type": "application/json"}

print(f"\n🔑 Solicitando token de autenticación (Body Method) en: {token_endpoint}")

try:
    response = requests.post(token_endpoint, headers=headers, data=json.dumps(payload))
    response.raise_for_status() 

    try:
        token = response.json().get('token')
    except json.JSONDecodeError:
        token = response.text

    if not token or len(token) < 10: 
        print("❌ Error: La respuesta de la API no contiene el token esperado.")
        print("Detalles de la respuesta:", response.text)
        return None

    print("✅ Token generado con éxito.")
    return token

except requests.exceptions.RequestException as e:
    print(f"❌ Error al generar token. Detalle: {e}")
    return None
Enter fullscreen mode Exit fullscreen mode

def start_session(auth_token):
# (Resto de la función start_session, sin cambios)
start_endpoint = f"{SERVER_URL}/api/{SESSION_NAME}/start-session"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {auth_token}"
}
payload = {"webhook": None, "waitQrCode": False}

print(f"\n🚀 Iniciando sesión '{SESSION_NAME}'...")
try:
    response = requests.post(start_endpoint, headers=headers, data=json.dumps(payload))
    response.raise_for_status()

    print("✅ Sesión iniciada.")
    print(f"🔔 Puedes ver el QR accediendo a: {SERVER_URL}/api/{SESSION_NAME}/qrcode-session")
    return True
except requests.exceptions.RequestException as e:
    print(f"❌ Error al iniciar la sesión: {e}")
    return False
Enter fullscreen mode Exit fullscreen mode

def check_connection(auth_token):
# (Resto de la función check_connection, sin cambios)
status_endpoint = f"{SERVER_URL}/api/{SESSION_NAME}/check-connection-session"
headers = {"Authorization": f"Bearer {auth_token}"}

timeout = time.time() + 120 

print("\n⏳ Esperando conexión (escanea el código QR)...")
while time.time() < timeout:
    try:
        response = requests.get(status_endpoint, headers=headers)
        response.raise_for_status()
        status = response.json().get('status')

        if status == 'CONNECTED':
            print("\n🎉 ¡Conexión exitosa! El código QR ha sido escaneado.")
            return True
        else:
            print(f"⏳ Estado actual: {status}. Reintentando en 5s...")

        time.sleep(5) 

    except requests.exceptions.RequestException as e:
        print(f"❌ Error al verificar estado: {e}. Reintentando en 5s...")
        time.sleep(5)
    except Exception as e:
        print(f"❌ Error inesperado: {e}")
        time.sleep(5)

print("\n⛔ El tiempo de espera para la conexión ha expirado.")
return False
Enter fullscreen mode Exit fullscreen mode

def send_text_message(auth_token):
# (Resto de la función send_text_message, sin cambios)
send_endpoint = f"{SERVER_URL}/api/{SESSION_NAME}/send-text"

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {auth_token}"
}

data = {
    "phone": TARGET_NUMBER, 
    "message": MESSAGE_TEXT
}

print(f"\n📤 Enviando mensaje '{MESSAGE_TEXT}' a {TARGET_NUMBER}...")
try:
    response = requests.post(send_endpoint, headers=headers, data=json.dumps(data))
    response.raise_for_status()

    print("✅ Mensaje enviado con éxito.")
    print(f"Respuesta de la API: {response.json()}")
except requests.exceptions.RequestException as e:
    print(f"❌ Error al enviar el mensaje: {e}")
Enter fullscreen mode Exit fullscreen mode

=================================================================

PROCEDIMIENTO PRINCIPAL

=================================================================

if name == "main":

token = generate_token()
if not token:
    print("\n⛔ Proceso terminado debido a un error de autenticación/conexión.")
    exit()

if not start_session(token):
    print("\n⛔ Proceso terminado debido a un error al iniciar la sesión.")
    exit()

if check_connection(token):
    send_text_message(token)
else:
    print("\n⛔ Proceso terminado. La conexión no se pudo establecer.")
Enter fullscreen mode Exit fullscreen mode