Add files via upload
This commit is contained in:
7 files changed
+485
No files matched your search
@@ -0,0 +1,2 @@
|
||||
GROQ_API_KEY = PASTE YOUR API GROQ_API_KEY
|
||||
#IF YOU DON'T KNOW HOW, THEN WATCH THE VIDEO https://youtu.be/TROMQ_Du7q8?si=UO7TnaUJ6JBCZvoi
|
||||
@@ -0,0 +1,194 @@
|
||||
import os
|
||||
import math
|
||||
import time
|
||||
import requests
|
||||
import webbrowser
|
||||
import re
|
||||
from difflib import SequenceMatcher
|
||||
from dotenv import load_dotenv
|
||||
from voice_player import edge_speak
|
||||
|
||||
load_dotenv()
|
||||
|
||||
MY_LAT = float(os.getenv("LAT", 40.7908711))
|
||||
MY_LON = float(os.getenv("LON", -73.3746079))
|
||||
|
||||
OPENSKY_STATES = "https://opensky-network.org/api/states/all"
|
||||
OPENSKY_FLIGHTS = "https://opensky-network.org/api/flights/aircraft"
|
||||
|
||||
def normalize(text: str):
|
||||
text = text.lower()
|
||||
text = re.sub(r"[^a-z0-9\s]", "", text)
|
||||
text = re.sub(r"\s+", " ", text).strip()
|
||||
return text
|
||||
|
||||
|
||||
def smart_match(user_input: str, command: str):
|
||||
user = normalize(user_input)
|
||||
cmd = normalize(command)
|
||||
|
||||
if cmd in user:
|
||||
return True
|
||||
|
||||
user_words = set(user.split())
|
||||
cmd_words = set(cmd.split())
|
||||
|
||||
if cmd_words and len(user_words & cmd_words) / len(cmd_words) >= 0.6:
|
||||
return True
|
||||
|
||||
return SequenceMatcher(None, user, cmd).ratio() >= 0.6
|
||||
|
||||
|
||||
def match_commands(user_input: str, commands: list):
|
||||
return any(smart_match(user_input, cmd) for cmd in commands)
|
||||
|
||||
def haversine(lat1, lon1, lat2, lon2):
|
||||
R = 6371
|
||||
d_lat = math.radians(lat2 - lat1)
|
||||
d_lon = math.radians(lon2 - lon1)
|
||||
a = (
|
||||
math.sin(d_lat / 2) ** 2
|
||||
+ math.cos(math.radians(lat1))
|
||||
* math.cos(math.radians(lat2))
|
||||
* math.sin(d_lon / 2) ** 2
|
||||
)
|
||||
return 2 * R * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
||||
|
||||
def get_nearby_aircraft(radius_km=200):
|
||||
lamin, lamax = MY_LAT - 1, MY_LAT + 1
|
||||
lomin, lomax = MY_LON - 1, MY_LON + 1
|
||||
|
||||
try:
|
||||
r = requests.get(
|
||||
f"{OPENSKY_STATES}?lamin={lamin}&lamax={lamax}&lomin={lomin}&lomax={lomax}",
|
||||
timeout=10
|
||||
)
|
||||
data = r.json()
|
||||
except:
|
||||
return []
|
||||
|
||||
aircraft = []
|
||||
|
||||
for s in data.get("states", []):
|
||||
lat, lon = s[6], s[5]
|
||||
if lat is None or lon is None:
|
||||
continue
|
||||
|
||||
dist = haversine(MY_LAT, MY_LON, lat, lon)
|
||||
if dist > radius_km:
|
||||
continue
|
||||
|
||||
velocity = s[9] or 0
|
||||
heading = s[10] or 0
|
||||
|
||||
aircraft.append({
|
||||
"icao24": s[0],
|
||||
"callsign": (s[1] or "Unknown").strip(),
|
||||
"country": s[2] or "Unknown",
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"distance": dist,
|
||||
"velocity": velocity,
|
||||
"heading": heading
|
||||
})
|
||||
|
||||
return aircraft
|
||||
|
||||
|
||||
def get_flight_route(icao24):
|
||||
now = int(time.time())
|
||||
begin = now - 6 * 3600
|
||||
|
||||
try:
|
||||
r = requests.get(
|
||||
f"{OPENSKY_FLIGHTS}?icao24={icao24}&begin={begin}&end={now}",
|
||||
timeout=10
|
||||
)
|
||||
flights = r.json()
|
||||
if flights:
|
||||
f = flights[-1]
|
||||
return f.get("estDepartureAirport"), f.get("estArrivalAirport")
|
||||
except:
|
||||
pass
|
||||
|
||||
return None, None
|
||||
|
||||
def generate_jarvis_aircraft_report(planes):
|
||||
total = len(planes)
|
||||
closest = min(planes, key=lambda x: x["distance"])
|
||||
|
||||
dist = closest["distance"]
|
||||
speed = closest["velocity"] * 3.6
|
||||
heading = closest["heading"]
|
||||
|
||||
if dist < 50:
|
||||
situation = "approaching your position"
|
||||
elif dist < 120:
|
||||
situation = "passing through nearby airspace"
|
||||
else:
|
||||
situation = "operating at a safe distance"
|
||||
|
||||
dep, arr = get_flight_route(closest["icao24"])
|
||||
dep = dep or "unknown origin"
|
||||
arr = arr or "unknown destination"
|
||||
|
||||
return (
|
||||
f"Sir, radar scan complete. "
|
||||
f"I have detected {total} aircraft in your vicinity. "
|
||||
f"The nearest one is {closest['callsign']}, registered in {closest['country']}. "
|
||||
f"It is approximately {dist:.1f} kilometers away, "
|
||||
f"traveling at {speed:.0f} kilometers per hour, "
|
||||
f"heading {heading:.0f} degrees, "
|
||||
f"currently {situation}. "
|
||||
f"Estimated route is from {dep} to {arr}. "
|
||||
f"No immediate threat detected."
|
||||
)
|
||||
|
||||
def speak_detailed_aircraft_report(player):
|
||||
planes = get_nearby_aircraft()
|
||||
|
||||
if not planes:
|
||||
text = "Sir, no aircraft detected in your operational airspace."
|
||||
edge_speak(text, player)
|
||||
player.write_log(text)
|
||||
return
|
||||
|
||||
text = generate_jarvis_aircraft_report(planes)
|
||||
edge_speak(text, player)
|
||||
player.write_log(text)
|
||||
|
||||
def handle_aircraft_command(user_text, player):
|
||||
if match_commands(user_text, [
|
||||
"give me aircraft details",
|
||||
"tell me detailed report",
|
||||
"give me full detailed aircraft report",
|
||||
"give me advanced aircraft report",
|
||||
"tell me aircraft details"
|
||||
]):
|
||||
speak_detailed_aircraft_report(player)
|
||||
return True
|
||||
|
||||
if match_commands(user_text, [
|
||||
"open aircraft",
|
||||
"open flight radar",
|
||||
"show aircraft map"
|
||||
]):
|
||||
webbrowser.open(f"https://www.flightradar24.com/{MY_LAT},{MY_LON}/10")
|
||||
edge_speak("Opening aircraft radar, sir.", player)
|
||||
player.write_log("Opening aircraft radar, sir.")
|
||||
return True
|
||||
|
||||
if match_commands(user_text, [
|
||||
"aircraft summary",
|
||||
"planes nearby",
|
||||
"give me aircraft"
|
||||
]):
|
||||
planes = get_nearby_aircraft()
|
||||
text = f"Sir, I currently detect {len(planes)} aircraft nearby."
|
||||
edge_speak(text, player)
|
||||
player.write_log(text)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
return False
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
from groq import Groq
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
load_dotenv()
|
||||
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
||||
if not GROQ_API_KEY:
|
||||
raise ValueError("❌ GROQ_API_KEY not found in .env")
|
||||
|
||||
client = Groq(api_key=GROQ_API_KEY)
|
||||
|
||||
SYSTEM_PROMPT = """
|
||||
Give useful and very shortly answers but use maximum 2-3 sentence(and keep that sentences short(a little short))
|
||||
add "sir" like jarvis but not in every sentence.
|
||||
"""
|
||||
|
||||
chat_history = []
|
||||
|
||||
def get_best_model():
|
||||
model_list = client.models.list()
|
||||
|
||||
available = [m.id for m in model_list.data]
|
||||
print("Available Groq Models:\n", available)
|
||||
|
||||
priority = [
|
||||
"llama-3.3-70b",
|
||||
"llama-3.2-90b",
|
||||
"llama-3.2-70b",
|
||||
"llama-3.1-8b",
|
||||
"mixtral-8x7b",
|
||||
"gemma2-9b-it"
|
||||
]
|
||||
|
||||
for name in priority:
|
||||
for model_id in available:
|
||||
if name in model_id.lower():
|
||||
print(f"\n✔ Selected model: {model_id}\n")
|
||||
return model_id
|
||||
|
||||
|
||||
return available[0]
|
||||
|
||||
BEST_MODEL = get_best_model()
|
||||
|
||||
def get_response(user_text):
|
||||
global chat_history
|
||||
|
||||
chat_history.append({"role": "user", "content": user_text})
|
||||
|
||||
messages = (
|
||||
[{"role": "system", "content": SYSTEM_PROMPT}]
|
||||
+ chat_history[-5:]
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=BEST_MODEL,
|
||||
messages=messages,
|
||||
max_tokens=200
|
||||
)
|
||||
|
||||
text = response.choices[0].message.content.strip()
|
||||
chat_history.append({"role": "assistant", "content": text})
|
||||
return text
|
||||
|
||||
|
||||
def reset_history():
|
||||
global chat_history
|
||||
chat_history = []
|
||||
@@ -0,0 +1,40 @@
|
||||
import asyncio
|
||||
import threading
|
||||
from voice_player import VoiceImagePlayer, edge_speak
|
||||
from speech_to_text import record_voice
|
||||
from groq_ai import get_response
|
||||
from aircraft_module import handle_aircraft_command
|
||||
|
||||
async def get_voice_input():
|
||||
return await asyncio.to_thread(record_voice)
|
||||
|
||||
async def ai_loop(player):
|
||||
|
||||
while True:
|
||||
user_text = await get_voice_input()
|
||||
if not user_text:
|
||||
continue
|
||||
|
||||
handled = handle_aircraft_command(user_text, player)
|
||||
if handled:
|
||||
continue
|
||||
|
||||
response = get_response(user_text)
|
||||
print("AI:", response)
|
||||
|
||||
edge_speak(response, player)
|
||||
player.write_log(f"You: {user_text}")
|
||||
player.write_log(f"AI: {response}")
|
||||
|
||||
def main_loop():
|
||||
player = VoiceImagePlayer("face.png", size=(900, 900))
|
||||
|
||||
def thread_target():
|
||||
asyncio.run(ai_loop(player))
|
||||
|
||||
threading.Thread(target=thread_target, daemon=True).start()
|
||||
player.root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main_loop()
|
||||
@@ -0,0 +1,32 @@
|
||||
# vosk_voice.py
|
||||
import sounddevice as sd
|
||||
import vosk
|
||||
import queue
|
||||
import sys
|
||||
import json
|
||||
|
||||
MODEL_PATH = "C:/Users/90553/Downloads/vosk/vosk-model-small-en-us-0.15" #put your path in here
|
||||
model = vosk.Model(MODEL_PATH)
|
||||
|
||||
q = queue.Queue()
|
||||
|
||||
def callback(indata, frames, time, status):
|
||||
if status:
|
||||
print(status, file=sys.stderr)
|
||||
q.put(bytes(indata))
|
||||
|
||||
def record_voice(prompt="🎙 I'm listening, sir...", timeout=None, phrase_time_limit=None):
|
||||
|
||||
print(prompt)
|
||||
rec = vosk.KaldiRecognizer(model, 16000)
|
||||
|
||||
with sd.RawInputStream(samplerate=16000, blocksize=8000, dtype='int16',
|
||||
channels=1, callback=callback):
|
||||
while True:
|
||||
data = q.get()
|
||||
if rec.AcceptWaveform(data):
|
||||
result = json.loads(rec.Result())
|
||||
text = result.get("text", "")
|
||||
if text.strip():
|
||||
print("👤 You:", text)
|
||||
return text
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
import os
|
||||
import io
|
||||
import random
|
||||
import threading
|
||||
import asyncio
|
||||
import edge_tts
|
||||
import sounddevice as sd
|
||||
import soundfile as sf
|
||||
from PIL import Image, ImageTk
|
||||
import tkinter as tk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
import re
|
||||
import html
|
||||
|
||||
# ---------------- TEXT NORMALIZATION ---------------- #
|
||||
|
||||
def normalize_text(text: str) -> str:
|
||||
text = text.strip()
|
||||
text = re.sub(r'\s+', ' ', text)
|
||||
return html.escape(text) # SSML güvenliği
|
||||
|
||||
# ---------------- SSML BUILDER ---------------- #
|
||||
|
||||
def build_ssml(text: str, voice: str) -> str:
|
||||
return (
|
||||
f'<speak version="1.0" xml:lang="en-US">'
|
||||
f'<voice name="{voice}">'
|
||||
f'{text}'
|
||||
f'</voice>'
|
||||
f'</speak>'
|
||||
)
|
||||
|
||||
# ---------------- UI CLASS ---------------- #
|
||||
|
||||
class VoiceImagePlayer:
|
||||
def __init__(self, face_path, size=(760, 760)):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("AI - EPISODE 2")
|
||||
self.root.resizable(False, False)
|
||||
self.root.geometry("760x900")
|
||||
|
||||
self.fixed_size = size
|
||||
self.face_original = Image.open(face_path).resize(self.fixed_size, Image.LANCZOS)
|
||||
|
||||
self.face_label = tk.Label(self.root, bg="#001f4d", borderwidth=0)
|
||||
self.face_label.place(relx=0.5, rely=0.4, anchor="center")
|
||||
|
||||
self.scale = 1.0
|
||||
self.playing = False
|
||||
|
||||
self.text_box = ScrolledText(
|
||||
self.root,
|
||||
fg="#00ffff",
|
||||
bg="#001f4d",
|
||||
insertbackground="#00ffff",
|
||||
height=12,
|
||||
borderwidth=0,
|
||||
wrap="word",
|
||||
font=("Consolas", 10),
|
||||
padx=10,
|
||||
pady=10
|
||||
)
|
||||
self.text_box.place(relx=0.5, rely=0.85, anchor="center")
|
||||
self.text_box.configure(state="disabled")
|
||||
|
||||
self.animate()
|
||||
self.root.protocol("WM_DELETE_WINDOW", lambda: os._exit(0))
|
||||
|
||||
# 🔥 BU METOT EKSİKTİ – ŞİMDİ VAR
|
||||
def write_log(self, text: str):
|
||||
self.text_box.configure(state="normal")
|
||||
self.text_box.insert(tk.END, text + "\n")
|
||||
self.text_box.see(tk.END)
|
||||
self.text_box.configure(state="disabled")
|
||||
|
||||
def start_animation(self):
|
||||
self.playing = True
|
||||
|
||||
def stop_animation(self):
|
||||
self.playing = False
|
||||
|
||||
def animate(self):
|
||||
if self.playing:
|
||||
self.scale += random.uniform(-0.01, 0.015)
|
||||
self.scale = min(max(self.scale, 1.02), 1.08)
|
||||
else:
|
||||
self.scale += (1.0 - self.scale) * 0.1
|
||||
|
||||
w, h = self.fixed_size
|
||||
resized = self.face_original.resize(
|
||||
(int(w * self.scale), int(h * self.scale)),
|
||||
Image.LANCZOS
|
||||
)
|
||||
|
||||
img = ImageTk.PhotoImage(resized)
|
||||
self.face_label.configure(image=img)
|
||||
self.face_label.image = img
|
||||
|
||||
self.root.after(50, self.animate)
|
||||
|
||||
# ---------------- VOICE ENGINE ---------------- #
|
||||
|
||||
VOICE = "en-GB-RyanNeural"
|
||||
# Erkek, stabil
|
||||
|
||||
def edge_speak(text: str, img_player: VoiceImagePlayer, voice: str = VOICE):
|
||||
if not text.strip():
|
||||
return
|
||||
|
||||
clean_text = text.strip()
|
||||
|
||||
def _thread():
|
||||
async def _run():
|
||||
audio_buffer = bytearray()
|
||||
first_audio = False
|
||||
|
||||
try:
|
||||
communicate = edge_tts.Communicate(
|
||||
clean_text,
|
||||
voice
|
||||
)
|
||||
|
||||
async for chunk in communicate.stream():
|
||||
if chunk["type"] == "audio":
|
||||
audio_buffer.extend(chunk["data"])
|
||||
if not first_audio:
|
||||
first_audio = True
|
||||
img_player.start_animation()
|
||||
|
||||
except Exception as e:
|
||||
print("TTS ERROR:", e)
|
||||
img_player.stop_animation()
|
||||
return
|
||||
|
||||
if not audio_buffer:
|
||||
img_player.stop_animation()
|
||||
return
|
||||
|
||||
data, samplerate = sf.read(io.BytesIO(audio_buffer), dtype="float32")
|
||||
sd.play(data, samplerate)
|
||||
sd.wait()
|
||||
img_player.stop_animation()
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_until_complete(_run())
|
||||
loop.close()
|
||||
|
||||
threading.Thread(target=_thread, daemon=True).start()
|
||||
Reference in new issue
Block a user