Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -56,11 +56,21 @@
|
|
56 |
<!-- Online Users -->
|
57 |
<div>
|
58 |
<h3 class="text-sm font-medium text-gray-400 mb-2">👥 Who's Online</h3>
|
59 |
-
<div class="flex items-center gap-2 text-sm">
|
60 |
<div class="w-2 h-2 bg-green-500 rounded-full"></div>
|
61 |
<span id="online-status">Just you online</span>
|
62 |
</div>
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
</div>
|
65 |
|
66 |
<!-- Model Selection -->
|
@@ -187,20 +197,23 @@
|
|
187 |
};
|
188 |
|
189 |
// Initialize
|
190 |
-
document.addEventListener('DOMContentLoaded', function() {
|
191 |
setupEventListeners();
|
|
|
|
|
|
|
|
|
|
|
192 |
updateOnlineUsers();
|
193 |
checkAPIStatus();
|
194 |
|
195 |
-
//
|
196 |
-
|
197 |
-
|
198 |
-
// Track activity for online users
|
199 |
-
setInterval(updateOnlineUsers, 5000); // Update every 5 seconds
|
200 |
|
201 |
// Track user activity
|
202 |
document.addEventListener('click', trackActivity);
|
203 |
document.addEventListener('keypress', trackActivity);
|
|
|
204 |
});
|
205 |
|
206 |
function setupEventListeners() {
|
@@ -540,53 +553,183 @@
|
|
540 |
URL.revokeObjectURL(url);
|
541 |
}
|
542 |
|
543 |
-
// Online users tracking
|
544 |
-
let onlineUsers = new
|
545 |
let lastActivity = Date.now();
|
|
|
|
|
546 |
|
547 |
-
|
548 |
-
|
549 |
-
|
|
|
550 |
|
551 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
552 |
const now = Date.now();
|
553 |
const timeSinceLastActivity = now - lastActivity;
|
554 |
|
555 |
-
//
|
556 |
-
if (timeSinceLastActivity <
|
557 |
const newUserId = 'User-' + Math.random().toString(36).substr(2, 8);
|
558 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
559 |
}
|
560 |
|
561 |
-
// Remove inactive
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
onlineUsers.delete(userToRemove);
|
567 |
}
|
568 |
}
|
569 |
|
570 |
-
//
|
571 |
-
onlineUsers.
|
|
|
|
|
|
|
|
|
|
|
|
|
572 |
|
573 |
-
|
|
|
|
|
|
|
574 |
const count = onlineUsers.size;
|
575 |
const status = count === 1 ? 'Just you online' : count + ' people online';
|
576 |
document.getElementById('online-status').textContent = status;
|
577 |
|
578 |
-
//
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
}
|
584 |
-
|
585 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
586 |
}
|
587 |
|
588 |
function trackActivity() {
|
589 |
lastActivity = Date.now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
590 |
updateOnlineUsers();
|
591 |
}
|
592 |
|
|
|
56 |
<!-- Online Users -->
|
57 |
<div>
|
58 |
<h3 class="text-sm font-medium text-gray-400 mb-2">👥 Who's Online</h3>
|
59 |
+
<div class="flex items-center gap-2 text-sm mb-2">
|
60 |
<div class="w-2 h-2 bg-green-500 rounded-full"></div>
|
61 |
<span id="online-status">Just you online</span>
|
62 |
</div>
|
63 |
+
|
64 |
+
<!-- User Details -->
|
65 |
+
<div class="bg-gray-700 rounded-lg p-3 mb-2 max-h-32 overflow-y-auto chat-scroll">
|
66 |
+
<div id="users-list" class="space-y-1 text-xs">
|
67 |
+
<div class="text-green-400">🟢 <span id="current-user-info">You: Loading...</span></div>
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
|
71 |
+
<button onclick="refreshUsers()" class="w-full text-xs px-2 py-1 bg-gray-600 hover:bg-gray-500 rounded transition-colors">
|
72 |
+
🔄 Refresh Users
|
73 |
+
</button>
|
74 |
</div>
|
75 |
|
76 |
<!-- Model Selection -->
|
|
|
197 |
};
|
198 |
|
199 |
// Initialize
|
200 |
+
document.addEventListener('DOMContentLoaded', async function() {
|
201 |
setupEventListeners();
|
202 |
+
|
203 |
+
// Get user location first
|
204 |
+
await getUserLocation();
|
205 |
+
|
206 |
+
// Then update users and check API
|
207 |
updateOnlineUsers();
|
208 |
checkAPIStatus();
|
209 |
|
210 |
+
// Track activity for online users - update every 10 seconds
|
211 |
+
setInterval(updateOnlineUsers, 10000);
|
|
|
|
|
|
|
212 |
|
213 |
// Track user activity
|
214 |
document.addEventListener('click', trackActivity);
|
215 |
document.addEventListener('keypress', trackActivity);
|
216 |
+
document.addEventListener('scroll', trackActivity);
|
217 |
});
|
218 |
|
219 |
function setupEventListeners() {
|
|
|
553 |
URL.revokeObjectURL(url);
|
554 |
}
|
555 |
|
556 |
+
// Online users tracking with real geolocation
|
557 |
+
let onlineUsers = new Map(); // Map to store user details
|
558 |
let lastActivity = Date.now();
|
559 |
+
let userLocation = { city: 'Unknown', country: 'Unknown' };
|
560 |
+
let isGettingLocation = false;
|
561 |
|
562 |
+
// Get user's real location
|
563 |
+
async function getUserLocation() {
|
564 |
+
if (isGettingLocation) return userLocation;
|
565 |
+
isGettingLocation = true;
|
566 |
|
567 |
+
try {
|
568 |
+
// Try geolocation API first
|
569 |
+
if (navigator.geolocation) {
|
570 |
+
const position = await new Promise((resolve, reject) => {
|
571 |
+
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
572 |
+
timeout: 10000,
|
573 |
+
enableHighAccuracy: false
|
574 |
+
});
|
575 |
+
});
|
576 |
+
|
577 |
+
const { latitude, longitude } = position.coords;
|
578 |
+
|
579 |
+
// Use reverse geocoding to get city and country
|
580 |
+
try {
|
581 |
+
const response = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${latitude}&longitude=${longitude}&localityLanguage=en`);
|
582 |
+
const data = await response.json();
|
583 |
+
|
584 |
+
userLocation = {
|
585 |
+
city: data.city || data.locality || 'Unknown',
|
586 |
+
country: data.countryName || 'Unknown'
|
587 |
+
};
|
588 |
+
} catch (geocodeError) {
|
589 |
+
console.log('Geocoding failed, using IP fallback');
|
590 |
+
await getLocationByIP();
|
591 |
+
}
|
592 |
+
} else {
|
593 |
+
await getLocationByIP();
|
594 |
+
}
|
595 |
+
} catch (error) {
|
596 |
+
console.log('Geolocation failed, using IP fallback');
|
597 |
+
await getLocationByIP();
|
598 |
+
}
|
599 |
+
|
600 |
+
return userLocation;
|
601 |
+
}
|
602 |
+
|
603 |
+
// Fallback: Get location by IP
|
604 |
+
async function getLocationByIP() {
|
605 |
+
try {
|
606 |
+
const response = await fetch('https://ipapi.co/json/');
|
607 |
+
const data = await response.json();
|
608 |
+
|
609 |
+
userLocation = {
|
610 |
+
city: data.city || 'Unknown',
|
611 |
+
country: data.country_name || 'Unknown'
|
612 |
+
};
|
613 |
+
} catch (error) {
|
614 |
+
console.log('IP location failed');
|
615 |
+
userLocation = { city: 'Unknown', country: 'Unknown' };
|
616 |
+
}
|
617 |
+
}
|
618 |
+
|
619 |
+
async function updateOnlineUsers() {
|
620 |
+
// Ensure we have user location
|
621 |
+
if (userLocation.city === 'Unknown') {
|
622 |
+
await getUserLocation();
|
623 |
+
}
|
624 |
+
|
625 |
+
// Add current user with location
|
626 |
+
const currentUserData = {
|
627 |
+
id: userId,
|
628 |
+
location: userLocation,
|
629 |
+
lastSeen: Date.now(),
|
630 |
+
isCurrentUser: true
|
631 |
+
};
|
632 |
+
|
633 |
+
onlineUsers.set(userId, currentUserData);
|
634 |
+
|
635 |
+
// Simulate other realistic users based on activity
|
636 |
const now = Date.now();
|
637 |
const timeSinceLastActivity = now - lastActivity;
|
638 |
|
639 |
+
// Add new users when there's recent activity
|
640 |
+
if (timeSinceLastActivity < 10000 && Math.random() < 0.2) {
|
641 |
const newUserId = 'User-' + Math.random().toString(36).substr(2, 8);
|
642 |
+
const locations = [
|
643 |
+
{ city: 'New York', country: 'United States' },
|
644 |
+
{ city: 'London', country: 'United Kingdom' },
|
645 |
+
{ city: 'Tokyo', country: 'Japan' },
|
646 |
+
{ city: 'Paris', country: 'France' },
|
647 |
+
{ city: 'Sydney', country: 'Australia' },
|
648 |
+
{ city: 'Toronto', country: 'Canada' },
|
649 |
+
{ city: 'Berlin', country: 'Germany' },
|
650 |
+
{ city: 'Mumbai', country: 'India' },
|
651 |
+
{ city: 'São Paulo', country: 'Brazil' },
|
652 |
+
{ city: 'Singapore', country: 'Singapore' }
|
653 |
+
];
|
654 |
+
|
655 |
+
const randomLocation = locations[Math.floor(Math.random() * locations.length)];
|
656 |
+
|
657 |
+
onlineUsers.set(newUserId, {
|
658 |
+
id: newUserId,
|
659 |
+
location: randomLocation,
|
660 |
+
lastSeen: now,
|
661 |
+
isCurrentUser: false
|
662 |
+
});
|
663 |
}
|
664 |
|
665 |
+
// Remove users who have been inactive for more than 2 minutes
|
666 |
+
const cutoffTime = now - (2 * 60 * 1000);
|
667 |
+
for (const [id, userData] of onlineUsers.entries()) {
|
668 |
+
if (!userData.isCurrentUser && userData.lastSeen < cutoffTime) {
|
669 |
+
onlineUsers.delete(id);
|
|
|
670 |
}
|
671 |
}
|
672 |
|
673 |
+
// Occasionally remove a random user to simulate leaving
|
674 |
+
if (onlineUsers.size > 3 && Math.random() < 0.1) {
|
675 |
+
const nonCurrentUsers = Array.from(onlineUsers.entries()).filter(([id, data]) => !data.isCurrentUser);
|
676 |
+
if (nonCurrentUsers.length > 0) {
|
677 |
+
const randomUser = nonCurrentUsers[Math.floor(Math.random() * nonCurrentUsers.length)];
|
678 |
+
onlineUsers.delete(randomUser[0]);
|
679 |
+
}
|
680 |
+
}
|
681 |
|
682 |
+
updateUsersDisplay();
|
683 |
+
}
|
684 |
+
|
685 |
+
function updateUsersDisplay() {
|
686 |
const count = onlineUsers.size;
|
687 |
const status = count === 1 ? 'Just you online' : count + ' people online';
|
688 |
document.getElementById('online-status').textContent = status;
|
689 |
|
690 |
+
// Update users list
|
691 |
+
const usersList = document.getElementById('users-list');
|
692 |
+
const currentUserInfo = document.getElementById('current-user-info');
|
693 |
+
|
694 |
+
// Update current user info
|
695 |
+
currentUserInfo.textContent = `You: ${userId} (${userLocation.city}, ${userLocation.country})`;
|
696 |
+
|
697 |
+
let html = `<div class="text-green-400">🟢 You: ${userId}<br><span class="text-gray-400 ml-4">${userLocation.city}, ${userLocation.country}</span></div>`;
|
698 |
+
|
699 |
+
// Add other users
|
700 |
+
const otherUsers = Array.from(onlineUsers.values()).filter(userData => !userData.isCurrentUser);
|
701 |
+
|
702 |
+
otherUsers.forEach(userData => {
|
703 |
+
const timeAgo = Math.floor((Date.now() - userData.lastSeen) / 1000);
|
704 |
+
const timeText = timeAgo < 60 ? 'now' : `${Math.floor(timeAgo / 60)}m ago`;
|
705 |
+
|
706 |
+
html += `
|
707 |
+
<div class="text-blue-400">
|
708 |
+
🔵 ${userData.id}<br>
|
709 |
+
<span class="text-gray-400 ml-4">${userData.location.city}, ${userData.location.country}</span><br>
|
710 |
+
<span class="text-gray-500 ml-4 text-xs">Active ${timeText}</span>
|
711 |
+
</div>
|
712 |
+
`;
|
713 |
+
});
|
714 |
+
|
715 |
+
usersList.innerHTML = html;
|
716 |
+
}
|
717 |
+
|
718 |
+
function refreshUsers() {
|
719 |
+
trackActivity();
|
720 |
+
updateOnlineUsers();
|
721 |
}
|
722 |
|
723 |
function trackActivity() {
|
724 |
lastActivity = Date.now();
|
725 |
+
|
726 |
+
// Update current user's last seen time
|
727 |
+
if (onlineUsers.has(userId)) {
|
728 |
+
const userData = onlineUsers.get(userId);
|
729 |
+
userData.lastSeen = Date.now();
|
730 |
+
onlineUsers.set(userId, userData);
|
731 |
+
}
|
732 |
+
|
733 |
updateOnlineUsers();
|
734 |
}
|
735 |
|