Análisis OMniLeads

Visión General

OMniLeads es un contact center open source que integra canales de mensajería, telefónica, y herramientas de gestión de campañas. Es el proyecto de referencia más completo en el ecosistema open source para contact center.

AspectoDetalle
Repositoriogithub.com/omnileads/omnileads
StackPython 3.9, Django 3.2, PostgreSQL, Redis, Kamailio
LicenciaGNU LGPL v3
EstadoEn desarrollo activo
DeployDocker Compose, Ansible

Arquitectura

OMniLeads sigue una arquitectura de monolito Django con 13 aplicaciones internas, cada una responsable de un subdominio del negocio:

Aplicaciones Principales

AppResponsabilidad
omnileadsCore del sistema, configuración global
campaignsGestión de campañas outbound
contactsGestión de contactos y segmentación
queuesColas de distribución de llamadas
ivrFlujos IVR y encuestas
dialerIntegración con discadores externos
telephonyIntegración con Asterisk/Kamailio
webhooksNotificaciones y eventos
reportsReporting y métricas
auditAuditoría de acciones
usersGestión de usuarios y permisos
brandsMulti-tenancy por marca
storageAlmacenamiento de grabaciones

Flujo de Datos

WhatsApp API ─┐
Facebook API ─┤
Instagram API ─┤→ Channel Adapters → Django Views → Domain Logic → PostgreSQL
Email API ────┤                                         ↓
SIP Proxy ────┘                              Redis (cache, events)
                                                    ↓
                                              WebSocket (realtime)

Modelo de Dominio

Campaña (Campaign)

Las campañas son la unidad central del sistema. Cada campaña tiene un ciclo de vida bien definido:

Estados de la Campaña:

EstadoDescripción
manualConfiguración inicial, sin activación
dialerModo discador outbound, activa llamadas
incomingModo recepción, solo recibe llamadas
previewModo preview, agente ve datos antes de conectar

Atributos clave:

  • name: Nombre de la campaña
  • type: manual | dialer | incoming | preview
  • queue: Cola asociada
  • dialer_type: tipo de discador (si aplica)
  • schedule: horario de operación
  • max_attempts: máximo de intentos por contacto
  • time_zone: timezone de la campaña

Contacto (Contact)

Representa una persona externa que interactúa con la organización:

  • Datos personales (nombre, email, teléfono)
  • Campos personalizados (dynamic fields)
  • Etiquetas y segmentación
  • Historial de interacciones
  • Fuente de origen (canal, campaña)

Perfil de Agente (AgentProfile)

Estado y configuración de cada agente del sistema:

  • Estado actual: available, pause, on_call, offline
  • Canales habilitados
  • Skills y competencias
  • Campañas asignadas
  • Estadísticas de productividad

Cola (Queue)

Define cómo se distribuyen las interacciones:

  • Estrategia de distribución: ring_all, round_robin, least_idle
  • Horario de operación
  • Overflow conditions
  • Música de espera y mensajes

IVR (Inboundivr)

Flujos de respuesta de voz interactiva:

  • nodos conectados en grafo
  • Tipos de nodo: menú, input, transferencia, hangup,http
  • Variables de sesión
  • Integración con ARI para control de llamadas

Canales

WhatsApp

OMniLeads soporta dos proveedores de WhatsApp Business API:

  1. GupShup: Proveedor indirecto, acceso rápido a API
  2. Meta Cloud API: API oficial directa de Meta

La integración se realiza mediante webhooks entrantes y polling de mensajes.

Facebook Messenger

Integración directa con la API de Facebook Messenger para páginas de negocio.

Instagram

Soporte para Direct Messages de Instagram Business.

Email

Recepción vía IMAP, envío vía SMTP. Soporte para plantillas HTML.

Webchat Widget

Widget JavaScript embeddable que permite聊天 desde cualquier sitio web.


Telefonía

Integración con Asterisk/Kamailio

OMniLeads utiliza un enfoque de dual stack telefónica:

  1. Kamailio: Proxy SIP para registro de agentes y routing de llamadas
  2. Asterisk: Media server para IVR, grabación, y bridging

Generación de Dialplan

El sistema genera dinámicamente configuración de Asterisk (dialplan) mediante conexiones SSH a los servidores telefónicos:

# Patrón simplificado (no código real)
def generate_dialplan(campaign):
    config = render_dialplan_template(campaign)
    ssh_connect(asterisk_host)
    ssh_write(config, '/etc/asterisk/extensions_custom.conf')
    ssh_command('asterisk -rx "dialplan reload"')

Debilidad: Este patrón de generación vía SSH es frágil y difícil de testear. Un enfoque basado en ARI sería más limpio.

Integración con Discador

La integración con discadores externos (Wombat Dialer / Omnidialer) se realiza vía HTTP REST:

  • El discador consulta la lista de contactos
  • El discador inicia llamadas y notifica eventos
  • OMniLeads actualiza el estado de la campaña

