Auto commit: 2026-02-16T13:04:02.148Z

This commit is contained in:
Flatlogic Bot 2026-02-16 13:04:02 +00:00
parent 9ac1e3b52d
commit 400e93e9fd
2 changed files with 153 additions and 50 deletions

38
api/photo_status.php Normal file
View File

@ -0,0 +1,38 @@
<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
$ip = explode(',', $ip)[0];
try {
$stmt = db()->prepare("SELECT COUNT(*) FROM messages WHERE ip_address = ? AND type = 'image' AND created_at > DATE_SUB(NOW(), INTERVAL 6 HOUR)");
$stmt->execute([$ip]);
$count = (int)$stmt->fetchColumn();
$limit = 5;
$remaining = max(0, $limit - $count);
$reset_in_seconds = 0;
if ($count >= $limit) {
// Find the oldest message in the 6-hour window
$stmt = db()->prepare("SELECT created_at FROM messages WHERE ip_address = ? AND type = 'image' AND created_at > DATE_SUB(NOW(), INTERVAL 6 HOUR) ORDER BY created_at ASC LIMIT 1");
$stmt->execute([$ip]);
$oldest = $stmt->fetchColumn();
if ($oldest) {
$oldest_time = strtotime($oldest);
$reset_time = $oldest_time + (6 * 3600);
$reset_in_seconds = max(0, $reset_time - time());
}
}
echo json_encode([
'count' => $count,
'limit' => $limit,
'remaining' => $remaining,
'reset_in_seconds' => $reset_in_seconds
]);
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}

165
index.php
View File

