antigravity-hackathon-backend / services /gmail_sync_service.py
anique-1's picture
fix: add 3s timeout to IMAP connection to prevent sync hanging
816a13c
Raw
History Blame Contribute Delete
4.24 kB
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()