Dialer

Enfoque Actual

OMniLeads no implementa su propio discador. En su lugar, se integra con discadores externos:

  • Wombat Dialer: Discador comercial (legacy)
  • Omnidialer: Discador open source moderno

Interfaz de Integración

La integración se realiza mediante endpoints REST:

EndpointMétodoDescripción
/api/v1/campaigns/{id}/contactsGETObtener contactos para discar
/api/v1/campaigns/{id}/statusPOSTNotificar estado de llamada
/api/v1/agents/{id}/statusPOSTNotificar cambio de estado de agente

Realtime

Django Channels WebSocket

OMniLeads utiliza Django Channels con Redis como capa de transporte para comunicación en tiempo real:

  • Actualización de estado de agentes
  • Nuevos mensajes entrantes
  • Cambios de estado de campañas
  • Notificaciones de SLA

Redis Streams

Los eventos de alto volumen (llamadas, mensajes) se procesan mediante Redis Streams, permitiendo:

  • Procesamiento asíncrono
  • Consumidores group
  • Persistencia temporal de eventos

Fortalezas

  1. Modelo de dominio completo: Campañas con 4 estados, IVR con grafo, colas, contactos con campos dinámicos
  2. Producción probada: Utilizado en entornos reales de contact center en Latam
  3. Cobertura de canales: WhatsApp (2 proveedores), Facebook, Instagram, Email
  4. Integración telefónica: Kamailio + Asterisk, generación de dialplan
  5. Documentación: Deploy con Docker y Ansible, documentación operativa

Debilidades

1. Archivo God de 4170 líneas

El archivo principal de vista contiene lógica de presentación, dominio, y acceso a datos mezcladas. Esto dificulta:

  • Testing unitario
  • Mantenimiento
  • Reutilización de lógica

2. Sin Multi-tenancy Real

OMniLeads opera como sistema single-tenant. No hay aislamiento de datos entre organizaciones不同. El concepto de brand existe pero no implementa separación real.

3. Mezcla de Síncrono/Asíncrono

La arquitectura mezcla operaciones síncronas (Django views) con asíncronas (Django Channels, Redis Streams) sin una capa clara de separación.

4. Django 3.2 (End of Life)

Django 3.2 alcanzó EOL en abril 2024. La migración a Django 4.x+ es necesaria pero no trivial.

5. Generación de Dialplan vía SSH

El patrón de generar configuración de Asterisk mediante SSH es frágil:

  • Sin atomicidad en la escritura
  • Sin rollback en caso de error
  • Sin validación previa del config
  • Dificulta el testing automatizado

6. Sin API REST Documentada

No existe documentación OpenAPI/Swagger de la API, dificultando integraciones de terceros.


Licencia: GNU LGPL v3

¿Qué Significa LGPL v3?

  • LGPL (Lesser General Public License): Permite usar la biblioteca en productos proprietarios, pero con restricciones
  • Si se modifica la biblioteca LGPL, las modificaciones deben开源
  • El producto que usa la biblioteca puede ser proprietario, pero el vínculo debe ser dinámico

Implicaciones para Nuestro Producto

Escenario¿Permitido?
Usar OMniLeads como referencia conceptual
Copiar fragmentos de códigoNo (obliga a LGPL)
Linkar dinámicamente contra OMniLeadsSí, pero el producto debe ser LGPL
Incluir OMniLeads como dependenciaSí, pero todo el stack debe ser LGPL

Decisión

No reutilizar código de OMniLeads. Usar solo como referencia de diseño y modelo de dominio.


Conceptos Reutilizables (Como Referencia)

1. Estados de Campaña

El ciclo de vida de 4 estados (Manual → Dialer/Incoming/Preview) es un patrón sólido:

manual ──┬──→ dialer (outbound)
         ├──→ incoming (inbound)
         └──→ preview (agente revisa antes de conectar)

2. Modelo de Grafo IVR

La representación de flujos IVR como grafo de nodos conectados es el patrón estándar de la industria. Cada nodo tiene tipo, datos de configuración, y conexiones a otros nodos.

3. Interfaz Abstracta de Discador

La separación entre el sistema de campañas y el discador externo es un buen patrón de desacoplamiento.

4. Patrón de Adaptadores de Canal

Cada canal (WhatsApp, Facebook, Email) implementa una interfaz común de recepción/envío de mensajes.


Patrones de Referencia

Channel Adapter Paralela

Cada canal mantiene su propio adapter con lógica específica:

ChannelAdapter (interfaz)
├── WhatsAppAdapter (GupShup/Meta)
├── FacebookAdapter
├── InstagramAdapter
├── EmailAdapter
└── WebchatAdapter

WebSocket Notification Dispatch

Notificaciones en tiempo real mediante patrón pub/sub sobre WebSocket:

Evento → Redis Stream → Consumer → WebSocket → Frontend