funcional
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
require_once 'config/config.php';
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$db->query(
|
||||
"INSERT INTO message_templates (name, template_name, language_code, status) VALUES (?, ?, ?, ?)",
|
||||
['Hello World Template', 'hello_world', 'en_US', 'approved']
|
||||
);
|
||||
|
||||
echo "✅ Plantilla agregada exitosamente\n";
|
||||
|
||||
// Verificar
|
||||
$stmt = $db->query("SELECT template_name, language_code, status FROM message_templates");
|
||||
$templates = $stmt->fetchAll();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
echo "• {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Agregar plantilla hello_world que funciona
|
||||
*/
|
||||
|
||||
require_once 'config/config.php';
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
echo "=== AGREGAR PLANTILLA QUE FUNCIONA ===\n\n";
|
||||
|
||||
// Agregar plantilla hello_world que sabemos que funciona
|
||||
$db->query(
|
||||
"INSERT INTO message_templates (template_name, language_code, status, body_text, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())",
|
||||
['hello_world', 'en_US', 'approved', 'Hello World! This is a test message from WhatsApp Business API.']
|
||||
);
|
||||
|
||||
echo "✅ Agregada plantilla 'hello_world' (en_US) - FUNCIONA CORRECTAMENTE\n\n";
|
||||
|
||||
// Mostrar plantillas finales
|
||||
echo "📋 Plantillas en la base de datos:\n";
|
||||
$stmt = $db->query("SELECT id, template_name, language_code, status, body_text FROM message_templates ORDER BY template_name");
|
||||
$templates = $stmt->fetchAll();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
echo "• ID: {$template['id']}\n";
|
||||
echo " Nombre: {$template['template_name']}\n";
|
||||
echo " Idioma: {$template['language_code']}\n";
|
||||
echo " Estado: {$template['status']}\n";
|
||||
echo " Texto: {$template['body_text']}\n";
|
||||
echo " -----\n";
|
||||
}
|
||||
|
||||
echo "\n🎉 Ahora puedes enviar mensajes usando la plantilla 'hello_world'\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener mensajes de una conversación específica
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
// Modo debug: desactivar autenticación si existe el parámetro debug
|
||||
$debugMode = isset($_GET['debug']) && $_GET['debug'] === 'true';
|
||||
|
||||
if (!$debugMode) {
|
||||
requireAuthentication();
|
||||
}
|
||||
|
||||
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 {
|
||||
$user_id = $_GET['user_id'] ?? null;
|
||||
|
||||
if (!$user_id) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'user_id es requerido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Obtener información del usuario
|
||||
$user = $db->fetch(
|
||||
"SELECT id, phone_number, name FROM users WHERE id = ?",
|
||||
[$user_id]
|
||||
);
|
||||
|
||||
if (!$user) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Usuario no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Obtener todos los mensajes de la conversación
|
||||
$messages = $db->fetchAll(
|
||||
"SELECT
|
||||
id,
|
||||
message_id,
|
||||
direction,
|
||||
message_type,
|
||||
content,
|
||||
status,
|
||||
created_at
|
||||
FROM conversations
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at ASC",
|
||||
[$user_id]
|
||||
);
|
||||
|
||||
// Formatear mensajes
|
||||
$messages = array_map(function($msg) {
|
||||
return [
|
||||
'id' => intval($msg['id']),
|
||||
'message_id' => $msg['message_id'] ?? '',
|
||||
'direction' => $msg['direction'],
|
||||
'message_type' => $msg['message_type'],
|
||||
'content' => $msg['content'] ?? '',
|
||||
'status' => $msg['status'] ?? 'sent',
|
||||
'created_at' => $msg['created_at'],
|
||||
'time' => date('H:i', strtotime($msg['created_at'])),
|
||||
'date' => date('d/m/Y', strtotime($msg['created_at']))
|
||||
];
|
||||
}, $messages);
|
||||
|
||||
// Marcar mensajes como leídos
|
||||
$db->query(
|
||||
"UPDATE conversations SET status = 'read' WHERE user_id = ? AND direction = 'incoming' AND status != 'read'",
|
||||
[$user_id]
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'user' => [
|
||||
'id' => intval($user['id']),
|
||||
'phone_number' => $user['phone_number'],
|
||||
'name' => $user['name'] ?? $user['phone_number']
|
||||
],
|
||||
'messages' => $messages,
|
||||
'total_messages' => count($messages)
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_conversation_detail.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* API - Obtener conversaciones agrupadas por usuario con último mensaje
|
||||
*/
|
||||
|
||||
require_once '../config/config.php';
|
||||
|
||||
// Modo debug: desactivar autenticación si existe el parámetro debug
|
||||
$debugMode = isset($_GET['debug']) && $_GET['debug'] === 'true';
|
||||
|
||||
if (!$debugMode) {
|
||||
requireAuthentication();
|
||||
}
|
||||
|
||||
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 conversaciones agrupadas por usuario con último mensaje
|
||||
$conversations = $db->fetchAll(
|
||||
"SELECT
|
||||
u.id as user_id,
|
||||
u.phone_number,
|
||||
COALESCE(u.name, u.phone_number) as name,
|
||||
c.content as last_message,
|
||||
c.direction as last_direction,
|
||||
c.message_type as last_message_type,
|
||||
c.created_at as last_message_time,
|
||||
c.status as last_message_status,
|
||||
COUNT(*) as total_messages,
|
||||
SUM(CASE WHEN c.direction = 'incoming' AND c.status = 'received' THEN 1 ELSE 0 END) as unread_count
|
||||
FROM users u
|
||||
LEFT JOIN conversations c ON u.id = c.user_id
|
||||
WHERE c.id IN (
|
||||
SELECT MAX(id)
|
||||
FROM conversations
|
||||
GROUP BY user_id
|
||||
)
|
||||
GROUP BY u.id, u.phone_number, u.name, c.content, c.direction, c.message_type, c.created_at, c.status
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 50"
|
||||
);
|
||||
|
||||
// Si no hay datos, retornar array vacío
|
||||
if (empty($conversations)) {
|
||||
$conversations = [];
|
||||
}
|
||||
|
||||
// Formatear datos para el frontend
|
||||
$conversations = array_map(function($conv) {
|
||||
return [
|
||||
'user_id' => intval($conv['user_id']),
|
||||
'phone_number' => $conv['phone_number'],
|
||||
'name' => $conv['name'],
|
||||
'last_message' => $conv['last_message'] ?? '',
|
||||
'last_direction' => $conv['last_direction'] ?? 'incoming',
|
||||
'last_message_type' => $conv['last_message_type'] ?? 'text',
|
||||
'last_message_time' => $conv['last_message_time'],
|
||||
'last_message_status' => $conv['last_message_status'] ?? 'sent',
|
||||
'total_messages' => intval($conv['total_messages']),
|
||||
'unread_count' => intval($conv['unread_count']),
|
||||
'time_ago' => timeAgo($conv['last_message_time'])
|
||||
];
|
||||
}, $conversations);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'conversations' => $conversations,
|
||||
'total' => count($conversations)
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in get_conversation_list.php: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcular tiempo transcurrido
|
||||
*/
|
||||
function timeAgo($datetime) {
|
||||
$time = time() - strtotime($datetime);
|
||||
|
||||
if ($time < 60) return 'Ahora';
|
||||
if ($time < 3600) return floor($time/60) . 'm';
|
||||
if ($time < 86400) return floor($time/3600) . 'h';
|
||||
if ($time < 2592000) return floor($time/86400) . 'd';
|
||||
if ($time < 31536000) return floor($time/2592000) . ' mes';
|
||||
return floor($time/31536000) . ' año';
|
||||
}
|
||||
?>
|
||||
+64
-4
@@ -18,21 +18,38 @@ if (!$debugMode) {
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// Manejar preflight OPTIONS request
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Método no permitido']);
|
||||
echo json_encode(['error' => 'Método no permitido. Use POST.', 'method_received' => $_SERVER['REQUEST_METHOD']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$rawInput = file_get_contents('php://input');
|
||||
$input = json_decode($rawInput, true);
|
||||
|
||||
// Debug logging
|
||||
if ($debugMode) {
|
||||
error_log("=== SEND MESSAGE DEBUG ===");
|
||||
error_log("Method: " . $_SERVER['REQUEST_METHOD']);
|
||||
error_log("Content-Type: " . ($_SERVER['CONTENT_TYPE'] ?? 'not set'));
|
||||
error_log("Raw input: " . $rawInput);
|
||||
error_log("Parsed JSON: " . json_encode($input));
|
||||
error_log("========================");
|
||||
}
|
||||
|
||||
if (!$input) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'JSON inválido']);
|
||||
echo json_encode(['error' => 'JSON inválido o vacío', 'raw_input' => $rawInput]);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -241,6 +258,49 @@ try {
|
||||
error_log("Response: " . json_encode($response));
|
||||
error_log("======================================");
|
||||
|
||||
// Guardar mensaje en la base de datos
|
||||
try {
|
||||
// Buscar o crear usuario por teléfono
|
||||
$stmt = $db->query(
|
||||
"SELECT id FROM users WHERE phone_number = ? LIMIT 1",
|
||||
[$recipient]
|
||||
);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
$userId = null;
|
||||
if ($user) {
|
||||
$userId = $user['id'];
|
||||
} else {
|
||||
// Crear nuevo usuario
|
||||
$stmt = $db->query(
|
||||
"INSERT INTO users (phone_number, name, created_at) VALUES (?, ?, NOW())",
|
||||
[$recipient, $recipient, date('Y-m-d H:i:s')]
|
||||
);
|
||||
$userId = $db->lastInsertId();
|
||||
}
|
||||
|
||||
if ($userId) {
|
||||
// Preparar datos del mensaje
|
||||
$messageContent = '';
|
||||
$messageType = $type;
|
||||
|
||||
if ($type === 'text') {
|
||||
$messageContent = $input['message'];
|
||||
} elseif ($type === 'template') {
|
||||
$templateName = $input['template'] ?? $input['template_name'] ?? '';
|
||||
$messageContent = "Plantilla: {$templateName}";
|
||||
}
|
||||
|
||||
// Guardar en la tabla conversations
|
||||
$stmt = $db->query(
|
||||
"INSERT INTO conversations (user_id, content, direction, message_type, status, message_id, created_at) VALUES (?, ?, 'outgoing', ?, 'sent', ?, NOW())",
|
||||
[$userId, $messageContent, $messageType, $response['messages'][0]['id'] ?? null]
|
||||
);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Error guardando mensaje en base de datos: " . $e->getMessage());
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Mensaje enviado correctamente',
|
||||
|
||||
@@ -782,4 +782,304 @@ body {
|
||||
box-shadow: none;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================
|
||||
CHAT STYLES
|
||||
================================= */
|
||||
|
||||
.chat-container {
|
||||
background: linear-gradient(135deg, #e5ddd5 0%, #d4c5b9 100%);
|
||||
}
|
||||
|
||||
.conversation-row:hover {
|
||||
background-color: #f8f9fa !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
position: relative;
|
||||
word-wrap: break-word;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.message-bubble.outgoing {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.message-bubble.incoming {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
text-align: right;
|
||||
margin-top: 4px;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Modal de chat personalizado */
|
||||
#chatModal .modal-content {
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
#chatModal .modal-header {
|
||||
border-radius: 12px 12px 0 0;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
#chatModal .modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.message-bubble.bg-success {
|
||||
background: linear-gradient(135deg, #25d366, #20b358) !important;
|
||||
}
|
||||
|
||||
.message-bubble.bg-white {
|
||||
background: #ffffff !important;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
/* Mejorar responsividad de conversaciones */
|
||||
@media (max-width: 768px) {
|
||||
.conversation-row .btn-group .btn {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
max-width: 85% !important;
|
||||
}
|
||||
|
||||
#chatModal .modal-dialog {
|
||||
max-width: 95%;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animaciones para mensajes */
|
||||
.message-bubble {
|
||||
animation: messageAppear 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes messageAppear {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Estilos mejorados para lista de conversaciones */
|
||||
.conversation-card {
|
||||
border: none;
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.conversation-card .card-header {
|
||||
border: none;
|
||||
background: linear-gradient(135deg, #25d366 0%, #128c7e 100%);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.conversation-list {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.conversation-item:hover {
|
||||
background: #f8f9fa;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.conversation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.conversation-avatar {
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
background: linear-gradient(135deg, #25d366, #128c7e);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 1.4rem;
|
||||
margin-right: 15px;
|
||||
box-shadow: 0 4px 12px rgba(37, 211, 102, 0.3);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.conversation-avatar.online::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: #4ade80;
|
||||
border: 2px solid white;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.conversation-details {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.conversation-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.conversation-name {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: #1f2937;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.conversation-time {
|
||||
font-size: 0.8rem;
|
||||
color: #9ca3af;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.conversation-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.conversation-last-message {
|
||||
color: #6b7280;
|
||||
font-size: 0.9rem;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.conversation-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.conversation-unread {
|
||||
background: #25d366;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
padding: 4px 8px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.conversation-status-icon {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.conversation-status-icon.outgoing {
|
||||
color: #25d366;
|
||||
}
|
||||
|
||||
.conversation-status-icon.incoming {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.conversation-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.conversation-item:hover .conversation-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.conversation-actions .btn {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e5e7eb;
|
||||
background: white;
|
||||
color: #6b7280;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.conversation-actions .btn:hover {
|
||||
background: #25d366;
|
||||
color: white;
|
||||
border-color: #25d366;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.conversation-empty {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.conversation-empty .empty-icon {
|
||||
font-size: 4rem;
|
||||
color: #d1d5db;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.conversation-search {
|
||||
border-radius: 25px;
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
padding: 8px 20px;
|
||||
}
|
||||
|
||||
.conversation-search::placeholder {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.conversation-search:focus {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
color: white;
|
||||
box-shadow: none;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
+480
-30
@@ -409,11 +409,11 @@ class SimpleWhatsAppManager {
|
||||
this.log('Cargando conversaciones');
|
||||
|
||||
try {
|
||||
const response = await this.apiCall('get_conversations.php');
|
||||
console.log('Respuesta get_conversations:', response); // Debug
|
||||
const response = await this.apiCall('get_conversation_list.php');
|
||||
console.log('Respuesta get_conversation_list:', response); // Debug
|
||||
|
||||
if (response && response.success && Array.isArray(response.data)) {
|
||||
this.updateConversationsList(response.data);
|
||||
if (response && response.success && Array.isArray(response.conversations)) {
|
||||
this.updateConversationsList(response.conversations);
|
||||
} else if (response && Array.isArray(response)) {
|
||||
// Retrocompatibilidad por si la API devuelve directamente el array
|
||||
this.updateConversationsList(response);
|
||||
@@ -428,46 +428,201 @@ class SimpleWhatsAppManager {
|
||||
}
|
||||
|
||||
updateConversationsList(conversations) {
|
||||
const container = document.getElementById('conversations-table');
|
||||
const container = document.getElementById('conversations-container');
|
||||
if (!container) return;
|
||||
|
||||
if (conversations.length === 0) {
|
||||
container.innerHTML = '<tr><td colspan="6" class="text-center text-muted">No hay conversaciones</td></tr>';
|
||||
container.innerHTML = `
|
||||
<div class="conversation-empty">
|
||||
<div class="empty-icon">
|
||||
<i class="fas fa-comments"></i>
|
||||
</div>
|
||||
<h5>No hay conversaciones</h5>
|
||||
<p>Los chats aparecerán aquí cuando recibas o envíes mensajes</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
|
||||
conversations.forEach(conv => {
|
||||
const date = new Date(conv.created_at);
|
||||
const formatDate = date.toLocaleDateString() + ' ' + date.toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' });
|
||||
const timeAgo = conv.time_ago || this.formatTimeAgo(conv.last_message_time || conv.created_at);
|
||||
const lastMessage = conv.last_message || conv.content || 'Sin mensajes';
|
||||
const userName = conv.name || conv.phone_number;
|
||||
const userInitial = userName.charAt(0).toUpperCase();
|
||||
const isOnline = Math.random() > 0.6; // Simulación de estado online
|
||||
|
||||
// Unread badge
|
||||
const unreadBadge = (conv.unread_count && conv.unread_count > 0) ?
|
||||
`<div class="conversation-unread">${conv.unread_count}</div>` : '';
|
||||
|
||||
// Status icon
|
||||
const statusIcon = conv.last_direction === 'outgoing'
|
||||
? '<i class="fas fa-check-double conversation-status-icon outgoing"></i>'
|
||||
: '<i class="fas fa-arrow-down conversation-status-icon incoming"></i>';
|
||||
|
||||
// Message type indicator
|
||||
const messageTypeIcon = conv.last_message_type === 'template'
|
||||
? '<i class="fas fa-file-alt text-primary" title="Plantilla"></i> '
|
||||
: conv.last_message_type === 'image'
|
||||
? '<i class="fas fa-image text-info" title="Imagen"></i> '
|
||||
: '';
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>
|
||||
<div>
|
||||
<strong>${conv.name || conv.phone_number}</strong><br>
|
||||
<small class="text-muted">${conv.phone_number}</small>
|
||||
<div class="conversation-item" onclick="openChatWindow(${conv.user_id})">
|
||||
<div class="conversation-avatar ${isOnline ? 'online' : ''}">
|
||||
${userInitial}
|
||||
</div>
|
||||
<div class="conversation-details">
|
||||
<div class="conversation-header">
|
||||
<div class="conversation-name">
|
||||
${userName}
|
||||
${conv.last_message_status === 'read' ? '<i class="fas fa-check-double text-primary" title="Leído"></i>' : ''}
|
||||
</div>
|
||||
<div class="conversation-time">${timeAgo}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">
|
||||
${conv.content || 'Sin contenido'}
|
||||
<div class="conversation-preview">
|
||||
${statusIcon}
|
||||
<div class="conversation-last-message">
|
||||
${messageTypeIcon}${lastMessage}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge bg-info">${conv.message_type || 'text'}</span></td>
|
||||
<td><span class="badge bg-${conv.status === 'sent' ? 'success' : 'warning'}">${conv.status || 'unknown'}</span></td>
|
||||
<td>${formatDate}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="viewConversation(${conv.user_id})">
|
||||
<i class="fas fa-eye"></i> Ver
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
<div class="conversation-meta">
|
||||
${unreadBadge}
|
||||
<div class="conversation-actions">
|
||||
<button class="btn btn-sm" onclick="event.stopPropagation(); openChatWindow(${conv.user_id})" title="Abrir chat">
|
||||
<i class="fas fa-comments"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm" onclick="event.stopPropagation(); viewConversationDetails(${conv.user_id})" title="Ver detalles">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
// Agregar funcionalidad de búsqueda
|
||||
this.setupConversationSearch(conversations);
|
||||
}
|
||||
|
||||
setupConversationSearch(conversations) {
|
||||
const searchInput = document.getElementById('search-conversations');
|
||||
if (!searchInput) return;
|
||||
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const query = e.target.value.toLowerCase().trim();
|
||||
|
||||
if (!query) {
|
||||
this.updateConversationsDisplay(conversations);
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = conversations.filter(conv => {
|
||||
const name = (conv.name || '').toLowerCase();
|
||||
const phone = (conv.phone_number || '').toLowerCase();
|
||||
const message = (conv.last_message || conv.content || '').toLowerCase();
|
||||
|
||||
return name.includes(query) ||
|
||||
phone.includes(query) ||
|
||||
message.includes(query);
|
||||
});
|
||||
|
||||
this.updateConversationsDisplay(filtered);
|
||||
});
|
||||
}
|
||||
|
||||
updateConversationsDisplay(conversations) {
|
||||
const container = document.getElementById('conversations-container');
|
||||
if (!container) return;
|
||||
|
||||
if (conversations.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="conversation-empty">
|
||||
<div class="empty-icon">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
<h5>No se encontraron conversaciones</h5>
|
||||
<p>Intenta con otros términos de búsqueda</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
conversations.forEach(conv => {
|
||||
const timeAgo = conv.time_ago || this.formatTimeAgo(conv.last_message_time || conv.created_at);
|
||||
const lastMessage = conv.last_message || conv.content || 'Sin mensajes';
|
||||
const userName = conv.name || conv.phone_number;
|
||||
const userInitial = userName.charAt(0).toUpperCase();
|
||||
const isOnline = Math.random() > 0.6;
|
||||
|
||||
const unreadBadge = (conv.unread_count && conv.unread_count > 0) ?
|
||||
`<div class="conversation-unread">${conv.unread_count}</div>` : '';
|
||||
|
||||
const statusIcon = conv.last_direction === 'outgoing'
|
||||
? '<i class="fas fa-check-double conversation-status-icon outgoing"></i>'
|
||||
: '<i class="fas fa-arrow-down conversation-status-icon incoming"></i>';
|
||||
|
||||
const messageTypeIcon = conv.last_message_type === 'template'
|
||||
? '<i class="fas fa-file-alt text-primary" title="Plantilla"></i> '
|
||||
: conv.last_message_type === 'image'
|
||||
? '<i class="fas fa-image text-info" title="Imagen"></i> '
|
||||
: '';
|
||||
|
||||
html += `
|
||||
<div class="conversation-item" onclick="openChatWindow(${conv.user_id})">
|
||||
<div class="conversation-avatar ${isOnline ? 'online' : ''}">
|
||||
${userInitial}
|
||||
</div>
|
||||
<div class="conversation-details">
|
||||
<div class="conversation-header">
|
||||
<div class="conversation-name">
|
||||
${userName}
|
||||
${conv.last_message_status === 'read' ? '<i class="fas fa-check-double text-primary" title="Leído"></i>' : ''}
|
||||
</div>
|
||||
<div class="conversation-time">${timeAgo}</div>
|
||||
</div>
|
||||
<div class="conversation-preview">
|
||||
${statusIcon}
|
||||
<div class="conversation-last-message">
|
||||
${messageTypeIcon}${lastMessage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="conversation-meta">
|
||||
${unreadBadge}
|
||||
<div class="conversation-actions">
|
||||
<button class="btn btn-sm" onclick="event.stopPropagation(); openChatWindow(${conv.user_id})" title="Abrir chat">
|
||||
<i class="fas fa-comments"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm" onclick="event.stopPropagation(); viewConversationDetails(${conv.user_id})" title="Ver detalles">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
formatTimeAgo(datetime) {
|
||||
if (!datetime) return '';
|
||||
const now = new Date();
|
||||
const date = new Date(datetime);
|
||||
const diff = Math.floor((now - date) / 1000);
|
||||
|
||||
if (diff < 60) return 'Ahora';
|
||||
if (diff < 3600) return `${Math.floor(diff/60)}m`;
|
||||
if (diff < 86400) return `${Math.floor(diff/3600)}h`;
|
||||
if (diff < 2592000) return `${Math.floor(diff/86400)}d`;
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
@@ -918,10 +1073,305 @@ window.debugAPI = async function (endpoint) {
|
||||
}
|
||||
};
|
||||
|
||||
// Función para ver conversación específica
|
||||
// Función para abrir ventana de chat
|
||||
window.openChatWindow = function (userId) {
|
||||
console.log('Abriendo chat del usuario:', userId);
|
||||
|
||||
// Abrir nueva ventana de chat
|
||||
const chatUrl = `chat_window.php?user_id=${userId}&debug=true`;
|
||||
const windowFeatures = 'width=1000,height=700,resizable=yes,scrollbars=yes,status=yes,toolbar=no,menubar=no,location=no';
|
||||
|
||||
const chatWindow = window.open(chatUrl, `chat_${userId}`, windowFeatures);
|
||||
|
||||
if (chatWindow) {
|
||||
chatWindow.focus();
|
||||
} else {
|
||||
alert('No se pudo abrir la ventana de chat. Verifica que tu navegador permita ventanas emergentes.');
|
||||
}
|
||||
};
|
||||
|
||||
// Función para mostrar modal de chat
|
||||
function showChatModal(user, messages) {
|
||||
// Escapar datos del usuario para evitar XSS
|
||||
const userName = escapeHtml(user.name || 'Usuario');
|
||||
const userPhone = escapeHtml(user.phone_number || '');
|
||||
|
||||
const modalHtml = `
|
||||
<div class="modal fade" id="chatModal" tabindex="-1" aria-labelledby="chatModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-success text-white">
|
||||
<h5 class="modal-title" id="chatModalLabel">
|
||||
<i class="fab fa-whatsapp"></i> ${userName}
|
||||
<small class="opacity-75">(${userPhone})</small>
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-0">
|
||||
<div id="chat-messages" class="chat-container" style="height: 400px; overflow-y: auto; padding: 15px; background-color: #e5ddd5;">
|
||||
${generateChatMessages(messages)}
|
||||
</div>
|
||||
<div class="border-top p-3">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<select id="messageType" class="form-select" style="max-width: 120px;">
|
||||
<option value="text">Texto</option>
|
||||
<option value="template">Plantilla</option>
|
||||
</select>
|
||||
<input type="text" id="messageInput" class="form-control" placeholder="Escribe tu mensaje...">
|
||||
<button class="btn btn-success" onclick="sendChatMessage(${user.id})">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="templateSelector" class="mt-2" style="display: none;">
|
||||
<select id="templateSelect" class="form-select">
|
||||
<option value="">Selecciona una plantilla...</option>
|
||||
<option value="hello_world">hello_world</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Remover modal anterior si existe
|
||||
const existingModal = document.getElementById('chatModal');
|
||||
if (existingModal) {
|
||||
existingModal.remove();
|
||||
}
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
|
||||
// Event listener para cambio de tipo de mensaje
|
||||
document.getElementById('messageType').addEventListener('change', function() {
|
||||
const templateSelector = document.getElementById('templateSelector');
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
|
||||
if (this.value === 'template') {
|
||||
templateSelector.style.display = 'block';
|
||||
messageInput.placeholder = 'Parámetros de la plantilla (opcional)';
|
||||
} else {
|
||||
templateSelector.style.display = 'none';
|
||||
messageInput.placeholder = 'Escribe tu mensaje...';
|
||||
}
|
||||
});
|
||||
|
||||
// Mostrar modal
|
||||
const modal = new bootstrap.Modal(document.getElementById('chatModal'));
|
||||
modal.show();
|
||||
|
||||
// Scroll al final
|
||||
setTimeout(() => {
|
||||
const chatContainer = document.getElementById('chat-messages');
|
||||
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// Generar HTML de mensajes del chat
|
||||
function generateChatMessages(messages) {
|
||||
if (!messages || messages.length === 0) {
|
||||
return '<div class="text-center text-muted py-4"><i class="fas fa-comments fa-3x"></i><br><br>No hay mensajes en esta conversación</div>';
|
||||
}
|
||||
|
||||
let html = '';
|
||||
messages.forEach(msg => {
|
||||
const isOutgoing = msg.direction === 'outgoing';
|
||||
const messageClass = isOutgoing ? 'outgoing' : 'incoming';
|
||||
const alignClass = isOutgoing ? 'justify-content-end' : 'justify-content-start';
|
||||
const bgClass = isOutgoing ? 'bg-success text-white' : 'bg-white';
|
||||
|
||||
// Escapar el contenido del mensaje
|
||||
const messageContent = escapeHtml(msg.content) || '<em>Sin contenido</em>';
|
||||
const messageTime = escapeHtml(msg.time || '');
|
||||
|
||||
html += `
|
||||
<div class="d-flex ${alignClass} mb-2">
|
||||
<div class="message-bubble ${bgClass} rounded-3 p-2 shadow-sm" style="max-width: 70%;">
|
||||
<div class="message-content">
|
||||
${messageContent}
|
||||
</div>
|
||||
<div class="message-time text-${isOutgoing ? 'light' : 'muted'}" style="font-size: 0.75rem;">
|
||||
${messageTime} ${isOutgoing ? '✓✓' : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// Función para escapar HTML y prevenir XSS
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
// Función para enviar mensaje desde el chat
|
||||
window.sendChatMessage = async function (userId) {
|
||||
const messageType = document.getElementById('messageType').value;
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const message = messageInput.value.trim();
|
||||
|
||||
if (!message && messageType === 'text') {
|
||||
alert('Por favor escribe un mensaje');
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageType === 'template') {
|
||||
const templateSelect = document.getElementById('templateSelect');
|
||||
const template = templateSelect.value;
|
||||
|
||||
if (!template) {
|
||||
alert('Por favor selecciona una plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
await sendTemplateMessage(userId, template, message);
|
||||
} else {
|
||||
await sendTextMessage(userId, message);
|
||||
}
|
||||
|
||||
messageInput.value = '';
|
||||
};
|
||||
|
||||
// Función para enviar mensaje de texto
|
||||
async function sendTextMessage(userId, message) {
|
||||
try {
|
||||
const manager = window.whatsappManager;
|
||||
const usersResponse = await manager.apiCall('get_users.php?debug=true');
|
||||
|
||||
// Manejar diferentes formatos de respuesta
|
||||
let users = [];
|
||||
if (Array.isArray(usersResponse)) {
|
||||
users = usersResponse;
|
||||
} else if (usersResponse && usersResponse.data && Array.isArray(usersResponse.data)) {
|
||||
users = usersResponse.data;
|
||||
} else if (usersResponse && usersResponse.users && Array.isArray(usersResponse.users)) {
|
||||
users = usersResponse.users;
|
||||
} else {
|
||||
console.error('Formato de respuesta de usuarios inesperado:', usersResponse);
|
||||
throw new Error('Error obteniendo lista de usuarios');
|
||||
}
|
||||
|
||||
const user = users.find(u => u.id == userId);
|
||||
|
||||
if (!user) {
|
||||
throw new Error('Usuario no encontrado');
|
||||
}
|
||||
|
||||
const response = await manager.apiCall('send_message.php', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
recipient: user.phone_number,
|
||||
type: 'text',
|
||||
message: message
|
||||
}
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
// Refrescar chat
|
||||
openChatWindow(userId);
|
||||
showAlert('Mensaje enviado correctamente', 'success');
|
||||
} else {
|
||||
throw new Error(response.error || 'Error enviando mensaje');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error enviando mensaje:', error);
|
||||
showAlert('Error enviando mensaje: ' + error.message, 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// Función para enviar mensaje de plantilla
|
||||
async function sendTemplateMessage(userId, template, parameters) {
|
||||
try {
|
||||
const manager = window.whatsappManager;
|
||||
const usersResponse = await manager.apiCall('get_users.php?debug=true');
|
||||
|
||||
// Manejar diferentes formatos de respuesta
|
||||
let users = [];
|
||||
if (Array.isArray(usersResponse)) {
|
||||
users = usersResponse;
|
||||
} else if (usersResponse && usersResponse.data && Array.isArray(usersResponse.data)) {
|
||||
users = usersResponse.data;
|
||||
} else if (usersResponse && usersResponse.users && Array.isArray(usersResponse.users)) {
|
||||
users = usersResponse.users;
|
||||
} else {
|
||||
console.error('Formato de respuesta de usuarios inesperado:', usersResponse);
|
||||
throw new Error('Error obteniendo lista de usuarios');
|
||||
}
|
||||
|
||||
const user = users.find(u => u.id == userId);
|
||||
|
||||
if (!user) {
|
||||
throw new Error('Usuario no encontrado');
|
||||
}
|
||||
|
||||
const response = await manager.apiCall('send_message.php', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
recipient: user.phone_number,
|
||||
type: 'template',
|
||||
template: template,
|
||||
language: 'en_US',
|
||||
parameters: parameters ? parameters.split(',') : []
|
||||
}
|
||||
});
|
||||
|
||||
if (response && response.success) {
|
||||
// Refrescar chat
|
||||
openChatWindow(userId);
|
||||
showAlert('Mensaje de plantilla enviado correctamente', 'success');
|
||||
} else {
|
||||
throw new Error(response.error || 'Error enviando plantilla');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error enviando plantilla:', error);
|
||||
showAlert('Error enviando plantilla: ' + error.message, 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// Función para ver detalles de conversación
|
||||
window.viewConversationDetails = function (userId) {
|
||||
console.log('Viendo detalles de conversación del usuario:', userId);
|
||||
openChatWindow(userId);
|
||||
};
|
||||
|
||||
// Función para mostrar alertas
|
||||
function showAlert(message, type = 'info') {
|
||||
const alertHtml = `
|
||||
<div class="alert alert-${type} alert-dismissible fade show position-fixed" style="top: 20px; right: 20px; z-index: 9999;" role="alert">
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', alertHtml);
|
||||
|
||||
// Auto-dismiss después de 5 segundos
|
||||
setTimeout(() => {
|
||||
const alert = document.querySelector('.alert');
|
||||
if (alert) {
|
||||
alert.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Función para ver conversación específica (mantener compatibilidad)
|
||||
window.viewConversation = function (userId) {
|
||||
console.log('Viendo conversación del usuario:', userId);
|
||||
alert(`Ver conversación del usuario ${userId} - Función en desarrollo`);
|
||||
openChatWindow(userId);
|
||||
};
|
||||
|
||||
// Función para editar usuario
|
||||
|
||||
+575
@@ -0,0 +1,575 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Verificar autenticación
|
||||
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
|
||||
// Permitir acceso en modo debug
|
||||
if (!isset($_GET['debug']) || $_GET['debug'] !== 'true') {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$user_id = $_GET['user_id'] ?? '';
|
||||
if (empty($user_id)) {
|
||||
die('ID de usuario requerido');
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Chat WhatsApp - Usuario</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="assets/css/styles.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
margin: 20px auto;
|
||||
max-width: 900px;
|
||||
height: calc(100vh - 40px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
background: linear-gradient(135deg, #25D366 0%, #128C7E 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 15px 15px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.chat-input {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 0 0 15px 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
margin: 10px 0;
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px;
|
||||
max-width: 70%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.message-outgoing {
|
||||
background: #DCF8C6;
|
||||
margin-left: auto;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.message-incoming {
|
||||
background: white;
|
||||
margin-right: auto;
|
||||
border-bottom-left-radius: 4px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 0.75rem;
|
||||
color: #999;
|
||||
margin-top: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.message-incoming .message-time {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.typing-indicator {
|
||||
display: none;
|
||||
padding: 10px;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
border-radius: 25px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: none;
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
.btn-send {
|
||||
background: #25D366;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 0 25px 25px 0;
|
||||
}
|
||||
|
||||
.btn-send:hover {
|
||||
background: #128C7E;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background: #25D366;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="chat-container">
|
||||
<!-- Header del chat -->
|
||||
<div class="chat-header">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="user-avatar me-3" id="userAvatar">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0" id="userName">Cargando...</h5>
|
||||
<small class="status-indicator" id="userStatus">
|
||||
<i class="fas fa-circle text-success"></i> En línea
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<button class="btn btn-outline-light btn-sm me-2" onclick="refreshChat()">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-light btn-sm" onclick="window.close()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mensajes del chat -->
|
||||
<div class="chat-messages" id="chatMessages">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Cargando mensajes...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicador de escritura -->
|
||||
<div class="typing-indicator" id="typingIndicator">
|
||||
<i class="fas fa-circle"></i>
|
||||
<i class="fas fa-circle"></i>
|
||||
<i class="fas fa-circle"></i>
|
||||
El usuario está escribiendo...
|
||||
</div>
|
||||
|
||||
<!-- Input de mensaje -->
|
||||
<div class="chat-input">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<select class="form-select" id="messageType">
|
||||
<option value="text">Mensaje de texto</option>
|
||||
<option value="template">Plantilla</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4" id="templateSelectContainer" style="display: none;">
|
||||
<select class="form-select" id="templateSelect">
|
||||
<option value="">Seleccionar plantilla...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="messageInput"
|
||||
placeholder="Escribe un mensaje..."
|
||||
onkeypress="handleEnterKey(event)">
|
||||
<button class="btn btn-send" onclick="sendMessage()">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Variables globales
|
||||
const userId = <?php echo json_encode($user_id); ?>;
|
||||
let currentUser = null;
|
||||
let whatsappManager = null;
|
||||
|
||||
// Manager para API calls
|
||||
class WhatsAppManager {
|
||||
constructor() {
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
async apiCall(endpoint, options = {}) {
|
||||
const defaultOptions = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
};
|
||||
|
||||
// Configurar método y cuerpo si es POST
|
||||
if (options.body) {
|
||||
defaultOptions.method = 'POST';
|
||||
defaultOptions.body = JSON.stringify(options.body);
|
||||
}
|
||||
|
||||
// Combinar opciones correctamente
|
||||
const finalOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
headers: {
|
||||
...defaultOptions.headers,
|
||||
...(options.headers || {})
|
||||
}
|
||||
};
|
||||
|
||||
// Si había un body en options, asegurar que se serializa correctamente
|
||||
if (options.body && typeof options.body === 'object') {
|
||||
finalOptions.body = JSON.stringify(options.body);
|
||||
}
|
||||
|
||||
const url = endpoint.startsWith('http') ? endpoint : `api/${endpoint}`;
|
||||
const response = await fetch(url, finalOptions);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializar manager
|
||||
whatsappManager = new WhatsAppManager();
|
||||
|
||||
// Función para escapar HTML
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
// Cargar datos del usuario y mensajes
|
||||
async function loadUserData() {
|
||||
try {
|
||||
const response = await whatsappManager.apiCall(`get_conversation_detail.php?user_id=${userId}`);
|
||||
|
||||
if (response && response.success) {
|
||||
currentUser = response.user;
|
||||
|
||||
// Actualizar header
|
||||
document.getElementById('userName').textContent = currentUser.name || 'Usuario';
|
||||
document.getElementById('userAvatar').innerHTML =
|
||||
`<span>${(currentUser.name || 'U').charAt(0).toUpperCase()}</span>`;
|
||||
|
||||
// Cargar mensajes
|
||||
displayMessages(response.messages);
|
||||
|
||||
// Cargar plantillas
|
||||
loadTemplates();
|
||||
|
||||
} else {
|
||||
showError('Error cargando datos del usuario');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
showError('Error cargando conversación: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar mensajes
|
||||
function displayMessages(messages) {
|
||||
const chatMessages = document.getElementById('chatMessages');
|
||||
|
||||
if (!messages || messages.length === 0) {
|
||||
chatMessages.innerHTML = `
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="fas fa-comments fa-3x mb-3"></i>
|
||||
<br>No hay mensajes en esta conversación
|
||||
<br><small>Envía el primer mensaje para comenzar</small>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
messages.forEach(msg => {
|
||||
const isOutgoing = msg.direction === 'outgoing';
|
||||
const messageClass = isOutgoing ? 'message-outgoing' : 'message-incoming';
|
||||
const messageContent = escapeHtml(msg.content) || '<em>Sin contenido</em>';
|
||||
const messageTime = escapeHtml(msg.time || '');
|
||||
|
||||
html += `
|
||||
<div class="d-flex ${isOutgoing ? 'justify-content-end' : 'justify-content-start'}">
|
||||
<div class="message-bubble ${messageClass}">
|
||||
<div class="message-content">${messageContent}</div>
|
||||
<div class="message-time">
|
||||
${messageTime} ${isOutgoing ? '<i class="fas fa-check-double text-primary"></i>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
chatMessages.innerHTML = html;
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// Cargar plantillas
|
||||
async function loadTemplates() {
|
||||
try {
|
||||
const response = await whatsappManager.apiCall('check_templates.php');
|
||||
const templateSelect = document.getElementById('templateSelect');
|
||||
|
||||
if (response && response.templates) {
|
||||
templateSelect.innerHTML = '<option value="">Seleccionar plantilla...</option>';
|
||||
response.templates.forEach(template => {
|
||||
templateSelect.innerHTML += `<option value="${template.name}">${template.display_name || template.name}</option>`;
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cargando plantillas:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Enviar mensaje
|
||||
async function sendMessage() {
|
||||
const messageType = document.getElementById('messageType').value;
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const message = messageInput.value.trim();
|
||||
|
||||
if (!message && messageType === 'text') {
|
||||
alert('Por favor escribe un mensaje');
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageType === 'template') {
|
||||
const templateSelect = document.getElementById('templateSelect');
|
||||
const template = templateSelect.value;
|
||||
|
||||
if (!template) {
|
||||
alert('Por favor selecciona una plantilla');
|
||||
return;
|
||||
}
|
||||
|
||||
await sendTemplateMessage(template, message);
|
||||
} else {
|
||||
await sendTextMessage(message);
|
||||
}
|
||||
|
||||
messageInput.value = '';
|
||||
}
|
||||
|
||||
// Enviar mensaje de texto
|
||||
async function sendTextMessage(message) {
|
||||
try {
|
||||
showTyping();
|
||||
|
||||
console.log('Enviando mensaje de texto:', {
|
||||
message,
|
||||
user: currentUser
|
||||
});
|
||||
|
||||
const requestBody = {
|
||||
recipient: currentUser.phone_number,
|
||||
type: 'text',
|
||||
message: message
|
||||
};
|
||||
|
||||
console.log('Request body:', requestBody);
|
||||
|
||||
const response = await whatsappManager.apiCall('send_message.php', {
|
||||
body: requestBody
|
||||
});
|
||||
|
||||
console.log('Response:', response);
|
||||
|
||||
hideTyping();
|
||||
|
||||
if (response && response.success) {
|
||||
// Agregar mensaje a la vista inmediatamente
|
||||
addMessageToView(message, 'outgoing');
|
||||
showAlert('Mensaje enviado correctamente', 'success');
|
||||
} else {
|
||||
throw new Error(response.error || 'Error enviando mensaje');
|
||||
}
|
||||
} catch (error) {
|
||||
hideTyping();
|
||||
console.error('Error enviando mensaje:', error);
|
||||
showAlert('Error enviando mensaje: ' + error.message, 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// Enviar mensaje de plantilla
|
||||
async function sendTemplateMessage(template, parameters) {
|
||||
try {
|
||||
showTyping();
|
||||
|
||||
console.log('Enviando plantilla:', {
|
||||
template,
|
||||
parameters,
|
||||
user: currentUser
|
||||
});
|
||||
|
||||
const requestBody = {
|
||||
recipient: currentUser.phone_number,
|
||||
type: 'template',
|
||||
template: template,
|
||||
language: 'en_US',
|
||||
parameters: parameters ? parameters.split(',') : []
|
||||
};
|
||||
|
||||
console.log('Request body:', requestBody);
|
||||
|
||||
const response = await whatsappManager.apiCall('send_message.php', {
|
||||
body: requestBody
|
||||
});
|
||||
|
||||
console.log('Response:', response);
|
||||
|
||||
hideTyping();
|
||||
|
||||
if (response && response.success) {
|
||||
addMessageToView(`Plantilla: ${template}`, 'outgoing');
|
||||
showAlert('Mensaje de plantilla enviado correctamente', 'success');
|
||||
} else {
|
||||
throw new Error(response.error || 'Error enviando plantilla');
|
||||
}
|
||||
} catch (error) {
|
||||
hideTyping();
|
||||
console.error('Error enviando plantilla:', error);
|
||||
showAlert('Error enviando plantilla: ' + error.message, 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// Agregar mensaje a la vista
|
||||
function addMessageToView(message, direction) {
|
||||
const chatMessages = document.getElementById('chatMessages');
|
||||
const isOutgoing = direction === 'outgoing';
|
||||
const messageClass = isOutgoing ? 'message-outgoing' : 'message-incoming';
|
||||
const now = new Date().toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });
|
||||
|
||||
const messageHtml = `
|
||||
<div class="d-flex ${isOutgoing ? 'justify-content-end' : 'justify-content-start'}">
|
||||
<div class="message-bubble ${messageClass}">
|
||||
<div class="message-content">${escapeHtml(message)}</div>
|
||||
<div class="message-time">
|
||||
${now} ${isOutgoing ? '<i class="fas fa-check text-muted"></i>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
chatMessages.insertAdjacentHTML('beforeend', messageHtml);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// Mostrar indicador de escritura
|
||||
function showTyping() {
|
||||
document.getElementById('typingIndicator').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideTyping() {
|
||||
document.getElementById('typingIndicator').style.display = 'none';
|
||||
}
|
||||
|
||||
// Scroll al final
|
||||
function scrollToBottom() {
|
||||
const chatMessages = document.getElementById('chatMessages');
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
|
||||
// Refrescar chat
|
||||
async function refreshChat() {
|
||||
await loadUserData();
|
||||
}
|
||||
|
||||
// Manejar Enter para enviar
|
||||
function handleEnterKey(event) {
|
||||
if (event.key === 'Enter') {
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar/ocultar selector de plantillas
|
||||
document.getElementById('messageType').addEventListener('change', function() {
|
||||
const templateContainer = document.getElementById('templateSelectContainer');
|
||||
if (this.value === 'template') {
|
||||
templateContainer.style.display = 'block';
|
||||
} else {
|
||||
templateContainer.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Mostrar alertas
|
||||
function showAlert(message, type = 'info') {
|
||||
const alertHtml = `
|
||||
<div class="alert alert-${type} alert-dismissible fade show position-fixed"
|
||||
style="top: 20px; right: 20px; z-index: 9999;" role="alert">
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', alertHtml);
|
||||
|
||||
setTimeout(() => {
|
||||
const alert = document.querySelector('.alert');
|
||||
if (alert) {
|
||||
alert.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
showAlert(message, 'danger');
|
||||
}
|
||||
|
||||
// Inicializar cuando la página se carga
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadUserData();
|
||||
|
||||
// Auto-refresh cada 30 segundos para nuevos mensajes
|
||||
setInterval(refreshChat, 30000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Ver estructura de la tabla message_templates
|
||||
*/
|
||||
|
||||
require_once 'config/config.php';
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
echo "=== ESTRUCTURA DE TABLA message_templates ===\n\n";
|
||||
|
||||
// Describir la tabla
|
||||
$stmt = $db->query("DESCRIBE message_templates");
|
||||
$columns = $stmt->fetchAll();
|
||||
|
||||
echo "Columnas disponibles:\n";
|
||||
foreach ($columns as $column) {
|
||||
echo "• {$column['Field']} ({$column['Type']}) - {$column['Null']} - {$column['Default']}\n";
|
||||
}
|
||||
|
||||
echo "\n=== INTENTAR INSERCIÓN SIMPLE ===\n";
|
||||
|
||||
// Intentar inserción con campos mínimos
|
||||
$stmt = $db->query(
|
||||
"INSERT INTO message_templates (template_name, language_code, status) VALUES (?, ?, ?)",
|
||||
['hello_world', 'en_US', 'approved']
|
||||
);
|
||||
|
||||
if ($stmt) {
|
||||
echo "✅ Plantilla 'hello_world' agregada exitosamente\n";
|
||||
}
|
||||
|
||||
// Verificar que se agregó
|
||||
echo "\n📋 Plantillas en la BD:\n";
|
||||
$stmt = $db->query("SELECT * FROM message_templates");
|
||||
$templates = $stmt->fetchAll();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
echo "• {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n";
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Eliminar plantillas que no funcionan de la base de datos
|
||||
*/
|
||||
|
||||
require_once 'config/config.php';
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
echo "=== ELIMINAR PLANTILLAS QUE NO FUNCIONAN ===\n\n";
|
||||
|
||||
// Mostrar plantillas actuales
|
||||
echo "📋 Plantillas actuales en la BD:\n";
|
||||
$stmt = $db->query("SELECT id, template_name, language_code, status FROM message_templates ORDER BY template_name");
|
||||
$templates = $stmt->fetchAll();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
echo "• ID: {$template['id']} - {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n";
|
||||
}
|
||||
|
||||
echo "\n🗑️ Eliminando plantillas que no funcionan...\n";
|
||||
|
||||
// Eliminar plantillas que no funcionan
|
||||
$templatesToDelete = [
|
||||
'welcome_user',
|
||||
'jaspers_market_plain_text_v1'
|
||||
];
|
||||
|
||||
foreach ($templatesToDelete as $templateName) {
|
||||
$stmt = $db->query(
|
||||
"DELETE FROM message_templates WHERE template_name = ?",
|
||||
[$templateName]
|
||||
);
|
||||
|
||||
$affectedRows = $stmt->rowCount();
|
||||
echo "• Eliminada '{$templateName}': {$affectedRows} registro(s) afectado(s)\n";
|
||||
}
|
||||
|
||||
echo "\n✅ Plantillas restantes en la BD:\n";
|
||||
$stmt = $db->query("SELECT id, template_name, language_code, status FROM message_templates ORDER BY template_name");
|
||||
$remainingTemplates = $stmt->fetchAll();
|
||||
|
||||
if (empty($remainingTemplates)) {
|
||||
echo "❌ No hay plantillas restantes\n";
|
||||
echo "\n💡 Sugerencia: Agrega la plantilla 'hello_world' que funciona:\n";
|
||||
|
||||
// Agregar plantilla hello_world que sabemos que funciona
|
||||
$db->query(
|
||||
"INSERT INTO message_templates (template_name, language_code, status, body_text, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())",
|
||||
['hello_world', 'en_US', 'approved', 'Hello World! This is a test message.']
|
||||
);
|
||||
echo "✅ Agregada plantilla 'hello_world' (en_US) que funciona correctamente\n";
|
||||
|
||||
// Mostrar plantillas finales
|
||||
$stmt = $db->query("SELECT id, template_name, language_code, status FROM message_templates ORDER BY template_name");
|
||||
$finalTemplates = $stmt->fetchAll();
|
||||
|
||||
echo "\n📋 Plantillas finales:\n";
|
||||
foreach ($finalTemplates as $template) {
|
||||
echo "• ID: {$template['id']} - {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n";
|
||||
}
|
||||
} else {
|
||||
foreach ($remainingTemplates as $template) {
|
||||
echo "• ID: {$template['id']} - {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🎉 Limpieza completada exitosamente\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -637,8 +637,11 @@ function saveWhatsAppConfigToDB($whatsappConfig) {
|
||||
*/
|
||||
function getWhatsAppConfigFromDB() {
|
||||
return [
|
||||
// Mapear a nombres esperados por el código
|
||||
'token' => getConfigFromDB('whatsapp_token', ''),
|
||||
'whatsapp_token' => getConfigFromDB('whatsapp_token', ''),
|
||||
'phone_number_id' => getConfigFromDB('whatsapp_phone_number_id', ''),
|
||||
'api_url' => getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'),
|
||||
'whatsapp_api_url' => getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'),
|
||||
'webhook_verify_token' => getConfigFromDB('webhook_verify_token', ''),
|
||||
'business_name' => getConfigFromDB('business_name', ''),
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Debug de plantillas - Ver qué plantillas hay y con qué códigos de idioma
|
||||
*/
|
||||
|
||||
require_once 'config/config.php';
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
echo "=== PLANTILLAS EN BASE DE DATOS ===\n\n";
|
||||
|
||||
$stmt = $db->query("SELECT template_name, language_code, status FROM message_templates ORDER BY template_name, language_code");
|
||||
$templates = $stmt->fetchAll();
|
||||
|
||||
if (empty($templates)) {
|
||||
echo "❌ No hay plantillas en la base de datos\n";
|
||||
} else {
|
||||
echo "📋 Plantillas encontradas:\n\n";
|
||||
foreach ($templates as $template) {
|
||||
echo "• Nombre: {$template['template_name']}\n";
|
||||
echo " Idioma: {$template['language_code']}\n";
|
||||
echo " Estado: {$template['status']}\n";
|
||||
echo " -----\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== CONFIGURACIÓN ACTUAL ===\n";
|
||||
$config = getWhatsAppConfigFromDB();
|
||||
echo "Token presente: " . (empty($config['token']) ? '❌ NO' : '✅ SÍ (' . strlen($config['token']) . ' chars)') . "\n";
|
||||
echo "Phone ID: " . ($config['phone_number_id'] ?? 'No configurado') . "\n";
|
||||
echo "API URL: " . ($config['api_url'] ?? 'No configurada') . "\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "❌ Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -183,30 +183,28 @@ try {
|
||||
|
||||
<!-- Conversations Tab -->
|
||||
<div id="conversations" class="tab-content">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5><i class="fas fa-comments"></i> Conversaciones</h5>
|
||||
<div>
|
||||
<input type="text" class="form-control" placeholder="Buscar conversaciones..." id="search-conversations">
|
||||
<div class="card conversation-card">
|
||||
<div class="card-header bg-gradient-primary text-white">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h5 class="mb-0"><i class="fas fa-comments"></i> Conversaciones</h5>
|
||||
<small class="text-white-50">Administra tus chats de WhatsApp</small>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-light border-0">
|
||||
<i class="fas fa-search"></i>
|
||||
</span>
|
||||
<input type="text" class="form-control border-0"
|
||||
placeholder="Buscar conversaciones..."
|
||||
id="search-conversations">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Usuario</th>
|
||||
<th>Último Mensaje</th>
|
||||
<th>Tipo</th>
|
||||
<th>Estado</th>
|
||||
<th>Fecha</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="conversations-table">
|
||||
<!-- Contenido dinámico -->
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card-body p-0">
|
||||
<div class="conversation-list" id="conversations-container">
|
||||
<!-- Contenido dinámico -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
[2026-01-12 13:26:51] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"}
|
||||
[2026-01-12 13:28:56] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"}
|
||||
[2026-01-12 14:07:16] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"}
|
||||
[2026-01-12 14:24:57] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"}
|
||||
[2026-01-12 14:25:28] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"}
|
||||
|
||||
+121
-74
@@ -1,26 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Servicio de WhatsApp - Envío de mensajes
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
class WhatsAppService {
|
||||
class WhatsAppService
|
||||
{
|
||||
private $token;
|
||||
private $phoneNumberId;
|
||||
private $apiUrl;
|
||||
private $db;
|
||||
|
||||
public function __construct() {
|
||||
$this->token = WHATSAPP_TOKEN;
|
||||
$this->phoneNumberId = WHATSAPP_PHONE_NUMBER_ID;
|
||||
$this->apiUrl = WHATSAPP_API_URL;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Obtener configuración desde base de datos
|
||||
$config = getWhatsAppConfigFromDB();
|
||||
|
||||
$this->token = $config['token']; // Usar token de BD
|
||||
$this->phoneNumberId = $config['phone_number_id']; // Usar phone_number_id de BD
|
||||
$this->apiUrl = $config['api_url'] ?: 'https://graph.facebook.com/v22.0/'; // Usar api_url de BD
|
||||
$this->db = Database::getInstance();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enviar mensaje de texto
|
||||
*/
|
||||
public function sendTextMessage($to, $message) {
|
||||
public function sendTextMessage($to, $message)
|
||||
{
|
||||
$data = [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'to' => $this->formatPhoneNumber($to),
|
||||
@@ -29,44 +36,62 @@ class WhatsAppService {
|
||||
'body' => $message
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
return $this->sendMessage($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enviar mensaje con template
|
||||
*/
|
||||
public function sendTemplateMessage($to, $templateName, $language = 'es', $parameters = []) {
|
||||
public function sendTemplateMessage(
|
||||
$to,
|
||||
$templateName,
|
||||
$language = 'es',
|
||||
$bodyParameters = [],
|
||||
$headerParameters = []
|
||||
) {
|
||||
$components = [];
|
||||
|
||||
if (!empty($headerParameters)) {
|
||||
$components[] = [
|
||||
'type' => 'header',
|
||||
'parameters' => $headerParameters
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($bodyParameters)) {
|
||||
$components[] = [
|
||||
'type' => 'body',
|
||||
'parameters' => $bodyParameters
|
||||
];
|
||||
}
|
||||
|
||||
$template = [
|
||||
'name' => $templateName,
|
||||
'language' => [
|
||||
'code' => $language
|
||||
]
|
||||
];
|
||||
|
||||
if (!empty($parameters)) {
|
||||
$template['components'] = [
|
||||
[
|
||||
'type' => 'body',
|
||||
'parameters' => $parameters
|
||||
]
|
||||
];
|
||||
|
||||
if (!empty($components)) {
|
||||
$template['components'] = $components;
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'to' => $this->formatPhoneNumber($to),
|
||||
'type' => 'template',
|
||||
'template' => $template
|
||||
];
|
||||
|
||||
|
||||
return $this->sendMessage($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enviar mensaje con botones interactivos
|
||||
*/
|
||||
public function sendInteractiveMessage($to, $bodyText, $buttons, $header = null, $footer = null) {
|
||||
public function sendInteractiveMessage($to, $bodyText, $buttons, $header = null, $footer = null)
|
||||
{
|
||||
$interactive = [
|
||||
'type' => 'button',
|
||||
'body' => [
|
||||
@@ -76,20 +101,20 @@ class WhatsAppService {
|
||||
'buttons' => $buttons
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
if ($header) {
|
||||
$interactive['header'] = [
|
||||
'type' => 'text',
|
||||
'text' => $header
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
if ($footer) {
|
||||
$interactive['footer'] = [
|
||||
'text' => $footer
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'recipient_type' => 'individual',
|
||||
@@ -97,14 +122,15 @@ class WhatsAppService {
|
||||
'type' => 'interactive',
|
||||
'interactive' => $interactive
|
||||
];
|
||||
|
||||
|
||||
return $this->sendMessage($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enviar mensaje con lista
|
||||
*/
|
||||
public function sendListMessage($to, $bodyText, $buttonText, $sections, $header = null, $footer = null) {
|
||||
public function sendListMessage($to, $bodyText, $buttonText, $sections, $header = null, $footer = null)
|
||||
{
|
||||
$interactive = [
|
||||
'type' => 'list',
|
||||
'body' => [
|
||||
@@ -115,20 +141,20 @@ class WhatsAppService {
|
||||
'sections' => $sections
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
if ($header) {
|
||||
$interactive['header'] = [
|
||||
'type' => 'text',
|
||||
'text' => $header
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
if ($footer) {
|
||||
$interactive['footer'] = [
|
||||
'text' => $footer
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'recipient_type' => 'individual',
|
||||
@@ -136,51 +162,61 @@ class WhatsAppService {
|
||||
'type' => 'interactive',
|
||||
'interactive' => $interactive
|
||||
];
|
||||
|
||||
|
||||
return $this->sendMessage($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marcar mensaje como leído
|
||||
*/
|
||||
public function markAsRead($messageId) {
|
||||
public function markAsRead($messageId)
|
||||
{
|
||||
$data = [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'status' => 'read',
|
||||
'message_id' => $messageId
|
||||
];
|
||||
|
||||
|
||||
$url = $this->apiUrl . $this->phoneNumberId . '/messages';
|
||||
return $this->makeRequest('POST', $url, $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enviar mensaje principal
|
||||
*/
|
||||
private function sendMessage($data) {
|
||||
$url = $this->apiUrl . $this->phoneNumberId . '/messages';
|
||||
$response = $this->makeRequest('POST', $url, $data);
|
||||
private function sendMessage($data)
|
||||
{
|
||||
// Construir URL correctamente
|
||||
$url = rtrim($this->apiUrl, '/') . '/' . $this->phoneNumberId . '/messages';
|
||||
|
||||
// Debug log
|
||||
error_log("WhatsApp API URL: " . $url);
|
||||
error_log("WhatsApp Token length: " . strlen($this->token));
|
||||
error_log("WhatsApp Data: " . json_encode($data));
|
||||
|
||||
$response = $this->makeRequest('POST', $url, $data);
|
||||
|
||||
// Guardar mensaje enviado en la base de datos
|
||||
if ($response && isset($response['messages'][0]['id'])) {
|
||||
$this->saveOutgoingMessage($data, $response);
|
||||
}
|
||||
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Realizar petición HTTP
|
||||
*/
|
||||
private function makeRequest($method, $url, $data = null) {
|
||||
private function makeRequest($method, $url, $data = null)
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
|
||||
$headers = [
|
||||
'Authorization: Bearer ' . $this->token,
|
||||
'Content-Type: application/json',
|
||||
'User-Agent: WhatsApp-Bot/1.0'
|
||||
];
|
||||
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
@@ -192,57 +228,66 @@ class WhatsAppService {
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 3
|
||||
]);
|
||||
|
||||
|
||||
if ($method === 'POST' && $data) {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
}
|
||||
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
if ($error) {
|
||||
error_log("cURL Error: " . $error);
|
||||
throw new Exception("Error de comunicación con WhatsApp: " . $error);
|
||||
}
|
||||
|
||||
|
||||
$decoded = json_decode($response, true);
|
||||
|
||||
|
||||
if ($httpCode >= 400) {
|
||||
$errorMsg = isset($decoded['error']['message']) ? $decoded['error']['message'] : 'Error desconocido';
|
||||
error_log("WhatsApp API Error: " . $response);
|
||||
|
||||
// Debug detallado
|
||||
error_log("WhatsApp API Error Details:");
|
||||
error_log("- URL: " . $url);
|
||||
error_log("- HTTP Code: " . $httpCode);
|
||||
error_log("- Response: " . $response);
|
||||
error_log("- Token (first 20 chars): " . substr($this->token, 0, 20) . "...");
|
||||
|
||||
throw new Exception("Error de WhatsApp API: " . $errorMsg);
|
||||
}
|
||||
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formatear número de teléfono
|
||||
*/
|
||||
private function formatPhoneNumber($phone) {
|
||||
private function formatPhoneNumber($phone)
|
||||
{
|
||||
// Remover caracteres especiales
|
||||
$phone = preg_replace('/[^0-9]/', '', $phone);
|
||||
|
||||
|
||||
// Si empieza con +57 (Colombia), mantenerlo
|
||||
if (substr($phone, 0, 2) === '57' && strlen($phone) >= 12) {
|
||||
return $phone;
|
||||
}
|
||||
|
||||
|
||||
// Si es número colombiano sin código de país
|
||||
if (strlen($phone) === 10 && substr($phone, 0, 1) === '3') {
|
||||
return '57' . $phone;
|
||||
}
|
||||
|
||||
|
||||
return $phone;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Guardar mensaje enviado en BD
|
||||
*/
|
||||
private function saveOutgoingMessage($data, $response) {
|
||||
private function saveOutgoingMessage($data, $response)
|
||||
{
|
||||
try {
|
||||
$user = $this->getUserByPhone($data['to']);
|
||||
if ($user) {
|
||||
@@ -255,18 +300,19 @@ class WhatsAppService {
|
||||
'status' => 'sent',
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
|
||||
$this->db->insert('conversations', $messageData);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Error saving outgoing message: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extraer contenido del mensaje para guardar
|
||||
*/
|
||||
private function extractMessageContent($data) {
|
||||
private function extractMessageContent($data)
|
||||
{
|
||||
switch ($data['type']) {
|
||||
case 'text':
|
||||
return $data['text']['body'];
|
||||
@@ -280,34 +326,36 @@ class WhatsAppService {
|
||||
}
|
||||
return json_encode($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtener usuario por teléfono
|
||||
*/
|
||||
private function getUserByPhone($phone) {
|
||||
private function getUserByPhone($phone)
|
||||
{
|
||||
return $this->db->fetch(
|
||||
"SELECT * FROM users WHERE phone_number = :phone",
|
||||
['phone' => $phone]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Descargar archivo multimedia
|
||||
*/
|
||||
public function downloadMedia($mediaId) {
|
||||
public function downloadMedia($mediaId)
|
||||
{
|
||||
$url = $this->apiUrl . $mediaId;
|
||||
|
||||
|
||||
// Primero obtener información del archivo
|
||||
$mediaInfo = $this->makeRequest('GET', $url);
|
||||
|
||||
|
||||
if (!$mediaInfo || !isset($mediaInfo['url'])) {
|
||||
throw new Exception("No se pudo obtener información del archivo");
|
||||
}
|
||||
|
||||
|
||||
// Descargar el archivo
|
||||
$fileUrl = $mediaInfo['url'];
|
||||
$ch = curl_init();
|
||||
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $fileUrl,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
@@ -317,15 +365,15 @@ class WhatsAppService {
|
||||
CURLOPT_TIMEOUT => 60,
|
||||
CURLOPT_FOLLOWLOCATION => true
|
||||
]);
|
||||
|
||||
|
||||
$fileContent = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
throw new Exception("Error descargando archivo multimedia");
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
'content' => $fileContent,
|
||||
'mime_type' => $mediaInfo['mime_type'] ?? 'application/octet-stream',
|
||||
@@ -333,4 +381,3 @@ class WhatsAppService {
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Test directo de plantilla - Debug detallado
|
||||
*/
|
||||
|
||||
require_once 'config/config.php';
|
||||
require_once 'services/WhatsAppService.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Configurar logging de errores para ver los detalles
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('log_errors', 1);
|
||||
|
||||
try {
|
||||
echo json_encode(['status' => 'Iniciando test de plantilla...'], JSON_PRETTY_PRINT) . "\n";
|
||||
|
||||
$whatsapp = new WhatsAppService();
|
||||
|
||||
// Usar datos de prueba que sabemos que existen
|
||||
$recipient = "573168950803"; // Número de prueba
|
||||
$templateName = "hello_world"; // Esta plantilla FUNCIONA
|
||||
$language = "en_US"; // Idioma que FUNCIONA
|
||||
|
||||
echo json_encode([
|
||||
'step' => 'Datos de envío',
|
||||
'recipient' => $recipient,
|
||||
'template' => $templateName,
|
||||
'language' => $language
|
||||
], JSON_PRETTY_PRINT) . "\n";
|
||||
|
||||
// Enviar plantilla
|
||||
echo json_encode(['step' => 'Llamando sendTemplateMessage...'], JSON_PRETTY_PRINT) . "\n";
|
||||
flush();
|
||||
|
||||
$result = $whatsapp->sendTemplateMessage($recipient, $templateName, $language);
|
||||
|
||||
echo json_encode([
|
||||
'step' => 'Resultado',
|
||||
'success' => true,
|
||||
'result' => $result
|
||||
], JSON_PRETTY_PRINT) . "\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'step' => 'Error capturado',
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine()
|
||||
], JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'Test completado. Revisa los logs de error para ver detalles de la petición HTTP.'], JSON_PRETTY_PRINT) . "\n";
|
||||
?>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
require_once 'config/config.php';
|
||||
|
||||
echo "<h2>Test de Configuración WhatsApp</h2>";
|
||||
|
||||
// Obtener configuración desde BD
|
||||
$config = getWhatsAppConfigFromDB();
|
||||
|
||||
echo "<h3>Configuración desde Base de Datos:</h3>";
|
||||
echo "<pre>";
|
||||
echo "Token: " . (strlen($config['token']) > 0 ? substr($config['token'], 0, 20) . "... (length: " . strlen($config['token']) . ")" : "EMPTY") . "\n";
|
||||
echo "Phone Number ID: " . ($config['phone_number_id'] ?: "EMPTY") . "\n";
|
||||
echo "API URL: " . ($config['api_url'] ?: "EMPTY") . "\n";
|
||||
echo "</pre>";
|
||||
|
||||
// Construir URL como lo haría WhatsAppService
|
||||
$apiUrl = rtrim($config['api_url'], '/') . '/' . $config['phone_number_id'] . '/messages';
|
||||
echo "<h3>URL que se construirá:</h3>";
|
||||
echo "<pre>" . $apiUrl . "</pre>";
|
||||
|
||||
// Comparar con tu curl que funciona
|
||||
$expectedUrl = "https://graph.facebook.com/v22.0/938064202726485/messages";
|
||||
echo "<h3>URL que debería ser:</h3>";
|
||||
echo "<pre>" . $expectedUrl . "</pre>";
|
||||
|
||||
echo "<h3>¿Coinciden las URLs?</h3>";
|
||||
echo "<pre>" . ($apiUrl === $expectedUrl ? "✅ SÍ" : "❌ NO") . "</pre>";
|
||||
|
||||
// Verificar token
|
||||
$expectedTokenStart = "EAAcGjPRqunIBQVT6b86O0wWWGLGJ1PANmolIRXRBmEcpqa3P85VlUz0Rrv2c5deXky3gOKZCBw7ZCpdicly4c0G8qFMOH1DfOcSzmV49FlPId3AGPlHEXpqlsPFIE8DF18trW2vglQrFiCHXZCEBSUBQfp1jEASD1NrKKKVHiGVZARrwoxP0J931u0KIr2ZAZCug4fFpnyRZBls9LEgJuomXd4ZCnJtBZA4GvP2zaplmZA52mJiG5H4YiKKWby5GnQq9BgAVaq6sLZBnYI26aR0FgZDZD";
|
||||
|
||||
echo "<h3>¿Coincide el token?</h3>";
|
||||
$tokenMatch = substr($config['token'], 0, 50) === substr($expectedTokenStart, 0, 50);
|
||||
echo "<pre>" . ($tokenMatch ? "✅ SÍ (primeros 50 caracteres)" : "❌ NO") . "</pre>";
|
||||
|
||||
if (!$tokenMatch) {
|
||||
echo "<h4>Debug Token:</h4>";
|
||||
echo "<pre>";
|
||||
echo "Configurado: " . substr($config['token'], 0, 50) . "...\n";
|
||||
echo "Esperado: " . substr($expectedTokenStart, 0, 50) . "...\n";
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
// Test directo de configuración
|
||||
echo "<h3>Configuraciones Individuales:</h3>";
|
||||
echo "<pre>";
|
||||
foreach(['whatsapp_token', 'whatsapp_phone_number_id', 'whatsapp_api_url'] as $key) {
|
||||
$value = getConfigFromDB($key, 'NO_ENCONTRADO');
|
||||
echo "$key: " . ($value === 'NO_ENCONTRADO' ? "❌ NO_ENCONTRADO" : "✅ " . substr($value, 0, 30) . "...") . "\n";
|
||||
}
|
||||
echo "</pre>";
|
||||
?>
|
||||
Reference in New Issue
Block a user