-
-
Notifications
You must be signed in to change notification settings - Fork 37.5k
Fix imap_email_content unknown status and replaying stale states #89563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,9 +95,25 @@ def __init__(self, user, password, server, port, folder, verify_ssl): | |
| self._folder = folder | ||
| self._verify_ssl = verify_ssl | ||
| self._last_id = None | ||
| self._last_message = None | ||
| self._unread_ids = deque([]) | ||
| self.connection = None | ||
|
|
||
| @property | ||
| def last_id(self) -> int | None: | ||
| """Return last email uid that was processed.""" | ||
| return self._last_id | ||
|
|
||
| @property | ||
| def last_unread_id(self) -> int | None: | ||
| """Return last email uid received.""" | ||
| # We assume the last id in the list is the last unread id | ||
| # We cannot know if that is the newest one, because it could arrive later | ||
| # https://stackoverflow.com/questions/12409862/python-imap-the-order-of-uids | ||
| if self._unread_ids: | ||
| return int(self._unread_ids[-1]) | ||
| return self._last_id | ||
|
|
||
| def connect(self): | ||
| """Login and setup the connection.""" | ||
| ssl_context = client_context() if self._verify_ssl else None | ||
|
|
@@ -128,21 +144,21 @@ def read_next(self): | |
| try: | ||
| self.connection.select(self._folder, readonly=True) | ||
|
|
||
| if not self._unread_ids: | ||
| search = f"SINCE {datetime.date.today():%d-%b-%Y}" | ||
| if self._last_id is not None: | ||
| search = f"UID {self._last_id}:*" | ||
|
|
||
| _, data = self.connection.uid("search", None, search) | ||
| self._unread_ids = deque(data[0].split()) | ||
| if self._last_id is None: | ||
| # search for today and yesterday | ||
| time_from = datetime.datetime.now() - datetime.timedelta(days=1) | ||
| search = f"SINCE {time_from:%d-%b-%Y}" | ||
| else: | ||
| search = f"UID {self._last_id}:*" | ||
|
|
||
| _, data = self.connection.uid("search", None, search) | ||
| self._unread_ids = deque(data[0].split()) | ||
| while self._unread_ids: | ||
| message_uid = self._unread_ids.popleft() | ||
| if self._last_id is None or int(message_uid) > self._last_id: | ||
| self._last_id = int(message_uid) | ||
| return self._fetch_message(message_uid) | ||
|
|
||
| return self._fetch_message(str(self._last_id)) | ||
| self._last_message = self._fetch_message(message_uid) | ||
| return self._last_message | ||
|
|
||
| except imaplib.IMAP4.error: | ||
| _LOGGER.info("Connection to %s lost, attempting to reconnect", self._server) | ||
|
|
@@ -254,22 +270,30 @@ def get_msg_text(email_message): | |
| def update(self) -> None: | ||
| """Read emails and publish state change.""" | ||
| email_message = self._email_reader.read_next() | ||
|
|
||
| if email_message is None: | ||
| self._message = None | ||
| self._state_attributes = {} | ||
| return | ||
|
|
||
| if self.sender_allowed(email_message): | ||
| message = EmailContentSensor.get_msg_subject(email_message) | ||
|
|
||
| if self._value_template is not None: | ||
| message = self.render_template(email_message) | ||
|
|
||
| self._message = message | ||
| self._state_attributes = { | ||
| ATTR_FROM: EmailContentSensor.get_msg_sender(email_message), | ||
| ATTR_SUBJECT: EmailContentSensor.get_msg_subject(email_message), | ||
| ATTR_DATE: email_message["Date"], | ||
| ATTR_BODY: EmailContentSensor.get_msg_text(email_message), | ||
| } | ||
| while ( | ||
| self._last_id is None or self._last_id != self._email_reader.last_unread_id | ||
| ): | ||
| if email_message is None: | ||
| self._message = None | ||
| self._state_attributes = {} | ||
| return | ||
|
|
||
| self._last_id = self._email_reader.last_id | ||
|
|
||
| if self.sender_allowed(email_message): | ||
| message = EmailContentSensor.get_msg_subject(email_message) | ||
|
|
||
| if self._value_template is not None: | ||
| message = self.render_template(email_message) | ||
|
|
||
| self._message = message | ||
| self._state_attributes = { | ||
| ATTR_FROM: EmailContentSensor.get_msg_sender(email_message), | ||
| ATTR_SUBJECT: EmailContentSensor.get_msg_subject(email_message), | ||
| ATTR_DATE: email_message["Date"], | ||
| ATTR_BODY: EmailContentSensor.get_msg_text(email_message), | ||
| } | ||
|
|
||
| if self._last_id == self._email_reader.last_unread_id: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How can this conditional ever be false?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have a look at that later today.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense now that I'm back at my desktop. Apparently I just needed a slightly larger monitor to see that 😉 |
||
| break | ||
| email_message = self._email_reader.read_next() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like this should only happen if we are going to break out of the loop instead of processing it and than throwing it away when we hit the next one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not know if an unseen mail has an allowed sender unless we process it. The current code base uses a custom
EmailReaderclass as backend.The code in
devneeds several update iterations before it reaches the last one.The improvement here is to iterate the messages until we reach the last one in a single update cycle.