diff --git a/gateway/platforms/email.py b/gateway/platforms/email.py index 0fffb82d0b9..4eb4972b24e 100644 --- a/gateway/platforms/email.py +++ b/gateway/platforms/email.py @@ -470,8 +470,15 @@ class EmailAdapter(BasePlatformAdapter): for att in attachments: media_urls.append(att["path"]) media_types.append(att["media_type"]) - if att["type"] == "image": + if att["type"] == "image" and msg_type == MessageType.TEXT: msg_type = MessageType.PHOTO + elif att["type"] == "document": + # Document wins over PHOTO for mixed attachments: run.py's + # image handling keys off the per-path image/* mime type + # regardless of message_type, but document-context injection + # gates strictly on MessageType.DOCUMENT — so DOCUMENT is the + # only classification that surfaces both. + msg_type = MessageType.DOCUMENT # Store thread context for reply threading self._thread_context[sender_addr] = { diff --git a/tests/gateway/test_email.py b/tests/gateway/test_email.py index 2354f9ec201..79b8bf4bc85 100644 --- a/tests/gateway/test_email.py +++ b/tests/gateway/test_email.py @@ -393,6 +393,68 @@ class TestDispatchMessage(unittest.TestCase): self.assertEqual(captured_events[0].message_type, MessageType.PHOTO) self.assertEqual(captured_events[0].media_urls, ["/tmp/img.jpg"]) + def test_document_attachment_sets_document_type(self): + """Email with a document attachment must set DOCUMENT so run.py injects file context.""" + import asyncio + from gateway.platforms.base import MessageType + adapter = self._make_adapter() + captured_events = [] + + async def capture_handle(event): + captured_events.append(event) + + adapter.handle_message = capture_handle + + msg_data = { + "uid": b"6", + "sender_addr": "user@test.com", + "sender_name": "User", + "subject": "Re: report", + "message_id": "", + "in_reply_to": "", + "body": "See attached", + "attachments": [{"path": "/tmp/report.pdf", "filename": "report.pdf", "type": "document", "media_type": "application/pdf"}], + "date": "", + } + + asyncio.run(adapter._dispatch_message(msg_data)) + self.assertEqual(len(captured_events), 1) + self.assertEqual(captured_events[0].message_type, MessageType.DOCUMENT) + self.assertEqual(captured_events[0].media_urls, ["/tmp/report.pdf"]) + + def test_mixed_image_and_document_prefers_document(self): + """DOCUMENT wins for mixed attachments — image handling keys off per-path + mime types, but document injection gates strictly on MessageType.DOCUMENT.""" + import asyncio + from gateway.platforms.base import MessageType + adapter = self._make_adapter() + captured_events = [] + + async def capture_handle(event): + captured_events.append(event) + + adapter.handle_message = capture_handle + + msg_data = { + "uid": b"7", + "sender_addr": "user@test.com", + "sender_name": "User", + "subject": "Re: both", + "message_id": "", + "in_reply_to": "", + "body": "Photo and PDF", + "attachments": [ + {"path": "/tmp/img.jpg", "filename": "img.jpg", "type": "image", "media_type": "image/jpeg"}, + {"path": "/tmp/report.pdf", "filename": "report.pdf", "type": "document", "media_type": "application/pdf"}, + ], + "date": "", + } + + asyncio.run(adapter._dispatch_message(msg_data)) + self.assertEqual(len(captured_events), 1) + self.assertEqual(captured_events[0].message_type, MessageType.DOCUMENT) + self.assertEqual(len(captured_events[0].media_urls), 2) + def test_source_built_correctly(self): """Session source should have correct chat_id and user info.""" import asyncio