forked from SpeedyFoxAi/jarvis-memory
145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Backfill emails to Qdrant for a specific user.
|
||
|
|
One-time use to populate memories from existing emails.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import imaplib
|
||
|
|
import email
|
||
|
|
from email.policy import default
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
import subprocess
|
||
|
|
|
||
|
|
# Authorized senders with their user IDs
|
||
|
|
# Add your authorized emails here
|
||
|
|
AUTHORIZED_SENDERS = {
|
||
|
|
# "your_email@gmail.com": "yourname",
|
||
|
|
# "spouse_email@gmail.com": "spousename"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Gmail IMAP settings
|
||
|
|
IMAP_SERVER = "imap.gmail.com"
|
||
|
|
IMAP_PORT = 993
|
||
|
|
|
||
|
|
# Load credentials
|
||
|
|
CRED_FILE = "/root/.openclaw/workspace/.gmail_imap.json"
|
||
|
|
|
||
|
|
def load_credentials():
|
||
|
|
try:
|
||
|
|
with open(CRED_FILE, 'r') as f:
|
||
|
|
return json.load(f)
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Error loading credentials: {e}")
|
||
|
|
return None
|
||
|
|
|
||
|
|
def store_email_memory(user_id, sender, subject, body, date):
|
||
|
|
"""Store email to Qdrant as memory for the user."""
|
||
|
|
try:
|
||
|
|
# Format as conversation-like entry
|
||
|
|
email_text = f"[EMAIL from {sender}]\nSubject: {subject}\nDate: {date}\n\n{body}"
|
||
|
|
|
||
|
|
# Store using auto_store.py (waits for completion)
|
||
|
|
script_path = "/root/.openclaw/workspace/skills/qdrant-memory/scripts/auto_store.py"
|
||
|
|
result = subprocess.run([
|
||
|
|
"python3", script_path,
|
||
|
|
f"[Email] {subject}",
|
||
|
|
email_text,
|
||
|
|
"--user-id", user_id
|
||
|
|
], capture_output=True, text=True, timeout=30)
|
||
|
|
|
||
|
|
if result.returncode == 0:
|
||
|
|
print(f" ✓ Stored: {subject[:50]}")
|
||
|
|
else:
|
||
|
|
print(f" ✗ Failed: {subject[:50]}")
|
||
|
|
except Exception as e:
|
||
|
|
print(f" ✗ Error: {e}")
|
||
|
|
|
||
|
|
def backfill(user_id=None, limit=20):
|
||
|
|
"""Backfill emails for specific user or all authorized senders."""
|
||
|
|
creds = load_credentials()
|
||
|
|
if not creds:
|
||
|
|
return
|
||
|
|
|
||
|
|
email_addr = creds.get("email")
|
||
|
|
app_password = creds.get("app_password")
|
||
|
|
|
||
|
|
if not email_addr or not app_password:
|
||
|
|
return
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Connect to IMAP
|
||
|
|
mail = imaplib.IMAP4_SSL(IMAP_SERVER, IMAP_PORT)
|
||
|
|
mail.login(email_addr, app_password)
|
||
|
|
mail.select("inbox")
|
||
|
|
|
||
|
|
# Get ALL emails
|
||
|
|
status, messages = mail.search(None, "ALL")
|
||
|
|
|
||
|
|
if status != "OK" or not messages[0]:
|
||
|
|
print("No emails found.")
|
||
|
|
mail.logout()
|
||
|
|
return
|
||
|
|
|
||
|
|
email_ids = messages[0].split()
|
||
|
|
print(f"Found {len(email_ids)} total emails")
|
||
|
|
|
||
|
|
# Filter by user if specified
|
||
|
|
target_emails = []
|
||
|
|
if user_id:
|
||
|
|
# Find email address for this user
|
||
|
|
for auth_email, uid in AUTHORIZED_SENDERS.items():
|
||
|
|
if uid == user_id:
|
||
|
|
target_emails.append(auth_email.lower())
|
||
|
|
else:
|
||
|
|
target_emails = [e.lower() for e in AUTHORIZED_SENDERS.keys()]
|
||
|
|
|
||
|
|
# Process emails
|
||
|
|
stored_count = 0
|
||
|
|
for eid in email_ids[-limit:]:
|
||
|
|
status, msg_data = mail.fetch(eid, "(RFC822)")
|
||
|
|
if status != "OK":
|
||
|
|
continue
|
||
|
|
|
||
|
|
msg = email.message_from_bytes(msg_data[0][1], policy=default)
|
||
|
|
sender = msg.get("From", "").lower()
|
||
|
|
subject = msg.get("Subject", "")
|
||
|
|
date = msg.get("Date", "")
|
||
|
|
|
||
|
|
# Extract body
|
||
|
|
body = ""
|
||
|
|
if msg.is_multipart():
|
||
|
|
for part in msg.walk():
|
||
|
|
if part.get_content_type() == "text/plain":
|
||
|
|
body = part.get_content()
|
||
|
|
break
|
||
|
|
else:
|
||
|
|
body = msg.get_content()
|
||
|
|
body = body.strip()[:2000] if body else ""
|
||
|
|
|
||
|
|
# Check if from target sender
|
||
|
|
for auth_email, uid in AUTHORIZED_SENDERS.items():
|
||
|
|
if auth_email.lower() in sender:
|
||
|
|
if user_id and uid != user_id:
|
||
|
|
continue
|
||
|
|
print(f"\nStoring for {uid}:")
|
||
|
|
store_email_memory(uid, sender, subject, body, date)
|
||
|
|
stored_count += 1
|
||
|
|
break
|
||
|
|
|
||
|
|
print(f"\nDone! Stored {stored_count} emails to Qdrant.")
|
||
|
|
|
||
|
|
mail.close()
|
||
|
|
mail.logout()
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Error: {e}")
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
import argparse
|
||
|
|
parser = argparse.ArgumentParser(description="Backfill emails to Qdrant")
|
||
|
|
parser.add_argument("--user-id", help="Specific user to backfill (rob or jennifer)")
|
||
|
|
parser.add_argument("--limit", type=int, default=20, help="Max emails to process")
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
backfill(user_id=args.user_id, limit=args.limit)
|