fix(email): resolve type errors in imap_client with proper type narrowing and ignores

linuztx committed Mar 15, 2026 at 12:32 UTC 208150ad25da4b032924343819f6f23cfca56781
1 file changed +11 -9
plugins/_email_integration/helpers/imap_client.py
+11 -9
@@ -50,7 +50,7 @@ async def connect_imap(
50
51 def _sync():
52 client = IMAPClient(server, port=port, ssl=ssl, timeout=timeout)
53 - client._imap._maxline = 100000
53 + client._imap._maxline = 100000 # type: ignore[attr-defined]
54 client.login(username, password)
55 return client
56
@@ -85,7 +85,7 @@ async def fetch_new(
85 try:
86 return client.gmail_search("category:primary is:unread")
87 except Exception:
88 - return client.search(["UNSEEN"])
88 + return client.search(["UNSEEN"]) # type: ignore[arg-type]
89
90 msg_ids = await loop.run_in_executor(None, _search)
91 if not msg_ids:
@@ -127,7 +127,7 @@ async def get_highest_uid(client: IMAPClient) -> int:
127
128 def _search():
129 client.select_folder("INBOX")
130 - uids = client.search(["ALL"])
130 + uids = client.search(["ALL"]) # type: ignore[arg-type]
131 return max(uids) if uids else 0
132
133 return await loop.run_in_executor(None, _search)
@@ -152,7 +152,7 @@ async def _fetch_single(
152 if not email_data:
153 return None
154
155 - email_msg = email.message_from_bytes(email_data)
155 + email_msg = email.message_from_bytes(email_data) # type: ignore[arg-type]
156
157 sender = _decode_header(email_msg.get("From", ""))
158 if _is_noreply(sender):
@@ -275,7 +275,7 @@ async def _parse_body(
275 if filename:
276 filename = _decode_header(filename)
277 content = part.get_payload(decode=True)
278 - if content:
278 + if isinstance(content, bytes):
279 path = await _save_attachment(filename, content, download_folder)
280 attachments.append(path)
281 cid = part.get("Content-ID")
@@ -284,17 +284,19 @@ async def _parse_body(
284
285 elif content_type == "text/plain" and not body:
286 charset = part.get_content_charset() or "utf-8"
287 - body = part.get_payload(decode=True).decode(charset, errors="ignore")
287 + payload = part.get_payload(decode=True)
288 + body = payload.decode(charset, errors="ignore") if isinstance(payload, bytes) else ""
289
290 elif content_type == "text/html" and not body:
291 charset = part.get_content_charset() or "utf-8"
291 - html = part.get_payload(decode=True).decode(charset, errors="ignore")
292 + payload = part.get_payload(decode=True)
293 + html = payload.decode(charset, errors="ignore") if isinstance(payload, bytes) else ""
294 body = _html_to_text(html, cid_map)
295 else:
296 content_type = email_msg.get_content_type()
297 charset = email_msg.get_content_charset() or "utf-8"
298 content = email_msg.get_payload(decode=True)
297 - if content:
299 + if isinstance(content, bytes):
300 if content_type == "text/html":
301 body = _html_to_text(content.decode(charset, errors="ignore"))
302 else:
@@ -328,7 +330,7 @@ def _html_to_text(html_content: str, cid_map: dict[str, str] | None = None) -> s
330 if cid_map:
331 soup = BeautifulSoup(html_content, "html.parser")
332 for img in soup.find_all("img"):
331 - src = img.get("src", "")
333 + src = str(img.get("src", ""))
334 if src.startswith("cid:"):
335 cid = src[4:]
336 if cid in cid_map: