import pyperclip from pynput import keyboard import pyautogui import tkinter as tk from tkinter import messagebox import time import threading import requests import queue # --- AYARLAR --- OLLAMA_URL = "http://localhost:11434/api/generate" MODEL_ADI = "gemini-3-flash-preview:latest" # Ana model (F8) TEXT_MODEL_CANDIDATES = [ MODEL_ADI, "gemini-3-flash-preview:cloud", ] KISAYOL_METIN = keyboard.Key.f8 # Metin secimi icin kisayol # Global değişkenler root = None gui_queue = queue.Queue() kisayol_basildi = False # --- MENÜ SEÇENEKLERİ VE PROMPT'LAR --- ISLEMLER = { "📝 Gramer Düzelt": "Bu metni Türkçe yazım ve dil bilgisi kurallarına göre düzelt, resmi ve akıcı olsun. Sadece sonucu ver.", "🇬🇧 İngilizceye Çevir": "Bu metni İngilizceye çevir. Sadece çeviriyi ver.", "🇹🇷 Türkçeye Çevir": "Bu metni Türkçeye çevir. Sadece çeviriyi ver.", "📑 Özetle (Madde Madde)": "Bu metni analiz et ve en önemli noktaları madde madde özetle.", "💼 Daha Resmi Yap": "Bu metni kurumsal bir e-posta diline çevir, çok resmi olsun.", "🐍 Python Koduna Çevir": "Bu metindeki isteği yerine getiren bir Python kodu yaz. Sadece kodu ver.", "📧 Cevap Yaz (Mail)": "Bu gelen bir e-posta, buna kibar ve profesyonel bir cevap metni taslağı yaz.", "🎮 PS5 Oyun Skor + Acımasız Yorum": ( "Seçili metni bir PS5 oyunu adı olarak ele al. Aşağıdaki formatta Türkçe cevap ver:\n" "1) Oyun: \n" "2) Topluluk Beğeni Skorları:\n" "- Metacritic User Score: \n" "- OpenCritic / benzer eleştirmen ortalaması: \n" "- Oyuncu yorumu ortalaması (PS Store vb.): \n" "3) Hüküm: sadece 'IYI' veya 'KOTU'\n" "4) Acımasız Yorum: 2-4 cümle, net ve sert.\n" "Kurallar: Kesin bilmediğin puanı uydurma, onun yerine 'bilgi yok' yaz. " "Yorumu skorlarla tutarlı kur." ), } def get_available_text_model(): """Metin işlemede kullanılabilir modeli seçer.""" preferred_models = [] for model in TEXT_MODEL_CANDIDATES: if model and model not in preferred_models: preferred_models.append(model) try: response = requests.get("http://localhost:11434/api/tags", timeout=5) if response.status_code != 200: return MODEL_ADI models = response.json().get("models", []) installed_lower = {m.get("name", "").lower(): m.get("name", "") for m in models} for candidate in preferred_models: candidate_lower = candidate.lower() if candidate_lower in installed_lower: return installed_lower[candidate_lower] candidate_base = candidate_lower.split(":")[0] for installed_name_lower, installed_name in installed_lower.items(): if installed_name_lower.startswith(candidate_base + ":"): return installed_name except Exception: pass return MODEL_ADI def ollama_cevap_al(prompt): """Ollama API'den cevap al.""" try: aktif_model = get_available_text_model() payload = { "model": aktif_model, "prompt": prompt, "stream": False, "options": { "temperature": 0.7, "top_p": 0.9, }, } response = requests.post(OLLAMA_URL, json=payload, timeout=60) if response.status_code == 200: result = response.json() return result.get("response", "").strip() err_msg = ( f"Ollama API Hatası: {response.status_code}\n" f"Model: {aktif_model}\n" f"Cevap: {response.text}" ) print(f"❌ {err_msg}") gui_queue.put((messagebox.showerror, ("API Hatası", err_msg))) return None except requests.exceptions.ConnectionError: err_msg = ( "Ollama'ya bağlanılamadı.\n" "Programın çalıştığından emin olun!\n" "(http://localhost:11434)" ) print(f"❌ {err_msg}") gui_queue.put((messagebox.showerror, ("Bağlantı Hatası", err_msg))) return None except Exception as e: err_msg = f"Beklenmeyen Hata: {e}" print(f"❌ {err_msg}") gui_queue.put((messagebox.showerror, ("Hata", err_msg))) return None def strip_code_fence(text): if not text: return text cleaned = text.strip() if cleaned.startswith("```"): lines = cleaned.splitlines() lines = lines[1:] if lines else [] while lines and lines[-1].strip() == "```": lines = lines[:-1] cleaned = "\n".join(lines).strip() return cleaned def secili_metni_kopyala(max_deneme=4): sentinel = f"__AI_ASISTAN__{time.time_ns()}__" try: pyperclip.copy(sentinel) except Exception: pass for _ in range(max_deneme): pyautogui.hotkey("ctrl", "c") time.sleep(0.2) metin = pyperclip.paste() if metin and metin.strip() and metin != sentinel: return metin return "" def pencere_modunda_gosterilsin_mi(komut_adi): return "PS5 Oyun Skor" in komut_adi def sonuc_penceresi_goster(baslik, icerik): pencere = tk.Toplevel(root) pencere.title(baslik) pencere.geometry("780x520") pencere.minsize(520, 320) pencere.attributes("-topmost", True) frame = tk.Frame(pencere, bg="#1f1f1f") frame.pack(fill="both", expand=True, padx=10, pady=10) text_alani = tk.Text( frame, wrap="word", bg="#2b2b2b", fg="white", insertbackground="white", font=("Segoe UI", 10), padx=10, pady=10, ) kaydirma = tk.Scrollbar(frame, command=text_alani.yview) text_alani.configure(yscrollcommand=kaydirma.set) text_alani.pack(side="left", fill="both", expand=True) kaydirma.pack(side="right", fill="y") text_alani.insert("1.0", icerik) text_alani.config(state="disabled") alt_frame = tk.Frame(pencere, bg="#1f1f1f") alt_frame.pack(fill="x", padx=10, pady=(0, 10)) def panoya_kopyala(): pyperclip.copy(icerik) tk.Button( alt_frame, text="Panoya Kopyala", command=panoya_kopyala, bg="#3d3d3d", fg="white", activebackground="#4d4d4d", activeforeground="white", relief="flat", padx=12, pady=6, ).pack(side="left") tk.Button( alt_frame, text="Kapat", command=pencere.destroy, bg="#3d3d3d", fg="white", activebackground="#4d4d4d", activeforeground="white", relief="flat", padx=12, pady=6, ).pack(side="right") pencere.focus_force() pencere.lift() def islemi_yap(komut_adi, secili_metin): prompt_emri = ISLEMLER[komut_adi] full_prompt = f"{prompt_emri}:\n\n'{secili_metin}'" print(f"🤖 İşlem: {komut_adi}") print("⏳ Ollama ile işleniyor...") sonuc = ollama_cevap_al(full_prompt) if not sonuc: print("❌ Sonuç alınamadı.") return sonuc = strip_code_fence(sonuc) if sonuc.startswith("'") and sonuc.endswith("'"): sonuc = sonuc[1:-1] if pencere_modunda_gosterilsin_mi(komut_adi): gui_queue.put((sonuc_penceresi_goster, (komut_adi, sonuc))) print("✅ Sonuç ayrı pencerede gösterildi.") return time.sleep(0.2) pyperclip.copy(sonuc) time.sleep(0.1) pyautogui.hotkey("ctrl", "v") print("✅ İşlem tamamlandı!") def process_queue(): """Kuyruktaki GUI işlemlerini ana thread'de çalıştırır.""" try: while True: try: task = gui_queue.get_nowait() except queue.Empty: break func, args = task func(*args) finally: if root: root.after(100, process_queue) def menu_goster(): """Metni kopyalar ve menüyü gösterir (ana thread).""" secili_metin = secili_metni_kopyala() if not secili_metin.strip(): gui_queue.put( ( messagebox.showwarning, ( "Secim Bulunamadi", "Lutfen once metin secin, sonra F8 ile menuyu acin.", ), ) ) return menu = tk.Menu( root, tearoff=0, bg="#2b2b2b", fg="white", activebackground="#4a4a4a", activeforeground="white", font=("Segoe UI", 10), ) def komut_olustur(k_adi, s_metin): def komut_calistir(): threading.Thread( target=islemi_yap, args=(k_adi, s_metin), daemon=True ).start() return komut_calistir for baslik in ISLEMLER.keys(): menu.add_command(label=baslik, command=komut_olustur(baslik, secili_metin)) menu.add_separator() menu.add_command(label="❌ İptal", command=lambda: None) try: x, y = pyautogui.position() menu.tk_popup(x, y) finally: menu.grab_release() def on_press(key): global kisayol_basildi try: if key == KISAYOL_METIN and not kisayol_basildi: kisayol_basildi = True gui_queue.put((menu_goster, ())) except AttributeError: pass def on_release(key): global kisayol_basildi try: if key == KISAYOL_METIN: kisayol_basildi = False except AttributeError: pass if __name__ == "__main__": print("=" * 60) print("🤖 AI Asistan - Metin İşleme") print("=" * 60) aktif_text_model = get_available_text_model() print(f"📦 Metin İşleme (F8): {aktif_text_model}") print() print("🔧 Kullanım:") print(" F8 - Metin sec ve AI islemleri yap") print() print("⚠️ Programı kapatmak için bu pencereyi kapatın veya Ctrl+C yapın.") print("=" * 60) try: test_response = requests.get("http://localhost:11434/api/tags", timeout=5) if test_response.status_code == 200: print("✅ Ollama bağlantısı başarılı!") else: print("⚠️ Ollama'ya bağlanılamadı, servisi kontrol edin!") except Exception: print("⚠️ Ollama çalışmıyor olabilir! 'ollama serve' ile başlatın.") print() listener = keyboard.Listener(on_press=on_press, on_release=on_release) listener.start() root = tk.Tk() root.withdraw() root.after(100, process_queue) try: root.mainloop() except KeyboardInterrupt: print("Kapatılıyor...")