import requests import os import json import streamlit as st from datetime import datetime, timedelta import time import uuid import streamlit.components.v1 as components # Page configuration st.set_page_config( page_title="Chat Flow 🕷", page_icon="💬", initial_sidebar_state="expanded" ) # Enhanced CSS with chat history styling and BLACK NEW CHAT BUTTON st.markdown(""" """, unsafe_allow_html=True) # File to store chat history HISTORY_FILE = "chat_history.json" USERS_FILE = "online_users.json" SESSIONS_FILE = "chat_sessions.json" # ================= REAL LOCATION DETECTION FUNCTIONS ================= def get_user_location_browser(): """Get user's real location using browser geolocation API""" # JavaScript code to get real user location location_js = """

🔍 Detecting your real location...

""" return location_js def reverse_geocode(lat, lon): """Convert coordinates to address using free APIs""" try: # Using Nominatim (OpenStreetMap) - Free and reliable url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json&addressdetails=1" headers = {'User-Agent': 'ChatFlow-App/1.0'} response = requests.get(url, headers=headers, timeout=10) if response.status_code == 200: data = response.json() address = data.get('address', {}) location_data = { 'latitude': lat, 'longitude': lon, 'city': address.get('city') or address.get('town') or address.get('village'), 'region': address.get('state') or address.get('province'), 'country': address.get('country'), 'country_code': address.get('country_code', '').upper(), 'postal': address.get('postcode'), 'address': data.get('display_name'), 'accuracy': 'GPS-based (Very High)', 'timestamp': datetime.now().isoformat(), 'api_used': 'Nominatim/OpenStreetMap', 'source': 'Browser Geolocation' } return location_data except Exception as e: st.error(f"Reverse geocoding error: {e}") return None def calculate_distance(lat1, lon1, lat2, lon2): """Calculate distance between two points in kilometers""" if not all([lat1, lon1, lat2, lon2]): return None try: from math import radians, cos, sin, asin, sqrt # Convert to radians lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) # Haversine formula dlat = lat2 - lat1 dlon = lon2 - lon1 a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 c = 2 * asin(sqrt(a)) r = 6371 # Earth's radius in kilometers distance = c * r return round(distance, 1) except Exception as e: print(f"Distance calculation error: {e}") return None def get_country_flag(country_code): """Get emoji flag for country code""" flags = { 'US': '🇺🇸', 'UK': '🇬🇧', 'CA': '🇨🇦', 'AU': '🇦🇺', 'DE': '🇩🇪', 'FR': '🇫🇷', 'IT': '🇮🇹', 'ES': '🇪🇸', 'JP': '🇯🇵', 'CN': '🇨🇳', 'IN': '🇮🇳', 'BR': '🇧🇷', 'RU': '🇷🇺', 'MX': '🇲🇽', 'NL': '🇳🇱', 'PK': '🇵🇰', 'BD': '🇧🇩', 'ID': '🇮🇩', 'NG': '🇳🇬', 'TR': '🇹🇷', 'EG': '🇪🇬', 'ZA': '🇿🇦', 'KR': '🇰🇷', 'TH': '🇹🇭', 'VN': '🇻🇳', 'PH': '🇵🇭', 'MY': '🇲🇾', 'SG': '🇸🇬', 'AE': '🇦🇪', 'SA': '🇸🇦' } return flags.get(country_code, '🌍') # ================= USER FUNCTIONS WITH REAL LOCATION ================= def get_user_id(): """Get unique ID for this user session""" if 'user_id' not in st.session_state: st.session_state.user_id = str(uuid.uuid4())[:8] return st.session_state.user_id def update_online_users_with_real_location(): """Update user status with REAL browser-based location""" try: # Load current users users = {} if os.path.exists(USERS_FILE): with open(USERS_FILE, 'r') as f: users = json.load(f) user_id = get_user_id() # Check if we have real location data from browser location_data = st.session_state.get(f'real_location_{user_id}') # Update user info with real location users[user_id] = { 'last_seen': datetime.now().isoformat(), 'name': f'User-{user_id}', 'location': location_data, 'session_start': users.get(user_id, {}).get('session_start', datetime.now().isoformat()) } # Clean up old users (not seen in 5 minutes) current_time = datetime.now() active_users = {} for uid, data in users.items(): try: last_seen = datetime.fromisoformat(data['last_seen']) if current_time - last_seen < timedelta(minutes=5): active_users[uid] = data except: continue # Save updated users with open(USERS_FILE, 'w') as f: json.dump(active_users, f, indent=2) return len(active_users) except Exception as e: st.error(f"Location tracking error: {e}") return 1 def show_real_location_detector(): """Show real location detector interface""" st.header("🌍 Real Location Detection") user_id = get_user_id() # Check if we already have location if f'real_location_{user_id}' in st.session_state: location = st.session_state[f'real_location_{user_id}'] if location: flag = get_country_flag(location.get('country_code', '')) st.success(f"✅ Location detected: {flag} {location.get('city', 'Unknown')}, {location.get('country', 'Unknown')}") return True # Show location detector st.info("🔍 Click below to detect your real location using browser GPS") # Embed JavaScript for real geolocation location_component = get_user_location_browser() components.html(location_component, height=100) # Manual coordinate input as backup with st.expander("🛠️ Manual Location Entry (Backup)"): st.write("If automatic detection doesn't work, enter your coordinates:") col1, col2 = st.columns(2) with col1: manual_lat = st.number_input("Latitude", value=0.0, format="%.6f") with col2: manual_lon = st.number_input("Longitude", value=0.0, format="%.6f") if st.button("🎯 Use These Coordinates"): if manual_lat != 0.0 and manual_lon != 0.0: location_data = reverse_geocode(manual_lat, manual_lon) if location_data: st.session_state[f'real_location_{user_id}'] = location_data st.success("✅ Manual location set!") st.rerun() else: st.error("❌ Could not resolve coordinates to address") return False def process_browser_location(): """Process location data from browser JavaScript""" user_id = get_user_id() # This would be called when JavaScript sends location data # In a real implementation, you'd use Streamlit's session state # or a custom component to receive the JavaScript data # For now, we'll simulate this with manual input # In production, you'd replace this with actual JavaScript communication pass def show_user_locations(): """Display all user locations with real coordinates""" st.header("🌍 Who's Online & Where") try: if not os.path.exists(USERS_FILE): st.info("No user data yet") return 0 with open(USERS_FILE, 'r') as f: users = json.load(f) if not users: st.info("No active users") return 0 # Get current user's location for distance calculation current_user_id = get_user_id() current_location = st.session_state.get(f'real_location_{current_user_id}') online_count = len(users) # Show count if online_count == 1: st.success("🟢 Just you online") else: st.success(f"🟢 {online_count} people online") st.divider() # Show each user with real location for user_id, data in users.items(): location = data.get('location') is_current_user = (user_id == current_user_id) # User header if is_current_user: st.markdown("**👤 You**") else: st.markdown(f"**👤 {data.get('name', user_id)}**") if location and location.get('city'): # Get flag flag = get_country_flag(location.get('country_code', '')) # Location string with accuracy indicator accuracy = location.get('accuracy', 'Unknown') location_str = f"{flag} {location['city']}, {location['country']}" if 'GPS' in accuracy: st.markdown(f"📍 **{location_str}** 🎯") st.caption("✅ Real GPS location") else: st.markdown(f"📍 **{location_str}**") st.caption("⚠️ Approximate location") # Calculate REAL distance to other users if not is_current_user and current_location and current_location.get('latitude'): distance = calculate_distance( current_location.get('latitude'), current_location.get('longitude'), location.get('latitude'), location.get('longitude') ) if distance: if distance < 1: st.caption("📏 Very close to you! (<1km)") elif distance < 10: st.caption(f"📏 ~{distance} km from you (same city)") elif distance < 100: st.caption(f"📏 ~{distance} km from you (nearby)") else: st.caption(f"📏 ~{distance} km from you") # Show detailed location info with st.expander(f"Real Location Details - {user_id}"): col1, col2 = st.columns(2) with col1: st.write(f"🎯 **Coordinates:** {location.get('latitude', 'N/A'):.6f}, {location.get('longitude', 'N/A'):.6f}") if location.get('region'): st.write(f"🏛️ **Region:** {location['region']}") if location.get('postal'): st.write(f"📮 **Postal:** {location['postal']}") with col2: st.write(f"🔍 **Source:** {location.get('source', 'Unknown')}") st.write(f"🎯 **Accuracy:** {location.get('accuracy', 'Unknown')}") st.write(f"⏰ **Detected:** {location.get('timestamp', 'Unknown')[:16]}") else: st.caption("📍 Location not detected yet") if is_current_user: st.caption("👆 Use the location detector above") # Show session info try: session_start = datetime.fromisoformat(data['session_start']) duration = datetime.now() - session_start minutes = int(duration.total_seconds() / 60) st.caption(f"🕐 Online for {minutes} minutes") except: st.caption("🕐 Session time unknown") st.divider() return online_count except Exception as e: st.error(f"Error showing locations: {e}") return 0 # ================= CHAT FUNCTIONS (unchanged) ================= # [All the existing chat functions remain the same] def load_chat_history(): """Load chat history from file""" try: if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: st.error(f"Error loading chat history: {e}") return [] def save_chat_history(messages): """Save chat history to file""" try: with open(HISTORY_FILE, 'w', encoding='utf-8') as f: json.dump(messages, f, ensure_ascii=False, indent=2) except Exception as e: st.error(f"Error saving chat history: {e}") def clear_chat_history(): """Clear chat history file""" try: if os.path.exists(HISTORY_FILE): os.remove(HISTORY_FILE) st.session_state.messages = [] except Exception as e: st.error(f"Error clearing chat history: {e}") def load_chat_sessions(): """Load all chat sessions""" try: if os.path.exists(SESSIONS_FILE): with open(SESSIONS_FILE, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: st.error(f"Error loading chat sessions: {e}") return {} def save_chat_sessions(sessions): """Save chat sessions to file""" try: with open(SESSIONS_FILE, 'w', encoding='utf-8') as f: json.dump(sessions, f, ensure_ascii=False, indent=2) except Exception as e: st.error(f"Error saving chat sessions: {e}") def get_session_id(): """Get or create session ID""" if 'session_id' not in st.session_state: st.session_state.session_id = str(uuid.uuid4()) return st.session_state.session_id def start_new_chat(): """Start a new chat session""" if st.session_state.messages: save_current_session() st.session_state.messages = [] st.session_state.session_id = str(uuid.uuid4()) # Initialize session state if "messages" not in st.session_state: st.session_state.messages = load_chat_history() if "session_id" not in st.session_state: st.session_state.session_id = str(uuid.uuid4()) # Get API key OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY") # ================= MAIN APP WITH REAL LOCATION ================= # Header st.title("Chat Flow 🕷") st.caption("10 powerful Models, one simple chat.") # Sidebar with REAL LOCATION TRACKING with st.sidebar: # New Chat Button (BLACK) if st.button("➕ New Chat", use_container_width=True, type="primary"): start_new_chat() st.rerun() st.divider() # REAL LOCATION DETECTION show_real_location_detector() st.divider() # Show online users with real locations online_count = show_user_locations() # Update location tracking update_online_users_with_real_location() # Quick refresh if st.button("🔄 Refresh Locations", use_container_width=True): st.rerun() st.divider() # Rest of the sidebar (chat sessions, settings, etc.) remains the same # [All existing sidebar code continues here...] # Main chat area remains the same # [All existing chat code continues here...] # Location Status Footer - Updated for real location user_id = get_user_id() location = st.session_state.get(f'real_location_{user_id}') if location and location.get('city'): flag = get_country_flag(location.get('country_code', '')) accuracy = location.get('accuracy', 'Unknown') if 'GPS' in accuracy: st.caption(f"📍 Your real location: {flag} {location['city']}, {location['country']} | ✅ GPS-accurate") else: st.caption(f"📍 Your location: {flag} {location['city']}, {location['country']} | Accuracy: {accuracy}") else: st.caption("📍 Real location not detected yet - click 'Allow Location Access' above")