import imaplib import email from email.header import decode_header import re import logging from config import settings from services.qwen_service import qwen_service import json logger = logging.getLogger(__name__) class GmailSyncService: def __init__(self): pass async def fetch_latest_meezan_transaction(self, credentials: dict = None): try: # Use dynamic credentials if provided, else fallback to settings gmail_user = credentials.get("gmail") if credentials else settings.SMTP_USER gmail_pass = credentials.get("app_password") if credentials else settings.SMTP_PASS if not gmail_user or not gmail_pass: logger.error("Missing Gmail credentials for sync.") return None # Connect to Gmail IMAP with 3-second timeout mail = imaplib.IMAP4_SSL("imap.gmail.com", timeout=3) mail.login(gmail_user, gmail_pass) mail.select("INBOX", readonly=True) logger.info(f"Successfully logged into {gmail_user} for sync.") # Sniper Search: Specifically look for the Meezan subject line search_query = '(OR SUBJECT "Credit Transaction Alert" SUBJECT "Debit Transaction Alert")' status, messages = mail.search(None, search_query) logger.info(f"Sniper Search Status: {status}, IDs Found: {messages}") if status != "OK" or not messages[0] or messages[0] == b'': logger.info("Specific subject search failed. Trying generic Meezan search...") status, messages = mail.search(None, 'FROM "meezanbank.com"') if status != "OK" or not messages[0] or messages[0] == b'': logger.info("No Meezan transaction alerts found.") return None # Get the IDs and take the newest one email_ids = messages[0].split() latest_id = email_ids[-1] status, data = mail.fetch(latest_id, "(RFC822)") raw_email = data[0][1] msg = email.message_from_bytes(raw_email) logger.info(f">>> FETCHED TARGET EMAIL: Subject='{msg.get('Subject')}'") # Extract body target_body = None if msg.is_multipart(): for part in msg.walk(): if part.get_content_type() == "text/plain": target_body = part.get_payload(decode=True).decode() break else: target_body = msg.get_payload(decode=True).decode() mail.logout() if not target_body: return None # Use Qwen to parse the email and check for fraud return await self.analyze_with_ai(target_body) except Exception as e: logger.error(f"Gmail Sync Error: {str(e)}") return None async def analyze_with_ai(self, email_body: str): prompt = f""" You are a NadraGuard Fraud Detection Agent. Analyze the following bank transaction email body and extract the details in JSON format. Also, provide a 'fraud_verdict' (SAFE or SUSPICIOUS) and a short 'reason'. EMAIL BODY: {email_body} JSON STRUCTURE: {{ "title": "Short title like 'Transfer to X'", "amount": float (negative if debit), "bank": "Meezan Bank", "date": "Extracted date", "fraud_verdict": "SAFE/SUSPICIOUS", "reason": "Why it is safe or suspicious" }} """ res = await qwen_service.generate_response(prompt, temperature=0.3) if not res["success"]: logger.error(f"AI response failed: {res.get('error')}") return None try: content = res.get("content", "") json_text = content.replace('```json', '').replace('```', '').strip() data = json.loads(json_text) return data except Exception as e: logger.error(f"Failed to parse AI response: {e}") return None gmail_sync_service = GmailSyncService()