Auto commit: 2026-02-19T20:04:20.670Z

This commit is contained in:
Flatlogic Bot 2026-02-19 20:04:20 +00:00
parent c3d9e7fcf2
commit 29838e7f6a
5 changed files with 188 additions and 15 deletions

View File

@ -46,19 +46,27 @@ if ($method === 'GET') {
");
$topRequester = $topStmt->fetchColumn();
$stmt = db()->prepare("SELECT m.*, f.points, f.loyalty_points, f.is_fan_of_month FROM messages m LEFT JOIN fans f ON m.username = f.name ORDER BY m.created_at DESC LIMIT 50");
$stmt = db()->prepare("SELECT m.*, f.points, f.loyalty_points, f.is_fan_of_month, f.chat_color, f.dj_day_until FROM messages m LEFT JOIN fans f ON m.username = f.name ORDER BY m.created_at DESC LIMIT 50");
$stmt->execute();
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($messages as &$msg) {
$points = $msg['points'] ?? 0;
$loyalty = $msg['loyalty_points'] ?? 0;
$msg['level_color'] = '#94a3b8';
$chatColor = $msg['chat_color'] ?? null;
$djDayUntil = $msg['dj_day_until'] ?? null;
$isDjDay = $djDayUntil && strtotime($djDayUntil) > time();
$msg['level_color'] = $chatColor ?: '#94a3b8';
$msg['level_emoji'] = '';
if ($msg['username'] === 'Lili Bot 🤖') {
$msg['level_color'] = '#00e676';
$msg['level_emoji'] = '🤖';
} elseif ($isDjDay) {
$msg['level_color'] = '#facc15';
$msg['level_emoji'] = '🎧';
$msg['is_guest_dj'] = true;
} elseif ($msg['username'] === $topRequester) {
$msg['level_color'] = '#facc15';
$msg['level_emoji'] = '👑';

View File

@ -13,7 +13,9 @@ $stmt = $db->prepare("
v.last_activity,
f.points,
f.loyalty_points,
f.is_fan_of_month
f.is_fan_of_month,
f.chat_color,
f.dj_day_until
FROM visitor_logs v
LEFT JOIN fans f ON v.username = f.name
WHERE v.last_activity > DATE_SUB(NOW(), INTERVAL 15 MINUTE)

View File

@ -8,7 +8,7 @@ try {
$username = $_GET['username'] ?? null;
// Get top fans from the fans table
$stmt = $pdo->query("SELECT name as username, points as total_likes, photo, is_fan_of_month FROM fans ORDER BY points DESC LIMIT 5");
$stmt = $pdo->query("SELECT name as username, points as total_likes, photo, is_fan_of_month, loyalty_points, chat_color, dj_day_until FROM fans ORDER BY points DESC LIMIT 5");
$fans = $stmt->fetchAll(PDO::FETCH_ASSOC);
// If username provided and not in top 5, fetch it specifically
@ -21,7 +21,7 @@ try {
}
}
if (!$found) {
$stmt = $pdo->prepare("SELECT name as username, points as total_likes, photo, is_fan_of_month FROM fans WHERE name = ?");
$stmt = $pdo->prepare("SELECT name as username, points as total_likes, photo, is_fan_of_month, loyalty_points, chat_color, dj_day_until FROM fans WHERE name = ?");
$stmt->execute([$username]);
$userFan = $stmt->fetch(PDO::FETCH_ASSOC);
if ($userFan) {
@ -77,6 +77,18 @@ foreach ($fans as &$fan) {
if ($fan['is_fan_of_month']) {
$fan['badges'][] = ['icon' => 'bi-star-fill', 'color' => '#facc15', 'label' => 'Fan del Mes'];
}
$isDj = $fan['dj_day_until'] && strtotime($fan['dj_day_until']) > time();
if ($isDj) {
$fan['badges'][] = ['icon' => 'bi-headphones', 'color' => '#facc15', 'label' => 'DJ INVITADO'];
$fan['level_color'] = '#facc15';
$fan['level_name'] = 'DJ INVITADO';
}
if ($fan['loyalty_points'] > 0) {
$fan['badges'][] = ['icon' => 'bi-heart-fill', 'color' => '#00e676', 'label' => 'Fan Leal'];
}
if ($points >= 1000) {
$fan['badges'][] = ['icon' => 'bi-gem', 'color' => '#38bdf8', 'label' => 'Diamante'];
} elseif ($points >= 500) {

73
api/redeem.php Normal file
View File

@ -0,0 +1,73 @@
<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
$username = $data['username'] ?? '';
$item = $data['item'] ?? '';
$color = $data['color'] ?? null;
if (empty($username) || empty($item)) {
echo json_encode(['success' => false, 'error' => 'Datos incompletos']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT loyalty_points, chat_color FROM fans WHERE name = ?");
$stmt->execute([$username]);
$fan = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$fan) {
echo json_encode(['success' => false, 'error' => 'Usuario no encontrado']);
exit;
}
$points = $fan['loyalty_points'];
$current_color = $fan['chat_color'];
if ($item === 'god_mode') {
$cost = 500;
if ($points < $cost) {
echo json_encode(['success' => false, 'error' => "Puntos insuficientes ($points/$cost)"]);
exit;
}
$new_color = $color ?: '#ffd700'; // Gold default
$stmt = $pdo->prepare("UPDATE fans SET loyalty_points = loyalty_points - ?, chat_color = ? WHERE name = ?");
$stmt->execute([$cost, $new_color, $username]);
echo json_encode(['success' => true, 'message' => '¡Modo Dios activado! Ahora tienes un color exclusivo.']);
} elseif ($item === 'dj_day') {
$cost = 2000;
if ($points < $cost) {
echo json_encode(['success' => false, 'error' => "Puntos insuficientes ($points/$cost)"]);
exit;
}
$stmt = $pdo->prepare("UPDATE fans SET loyalty_points = loyalty_points - ?, dj_day_until = DATE_ADD(NOW(), INTERVAL 1 DAY) WHERE name = ?");
$stmt->execute([$cost, $username]);
echo json_encode(['success' => true, 'message' => '¡Eres DJ por un día! Tu nombre destacará en toda la sala.']);
} elseif ($item === 'change_color') {
// If they already have god mode, changing color might be cheaper or free?
// Let's say 100 points to change color if already bought, or free if we want.
if (!$current_color) {
echo json_encode(['success' => false, 'error' => 'Primero debes desbloquear el Modo Dios']);
exit;
}
$cost = 50;
if ($points < $cost) {
echo json_encode(['success' => false, 'error' => "Puntos insuficientes ($points/$cost)"]);
exit;
}
$stmt = $pdo->prepare("UPDATE fans SET loyalty_points = loyalty_points - ?, chat_color = ? WHERE name = ?");
$stmt->execute([$cost, $color, $username]);
echo json_encode(['success' => true, 'message' => '¡Color actualizado!']);
} else {
echo json_encode(['success' => false, 'error' => 'Ítem no reconocido']);
}
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}

View File

@ -2226,8 +2226,13 @@ $twitter_link = "https://twitter.com/";
<div class="shop-modal-content" onclick="event.stopPropagation()">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<h2 style="margin: 0; color: #facc15;"><i class="bi bi-shop"></i> Tienda Radio</h2>
<div id="shop-user-points" style="background: rgba(250, 204, 21, 0.2); color: #facc15; padding: 5px 15px; border-radius: 20px; font-weight: 800; font-size: 0.9rem;">
0 pts
<div style="text-align: right;">
<div id="shop-user-points" style="background: rgba(56, 189, 248, 0.2); color: #38bdf8; padding: 2px 12px; border-radius: 20px; font-weight: 800; font-size: 0.8rem; margin-bottom: 5px;">
0 PTS
</div>
<div id="shop-user-loyalty" style="background: rgba(0, 230, 118, 0.2); color: #00e676; padding: 2px 12px; border-radius: 20px; font-weight: 800; font-size: 0.8rem;">
0 LP
</div>
</div>
</div>
@ -2251,6 +2256,24 @@ $twitter_link = "https://twitter.com/";
<div class="shop-item-price">1000 <i class="bi bi-lightning-fill"></i></div>
</div>
<div class="shop-item" onclick="promptBuyGodMode()">
<div class="shop-item-icon" style="color: #facc15;"><i class="bi bi-stars"></i></div>
<div class="shop-item-info">
<div class="shop-item-name">Modo Dios</div>
<div class="shop-item-desc">Colores personalizados para tu nombre en el chat.</div>
</div>
<div class="shop-item-price">500 <i class="bi bi-heart-fill" style="color: #00e676;"></i></div>
</div>
<div class="shop-item" onclick="promptBuyDjDay()">
<div class="shop-item-icon" style="color: #f472b6;"><i class="bi bi-headphones"></i></div>
<div class="shop-item-info">
<div class="shop-item-name">DJ por un día</div>
<div class="shop-item-desc">Nombre dorado y estatus de DJ Invitado.</div>
</div>
<div class="shop-item-price">2000 <i class="bi bi-heart-fill" style="color: #00e676;"></i></div>
</div>
<button onclick="closeShopModal()" style="width: 100%; margin-top: 1.5rem; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: white; padding: 0.8rem; border-radius: 12px; cursor: pointer; font-weight: 700;">Cerrar Tienda</button>
</div>
</div>
@ -2604,21 +2627,25 @@ $twitter_link = "https://twitter.com/";
const flagUrl = user.country_code ? `https://flagcdn.com/w20/${user.country_code.toLowerCase()}.png` : null;
const isTopFan = user.points > 10 || user.is_fan_of_month == 1;
const isLoyal = user.loyalty_points > 0;
const userColor = isLoyal ? "#00e676" : getUserColor(user.username);
const isGuestDj = user.dj_day_until && (new Date(user.dj_day_until) > new Date());
let userColor = user.chat_color || (isLoyal ? "#00e676" : getUserColor(user.username));
if (isGuestDj) userColor = "#facc15";
return `
<div style="display: flex; align-items: center; justify-content: space-between; background: rgba(255,255,255,0.03); padding: 10px 15px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); transition: all 0.3s; position: relative; ${isLoyal ? "box-shadow: 0 0 10px rgba(0, 230, 118, 0.1);" : ""}">
<div style="display: flex; align-items: center; justify-content: space-between; background: rgba(255,255,255,0.03); padding: 10px 15px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); transition: all 0.3s; position: relative; ${isLoyal ? "box-shadow: 0 0 10px rgba(0, 230, 118, 0.1);" : ""} ${isGuestDj ? "border-color: #facc15; background: rgba(250, 204, 21, 0.05);" : ""}">
<div style="display: flex; align-items: center; gap: 10px;">
<div style="width: 38px; height: 38px; background: ${userColor}; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1rem; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.3); position: relative; ${isVeryActive ? `box-shadow: 0 0 0 2px rgba(255,255,255,0.1), 0 0 12px ${userColor};` : ''} ${isLoyal ? "animation: pulse 2s infinite;" : ""}">
${user.username.charAt(0).toUpperCase()}
<div style="width: 38px; height: 38px; background: ${userColor}; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1rem; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.3); position: relative; ${isVeryActive ? `box-shadow: 0 0 0 2px rgba(255,255,255,0.1), 0 0 12px ${userColor};` : ''} ${isLoyal || isGuestDj ? "animation: pulse 2s infinite;" : ""}">
${isGuestDj ? '<i class="bi bi-headphones"></i>' : user.username.charAt(0).toUpperCase()}
${isVeryActive ? '<span style="position: absolute; bottom: 0; right: 0; width: 10px; height: 10px; background: #25D366; border: 2px solid #000; border-radius: 50%;"></span>' : ''}
</div>
<div>
<div style="font-weight: 700; font-size: 0.95rem; display: flex; align-items: center; gap: 6px;">
${user.username}
<span style="color: ${userColor};">${user.username}</span>
${flagUrl ? `<img src="${flagUrl}" style="width: 16px; height: auto; border-radius: 2px; vertical-align: middle;" title="${user.country_code}">` : ''}
${isLoyal ? `<span style="background: #00e676; color: white; font-size: 0.55rem; padding: 2px 6px; border-radius: 10px; font-weight: 800; text-transform: uppercase;"><i class="bi bi-shield-fill-check"></i> LEAL</span>` : ""}
${isTopFan && !isLoyal ? `<span style="background: #FFD700; color: #000; font-size: 0.6rem; padding: 2px 6px; border-radius: 10px; font-weight: 800; text-transform: uppercase;">TOP</span>` : ''}
${isGuestDj ? `<span style="background: #facc15; color: black; font-size: 0.55rem; padding: 2px 6px; border-radius: 10px; font-weight: 800; text-transform: uppercase;">DJ INVITADO</span>` : ""}
${isLoyal && !isGuestDj ? `<span style="background: #00e676; color: white; font-size: 0.55rem; padding: 2px 6px; border-radius: 10px; font-weight: 800; text-transform: uppercase;"><i class="bi bi-shield-fill-check"></i> LEAL</span>` : ""}
${isTopFan && !isLoyal && !isGuestDj ? `<span style="background: #FFD700; color: #000; font-size: 0.6rem; padding: 2px 6px; border-radius: 10px; font-weight: 800; text-transform: uppercase;">TOP</span>` : ''}
</div>
<div style="font-size: 0.7rem; opacity: 0.6;">
<i class="bi bi-clock"></i> ${timeAgo(user.last_activity)}
@ -3568,7 +3595,9 @@ $twitter_link = "https://twitter.com/";
// Update shop points display
const shopPoints = document.getElementById('shop-user-points');
if (shopPoints) shopPoints.innerText = `${parseInt(fan.total_likes).toLocaleString()} pts`;
const shopLoyalty = document.getElementById('shop-user-loyalty');
if (shopPoints) shopPoints.innerText = `${parseInt(fan.total_likes).toLocaleString()} PTS`;
if (shopLoyalty) shopLoyalty.innerText = `${parseInt(fan.loyalty_points || 0).toLocaleString()} LP`;
container.style.display = 'block';
badge.innerText = `Nivel ${fan.level}`;
@ -4070,6 +4099,55 @@ $twitter_link = "https://twitter.com/";
}
}
async function redeemItem(item, color = null) {
const userName = document.getElementById('user-name').value.trim();
if (!userName) return;
try {
const response = await fetch('api/redeem.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: userName, item: item, color: color })
});
const result = await response.json();
if (result.success) {
confetti({
particleCount: 200,
spread: 100,
origin: { y: 0.6 },
colors: ['#facc15', '#f472b6', '#38bdf8', '#ffffff']
});
alert(result.message);
closeShopModal();
fetchLeaderboard();
if (typeof fetchMessages === 'function') fetchMessages();
} else {
alert('Error: ' + result.error);
}
} catch (error) {
console.error('Redeem error:', error);
alert('Error de conexión al realizar el canje.');
}
}
function promptBuyGodMode() {
const color = prompt('MODO DIOS: Elige un color hexadecimal para tu nombre (ej: #FFD700 para Oro, #00FFFF para Cian):', '#FFD700');
if (color) {
if (!/^#[0-9A-F]{6}$/i.test(color)) {
alert('Por favor, ingresa un código hexadecimal válido (ej: #FFD700).');
return;
}
redeemItem('god_mode', color);
}
}
function promptBuyDjDay() {
if (confirm('¿Quieres convertirte en DJ INVITADO por 2000 pts? Tu nombre destacará en toda la sala por 24 horas.')) {
redeemItem('dj_day');
}
}
setInterval(fetchActivePerks, 10000);
fetchActivePerks();
// --- End Radio Shop Functionality ---