Ver Fonte

Unify send_image on _materialise_image; rename the shared sender

send_image hand-rolled its own mkstemp/download/unlink dance with a
second downloader (_download_to_file: uncapped urllib, whole body in
RAM) next to _materialise_image's capped httpx path. It now resolves
through _materialise_image like send_multiple_images, so one download
mechanism with one size cap serves both; the link fallback semantics
are unchanged (materialise failure or failed post -> link; upload
failure is already answered by the text notice).

With its last caller gone, _download_to_file and its ssl/urllib
imports are deleted. _send_local_attachment renames to
_send_local_file_as_attachment — a local file becomes an attachment,
which is what it does.
Paul Klumpp há 1 semana atrás
pai
commit
f870333565
1 ficheiros alterados com 25 adições e 61 exclusões
  1. 25 61
      adapter.py

+ 25 - 61
adapter.py

@@ -28,8 +28,6 @@ import hashlib
 import logging
 import mimetypes
 import os
-import ssl
-import urllib.request as _urllib_request
 from datetime import UTC, datetime
 from enum import StrEnum
 from typing import Any, cast
@@ -216,17 +214,6 @@ def _write_file_bytes(path: str, data: bytes) -> None:
         f.write(data)
 
 
-def _download_to_file(url: str, path: str, timeout: float) -> None:
-    """Download ``url`` to ``path`` synchronously (run via asyncio.to_thread)."""
-    req = _urllib_request.Request(url, headers={"User-Agent": "Hermes/1.0"})
-    try:
-        ctx = ssl.create_default_context()
-    except Exception:
-        ctx = None
-    with _urllib_request.urlopen(req, timeout=timeout, context=ctx) as resp:
-        _write_file_bytes(path, resp.read())
-
-
 # --------------------------------------------------------------------------- #
 # Adapter
 # --------------------------------------------------------------------------- #
@@ -2270,7 +2257,7 @@ class ChattoAdapter(BasePlatformAdapter):
         except Exception as e:
             return SendResult(success=False, error=str(e), retryable=False)
 
-    async def _send_local_attachment(
+    async def _send_local_file_as_attachment(
         self,
         chat_id: str,
         file_path: str,
@@ -2338,7 +2325,7 @@ class ChattoAdapter(BasePlatformAdapter):
 
         BasePlatformAdapter override
         """
-        return await self._send_local_attachment(
+        return await self._send_local_file_as_attachment(
             chat_id,
             image_path,
             caption,
@@ -2362,11 +2349,11 @@ class ChattoAdapter(BasePlatformAdapter):
         ``file_name`` exists in the base-class signature and is accepted for
         compatibility, but Chatto takes the recipient-visible filename from
         the upload session (derived from the local path); failures are logged
-        and noticed by ``_send_local_attachment`` itself.
+        and noticed by ``_send_local_file_as_attachment`` itself.
 
         BasePlatformAdapter override
         """
-        return await self._send_local_attachment(
+        return await self._send_local_file_as_attachment(
             chat_id,
             file_path,
             caption,
@@ -2390,7 +2377,7 @@ class ChattoAdapter(BasePlatformAdapter):
 
         BasePlatformAdapter override
         """
-        return await self._send_local_attachment(
+        return await self._send_local_file_as_attachment(
             chat_id,
             video_path,
             caption,
@@ -2415,7 +2402,7 @@ class ChattoAdapter(BasePlatformAdapter):
 
         BasePlatformAdapter override
         """
-        return await self._send_local_attachment(
+        return await self._send_local_file_as_attachment(
             chat_id,
             audio_path,
             caption,
@@ -2434,55 +2421,32 @@ class ChattoAdapter(BasePlatformAdapter):
     ) -> SendResult:
         """Send an image to a Chatto room.
 
-        Tries to download the image from the URL and upload it as a native
-        attachment.  Falls back to sending the URL as a link (Chatto renders
-        link previews) if the download fails.
+        Materialises the URL (size-capped download, like every inbound
+        attachment) and uploads it as a native attachment. Falls back to the
+        plain URL as a link — Chatto renders link previews — when the URL
+        cannot be materialised or the post after a successful upload fails.
+        An upload failure needs no fallback on top: the text notice of
+        ``_send_local_file_as_attachment`` has already gone out.
 
         BasePlatformAdapter override
         """
-        # Try downloading and uploading as attachment
-        try:
-            import tempfile
-
-            # Download to a temp file
-            parsed = urlsplit(image_url)
-            url_path = parsed.path
-            ext = os.path.splitext(url_path)[1] or ".png"
-            tmp_fd, tmp_path = tempfile.mkstemp(suffix=ext, prefix="chatto_img_")
+        link_text = f"{caption}\n{image_url}" if caption else image_url
+        path, is_temp = await self._materialise_image(image_url)
+        if path is not None:
             try:
-                os.close(tmp_fd)
-                await asyncio.to_thread(
-                    _download_to_file,
-                    image_url,
-                    tmp_path,
-                    ChattoConstants.HTTP_TIMEOUT,
+                result = await self._send_local_file_as_attachment(
+                    chat_id, path, caption, reply_to, metadata, kind="image"
                 )
-
-                # Upload as attachment
-                result = await self.send_image_file(
-                    chat_id,
-                    tmp_path,
-                    caption=caption,
-                    reply_to=reply_to,
-                    metadata=metadata,
-                )
-                if result.success:
-                    return result
             finally:
-                try:
-                    os.unlink(tmp_path)
-                except OSError:
-                    pass
-        except Exception as e:
-            logger.debug(
-                "Chatto: send_image download/upload failed, falling back to link: %s", e
-            )
+                if is_temp:
+                    try:
+                        os.unlink(path)
+                    except OSError:
+                        pass
+            if result.success:
+                return result
 
-        # Fallback: send as link (Chatto renders link previews)
-        text = image_url
-        if caption:
-            text = f"{caption}\n{image_url}"
-        return await self.send(chat_id, text, reply_to=reply_to, metadata=metadata)
+        return await self.send(chat_id, link_text, reply_to=reply_to, metadata=metadata)
 
     async def _materialise_image(self, image_url: str) -> tuple[str | None, bool]:
         """Resolve one ``send_multiple_images`` entry to a local file path.