plazbot-cli
Version:
CLI para Plazbot SDK
101 lines (86 loc) • 3.54 kB
text/typescript
import { Command } from 'commander';
import { Message } from 'plazbot';
import { getStoredCredentials } from '../../utils/credentials';
import { logger } from '../../utils/logger';
import { createSpinner, theme, section, progressBar } from '../../utils/ui';
import fs from 'fs/promises';
export const broadcastCommand = new Command('broadcast')
.description('Envio masivo de mensajes de template WhatsApp')
.requiredOption('-t, --template <name>', 'Nombre del template')
.requiredOption('--phones <file>', 'Archivo CSV con numeros de telefono (uno por linea)')
.option('--var <vars...>', 'Variables del body (formato: name=valor)')
.option('--delay <ms>', 'Delay entre mensajes en ms', '1000')
.option('--dev', 'Usar ambiente de desarrollo', false)
.action(async (options: any) => {
try {
const credentials = await getStoredCredentials();
const messageClient = new Message({
workspaceId: credentials.workspace,
apiKey: credentials.apiKey,
zone: credentials.zone,
...(options.dev && { customUrl: "http://localhost:5090" })
});
// Leer archivo de telefonos
let phones: string[];
try {
const content = await fs.readFile(options.phones, 'utf-8');
phones = content.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
} catch {
throw new Error(`No se pudo leer el archivo: ${options.phones}`);
}
if (phones.length === 0) {
throw new Error('El archivo no contiene numeros de telefono');
}
// Parsear variables
const variablesBody = parseVariables(options.var);
console.log(section('Broadcast WhatsApp'));
logger.label('Template', options.template);
logger.label('Destinatarios', String(phones.length));
logger.label('Delay', `${options.delay}ms`);
if (variablesBody.length > 0) {
logger.label('Variables', variablesBody.map(v => `${v.variable}=${v.value}`).join(', '));
}
console.log();
const delay = parseInt(options.delay) || 1000;
let sent = 0;
let failed = 0;
for (let i = 0; i < phones.length; i++) {
const phone = phones[i];
try {
const params: any = {
to: phone,
template: options.template,
};
if (variablesBody.length > 0) params.variablesBody = variablesBody;
await messageClient.onConversation(params);
sent++;
} catch {
failed++;
console.log(theme.error(` ✖ Fallo: ${phone}`));
}
// Progress
console.log(` ${progressBar(i + 1, phones.length)}`);
// Rate limiting
if (i < phones.length - 1) {
await new Promise(resolve => setTimeout(resolve, delay));
}
}
console.log(section('Resultado'));
logger.label('Enviados', theme.success(String(sent)));
logger.label('Fallidos', failed > 0 ? theme.error(String(failed)) : '0');
logger.label('Total', String(phones.length));
} catch (error: unknown) {
const message = (error as Error)?.message || 'Error desconocido';
logger.error(message);
process.exit(1);
}
});
function parseVariables(vars: string[] | undefined): { variable: string; value: string }[] {
if (!vars) return [];
return vars.map(v => {
const [variable, ...rest] = v.split('=');
return { variable: variable.trim(), value: rest.join('=').trim() };
}).filter(v => v.variable && v.value);
}