Skip to content

Controladores y Canales

Los controladores y canales trabajan juntos para gestionar la comunicación entre tu bot y los usuarios. Los controladores son componentes responsables de procesar las interacciones, implementar lógica de enrutamiento y conectar los mensajes con los mindsets apropiados. Los canales son las plataformas de mensajería (WhatsApp, Telegram, WebSocket, etc.) a través de las cuales los usuarios se comunican con tu bot.

Esta arquitectura separada permite que un mismo controlador maneje múltiples canales simultáneamente, creando experiencias multicanal sin duplicar lógica de negocio. Los controladores actúan como el cerebro que decide qué hacer con cada mensaje, mientras que los canales son las vías de comunicación.

Controladores

Los controladores son clases decoradas con @chatController() que reciben mensajes de los usuarios y determinan cómo procesarlos. Pueden:

  • Conectar mensajes con mindsets específicos usando la inyección de ChatBot
  • Implementar lógica de enrutamiento para seleccionar dinámicamente qué mindset debe procesar cada mensaje
  • Gestionar el flujo conversacional según el contexto del usuario, su estado o condiciones de negocio
  • Manejar múltiples canales desde un mismo punto centralizado

Importaciones básicas:

import {
chatController,
chatBot,
ChatBot,
type IReceivedMessage
} from '@wabot-dev/framework'

Canales

Los canales se configuran mediante decoradores que se aplican a los métodos del controlador. Cada decorador configura una plataforma de mensajería específica:

Decorador @cmd()

El decorador @cmd() configura un canal genérico de línea de comandos, útil para desarrollo, testing y ambientes de consola.

import { cmd } from '@wabot-dev/framework'
@cmd()
onMessage(context: IReceivedMessage) {
// Manejar mensaje
}

Decorador @whatsApp()

El decorador @whatsApp() configura un canal de WhatsApp Business API. Requiere especificar el número de teléfono asociado a la cuenta de WhatsApp Business.

import { whatsApp } from '@wabot-dev/framework'
@whatsApp({
number: "573001234567" // Número en formato internacional sin +
})
onWhatsAppMessage(context: IReceivedMessage) {
// Manejar mensaje de WhatsApp
}

Decorador @telegram()

El decorador @telegram() configura un canal de Telegram Bot API. Requiere el token del bot obtenido desde BotFather.

import { telegram } from '@wabot-dev/framework'
@telegram({
botToken: process.env.TELEGRAM_BOT_TOKEN
})
onTelegramMessage(context: IReceivedMessage) {
// Manejar mensaje de Telegram
}

Decorador @socket()

El decorador @socket() configura un canal de WebSocket para comunicación en tiempo real desde aplicaciones web. Permite especificar namespace y middlewares de autenticación.

import { socket } from '@wabot-dev/framework'
@socket({
namespace: 'sales', // Namespace personalizado
handshakeMidlewares: [AuthMiddleware] // Middlewares opcionales
})
onWebSocketMessage(context: IReceivedMessage) {
// Manejar mensaje de WebSocket
}

Ejemplo básico: Controlador simple con un canal

Este ejemplo muestra un controlador básico que maneja mensajes desde línea de comandos y los procesa con un único mindset:

src/controllers/BasicSalesController.ts

