Mercurial > libervia-backend
changeset 4398:7ef21e3e5ac9
component email (imap): fetch all recent emails instead of only the last one.
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 11 Sep 2025 21:10:35 +0200 |
parents | 6e3a0ef1c561 |
children | fe09446a09ce |
files | libervia/backend/plugins/plugin_comp_email_gateway/imap.py |
diffstat | 1 files changed, 52 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_comp_email_gateway/imap.py Sat Sep 06 16:31:24 2025 +0200 +++ b/libervia/backend/plugins/plugin_comp_email_gateway/imap.py Thu Sep 11 21:10:35 2025 +0200 @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +from collections.abc import Awaitable from email.message import EmailMessage from email.parser import BytesParser, Parser from email import policy @@ -84,6 +85,7 @@ return self._idling = True self._idle_timer = reactor.callLater(29 * 60, self.on_idle_timeout) + log.debug("Starting IDLE mode.") await self.sendCommand( imap4.Command( b"IDLE", @@ -101,7 +103,7 @@ # Send DONE command to exit IDLE mode. self.sendLine(b"DONE") self._idling = False - log.debug("IDLE mode terminated") + log.debug("IDLE mode terminated.") def on_idle_timeout(self): """Called when IDLE mode timeout is reached.""" @@ -126,34 +128,53 @@ @param recent: Number of recent messages. """ log.debug(f"New messages: {exists}, Recent messages: {recent}") - log.debug("Retrieving last message.") + + if recent is None: + log.debug("No recent messages, skipping fetch.") + defer.ensureDeferred(self.idle()) + return + self.idle_exit() - mess_data = await self.fetchMessage("*") - for message in mess_data.values(): - try: - content = message["RFC822"] - except KeyError: - log.warning(f"Can't find content for {message}.") - continue - else: - if isinstance(content, str): - parser = Parser(policy=policy.default) - parser_method = parser.parsestr - elif isinstance(content, bytes): - parser = BytesParser(policy=policy.default) - parser_method = parser.parsebytes - else: - log.error(f"Invalid content: {content}") - continue + + # We retrieve recent messages. + recent_uids = await self.search(imap4.Query(recent=True), uid=True) + message_set = imap4.MessageSet() + for recent_uid in recent_uids: + message_set.add(recent_uid) + + try: + mess_data = await self.fetchMessage(message_set, uid=True) + + # Process all fetched messages. + log.debug(f"Fetched {len(mess_data)} messages.") + for message in mess_data.values(): try: - parsed = parser_method(content) - except Exception as e: - log.warning(f"Can't parse content of email: {e}") + content = message["RFC822"] + except KeyError: + log.warning(f"Can't find content for {message}.") continue else: - assert self.factory is not None - factory = cast(IMAPClientFactory, self.factory) - await factory.on_new_email(parsed) + if isinstance(content, str): + parser = Parser(policy=policy.default) + parser_method = parser.parsestr + elif isinstance(content, bytes): + parser = BytesParser(policy=policy.default) + parser_method = parser.parsebytes + else: + log.error(f"Invalid content: {content}.") + continue + try: + parsed = parser_method(content) + except Exception as e: + log.warning(f"Can't parse content of email: {e}.") + continue + else: + assert self.factory is not None + factory = cast(IMAPClientFactory, self.factory) + await factory.on_new_email(parsed) + + except Exception as e: + log.error(f"Error fetching recent messages: {e}") defer.ensureDeferred(self.idle()) @@ -215,13 +236,16 @@ def __init__( self, user_data: UserData, - on_new_email: Callable[[EmailMessage], None], + on_new_email: Callable[[EmailMessage], Awaitable[None]], connected: defer.Deferred, ) -> None: """Initialize the IMAP client factory. - @param username: The username to use for authentication. - @param password: The password to use for authentication. + @param user_data: User data containing credentials and other user-specific + information. + @param on_new_email: Called when a new email is received. + @param connected: Deferred that will be fired when the IMAP connection is + established. """ credentials = user_data.credentials self.user_data = user_data