Files
whatsapp/api/send_broadcast.php
T
2026-02-03 21:30:55 -05:00

253 lines
10 KiB
PHP

<?php
/**
* API - Envío masivo de mensajes
* Fecha: 13 de noviembre de 2025
*/
require_once '../config/config.php';
// Verificar autenticación
requireAuthentication();
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Método no permitido']);
exit;
}
try {
$input = json_decode(file_get_contents('php://input'), true);
if (!$input) {
http_response_code(400);
echo json_encode(['error' => 'Datos requeridos']);
exit;
}
$selectionType = $input['selection_type'] ?? 'filter';
$messageType = $input['message_type'] ?? 'text';
$db = Database::getInstance();
$whatsappService = new WhatsAppService();
// Obtener usuarios según tipo de selección
$users = [];
if ($selectionType === 'specific') {
// Usuarios específicos seleccionados
if (!isset($input['user_ids']) || !is_array($input['user_ids']) || empty($input['user_ids'])) {
http_response_code(400);
echo json_encode(['error' => 'Debes seleccionar al menos un usuario']);
exit;
}
$userIds = array_map('intval', $input['user_ids']);
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
$query = "SELECT id, phone_number, name FROM users WHERE id IN ($placeholders) ORDER BY id ASC";
$users = $db->fetchAll($query, $userIds);
} else {
// Por filtro
$filter = $input['filter'] ?? 'all';
$whereClause = "1=1";
switch ($filter) {
case 'active':
$whereClause .= " AND EXISTS (
SELECT 1 FROM conversations c
WHERE c.user_id = u.id
AND c.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
)";
break;
case 'recent':
$whereClause .= " AND u.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
break;
case 'all':
// Incluir todos los usuarios
break;
}
$query = "SELECT id, phone_number, name FROM users u WHERE " . $whereClause . " ORDER BY id ASC";
$users = $db->fetchAll($query);
}
if (empty($users)) {
http_response_code(404);
echo json_encode([
'error' => 'No se encontraron usuarios para enviar',
'selection_type' => $selectionType
]);
exit;
}
$sentCount = 0;
$errorCount = 0;
$errors = [];
foreach ($users as $user) {
try {
$response = null;
$messageContent = '';
// Enviar según tipo de mensaje
if ($messageType === 'template') {
// Enviar plantilla
if (!isset($input['template_id'])) {
throw new Exception('ID de plantilla requerido');
}
$templateId = intval($input['template_id']);
$template = $db->fetch("SELECT * FROM message_templates WHERE id = ?", [$templateId]);
if (!$template) {
throw new Exception('Plantilla no encontrada');
}
$status = strtoupper($template['status']);
if ($status !== 'APPROVED') {
throw new Exception('La plantilla no está aprobada. Status: ' . $template['status']);
}
// Preparar variables
$templateVariables = $input['template_variables'] ?? [];
error_log('📝 Variables recibidas: ' . json_encode($templateVariables));
error_log('🔍 Tipo: ' . gettype($templateVariables));
// Detectar si son variables numéricas o con nombres
$isNumericVars = false;
$isNamedVars = false;
if (is_array($templateVariables) && !empty($templateVariables)) {
$firstKey = array_key_first($templateVariables);
if (is_int($firstKey) && isset($templateVariables[0])) {
// Array indexado numéricamente [0 => "val1", 1 => "val2"]
$isNumericVars = true;
error_log('✅ Variables tipo: Array numérico indexado');
} elseif (is_string($firstKey) && !is_numeric($firstKey)) {
// Array asociativo con nombres ["nombre_tema" => "val1", "fecha" => "val2"]
$isNamedVars = true;
error_log('✅ Variables tipo: Objeto con nombres (asociativo)');
} elseif (is_numeric($firstKey)) {
// Array con claves numéricas [1 => "val1", 2 => "val2"]
$isNumericVars = true;
error_log('✅ Variables tipo: Array con claves numéricas');
}
}
// Procesar según el tipo
$processedVariables = [];
if ($isNamedVars) {
// Variables con nombres: pasar como objeto asociativo
$processedVariables = $templateVariables;
error_log('📦 Enviando como objeto con nombres: ' . json_encode($processedVariables));
} elseif ($isNumericVars) {
// Variables numéricas: ordenar y convertir a array indexado
ksort($templateVariables, SORT_NUMERIC);
foreach ($templateVariables as $value) {
$processedVariables[] = $value;
}
error_log('📊 Enviando como array ordenado: ' . json_encode($processedVariables));
} else {
error_log('⚠️ Sin variables o formato desconocido');
}
error_log('📤 Variables procesadas: ' . json_encode($processedVariables));
// Verificar variables si la plantilla usa formato numérico
$expectedVars = preg_match_all('/\{\{(\d+)\}\}/', $template['body_text'] ?? '', $matches);
if ($expectedVars > 0 && $isNumericVars && count($processedVariables) !== $expectedVars) {
throw new Exception("La plantilla requiere {$expectedVars} variable(s) numéricas, pero se enviaron " . count($processedVariables));
}
$response = $whatsappService->sendTemplateMessage(
$user['phone_number'],
$template['template_name'] ?? $template['name'],
$template['language_code'] ?? 'es',
$processedVariables
);
// Construir contenido para BD (reemplazar variables en body_text)
$messageContent = $template['body_text'] ?? $template['name'];
if ($isNamedVars) {
// Reemplazar por nombres
foreach ($templateVariables as $key => $value) {
$messageContent = str_replace('{{' . $key . '}}', $value, $messageContent);
}
} else {
// Reemplazar por índice numérico
foreach ($processedVariables as $index => $value) {
$messageContent = str_replace('{{' . ($index + 1) . '}}', $value, $messageContent);
}
}
} else {
// Enviar texto simple
$message = trim($input['message'] ?? '');
if (empty($message)) {
throw new Exception('Mensaje vacío');
}
$response = $whatsappService->sendTextMessage($user['phone_number'], $message);
$messageContent = $message;
}
if ($response && isset($response['success']) && $response['success']) {
$sentCount++;
// Guardar mensaje en BD
$db->insert('conversations', [
'user_id' => $user['id'],
'message_id' => $response['message_id'] ?? null,
'content' => $messageContent,
'direction' => 'outgoing',
'message_type' => $messageType === 'template' ? 'template' : 'text',
'status' => 'sent',
'created_at' => date('Y-m-d H:i:s')
]);
} else {
$errorCount++;
$errors[] = [
'phone' => $user['phone_number'],
'error' => 'Respuesta inválida de WhatsApp'
];
}
// Pausa para no saturar la API (250ms entre mensajes)
usleep(250000);
} catch (Exception $e) {
$errorCount++;
$errors[] = [
'phone' => $user['phone_number'],
'name' => $user['name'] ?? 'Sin nombre',
'error' => $e->getMessage()
];
error_log("Error sending broadcast to {$user['phone_number']}: " . $e->getMessage());
}
}
echo json_encode([
'success' => true,
'sent_count' => $sentCount,
'error_count' => $errorCount,
'total_users' => count($users),
'selection_type' => $selectionType,
'message_type' => $messageType,
'errors' => $errorCount > 0 ? $errors : null
]);
} catch (Exception $e) {
error_log("Error in send_broadcast.php: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
}
?>