Add Discord-native hybrid front-end for Jarvis (bot + bridge)
Some checks failed
Release / semantic-release (push) Successful in 59s
tests / Unit tests (Linux, Python 3.11) (push) Successful in 13m45s
Release / build-linux (push) Failing after 7m47s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled

Transform isair/jarvis into a Discord-controlled voice assistant running on
the Ubuntu VNC desktop, keeping the mature ~39k-line Python brain intact.

- bot/ (Node + bun, discord.js): /자비스 slash commands (ephemeral),
  voice channel join + voice receive/playback, pluggable VNC screen broadcast
  (selfbot live / noVNC / screenshot)
- bridge/ (Python, Flask): wraps jarvis STT + run_reply_engine + Piper TTS
  behind a thin localhost HTTP API
- .env.example, scripts/ (start_bridge/start_bot/dev), README rewrite,
  docs/language-comparison.md and docs/vnc-xfce-setup.md

Language decision: hybrid (Python brain + Node/bun Discord layer) because
Discord blocks bot video; native screen broadcast only works via a Node
selfbot library.
This commit is contained in:
javis-bot
2026-06-09 14:51:05 +09:00
parent a5bf8d1826
commit c4abf63f38
308 changed files with 94135 additions and 1 deletions

280
scripts/setup_geolocation.py Executable file
View File

