import os import datetime import mimetypes import time from imap_tools import MailBox, AND from sqlalchemy.orm import Session from database import SessionLocal, Email, Attachment from dotenv import load_dotenv load_dotenv() MAIL_SERVER = os.getenv("MAIL_SERVER") MAIL_USERNAME = os.getenv("MAIL_USERNAME") MAIL_PASSWORD = os.getenv("MAIL_PASSWORD") MAIL_PORT = int(os.getenv("MAIL_PORT", 993)) def fetch_and_store_emails(): """ Connects to Gmail, fetches UNREAD emails, stores them in DB, and marks as READ. """ print(f"[{datetime.datetime.now()}] Checking for new emails...") if not MAIL_USERNAME or not MAIL_PASSWORD or MAIL_USERNAME == "your-email@gmail.com": print("Skipping Email Sync: Credentials not configured.") return {"status": "skipped", "message": "Credentials not configured"} db: Session = SessionLocal() count = 0 try: # Connect to Inbox # imap-tools MailBox defaults to SSL (port 993) if not specified, but we make it explicit here. with MailBox(MAIL_SERVER, port=MAIL_PORT).login(MAIL_USERNAME, MAIL_PASSWORD, initial_folder='INBOX') as mailbox: # Fetch UNREAD emails for msg in mailbox.fetch(AND(seen=False)): print(f"Processing Email: {msg.subject}") # Check if already exists (optional, but good practice if marking read fails) # For now using simplistic approach: if we fetch it, we save it. new_email = Email( subject=msg.subject, sender=msg.from_, body=msg.text or msg.html, received_date=msg.date, is_read=False # Consumed by app, but 'seen' on server ) db.add(new_email) db.commit() # Commit to get ID db.refresh(new_email) # Process Attachments for i, att in enumerate(msg.attachments): filename = att.filename if not filename: # Fallback for missing filename ext = mimetypes.guess_extension(att.content_type) or ".bin" timestamp = int(time.time()) filename = f"attachment_{timestamp}_{i}{ext}" print(f" - Attachment: {filename}") new_attachment = Attachment( email_id=new_email.id, filename=filename, content_type=att.content_type, file_content=att.payload # raw bytes ) db.add(new_attachment) db.commit() # Mark as Read on Server (Auto-done by fetch unless bulk=True or mark_seen=False) # imap-tools fetches are auto-seen by default. count += 1 print(f"Sync Complete. {count} new emails.") return {"status": "success", "count": count} except Exception as e: error_msg = str(e) print(f"Error fetching emails: {error_msg}") if "AUTHENTICATIONFAILED" in error_msg or "Invalid credentials" in error_msg: return { "status": "error", "message": f"Authentication Failed: {error_msg}. \n[HINT]: If using Gmail, you MUST use an 'App Password', not your login password. Also ensure IMAP is enabled in Gmail Settings." } return {"status": "error", "message": error_msg} finally: db.close() if __name__ == "__main__": fetch_and_store_emails()