w
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Exportar usuarios a CSV
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
// Headers para descarga de archivo
|
||||
header('Content-Type: text/csv; charset=utf-8');
|
||||
header('Content-Disposition: attachment; filename="usuarios_whatsapp_' . date('Y-m-d') . '.csv"');
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Obtener todos los usuarios con información adicional
|
||||
$users = $db->fetchAll(
|
||||
"SELECT
|
||||
u.id,
|
||||
u.phone_number,
|
||||
u.name,
|
||||
u.email,
|
||||
u.status,
|
||||
u.created_at,
|
||||
u.updated_at,
|
||||
COUNT(c.id) as total_messages,
|
||||
MAX(c.created_at) as last_activity
|
||||
FROM users u
|
||||
LEFT JOIN conversations c ON u.id = c.user_id
|
||||
GROUP BY u.id
|
||||
ORDER BY u.created_at DESC"
|
||||
);
|
||||
|
||||
// Crear el archivo CSV
|
||||
$output = fopen('php://output', 'w');
|
||||
|
||||
// BOM para UTF-8
|
||||
fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||
|
||||
// Encabezados
|
||||
fputcsv($output, [
|
||||
'ID',
|
||||
'Teléfono',
|
||||
'Nombre',
|
||||
'Email',
|
||||
'Estado',
|
||||
'Total Mensajes',
|
||||
'Última Actividad',
|
||||
'Fecha Registro'
|
||||
], ';');
|
||||
|
||||
// Datos
|
||||
foreach ($users as $user) {
|
||||
fputcsv($output, [
|
||||
$user['id'],
|
||||
$user['phone_number'],
|
||||
$user['name'] ?? 'Sin nombre',
|
||||
$user['email'] ?? 'Sin email',
|
||||
$user['status'],
|
||||
$user['total_messages'],
|
||||
$user['last_activity'] ? date('d/m/Y H:i', strtotime($user['last_activity'])) : 'Nunca',
|
||||
date('d/m/Y H:i', strtotime($user['created_at']))
|
||||
], ';');
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in export_users.php: " . $e->getMessage());
|
||||
|
||||
// Cambiar headers para error
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Content-Disposition: inline');
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener datos para gráficos
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Obtener datos de los últimos 7 días
|
||||
$chartData = $db->fetchAll(
|
||||
"SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as message_count
|
||||
FROM conversations
|
||||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC"
|
||||
);
|
||||
|
||||
// Formatear datos para Chart.js
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
foreach ($chartData as $row) {
|
||||
$labels[] = date('d/m', strtotime($row['date']));
|
||||
$data[] = (int)$row['message_count'];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'labels' => $labels,
|
||||
'data' => $data
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_chart_data.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener conversaciones recientes
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$conversations = $db->fetchAll(
|
||||
"SELECT
|
||||
c.id,
|
||||
c.user_id,
|
||||
c.content,
|
||||
c.direction,
|
||||
c.message_type,
|
||||
c.status,
|
||||
c.created_at,
|
||||
u.phone_number,
|
||||
u.name
|
||||
FROM conversations c
|
||||
JOIN users u ON c.user_id = u.id
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 50"
|
||||
);
|
||||
|
||||
echo json_encode($conversations);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_conversations.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener logs del webhook
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$limit = $_GET['limit'] ?? 50;
|
||||
$offset = $_GET['offset'] ?? 0;
|
||||
|
||||
$logs = $db->fetchAll(
|
||||
"SELECT
|
||||
id,
|
||||
status_code,
|
||||
ip_address,
|
||||
created_at,
|
||||
SUBSTRING(request_body, 1, 100) as request_preview,
|
||||
SUBSTRING(response_body, 1, 100) as response_preview
|
||||
FROM webhook_logs
|
||||
ORDER BY created_at DESC
|
||||
LIMIT :limit OFFSET :offset",
|
||||
[
|
||||
'limit' => (int)$limit,
|
||||
'offset' => (int)$offset
|
||||
]
|
||||
);
|
||||
|
||||
echo json_encode($logs);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_logs.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener menús
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$menus = $db->fetchAll(
|
||||
"SELECT
|
||||
id,
|
||||
name,
|
||||
title,
|
||||
description,
|
||||
parent_id,
|
||||
is_root,
|
||||
is_active,
|
||||
order_position,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM menus
|
||||
ORDER BY order_position ASC, title ASC"
|
||||
);
|
||||
|
||||
// Obtener opciones de cada menú
|
||||
foreach ($menus as &$menu) {
|
||||
$options = $db->fetchAll(
|
||||
"SELECT * FROM menu_options
|
||||
WHERE menu_id = :menu_id
|
||||
ORDER BY option_number ASC",
|
||||
['menu_id' => $menu['id']]
|
||||
);
|
||||
$menu['options'] = $options;
|
||||
}
|
||||
|
||||
echo json_encode($menus);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_menus.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener mensajes recientes para dashboard
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$recentMessages = $db->fetchAll(
|
||||
"SELECT
|
||||
c.content,
|
||||
c.direction,
|
||||
c.message_type,
|
||||
c.created_at,
|
||||
u.phone_number,
|
||||
u.name
|
||||
FROM conversations c
|
||||
JOIN users u ON c.user_id = u.id
|
||||
WHERE c.content IS NOT NULL AND c.content != ''
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 10"
|
||||
);
|
||||
|
||||
echo json_encode($recentMessages);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_recent_messages.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener configuración del sistema
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$configs = $db->fetchAll(
|
||||
"SELECT config_key, config_value FROM system_config"
|
||||
);
|
||||
|
||||
$settings = [];
|
||||
foreach ($configs as $config) {
|
||||
$settings[$config['config_key']] = $config['config_value'];
|
||||
}
|
||||
|
||||
echo json_encode($settings);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_settings.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener estadísticas del dashboard
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Total de usuarios
|
||||
$totalUsers = $db->fetch("SELECT COUNT(*) as count FROM users")['count'];
|
||||
|
||||
// Mensajes hoy
|
||||
$messagesToday = $db->fetch(
|
||||
"SELECT COUNT(*) as count FROM conversations WHERE DATE(created_at) = CURDATE()"
|
||||
)['count'];
|
||||
|
||||
// Usuarios activos (último mes)
|
||||
$activeUsers = $db->fetch(
|
||||
"SELECT COUNT(DISTINCT user_id) as count FROM conversations
|
||||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)"
|
||||
)['count'];
|
||||
|
||||
// Total de mensajes
|
||||
$totalMessages = $db->fetch("SELECT COUNT(*) as count FROM conversations")['count'];
|
||||
|
||||
$stats = [
|
||||
'total_users' => (int)$totalUsers,
|
||||
'messages_today' => (int)$messagesToday,
|
||||
'active_users' => (int)$activeUsers,
|
||||
'total_messages' => (int)$totalMessages
|
||||
];
|
||||
|
||||
echo json_encode($stats);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_stats.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener plantillas de mensaje
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$templates = $db->fetchAll(
|
||||
"SELECT
|
||||
id,
|
||||
name,
|
||||
template_name,
|
||||
language_code,
|
||||
category,
|
||||
status,
|
||||
created_at
|
||||
FROM message_templates
|
||||
ORDER BY name ASC"
|
||||
);
|
||||
|
||||
echo json_encode($templates);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_templates.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener usuarios
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$users = $db->fetchAll(
|
||||
"SELECT
|
||||
u.id,
|
||||
u.phone_number,
|
||||
u.name,
|
||||
u.email,
|
||||
u.status,
|
||||
u.current_menu_id,
|
||||
u.current_step,
|
||||
u.created_at,
|
||||
u.updated_at,
|
||||
m.name as current_menu
|
||||
FROM users u
|
||||
LEFT JOIN menus m ON u.current_menu_id = m.id
|
||||
ORDER BY u.created_at DESC"
|
||||
);
|
||||
|
||||
echo json_encode($users);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_users.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Guardar menú
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
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 || !isset($input['name']) || !isset($input['title'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Nombre y título son requeridos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Verificar si es menú raíz
|
||||
$isRoot = empty($input['parent_id']) ? 1 : 0;
|
||||
|
||||
// Obtener próximo order_position
|
||||
$maxOrder = $db->fetch(
|
||||
"SELECT COALESCE(MAX(order_position), 0) as max_order FROM menus"
|
||||
);
|
||||
|
||||
$menuData = [
|
||||
'name' => $input['name'],
|
||||
'title' => $input['title'],
|
||||
'description' => $input['description'] ?? '',
|
||||
'parent_id' => empty($input['parent_id']) ? null : $input['parent_id'],
|
||||
'is_root' => $isRoot,
|
||||
'is_active' => $input['is_active'] ?? 1,
|
||||
'order_position' => $maxOrder['max_order'] + 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
$menuId = $db->insert('menus', $menuData);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Menú guardado correctamente',
|
||||
'menu_id' => $menuId
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in save_menu.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Guardar configuración del sistema
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
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 inválidos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Mapear configuraciones
|
||||
$configMap = [
|
||||
'whatsapp_token' => $input['whatsapp_token'] ?? '',
|
||||
'phone_number_id' => $input['phone_number_id'] ?? '',
|
||||
'webhook_verify_token' => $input['webhook_token'] ?? '',
|
||||
'business_name' => $input['business_name'] ?? '',
|
||||
'welcome_message' => $input['welcome_message'] ?? ''
|
||||
];
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($configMap as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
// Verificar si la configuración existe
|
||||
$existing = $db->fetch(
|
||||
"SELECT id FROM system_config WHERE config_key = :key",
|
||||
['key' => $key]
|
||||
);
|
||||
|
||||
if ($existing) {
|
||||
// Actualizar
|
||||
$db->update(
|
||||
'system_config',
|
||||
['config_value' => $value, 'updated_at' => date('Y-m-d H:i:s')],
|
||||
'config_key = :key',
|
||||
['key' => $key]
|
||||
);
|
||||
} else {
|
||||
// Insertar
|
||||
$db->insert('system_config', [
|
||||
'config_key' => $key,
|
||||
'config_value' => $value,
|
||||
'description' => 'Configurado desde la interfaz web',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Configuración guardada correctamente'
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$db->rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in save_settings.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Envío masivo de mensajes
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
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 || !isset($input['message'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Mensaje requerido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$filter = $input['filter'] ?? 'all';
|
||||
$message = $input['message'];
|
||||
|
||||
$db = Database::getInstance();
|
||||
$whatsappService = new WhatsAppService();
|
||||
|
||||
// Construir query según filtro
|
||||
$whereClause = "WHERE u.status = 'active'";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Obtener usuarios según filtro
|
||||
$users = $db->fetchAll(
|
||||
"SELECT phone_number FROM users u " . $whereClause
|
||||
);
|
||||
|
||||
$sentCount = 0;
|
||||
$errorCount = 0;
|
||||
|
||||
foreach ($users as $user) {
|
||||
try {
|
||||
$response = $whatsappService->sendTextMessage($user['phone_number'], $message);
|
||||
if ($response) {
|
||||
$sentCount++;
|
||||
}
|
||||
|
||||
// Pequeña pausa para no saturar la API
|
||||
usleep(100000); // 0.1 segundo
|
||||
|
||||
} catch (Exception $e) {
|
||||
$errorCount++;
|
||||
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)
|
||||
]);
|
||||
|
||||
} 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()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Enviar mensaje individual
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
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 || !isset($input['recipient'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Datos inválidos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$recipient = $input['recipient'];
|
||||
$type = $input['type'] ?? 'text';
|
||||
|
||||
$whatsappService = new WhatsAppService();
|
||||
$response = null;
|
||||
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
if (!isset($input['message']) || empty($input['message'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Mensaje requerido']);
|
||||
exit;
|
||||
}
|
||||
$response = $whatsappService->sendTextMessage($recipient, $input['message']);
|
||||
break;
|
||||
|
||||
case 'template':
|
||||
if (!isset($input['template']) || empty($input['template'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Plantilla requerida']);
|
||||
exit;
|
||||
}
|
||||
$language = $input['language'] ?? 'es';
|
||||
$parameters = $input['parameters'] ?? [];
|
||||
$response = $whatsappService->sendTemplateMessage($recipient, $input['template'], $language, $parameters);
|
||||
break;
|
||||
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Tipo de mensaje no válido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($response) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Mensaje enviado correctamente',
|
||||
'whatsapp_response' => $response
|
||||
]);
|
||||
} else {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error enviando mensaje']);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in send_message.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
/**
|
||||
* Webhook para recibir mensajes de WhatsApp
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
// Headers para API
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
class WhatsAppWebhook {
|
||||
private $db;
|
||||
private $whatsappService;
|
||||
private $botService;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = Database::getInstance();
|
||||
$this->whatsappService = new WhatsAppService();
|
||||
$this->botService = new BotService();
|
||||
}
|
||||
|
||||
public function handleRequest() {
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if ($method === 'GET') {
|
||||
$this->verifyWebhook();
|
||||
} elseif ($method === 'POST') {
|
||||
$this->processIncomingMessage();
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Método no permitido']);
|
||||
}
|
||||
}
|
||||
|
||||
private function verifyWebhook() {
|
||||
$verifyToken = $_GET['hub_verify_token'] ?? '';
|
||||
$challenge = $_GET['hub_challenge'] ?? '';
|
||||
$mode = $_GET['hub_mode'] ?? '';
|
||||
|
||||
if ($mode === 'subscribe' && $verifyToken === WEBHOOK_VERIFY_TOKEN) {
|
||||
echo $challenge;
|
||||
exit;
|
||||
} else {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Token de verificación inválido']);
|
||||
}
|
||||
}
|
||||
|
||||
private function processIncomingMessage() {
|
||||
$input = file_get_contents('php://input');
|
||||
$data = json_decode($input, true);
|
||||
|
||||
// Registrar webhook en logs
|
||||
$this->logWebhook($input, json_encode(['status' => 'received']), 200);
|
||||
|
||||
if (!$data || !isset($data['entry'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Datos inválidos']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($data['entry'] as $entry) {
|
||||
if (isset($entry['changes'])) {
|
||||
foreach ($entry['changes'] as $change) {
|
||||
if ($change['field'] === 'messages') {
|
||||
$this->processMessages($change['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'success']);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error processing webhook: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor']);
|
||||
}
|
||||
}
|
||||
|
||||
private function processMessages($value) {
|
||||
if (!isset($value['messages'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($value['messages'] as $message) {
|
||||
$phoneNumber = $message['from'];
|
||||
$messageId = $message['id'];
|
||||
$timestamp = $message['timestamp'];
|
||||
|
||||
// Verificar si ya procesamos este mensaje
|
||||
$existing = $this->db->fetch(
|
||||
"SELECT id FROM conversations WHERE message_id = :message_id",
|
||||
['message_id' => $messageId]
|
||||
);
|
||||
|
||||
if ($existing) {
|
||||
continue; // Ya procesamos este mensaje
|
||||
}
|
||||
|
||||
// Obtener o crear usuario
|
||||
$user = $this->getUserByPhone($phoneNumber);
|
||||
if (!$user) {
|
||||
$userId = $this->createUser($phoneNumber);
|
||||
$user = $this->getUserById($userId);
|
||||
}
|
||||
|
||||
// Procesar diferentes tipos de mensaje
|
||||
$messageText = '';
|
||||
$messageType = 'text';
|
||||
$mediaUrl = null;
|
||||
|
||||
if (isset($message['text'])) {
|
||||
$messageText = $message['text']['body'];
|
||||
$messageType = 'text';
|
||||
} elseif (isset($message['image'])) {
|
||||
$messageText = $message['image']['caption'] ?? '';
|
||||
$messageType = 'image';
|
||||
$mediaUrl = $message['image']['id'];
|
||||
} elseif (isset($message['audio'])) {
|
||||
$messageType = 'audio';
|
||||
$mediaUrl = $message['audio']['id'];
|
||||
} elseif (isset($message['video'])) {
|
||||
$messageText = $message['video']['caption'] ?? '';
|
||||
$messageType = 'video';
|
||||
$mediaUrl = $message['video']['id'];
|
||||
} elseif (isset($message['document'])) {
|
||||
$messageText = $message['document']['filename'] ?? '';
|
||||
$messageType = 'document';
|
||||
$mediaUrl = $message['document']['id'];
|
||||
}
|
||||
|
||||
// Guardar mensaje en base de datos
|
||||
$this->saveMessage([
|
||||
'user_id' => $user['id'],
|
||||
'message_id' => $messageId,
|
||||
'direction' => 'incoming',
|
||||
'message_type' => $messageType,
|
||||
'content' => $messageText,
|
||||
'media_url' => $mediaUrl,
|
||||
'status' => 'received'
|
||||
]);
|
||||
|
||||
// Procesar con bot
|
||||
$this->botService->processMessage($user, $messageText, $messageType);
|
||||
}
|
||||
|
||||
// Procesar estados de mensajes (entregado, leído, etc.)
|
||||
if (isset($value['statuses'])) {
|
||||
$this->processMessageStatuses($value['statuses']);
|
||||
}
|
||||
}
|
||||
|
||||
private function processMessageStatuses($statuses) {
|
||||
foreach ($statuses as $status) {
|
||||
$messageId = $status['id'];
|
||||
$newStatus = $status['status']; // sent, delivered, read, failed
|
||||
|
||||
$this->db->update(
|
||||
'conversations',
|
||||
['status' => $newStatus],
|
||||
'message_id = :message_id',
|
||||
['message_id' => $messageId]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getUserByPhone($phoneNumber) {
|
||||
return $this->db->fetch(
|
||||
"SELECT * FROM users WHERE phone_number = :phone",
|
||||
['phone' => $phoneNumber]
|
||||
);
|
||||
}
|
||||
|
||||
private function getUserById($userId) {
|
||||
return $this->db->fetch(
|
||||
"SELECT * FROM users WHERE id = :id",
|
||||
['id' => $userId]
|
||||
);
|
||||
}
|
||||
|
||||
private function createUser($phoneNumber) {
|
||||
return $this->db->insert('users', [
|
||||
'phone_number' => $phoneNumber,
|
||||
'status' => 'active',
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
private function saveMessage($messageData) {
|
||||
return $this->db->insert('conversations', $messageData);
|
||||
}
|
||||
|
||||
private function logWebhook($requestBody, $responseBody, $statusCode) {
|
||||
if (ENABLE_LOGGING) {
|
||||
$this->db->insert('webhook_logs', [
|
||||
'request_body' => $requestBody,
|
||||
'response_body' => $responseBody,
|
||||
'status_code' => $statusCode,
|
||||
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar la solicitud
|
||||
try {
|
||||
$webhook = new WhatsAppWebhook();
|
||||
$webhook->handleRequest();
|
||||
} catch (Exception $e) {
|
||||
error_log("Fatal error in webhook: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error fatal del servidor']);
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user