This commit is contained in:
lizandrogd
2026-01-21 02:45:59 -05:00
parent edfa5f68d9
commit 845762f916
10 changed files with 207 additions and 8 deletions
+10 -6
View File
@@ -21,6 +21,8 @@ try {
COALESCE(u.name, u.phone_number) AS name,
u.phone_number,
u.avatar_url,
u.bot_enabled,
u.on_hold,
lm.message_id AS last_message_id,
lm.content AS last_message,
lm.direction AS direction,
@@ -48,15 +50,17 @@ try {
// Formatear datos para el frontend
$conversations = array_map(function($conv) {
return [
'id' => intval($conv['id']),
'user_id' => intval($conv['user_id']),
'content' => $conv['content'] ?? '',
'name' => $conv['name'] ?? $conv['phone_number'],
'phone_number' => $conv['phone_number'],
'avatar_url' => $conv['avatar_url'] ?? null,
'last_message' => $conv['last_message'] ?? '',
'last_time' => $conv['last_time'] ?? null,
'direction' => $conv['direction'] ?? 'incoming',
'message_type' => $conv['message_type'] ?? 'text',
'status' => $conv['status'] ?? 'sent',
'created_at' => $conv['created_at'],
'phone_number' => $conv['phone_number'],
'name' => $conv['name'] ?? $conv['phone_number']
'unread_count' => intval($conv['unread_count'] ?? 0),
'bot_enabled' => isset($conv['bot_enabled']) ? (bool)$conv['bot_enabled'] : true,
'on_hold' => isset($conv['on_hold']) ? (bool)$conv['on_hold'] : false
];
}, $conversations);
+2 -1
View File
@@ -27,7 +27,8 @@ try {
$approvedOnly = isset($_GET['approved_only']) && $_GET['approved_only'] == '1';
$limit = isset($_GET['limit']) ? intval($_GET['limit']) : null;
$sql = "SELECT id, name, template_name, language_code, category, status, body_text, created_at FROM message_templates";
// Ajuste: some DBs may not have body_text column; omit it to avoid SQL errors
$sql = "SELECT id, name, template_name, language_code, category, status, created_at FROM message_templates";
$params = [];
if ($approvedOnly) {
+29
View File
@@ -0,0 +1,29 @@
<?php
require_once '../config/config.php';
header('Content-Type: application/json; charset=utf-8');
requireAuthentication();
try {
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
$userId = isset($input['user_id']) ? intval($input['user_id']) : 0;
if (!$userId) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'user_id required']);
exit;
}
$db = Database::getInstance();
$user = $db->fetch('SELECT phone_number FROM users WHERE id = :id', ['id' => $userId]);
if (!$user) {
http_response_code(404);
echo json_encode(['success' => false, 'error' => 'User not found']);
exit;
}
$db->update('users', ['on_hold' => 0], 'id = :id', ['id' => $userId]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
+24
View File
@@ -0,0 +1,24 @@
<?php
require_once '../config/config.php';
header('Content-Type: application/json; charset=utf-8');
requireAuthentication();
try {
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
$userId = isset($input['user_id']) ? intval($input['user_id']) : 0;
$enabled = isset($input['enabled']) ? (int)$input['enabled'] : null;
if (!$userId || $enabled === null) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'user_id and enabled required']);
exit;
}
$db = Database::getInstance();
$db->update('users', ['bot_enabled' => $enabled], 'id = :id', ['id' => $userId]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
+69 -1
View File
@@ -471,10 +471,16 @@
<i class="fas fa-user"></i>
</div>
<div class="chat-header-info">
<h6 id="chat-name">Usuario <button class="btn btn-sm btn-link" id="edit-user-btn" title="Editar usuario"><i class="fas fa-user-edit"></i></button></h6>
<h6 id="chat-name">Usuario <button class="btn btn-sm btn-link" id="edit-user-btn" title="Editar usuario"><i class="fas fa-user-edit"></i></button>
<span id="hold-indicator" style="display:none; margin-left:8px; color:#b85; font-weight:600">EN ESPERA</span>
</h6>
<small id="chat-phone">+1234567890</small>
<div style="font-size:12px; margin-top:4px;"><button id="release-hold-btn" class="btn btn-sm btn-outline-primary" style="display:none">Liberar espera</button></div>
</div>
<div style="margin-left: auto; display:flex; gap:8px; align-items:center;">
<div id="bot-toggle-container" style="display:flex;align-items:center;gap:6px;margin-right:8px;">
<button class="btn btn-sm btn-outline-secondary" id="bot-toggle">Bot: On</button>
</div>
<button class="btn btn-sm btn-outline-danger" id="delete-conversation-btn" title="Eliminar conversación"><i class="fas fa-trash"></i></button>
</div>
</div>
@@ -728,6 +734,68 @@
});
document.querySelector(`[data-user-id="${userId}"]`)?.classList.add('active');
// Actualizar estado del toggle del bot según datos de la conversación
const conv = this.conversations.find(c => c.user_id === userId);
if (conv) {
const btn = document.getElementById('bot-toggle');
const holdIndicator = document.getElementById('hold-indicator');
const releaseBtn = document.getElementById('release-hold-btn');
if (holdIndicator) {
holdIndicator.style.display = conv.on_hold ? 'inline' : 'none';
}
if (releaseBtn) {
releaseBtn.style.display = conv.on_hold ? 'inline-block' : 'none';
releaseBtn.onclick = async () => {
try {
const resp = await fetch('api/release_hold.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_id: userId })
});
const json = await resp.json();
if (json && json.success) {
conv.on_hold = false;
holdIndicator.style.display = 'none';
releaseBtn.style.display = 'none';
await this.loadConversations();
} else {
alert('Error al liberar la espera');
}
} catch (e) {
console.error('Error releasing hold', e);
}
};
}
if (btn) {
btn.textContent = conv.bot_enabled ? 'Bot: On' : 'Bot: Off';
btn.classList.toggle('btn-outline-danger', !conv.bot_enabled);
btn.classList.toggle('btn-outline-secondary', conv.bot_enabled);
btn.onclick = async () => {
try {
const resp = await fetch('api/set_bot_enabled.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_id: userId, enabled: conv.bot_enabled ? 0 : 1 })
});
const json = await resp.json();
if (json && json.success) {
conv.bot_enabled = !conv.bot_enabled;
btn.textContent = conv.bot_enabled ? 'Bot: On' : 'Bot: Off';
btn.classList.toggle('btn-outline-danger', !conv.bot_enabled);
btn.classList.toggle('btn-outline-secondary', conv.bot_enabled);
} else {
alert('Error al cambiar el estado del bot');
}
} catch (e) {
console.error('Error toggling bot', e);
}
};
}
}
// Cargar mensajes
await this.loadMessages(userId);
// Cargar respuestas rápidas
+15
View File
@@ -0,0 +1,15 @@
<?php
require_once __DIR__ . '/../config/config.php';
$db = Database::getInstance();
$col = $db->fetch("SHOW COLUMNS FROM users LIKE 'bot_enabled'");
if ($col) {
echo "Column bot_enabled already exists\n";
exit(0);
}
try {
$db->query("ALTER TABLE users ADD COLUMN bot_enabled TINYINT(1) NULL DEFAULT 1 AFTER bot_paused_until");
echo "Added column bot_enabled\n";
} catch (Exception $e) {
echo "Failed to add bot_enabled: " . $e->getMessage() . "\n";
exit(1);
}
+9
View File
@@ -0,0 +1,9 @@
<?php
require_once __DIR__ . '/../config/config.php';
$db = Database::getInstance();
try {
$cols = $db->fetchAll('DESCRIBE message_templates');
foreach ($cols as $c) echo $c['Field'] . ' => ' . $c['Type'] . PHP_EOL;
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
}
+31
View File
@@ -0,0 +1,31 @@
<?php
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../services/BotService.php';
$db = Database::getInstance();
$bot = new BotService();
$phone = '573012345678';
$db->execute('DELETE FROM users WHERE phone_number = :p', ['p' => $phone]);
$userId = $db->insert('users', ['phone_number' => $phone, 'status' => 'active', 'bot_enabled' => 0, 'welcome_sent_at' => date('Y-m-d H:i:s'), 'created_at' => date('Y-m-d H:i:s')]);
$user = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]);
// Clear conversations
$db->execute('DELETE FROM conversations WHERE user_id = :uid', ['uid' => $userId]);
// Send message while bot disabled
$bot->processMessage($user, 'hola', 'text');
$outs = $db->fetchAll("SELECT * FROM conversations WHERE user_id = :uid AND direction = 'outgoing'", ['uid' => $userId]);
echo "Outgoing after disabled: " . count($outs) . "\n";
// Enable bot
$db->update('users', ['bot_enabled' => 1], 'id = :id', ['id' => $userId]);
$user2 = $db->fetch('SELECT * FROM users WHERE id = :id', ['id' => $userId]);
$bot->processMessage($user2, 'hola', 'text');
$outs2 = $db->fetchAll("SELECT * FROM conversations WHERE user_id = :uid AND direction = 'outgoing'", ['uid' => $userId]);
echo "Outgoing after enabled: " . count($outs2) . "\n";
// Cleanup
$db->execute('DELETE FROM conversations WHERE user_id = :uid', ['uid' => $userId]);
$db->execute('DELETE FROM users WHERE id = :id', ['id' => $userId]);
echo "Done\n";
+13
View File
@@ -0,0 +1,13 @@
<?php
// Script de prueba para ejecutar api/get_templates.php en modo debug
$_GET['debug'] = 'true';
chdir(__DIR__ . '/../api');
ob_start();
try {
include 'get_templates.php';
} catch (Throwable $t) {
echo "Throwable: " . $t->getMessage() . "\n";
echo $t->getTraceAsString() . "\n";
}
$out = ob_get_clean();
echo $out;
+5
View File
@@ -51,6 +51,11 @@ class BotService {
return;
}
// Si el usuario ha desactivado el bot manualmente (toggle), no responder
if (isset($user['bot_enabled']) && !$user['bot_enabled']) {
return;
}
// Si el asesor ya respondió después del último mensaje del usuario, frenar al bot
$lastIncoming = $this->db->fetch("SELECT created_at FROM conversations WHERE user_id = :uid AND direction = 'incoming' ORDER BY created_at DESC LIMIT 1", ['uid' => $user['id']]);
$lastOutgoing = $this->db->fetch("SELECT created_at FROM conversations WHERE user_id = :uid AND direction = 'outgoing' ORDER BY created_at DESC LIMIT 1", ['uid' => $user['id']]);