@@ -0,0 +1,280 @@
#!/usr/bin/env python3
"""
Setup script for GeoLite2 geolocation database.
This script helps users set up the MaxMind GeoLite2 database required for
location-based features in Jarvis.
Since MaxMind requires registration for free access to GeoLite2 data (as of 2019),
this script provides instructions and utilities to help with the setup process.
"""
import os
import sys
import subprocess
from pathlib import Path
from typing import Optional
# Add the src directory to path for imports
script_dir = Path(__file__).parent
src_dir = script_dir.parent / "src"
sys.path.insert(0, str(src_dir))
try:
# Location utilities live under utils.location after refactor.
from jarvis.utils.location import (
_get_database_path,
is_location_available,
get_location_info,
setup_location_database,
_get_local_network_ip,
_get_external_ip_automatically,
)
from jarvis.config import load_settings
SETTINGS = load_settings()
JARVIS_AVAILABLE = True
except ImportError as e:
print(
"Warning: Could not import Jarvis location utilities from 'jarvis.utils.location'.\n"
f" Import error: {e}\n"
" Make sure you're running from the repository root and that 'src' is on PYTHONPATH.\n"
" Example (zsh/bash): export PYTHONPATH=\"$(pwd)/src:$PYTHONPATH\"\n"
" Or install the project in editable mode once packaging is set up (pip install -e .)."
)
JARVIS_AVAILABLE = False
def check_dependencies() -> bool:
"""Check if required dependencies are installed."""
try:
import geoip2
return True
except ImportError:
return False
def install_dependencies() -> bool:
"""Install required dependencies."""
print("Installing geoip2 dependency...")
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", "geoip2==4.8.0"])
return True
except subprocess.CalledProcessError:
return False
def get_database_info() -> dict:
"""Get information about the database location and status."""
if not JARVIS_AVAILABLE:
base_dir = Path.home() / ".local" / "share" / "jarvis" / "geoip"
db_path = base_dir / "GeoLite2-City.mmdb"
else:
db_path = _get_database_path()
return {
"path": db_path,
"directory": db_path.parent,
"exists": db_path.exists(),
"size": db_path.stat().st_size if db_path.exists() else 0,
}
def print_setup_instructions():
"""Print instructions for setting up the GeoLite2 database."""
db_info = get_database_info()
print("\n" + "="*60)
print("📍 JARVIS GEOLOCATION SETUP")
print("="*60)
print(f"Database location: {db_info['path']}")
print(f"Database exists: {'✅ Yes' if db_info['exists'] else '❌ No'}")
if db_info['exists']:
size_mb = db_info['size'] / (1024 * 1024)
print(f"Database size: {size_mb:.1f} MB")
if JARVIS_AVAILABLE:
print("\n🧪 Testing location detection...")
try:
location = get_location_info(settings=SETTINGS)
if "error" in location:
print(f"❌ Location test failed: {location['error']}")
else:
print("✅ Location detection working!")
print(f" Detected: {location.get('city', 'Unknown')}, {location.get('country', 'Unknown')}")
except Exception as e:
print(f"❌ Location test error: {e}")
else:
print("\n📋 SETUP INSTRUCTIONS:")
print("1. Register for a free MaxMind account:")
print(" https://www.maxmind.com/en/geolite2/signup")
print()
print("2. Generate a license key in your account dashboard")
print()
print("3. Download GeoLite2 City database:")
print(" - Go to: https://www.maxmind.com/en/accounts/current/geoip/downloads")
print(" - Download: GeoLite2 City (MMDB format)")
print(" - Extract the .tar.gz file")
print()
print("4. Copy the database file:")
print(f" cp GeoLite2-City_*/GeoLite2-City.mmdb {db_info['path']}")
print()
print("5. Location detection is automatic!")
print(" Jarvis will attempt to detect your external IP using:")
print(" - UPnP (queries your local router)")
print(" - Socket routing (minimal external contact)")
print(" - Optional single DNS query (OpenDNS) if behind CGNAT (config: location_cgnat_resolve_public_ip=true)")
print()
print(" If automatic detection fails, manually configure:")
print(" Add to ~/.config/jarvis/config.json:")
print(' {')
print(' "location_auto_detect": false,')
print(' "location_ip_address": "YOUR_PUBLIC_IP_HERE"')
print(' }')
print()
print(" 💡 To find your public IP: https://whatismyipaddress.com")
print()
print("6. Run this script again to test the setup")
# Create directory if it doesn't exist
db_info['directory'].mkdir(parents=True, exist_ok=True)
print(f"\n✅ Created directory: {db_info['directory']}")
def test_location_features():
"""Test the location detection features."""
if not JARVIS_AVAILABLE:
print("❌ Cannot test: Jarvis modules not available")
return False
print("\n🔍 Testing location features...")
# Test if location is available
if not is_location_available():
print("❌ Location database not available")
return False
# Test automatic external IP detection
print("Testing automatic external IP detection...")
external_ip = _get_external_ip_automatically()
if external_ip:
print(f"✅ External IP automatically detected: {external_ip}")
else:
print("⚠️ Automatic IP detection failed")
print("💡 You may need to manually configure 'location_ip_address'")
# Test local IP detection (fallback)
print("\nTesting local IP detection (fallback)...")
local_ip = _get_local_network_ip()
if local_ip:
print(f"✅ Local IP detected: {local_ip}")
else:
print("⚠️ Could not detect local IP")
# Test location detection
try:
location = get_location_info(settings=SETTINGS)
if "error" in location:
print(f"⚠️ Location detection result: {location['error']}")
reason = location.get("reason")
advice = location.get("advice")
if reason == "cgnat_not_found":
print("💡 Carrier-grade NAT (100.64.0.0/10) and IP not in GeoLite2. Cannot derive precise location.")
print(" Configure a real public IP in ~/.config/jarvis/config.json:")
print(" { 'location_ip_address': 'YOUR_PUBLIC_IP', 'location_auto_detect': false }")
elif reason == "not_found":
print("💡 IP not found in free GeoLite2 dataset. It may be new or CGNAT.")
elif "No IP address available" in location['error']:
print("💡 No IP available. Provide 'location_ip_address' in config.")
if advice:
print(f" Advice: {advice}")
return False
print("✅ Location detection working!")
print(f" IP: {location.get('ip', 'Unknown')}")
print(f" Location: {location.get('city', 'Unknown')}, {location.get('region', '')}, {location.get('country', 'Unknown')}")
if location.get('latitude') and location.get('longitude'):
print(f" Coordinates: {location['latitude']}, {location['longitude']}")
if location.get('timezone'):
print(f" Timezone: {location['timezone']}")
return True
except Exception as e:
print(f"❌ Location test error: {e}")
return False
def create_test_config():
"""Create a test configuration file with location enabled."""
config_path = Path.home() / ".config" / "jarvis" / "config.json"
if config_path.exists():
print(f"✅ Config file already exists: {config_path}")
print("To enable location features, add to your config:")
print(' "location_ip_address": "YOUR_PUBLIC_IP_HERE"')
return
config_path.parent.mkdir(parents=True, exist_ok=True)
test_config = {
"location_enabled": True,
"location_cache_minutes": 60,
"location_ip_address": None,
"location_auto_detect": True,
"voice_debug": True
}
import json
with open(config_path, 'w') as f:
json.dump(test_config, f, indent=2)
print(f"✅ Created test config: {config_path}")
print("💡 Location features will auto-detect your IP address")
print(" If auto-detection fails, manually set 'location_ip_address'")
def main():
"""Main setup function."""
print("🌍 Jarvis Geolocation Setup")
# Check dependencies
if not check_dependencies():
print("❌ geoip2 library not found")
print("Installing dependencies...")
if not install_dependencies():
print("❌ Failed to install dependencies")
sys.exit(1)
print("✅ Dependencies installed")
else:
print("✅ Dependencies available")
# Print setup instructions
print_setup_instructions()
# Test if everything is working
db_info = get_database_info()
if db_info['exists']:
test_success = test_location_features()
if test_success:
print("\n🎉 Geolocation setup complete!")
print("Location metadata will now be included in agent context.")
else:
print("\n⚠️ Database exists but testing failed")
print("Please check the database file is valid.")
else:
print("\n⏳ Database not found - follow the instructions above")
print("\n💡 Privacy Note: Jarvis respects your privacy by:")
print(" - Using UPnP (local router) and socket routing instead of third-party services")
print(" - Working entirely with local databases")
print(" - Giving you full control over IP detection methods")
print("\n💡 Tip: Set JARVIS_VOICE_DEBUG=1 to see location info in debug output")
if __name__ == "__main__":
main()