Comandos Asíncronos
Los comandos asíncronos te permiten ejecutar tareas fuera del flujo principal de la aplicación. Son ideales para operaciones lentas como envío de emails, procesamiento de archivos o integraciones con servicios externos.
Conceptos clave
@command('name'): registra una clase como un comando ejecutable.@commandHandler(CommandClass): registra un handler que procesa un comando.ICommandHandler<C>: interfaz que debe implementar cada handler.Async: servicio singleton para ejecutar o programar comandos.runCommandHandlers([]): activa el sistema de procesamiento de comandos.
Paso 1: Definir un comando
Un comando es una clase decorada con @command que extiende Storable. Define los datos que necesita el handler para ejecutarse:
import { command, Storable, isString, isNotEmpty } from '@wabot-dev/framework'
@command('send-email')export class SendEmailCommand extends Storable<{ to: string subject: string body: string}> {}Los datos del comando se validan usando los mismos decoradores de validación que el resto del framework.
Paso 2: Crear el handler
El handler implementa ICommandHandler<C> y contiene la lógica de ejecución:
import { commandHandler, ICommandHandler, injectable } from '@wabot-dev/framework'
@commandHandler(SendEmailCommand)export class SendEmailHandler implements ICommandHandler<SendEmailCommand> { constructor(private emailService: EmailService) {}
async handle(command: SendEmailCommand) { await this.emailService.send({ to: command.data.to, subject: command.data.subject, body: command.data.body, }) }}Paso 3: Ejecutar comandos
Usa el servicio Async para ejecutar o programar comandos:
import { Async, injectable } from '@wabot-dev/framework'
@injectable()export class NotificationService { constructor(private async: Async) {}
// Ejecutar inmediatamente (en background) async sendWelcomeEmail(userEmail: string) { await this.async.runCommand(SendEmailCommand, { to: userEmail, subject: 'Bienvenido', body: 'Gracias por registrarte.', }) }
// Programar para después async scheduleReminder(userEmail: string) { await this.async.scheduleCommand( SendEmailCommand, { to: userEmail, subject: 'Recordatorio', body: 'No olvides completar tu perfil.', }, { hours: 24 }, // Ejecutar en 24 horas ) }
// Programar para una fecha específica async scheduleReport(adminEmail: string) { const tomorrow = new Date() tomorrow.setDate(tomorrow.getDate() + 1) tomorrow.setHours(9, 0, 0, 0)
await this.async.scheduleCommand( SendEmailCommand, { to: adminEmail, subject: 'Reporte diario', body: 'Aquí está tu reporte...', }, tomorrow, // Ejecutar mañana a las 9:00 ) }}Opciones de scheduling
El segundo parámetro de scheduleCommand acepta:
Date: fecha exacta de ejecución.{ seconds: number }: delay en segundos.{ minutes: number }: delay en minutos.{ hours: number }: delay en horas.{ days: number }: delay en días.
Paso 4: Ciclo de vida
Cada comando pasa por estos estados:
- Scheduled: el comando fue registrado y espera ejecución.
- Running: el handler está procesando el comando.
- Success: el handler terminó sin errores.
- Failed: el handler lanzó un error.
Los comandos fallidos se reintentan automáticamente según la configuración del framework.
Paso 5: Activar el sistema
Registra tus handlers en el punto de entrada de la aplicación:
import { runCommandHandlers } from '@wabot-dev/framework'
runCommandHandlers([ SendEmailHandler, GenerateReportHandler, ProcessPaymentHandler,])Ejemplo completo
Sistema de notificaciones programadas para un bot de ventas:
import { command, commandHandler, ICommandHandler, Storable, Async, singleton, injectable, runCommandHandlers,} from '@wabot-dev/framework'
// --- Comando ---
@command('send-notification')export class SendNotificationCommand extends Storable<{ prospectId: string type: 'followup' | 'offer_expiring' | 'welcome' message: string}> {}
// --- Handler ---
@commandHandler(SendNotificationCommand)export class SendNotificationHandler implements ICommandHandler<SendNotificationCommand> { constructor( private whatsappService: WhatsAppService, private prospectRepository: ProspectRepository, ) {}
async handle(command: SendNotificationCommand) { const prospect = await this.prospectRepository.findById(command.data.prospectId) if (!prospect) return
const phone = prospect.contactInfo.find(c => c.type === 'phone') if (!phone) return
await this.whatsappService.sendMessage(phone.value, command.data.message) }}
// --- Servicio que programa notificaciones ---
@singleton()export class FollowUpService { constructor(private async: Async) {}
async scheduleFollowUp(prospectId: string) { // Enviar seguimiento en 2 horas await this.async.scheduleCommand( SendNotificationCommand, { prospectId, type: 'followup', message: 'Hola, quería saber si tuviste oportunidad de revisar nuestra propuesta.', }, { hours: 2 }, ) }
async scheduleOfferExpiry(prospectId: string, expiresAt: Date) { // Notificar 24 horas antes de que expire la oferta const notifyAt = new Date(expiresAt.getTime() - 24 * 60 * 60 * 1000)
await this.async.scheduleCommand( SendNotificationCommand, { prospectId, type: 'offer_expiring', message: 'Tu oferta especial vence mañana. ¿Te gustaría aprovecharla?', }, notifyAt, ) }
async sendWelcome(prospectId: string) { // Enviar inmediatamente await this.async.runCommand(SendNotificationCommand, { prospectId, type: 'welcome', message: 'Bienvenido. Soy Martín, tu asesor comercial. ¿En qué puedo ayudarte?', }) }}
// --- Registro ---runCommandHandlers([SendNotificationHandler])Siguiente paso
Programa tareas recurrentes con expresiones cron: Tareas Programadas (Cron).