feat: Increase max email fetch results and enhance email body extraction
This commit is contained in:
+1
-1
@@ -364,7 +364,7 @@ def _sync_emails_once(cfg: dict) -> int:
|
|||||||
days_back = max(1, delta_days)
|
days_back = max(1, delta_days)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
max_results = 5
|
max_results = 100
|
||||||
client = ZohoClient(
|
client = ZohoClient(
|
||||||
email=cfg.get("zoho_email") or account_email,
|
email=cfg.get("zoho_email") or account_email,
|
||||||
app_password=cfg.get("zoho_app_password"),
|
app_password=cfg.get("zoho_app_password"),
|
||||||
|
|||||||
+54
-11
@@ -133,7 +133,6 @@ class ZohoClient:
|
|||||||
date_header = email_message.get("Date", "")
|
date_header = email_message.get("Date", "")
|
||||||
email_date = parse_email_date_safely(date_header)
|
email_date = parse_email_date_safely(date_header)
|
||||||
|
|
||||||
|
|
||||||
# Ensure both dates are timezone-aware for comparison
|
# Ensure both dates are timezone-aware for comparison
|
||||||
if email_date and latest_date:
|
if email_date and latest_date:
|
||||||
print(f"📅 Email date: {email_date} Latest: {latest_date}")
|
print(f"📅 Email date: {email_date} Latest: {latest_date}")
|
||||||
@@ -142,11 +141,19 @@ class ZohoClient:
|
|||||||
latest_date = latest_date.replace(tzinfo=timezone.utc)
|
latest_date = latest_date.replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
if (email_date > latest_date) or first_time:
|
if (email_date > latest_date) or first_time:
|
||||||
# Extract headers
|
# Extract headers
|
||||||
print(f"📅 Email date: {email_date} Latest: {latest_date}")
|
print(
|
||||||
subject = self._decode_header(email_message.get("Subject", ""))
|
f"📅 Email date: {email_date} Latest: {latest_date}"
|
||||||
from_header = self._decode_header(email_message.get("From", ""))
|
)
|
||||||
to_header = self._decode_header(email_message.get("To", ""))
|
subject = self._decode_header(
|
||||||
|
email_message.get("Subject", "")
|
||||||
|
)
|
||||||
|
from_header = self._decode_header(
|
||||||
|
email_message.get("From", "")
|
||||||
|
)
|
||||||
|
to_header = self._decode_header(
|
||||||
|
email_message.get("To", "")
|
||||||
|
)
|
||||||
|
|
||||||
message_id = email_message.get("Message-ID", "")
|
message_id = email_message.get("Message-ID", "")
|
||||||
in_reply_to = email_message.get("In-Reply-To", "")
|
in_reply_to = email_message.get("In-Reply-To", "")
|
||||||
@@ -157,7 +164,6 @@ class ZohoClient:
|
|||||||
# Get email body snippet
|
# Get email body snippet
|
||||||
body = self._get_email_body(email_message)
|
body = self._get_email_body(email_message)
|
||||||
|
|
||||||
|
|
||||||
email_data = {
|
email_data = {
|
||||||
"id": num.decode(),
|
"id": num.decode(),
|
||||||
"threadId": thread_id,
|
"threadId": thread_id,
|
||||||
@@ -227,18 +233,22 @@ class ZohoClient:
|
|||||||
return str(header_value)
|
return str(header_value)
|
||||||
|
|
||||||
def _get_email_body(self, email_message) -> str:
|
def _get_email_body(self, email_message) -> str:
|
||||||
"""Extract email body text"""
|
"""Extract email body text - get only the main content, not quoted replies"""
|
||||||
body = ""
|
body = ""
|
||||||
|
|
||||||
if email_message.is_multipart():
|
if email_message.is_multipart():
|
||||||
|
# Get only the first text/plain part (main content)
|
||||||
for part in email_message.walk():
|
for part in email_message.walk():
|
||||||
if part.get_content_type() == "text/plain":
|
if part.get_content_type() == "text/plain" and not part.get_filename():
|
||||||
try:
|
try:
|
||||||
body += part.get_payload(decode=True).decode(
|
content = part.get_payload(decode=True).decode(
|
||||||
"utf-8", errors="ignore"
|
"utf-8", errors="ignore"
|
||||||
)
|
)
|
||||||
|
# Take only the first text part we find
|
||||||
|
body = content
|
||||||
|
break # Stop after first text/plain part
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
continue
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
body = email_message.get_payload(decode=True).decode(
|
body = email_message.get_payload(decode=True).decode(
|
||||||
@@ -247,8 +257,41 @@ class ZohoClient:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Optional: Clean up the body by removing quoted content
|
||||||
|
body = self._clean_email_body(body)
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
def _clean_email_body(self, body: str) -> str:
|
||||||
|
"""Clean email body by removing quoted content and signatures"""
|
||||||
|
if not body:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
lines = body.split("\n")
|
||||||
|
cleaned_lines = []
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Stop at common quote indicators
|
||||||
|
if (
|
||||||
|
line.startswith("---- On ")
|
||||||
|
or line.startswith("On ")
|
||||||
|
and "wrote:" in line
|
||||||
|
or line.startswith("From:")
|
||||||
|
or line.startswith("> ")
|
||||||
|
or line.startswith("-----Original Message-----")
|
||||||
|
or line.startswith("---------- Forwarded message ---------")
|
||||||
|
):
|
||||||
|
break
|
||||||
|
|
||||||
|
cleaned_lines.append(line)
|
||||||
|
|
||||||
|
# Remove trailing empty lines
|
||||||
|
while cleaned_lines and not cleaned_lines[-1]:
|
||||||
|
cleaned_lines.pop()
|
||||||
|
|
||||||
|
return "\n".join(cleaned_lines)
|
||||||
|
|
||||||
def get_thread_messages(self, thread_id: str) -> List[Dict[str, Any]]:
|
def get_thread_messages(self, thread_id: str) -> List[Dict[str, Any]]:
|
||||||
"""Get all messages in a thread (simplified for IMAP)"""
|
"""Get all messages in a thread (simplified for IMAP)"""
|
||||||
# For IMAP, we'll return a single message since thread grouping is more complex
|
# For IMAP, we'll return a single message since thread grouping is more complex
|
||||||
|
|||||||
+34
-4
@@ -61,7 +61,17 @@ th { background: #0f152a; color: var(--muted); text-align: left; position: stick
|
|||||||
tbody tr:hover { background: #0e1426; }
|
tbody tr:hover { background: #0e1426; }
|
||||||
|
|
||||||
pre, code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
pre, code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
||||||
pre { background: #0b1121; padding: 0.75rem; border-radius: 8px; white-space: pre-wrap; border: 1px solid var(--border); }
|
pre {
|
||||||
|
background: #0b1121;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/* Chat-style messages */
|
/* Chat-style messages */
|
||||||
.messages { display: flex; flex-direction: column; gap: 0.75rem; }
|
.messages { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||||
@@ -72,12 +82,25 @@ pre { background: #0b1121; padding: 0.75rem; border-radius: 8px; white-space: pr
|
|||||||
max-width: 800px; width: fit-content;
|
max-width: 800px; width: fit-content;
|
||||||
background: #0f152a; border: 1px solid var(--border); border-radius: 12px;
|
background: #0f152a; border: 1px solid var(--border); border-radius: 12px;
|
||||||
padding: 0.75rem 0.9rem; box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
padding: 0.75rem 0.9rem; box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
.incoming .msg-bubble { background: #0f152a; }
|
.incoming .msg-bubble { background: #0f152a; }
|
||||||
.outgoing .msg-bubble { background: var(--brand-weak); border-color: #345fb0; }
|
.outgoing .msg-bubble { background: var(--brand-weak); border-color: #345fb0; }
|
||||||
.msg-meta { font-size: 0.78rem; color: var(--muted); margin-bottom: 0.35rem; }
|
.msg-meta {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
.msg-subject { font-size: 0.9rem; margin-bottom: 0.25rem; color: var(--text); }
|
.msg-subject { font-size: 0.9rem; margin-bottom: 0.25rem; color: var(--text); }
|
||||||
.msg-body { font-size: 0.92rem; }
|
.msg-body {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
.row { display: flex; gap: 1rem; flex-wrap: wrap; }
|
.row { display: flex; gap: 1rem; flex-wrap: wrap; }
|
||||||
.col { flex: 1 1 360px; }
|
.col { flex: 1 1 360px; }
|
||||||
@@ -87,7 +110,14 @@ a { color: var(--brand); }
|
|||||||
a:hover { text-decoration: underline; }
|
a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
/* Small helpers */
|
/* Small helpers */
|
||||||
.pill { padding: 0.15rem 0.5rem; border-radius: 999px; border: 1px solid var(--border); }
|
.pill {
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
.right { text-align: right; }
|
.right { text-align: right; }
|
||||||
.mt-1 { margin-top: 0.5rem; } .mt-2 { margin-top: 1rem; }
|
.mt-1 { margin-top: 0.5rem; } .mt-2 { margin-top: 1rem; }
|
||||||
.mb-1 { margin-bottom: 0.5rem; } .mb-2 { margin-bottom: 1rem; }
|
.mb-1 { margin-bottom: 0.5rem; } .mb-2 { margin-bottom: 1rem; }
|
||||||
|
|||||||
Reference in New Issue
Block a user