From e3496387009c5a6bafafe05e5caba20ca4740a58 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Mon, 29 Jun 2026 17:09:08 -0400 Subject: [PATCH] fix: fetch() selects message body by structure, not length (v0.1.3) select the literal payload by isinstance bytearray instead of len>20. aioimaplib stores the message body as the only bytearray in the response; every other line (including the ' FETCH (...' header) is plain bytes. the length heuristic matched the header line first for any 2+ digit message id or BODY[]/UID fetch, returning a blank Message and silently breaking OTP retrieval on real mailboxes. Signed-off-by: disqualifier --- README.md | 8 ++++---- pyproject.toml | 2 +- src/aiomail/__init__.py | 2 +- src/aiomail/client.py | 6 +++++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5542ec7..48bf2fd 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,16 @@ This reads codes from email; it does not generate them (that is `pyotp`'s job). `requirements.txt`: ``` -aiomail @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.2 +aiomail @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.3 # OAuth token providers (Microsoft / Google) need the extra: -aiomail[oauth] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.2 +aiomail[oauth] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.3 ``` Direct: ```bash -pip install "aiomail @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.2" -pip install "aiomail[oauth] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.2" +pip install "aiomail @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.3" +pip install "aiomail[oauth] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiomail.git@v0.1.3" ``` Requires `aioimaplib` and `beautifulsoup4` (pulled transitively). The `oauth` diff --git a/pyproject.toml b/pyproject.toml index bcf2687..504d55d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "aiomail" -version = "0.1.2" +version = "0.1.3" description = "async IMAP one-time-code retrieval with password/OAuth2 auth and dynamic matching" requires-python = ">=3.10" dependencies = [ diff --git a/src/aiomail/__init__.py b/src/aiomail/__init__.py index d745155..32ade03 100644 --- a/src/aiomail/__init__.py +++ b/src/aiomail/__init__.py @@ -29,4 +29,4 @@ __all__ = [ "DEFAULT_FOLDERS", ] -__version__ = "0.1.2" +__version__ = "0.1.3" diff --git a/src/aiomail/client.py b/src/aiomail/client.py index 995347b..428cdae 100644 --- a/src/aiomail/client.py +++ b/src/aiomail/client.py @@ -169,7 +169,11 @@ class IMAPClient: if result != "OK" or not data: return None for item in data: - if isinstance(item, (bytes, bytearray)) and len(item) > 20: + # aioimaplib stores the literal message payload as the only bytearray in + # the response; every other line (including the ` FETCH (...` header) + # is plain bytes. select by structure, not length — a length heuristic + # mismatches the header line for any 2+ digit id or a BODY[]/UID fetch. + if isinstance(item, bytearray): return email.message_from_bytes(bytes(item)) if isinstance(item, tuple) and len(item) > 1: return email.message_from_bytes(item[1])