@ -579,12 +579,27 @@ $facebook_link = "https://www.facebook.com/profile.php?id=61587890927489";
box-shadow: 0 0 20px rgba(24, 119, 242, 0.5);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes alert-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.3); background: #facc15 !important; }
100% { transform: scale(1); }
}
.counter-alert {
animation: alert-pulse 1s infinite ease-in-out;
box-shadow: 0 0 10px #facc15;
}
/* Responsive */
@media (max-width: 992px) {
.app-container {
@ -725,9 +740,11 @@ $facebook_link = "https://www.facebook.com/profile.php?id=61587890927489";
<button onclick="toggleEmojiPicker()" style="background: rgba(255,255,255,0.1); border: none; border-radius: 8px; color: white; padding: 0 0.5rem; cursor: pointer;">
<i class="bi bi-emoji-smile"></i>
</button>
<button onclick="document.getElementById('chat-file').click()" style="background: rgba(255,255,255,0.1); border: none; border-radius: 8px; color: white; padding: 0 0.5rem; cursor: pointer;">
<button id="camera-btn" onclick="document.getElementById('chat-file').click()" title="Subir foto" style="background: rgba(255,255,255,0.1); border: none; border-radius: 8px; color: white; padding: 0 0.5rem; cursor: pointer; position: relative; transition: all 0.3s;">
<i class="bi bi-camera-fill"></i>
<span id="photo-counter" style="position: absolute; top: -10px; right: -5px; background: var(--primary-color); color: white; font-size: 0.6rem; padding: 1px 4px; border-radius: 10px; font-weight: bold; border: 1px solid rgba(255,255,255,0.3); display: none;">0/5</span>
</button>
<div id="photo-limit-msg" style="display: none; position: absolute; bottom: -20px; left: 0; width: 100%; text-align: center; font-size: 0.65rem; color: #ff4444; font-weight: bold; animation: fadeIn 0.3s;"></div>
<input type="file" id="chat-file" style="display: none;" accept="image/*" onchange="uploadImage(this)">
<input type="text" id="chat-msg" placeholder="Escribe un mensaje..." style="flex: 1; font-size: 0.8rem; padding: 0.5rem; border-radius: 8px; border: none; background: rgba(255,255,255,0.1); color: white;">
<button onclick="sendChatMessage()" style="background: var(--primary-color); border: none; border-radius: 8px; color: white; padding: 0 1rem; cursor: pointer;">
@ -806,58 +823,50 @@ $facebook_link = "https://www.facebook.com/profile.php?id=61587890927489";
ctx.clearRect(0, 0, canvas.width, canvas.height);
const barCount = 40; // Número de barras fijas para un look más limpio
const logicalWidth = canvas.width / window.devicePixelRatio;
const logicalHeight = canvas.height / window.devicePixelRatio;
const gap = 4;
const barWidth = (logicalWidth / barCount) - gap;
colorOffset += 2;
for (let i = 0; i < barCount; i++) {
// Mapear el índice de la barra a la frecuencia (usando una escala logarítmica para mejor visual)
const frequencyIndex = Math.floor(Math.pow(i / barCount, 1.5) * (bufferLength / 2));
const barHeight = (dataArray[frequencyIndex] / 255) * logicalHeight * 0.8 + 5;
const x = i * (barWidth + gap);
const barHue = (colorOffset + (i / barCount) * 200) % 360;
const gradient = ctx.createLinearGradient(0, logicalHeight, 0, logicalHeight - barHeight);
gradient.addColorStop(0, `hsla(${barHue}, 100%, 50%, 0.2)`);
gradient.addColorStop(0.5, `hsla(${barHue}, 100%, 50%, 0.8)`);
gradient.addColorStop(1, `hsla(${(barHue + 60) % 360}, 100%, 70%, 1)`);
ctx.fillStyle = gradient;
// Dibujar barra con bordes redondeados superiores
ctx.beginPath();
if (ctx.roundRect) {
ctx.roundRect(x, logicalHeight - barHeight, barWidth, barHeight, [5, 5, 0, 0]);
} else {
ctx.rect(x, logicalHeight - barHeight, barWidth, barHeight);
}
ctx.fill();
// Añadir un "cap" o punto brillante arriba de la barra
ctx.fillStyle = `hsla(${(barHue + 60) % 360}, 100%, 80%, 1)`;
ctx.beginPath();
ctx.arc(x + barWidth/2, logicalHeight - barHeight, barWidth/3, 0, Math.PI * 2);
ctx.fill();
}
// Efecto de brillo dinámico en la tarjeta basado en el volumen medio
let sum = 0;
for(let i=0; i<bufferLength; i++) sum += dataArray[i];
const average = sum / bufferLength;
const barCount = bufferLength / 1.5;
const logicalWidth = canvas.width / window.devicePixelRatio;
const logicalHeight = canvas.height / window.devicePixelRatio;
const barWidth = (logicalWidth / barCount) * 0.7;
let x = logicalWidth / 2;
let xLeft = logicalWidth / 2;
colorOffset += 1;
for (let i = 0; i < barCount; i++) {
const barHeight = (dataArray[i] / 255) * logicalHeight * 0.9;
const barHue = (colorOffset + (i / barCount) * 360) % 360;
const saturation = 100;
const lightness = 60 + (dataArray[i] / 255) * 15;
const gradient = ctx.createLinearGradient(0, logicalHeight, 0, logicalHeight - barHeight);
gradient.addColorStop(0, `hsla(${barHue}, ${saturation}%, ${lightness}%, 0.1)`);
gradient.addColorStop(0.4, `hsla(${barHue}, ${saturation}%, ${lightness}%, 0.8)`);
gradient.addColorStop(1, `hsla(${(barHue + 40) % 360}, ${saturation}%, ${lightness + 20}%, 1)`);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.roundRect(x, logicalHeight - barHeight, barWidth, barHeight, [10, 10, 0, 0]);
ctx.fill();
ctx.fillStyle = `hsla(${(barHue + 40) % 360}, 100%, 80%, 0.8)`;
ctx.beginPath();
ctx.arc(x + barWidth/2, logicalHeight - barHeight, barWidth/2, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.roundRect(xLeft - barWidth, logicalHeight - barHeight, barWidth, barHeight, [10, 10, 0, 0]);
ctx.fill();
ctx.fillStyle = `hsla(${(barHue + 40) % 360}, 100%, 80%, 0.8)`;
ctx.beginPath();
ctx.arc(xLeft - barWidth/2, logicalHeight - barHeight, barWidth/2, 0, Math.PI * 2);
ctx.fill();
x += barWidth + 3;
xLeft -= barWidth + 3;
}
const dominantHue = (average * 2.5 + colorOffset) % 360;
const dominantHue = (average * 2 + colorOffset) % 360;
document.documentElement.style.setProperty('--dynamic-glow', `hsla(${dominantHue}, 100%, 60%, 0.8)`);
document.documentElement.style.setProperty('--dynamic-glow-dim', `hsla(${dominantHue}, 100%, 60%, 0.3)`);
}
@ -988,6 +997,7 @@ $facebook_link = "https://www.facebook.com/profile.php?id=61587890927489";
const result = await response.json();
if (result.success) {
sendChatMessage(result.url, 'image');
updatePhotoCounter();
} else {
alert(result.error || 'Error al subir la imagen');
}
@ -1087,9 +1097,64 @@ $facebook_link = "https://www.facebook.com/profile.php?id=61587890927489";
if (e.key === 'Enter') sendChatMessage();
});
async function updatePhotoCounter() {
try {
const response = await fetch('api/photo_status.php');
const data = await response.json();
const counter = document.getElementById('photo-counter');
const cameraBtn = document.getElementById('camera-btn');
const fileInput = document.getElementById('chat-file');
const limitMsg = document.getElementById('photo-limit-msg');
if (data && typeof data.count !== 'undefined') {
counter.innerText = `${data.count}/${data.limit}`;
counter.style.display = 'block';
// Alerta cuando solo queda 1 foto (4 de 5)
if (data.count === data.limit - 1) {
counter.classList.add('counter-alert');
} else {
counter.classList.remove('counter-alert');
}
if (data.count >= data.limit) {
counter.style.background = '#ff4444';
// Bloquear visualmente el botón
cameraBtn.style.opacity = '0.4';
cameraBtn.style.filter = 'grayscale(1)';
cameraBtn.style.pointerEvents = 'none';
cameraBtn.title = 'Límite de fotos alcanzado';
fileInput.disabled = true;
// Mostrar tiempo restante
if (data.reset_in_seconds > 0) {
const hours = Math.floor(data.reset_in_seconds / 3600);
const minutes = Math.floor((data.reset_in_seconds % 3600) / 60);
const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
limitMsg.innerText = `Disponible en ${timeStr}`;
limitMsg.style.display = 'block';
}
} else {
counter.style.background = 'var(--primary-color)';
// Restaurar el botón
cameraBtn.style.opacity = '1';
cameraBtn.style.filter = 'none';
cameraBtn.style.pointerEvents = 'auto';
cameraBtn.title = 'Subir foto';
fileInput.disabled = false;
limitMsg.style.display = 'none';
}
}
} catch (error) {
console.error('Error updating photo counter:', error);
}
}
// Initial fetch and poll
fetchMessages();
updatePhotoCounter();
setInterval(fetchMessages, 3000);
setInterval(updatePhotoCounter, 30000); // Check limit status every 30s
// --- End Chat Functionality ---
async function likeSong() {