Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 27 additions & 78 deletions helpdesk/overrides/email_account.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
from email import message_from_string

import frappe
from frappe import _
Expand Down Expand Up @@ -58,89 +57,39 @@ def parent_communication(self):

class CustomEmailAccount(EmailAccount):
def get_inbound_mails(self) -> list[InboundMail]:
"""retrive and return inbound mails."""
"""Return the framework's inbound mails, filtered and re-wrapped.

This used to be a full copy of `EmailAccount.get_inbound_mails`, which meant every
fix the framework made to the fetch loop was silently lost on any site where
helpdesk is installed -- including the per-folder IMAP UID cursor, whose absence
made each poll retrieve only the newest message of every folder. Delegate the
fetching and keep only what is specific to helpdesk: skipping auto-generated
mails, and threading through CustomInboundMail.
"""
mails = []

def process_mail(messages, append_to=None):
for index, message in enumerate(messages.get("latest_messages", [])):
try:
_msg = message_from_string(
message.decode("utf-8", errors="replace")
)

for mail in super().get_inbound_mails():
try:
if mail.mail.get("X-Auto-Generated"):
# Important: If the email is auto-generated, we do not create a ticket
if _msg.get("X-Auto-Generated"):
continue

uid = (
messages["uid_list"][index]
if messages.get("uid_list")
else None
)
seen_status = messages.get("seen_status", {}).get(uid)
if self.email_sync_option != "UNSEEN" or seen_status != "SEEN":
_inbound_mail = CustomInboundMail(
message,
self,
frappe.safe_decode(uid),
seen_status,
append_to,
)
mails.append(_inbound_mail)
except Exception as e:
# Log the error but continue processing other emails
frappe.log_error(
title=_(
"Error processing email at index {0}, message: {1}"
).format(index, e),
message=frappe.get_traceback(),
)
self.handle_bad_emails(index, message, frappe.get_traceback())
continue

if not self.enable_incoming:
return []

try:
if self.service == "Frappe Mail":
frappe_mail_client = self.get_frappe_mail_client()
messages = frappe_mail_client.pull_raw(
last_received_at=self.last_synced_at
)
process_mail(messages)
self.db_set(
"last_synced_at",
messages["last_received_at"],
update_modified=False,
mails.append(
CustomInboundMail(
mail.raw_message,
self,
mail.uid,
mail.seen_status,
mail.append_to,
)
)
else:
email_sync_rule = self.build_email_sync_rule()
email_server = self.get_incoming_server(
in_receive=True, email_sync_rule=email_sync_rule
except Exception as e:
# Log the error but continue processing other emails
frappe.log_error(
title=_("Error processing email {0}, message: {1}").format(mail.uid, e),
message=frappe.get_traceback(),
)
if self.use_imap:
# process all given imap folder
for folder in self.imap_folder:
if email_server.select_imap_folder(folder.folder_name):
email_server.settings["uid_validity"] = folder.uidvalidity
messages = (
email_server.get_messages(
folder=f'"{folder.folder_name}"'
)
or {}
)
process_mail(messages, folder.append_to)
else:
# process the pop3 account
messages = email_server.get_messages() or {}
process_mail(messages)

# close connection to mailserver
email_server.logout()
except Exception:
self.log_error(
title=_("Error while connecting to email account {0}").format(self.name)
)
return []
self.handle_bad_emails(mail.uid, mail.raw_message, frappe.get_traceback())
continue

return mails