| 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: |
| |
| 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 |
|
|
| |
| 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.") |
|
|
| |
| 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 |
|
|
| |
| 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')}'") |
|
|
| |
| 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 |
|
|
| |
| 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() |
|
|