import { SalesBotMindset } from '@/mindsets/SalesBotMindset'
import {
chatBot,
ChatBot,
chatController,
cmd,
type IReceivedMessage
} from '@wabot-dev/framework'
@chatController()
export class BasicSalesController {
constructor(
@chatBot(SalesBotMindset) private salesBot: ChatBot
) {}
@cmd()
onMessage(context: IReceivedMessage) {
this.salesBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
}

Ejemplo: Controlador para WhatsApp

Este ejemplo configura un controlador específicamente para WhatsApp, permitiendo que tu bot vendedor interactúe con clientes a través de esta plataforma:

src/controllers/WhatsAppSalesController.ts

import { SalesBotMindset } from '@/mindsets/SalesBotMindset'
import {
chatBot,
ChatBot,
chatController,
whatsApp,
type IReceivedMessage
} from '@wabot-dev/framework'
@chatController()
export class WhatsAppSalesController {
constructor(
@chatBot(SalesBotMindset) private salesBot: ChatBot
) {}
@whatsApp({
number: "573001234567"
})
onMessage(context: IReceivedMessage) {
this.salesBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
}

Ejemplo: Controlador con WebSocket para chat en vivo

Este ejemplo muestra cómo configurar un controlador que escucha conexiones WebSocket, ideal para integrar tu bot vendedor en una aplicación web con chat en vivo:

src/controllers/WebChatSalesController.ts

import { SalesBotMindset } from '@/mindsets/SalesBotMindset'
import {
chatBot,
ChatBot,
chatController,
socket,
type IReceivedMessage
} from '@wabot-dev/framework'
import { SalesAuthMiddleware } from '@/middlewares/SalesAuthMiddleware'
@chatController()
export class WebChatSalesController {
constructor(
@chatBot(SalesBotMindset) private salesBot: ChatBot
) {}
@socket({
namespace: 'sales-chat',
handshakeMidlewares: [SalesAuthMiddleware]
})
onMessage(context: IReceivedMessage) {
this.salesBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
}

Ejemplo multicanal: WhatsApp y Telegram simultáneos

Este ejemplo muestra cómo un mismo controlador puede manejar múltiples canales, permitiendo que tu bot vendedor atienda clientes desde WhatsApp y Telegram con la misma lógica de negocio:

src/controllers/MultiChannelSalesController.ts

import { SalesBotMindset } from '@/mindsets/SalesBotMindset'
import {
chatBot,
ChatBot,
chatController,
whatsApp,
telegram,
type IReceivedMessage
} from '@wabot-dev/framework'
@chatController()
export class MultiChannelSalesController {
constructor(
@chatBot(SalesBotMindset) private salesBot: ChatBot
) {}
@whatsApp({
number: "573001234567"
})
onWhatsAppMessage(context: IReceivedMessage) {
this.handleMessage(context)
}
@telegram({
botToken: process.env.TELEGRAM_BOT_TOKEN
})
onTelegramMessage(context: IReceivedMessage) {
this.handleMessage(context)
}
private handleMessage(context: IReceivedMessage) {
this.salesBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
}

Ejemplo avanzado: Controlador con múltiples mindsets y enrutamiento dinámico

Este ejemplo demuestra un controlador que gestiona múltiples mindsets especializados y selecciona dinámicamente cuál usar según el estado del prospecto en el proceso de venta:

src/controllers/SmartSalesController.ts

import {
ProspectQualifierMindset,
OfferNegotiationMindset,
PostSalesMindset
} from '@/mindsets'
import { ProspectRepository } from '@/repositories/ProspectRepository'
import { OfferRepository } from '@/repositories/OfferRepository'
import {
Chat,
chatBot,
ChatBot,
chatController,
whatsApp,
type IReceivedMessage,
} from '@wabot-dev/framework'
@chatController()
export class SmartSalesController {
constructor(
@chatBot(ProspectQualifierMindset) private qualifierBot: ChatBot,
@chatBot(OfferNegotiationMindset) private negotiationBot: ChatBot,
@chatBot(PostSalesMindset) private postSalesBot: ChatBot,
private prospectRepository: ProspectRepository,
private offerRepository: OfferRepository,
private chat: Chat,
) {}
@whatsApp({
number: "573001234567"
})
async onMessage(context: IReceivedMessage) {
const selectedBot = await this.selectAppropriateBot()
selectedBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
private async selectAppropriateBot(): Promise<ChatBot> {
const prospect = await this.prospectRepository.findByChatId(this.chat.id)
// Si no existe prospecto, usar bot de calificación
if (!prospect) {
return this.qualifierBot
}
// Si ya cerró una venta, usar bot post-venta
if (prospect.status === 'won') {
return this.postSalesBot
}
// Si tiene una oferta activa, usar bot de negociación
const activeOffer = await this.offerRepository.findByChatId(this.chat.id)
if (activeOffer?.isActive) {
return this.negotiationBot
}
// Si está calificado pero sin oferta, usar bot de negociación
if (prospect.isQualified()) {
return this.negotiationBot
}
// Por defecto, continuar con calificación
return this.qualifierBot
}
}

Ejemplo avanzado: Controlador multicanal con enrutamiento complejo

Este ejemplo combina múltiples canales con lógica de enrutamiento sofisticada, permitiendo diferentes experiencias según el canal y el estado del cliente:

src/controllers/EnterpriseMultiChannelController.ts

import {
ProspectQualifierMindset,
StandardSalesMindset,
VIPSalesMindset,
TechnicalSupportMindset
} from '@/mindsets'
import { ProspectRepository } from '@/repositories/ProspectRepository'
import { TicketRepository } from '@/repositories/TicketRepository'
import {
Chat,
chatBot,
ChatBot,
chatController,
whatsApp,
telegram,
socket,
type IReceivedMessage,
} from '@wabot-dev/framework'
import { WebAuthMiddleware } from '@/middlewares/WebAuthMiddleware'
@chatController()
export class EnterpriseMultiChannelController {
constructor(
@chatBot(ProspectQualifierMindset) private qualifierBot: ChatBot,
@chatBot(StandardSalesMindset) private standardSalesBot: ChatBot,
@chatBot(VIPSalesMindset) private vipSalesBot: ChatBot,
@chatBot(TechnicalSupportMindset) private techSupportBot: ChatBot,
private prospectRepository: ProspectRepository,
private ticketRepository: TicketRepository,
private chat: Chat,
) {}
@whatsApp({
number: "573001234567"
})
async onWhatsAppMessage(context: IReceivedMessage) {
await this.routeMessage(context, 'whatsapp')
}
@telegram({
botToken: process.env.TELEGRAM_BOT_TOKEN
})
async onTelegramMessage(context: IReceivedMessage) {
await this.routeMessage(context, 'telegram')
}
@socket({
namespace: 'enterprise-sales',
handshakeMidlewares: [WebAuthMiddleware]
})
async onWebMessage(context: IReceivedMessage) {
await this.routeMessage(context, 'web')
}
private async routeMessage(context: IReceivedMessage, channel: string) {
const selectedBot = await this.selectBotByContext(channel)
selectedBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
private async selectBotByContext(channel: string): Promise<ChatBot> {
const prospect = await this.prospectRepository.findByChatId(this.chat.id)
// Nuevo prospecto sin historial
if (!prospect) {
return this.qualifierBot
}
// Cliente con ticket técnico activo - siempre prioridad
const activeTicket = await this.ticketRepository.findActiveByProspectId(prospect.id)
if (activeTicket?.isTechnical()) {
return this.techSupportBot
}
// Cliente VIP (presupuesto > $50k o más de 10 compras)
const isVIP =
prospect.budgetRange === 'over_50k' ||
(await this.prospectRepository.countPurchasesByProspectId(prospect.id)) >= 10
if (isVIP) {
return this.vipSalesBot
}
// Cliente calificado estándar
if (prospect.isQualified()) {
return this.standardSalesBot
}
// Continuar con calificación
return this.qualifierBot
}
}

Ejemplo: Controlador con lógica de negocio específica por canal

En algunos casos, puedes querer implementar comportamientos diferentes según el canal de comunicación:

src/controllers/ChannelSpecificSalesController.ts

import {
QuickSalesMindset,
DetailedSalesMindset
} from '@/mindsets'
import {
chatBot,
ChatBot,
chatController,
whatsApp,
socket,
type IReceivedMessage,
} from '@wabot-dev/framework'
@chatController()
export class ChannelSpecificSalesController {
constructor(
@chatBot(QuickSalesMindset) private quickSalesBot: ChatBot,
@chatBot(DetailedSalesMindset) private detailedSalesBot: ChatBot,
) {}
// WhatsApp: Ventas rápidas y directas (usuarios móviles)
@whatsApp({
number: "573001234567"
})
onWhatsAppMessage(context: IReceivedMessage) {
// Bot optimizado para respuestas cortas y concisas
this.quickSalesBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
// WebSocket: Ventas detalladas con más información (usuarios web)
@socket({
namespace: 'detailed-sales'
})
onWebMessage(context: IReceivedMessage) {
// Bot que proporciona explicaciones más extensas y técnicas
this.detailedSalesBot.sendMessage(context.message, (replyMessage) => {
context.reply(replyMessage)
})
}
}

Contexto del mensaje (IReceivedMessage)

Todos los métodos que manejan mensajes reciben un objeto context de tipo IReceivedMessage que contiene:

