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")