import logging import re try: import pyautogui PYAUTOGUI_AVAILABLE = True except ImportError: PYAUTOGUI_AVAILABLE = False from backend.connectors.gmail_mcp_connector import watch_for_otp_email # Matches strings like "code sent to", "verification code", "check your email" OTP_PROMPT_PATTERN = re.compile(r"(code sent to|check your email|verification code|one-time password)", re.IGNORECASE) async def autofill_otp_field(otp_code: str, current_site: str = "unknown"): """ Simulates keyboard input to type the OTP code. If the field is already active, this will fill it. """ if not PYAUTOGUI_AVAILABLE: logging.error("pyautogui not available. Cannot dispatch text input.") # Fallback to voice notification from core.voice import speak speak(f"I have your verification code. It is: {otp_code}. I couldn't autofill it because I lack UI automation permissions.") return logging.info(f"Autofilling OTP code: {otp_code}") # We use pyautogui to type out the code directly into the focused field pyautogui.write(otp_code, interval=0.05) # REQUIRED FIX: add a confirmation step (visual) before auto-pressing enter import ctypes result = ctypes.windll.user32.MessageBoxW(0, f"JARVIS has typed the OTP code {otp_code} for {current_site}.\n\nAuto-submit (press Enter)?", "JARVIS OTP Autofill", 4) if result == 6: # IDYES # Optionally press Enter to submit pyautogui.press('enter') from core.voice import speak speak("Verification code successfully entered.") # REQUIRED FIX: Write to a dedicated otp_autofill_log SQLite table try: import sqlite3 from backend.services.usb_monitor import get_db_path with sqlite3.connect(get_db_path()) as conn: conn.execute("CREATE TABLE IF NOT EXISTS otp_autofill_log (id INTEGER PRIMARY KEY AUTOINCREMENT, site TEXT, code TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)") conn.execute("INSERT INTO otp_autofill_log (site, code) VALUES (?, ?)", (current_site, otp_code)) conn.commit() except Exception as e: logging.error(f"Failed to log OTP autofill: {e}") else: logging.info("User rejected OTP auto-submit.") from core.voice import speak speak("Auto-submit canceled.") async def on_screen_text_detected(text: str, persona: str): """ Hook intended to be called by the 24/7 screen engine's OCR output. """ if not text: return if OTP_PROMPT_PATTERN.search(text): logging.info(f"[{persona.upper()}] Detected OTP prompt on screen.") from core.voice import speak speak("I see a verification prompt. I'm monitoring your inbox for the code now.") # Try to infer the site from the text context (naive implementation) current_site = "unknown" if "github" in text.lower(): current_site = "github.com" elif "riot" in text.lower() or "valorant" in text.lower(): current_site = "riotgames.com" # Kick off the OTP watcher otp = await watch_for_otp_email(timeout_seconds=60, current_site=current_site) if otp: await autofill_otp_field(otp, current_site=current_site) else: speak("Couldn't find the verification code in time. Please enter it manually.")