  • context.message: El contenido del mensaje enviado por el usuario
  • context.reply(message): Método para enviar una respuesta al usuario
  • Información adicional del canal: Metadata específica de cada plataforma
@whatsApp({ number: "573001234567" })
onMessage(context: IReceivedMessage) {
// Acceder al mensaje del usuario
const userMessage = context.message
// Procesar con el bot
this.salesBot.sendMessage(userMessage, (replyMessage) => {
// Enviar respuesta al usuario
context.reply(replyMessage)
})
}

Mejores prácticas

  1. Separación de responsabilidades: Mantén la lógica de negocio en los mindsets y usa los controladores solo para enrutamiento y comunicación.

  2. Reutilización de código: Cuando múltiples canales comparten la misma lógica, extrae el manejo de mensajes a métodos privados.

  3. Nombres descriptivos: Usa nombres de métodos que indiquen claramente qué canal están manejando (ej: onWhatsAppMessage, onTelegramMessage).

  4. Validación de contexto: En controladores con enrutamiento complejo, valida siempre que los datos necesarios estén disponibles antes de seleccionar un bot.

  5. Logging y monitoreo: Considera agregar logs en los puntos de enrutamiento para facilitar el debugging y análisis de flujos conversacionales.

  6. Manejo de errores: Implementa try-catch en la lógica de enrutamiento compleja para evitar que errores de un canal afecten a otros.

Siguiente paso

Aprende a crear APIs HTTP con controladores REST: Controladores REST.