fix(dingtalk): clarify webhook media behavior

This commit is contained in:
qWaitCrypto 2026-05-09 22:04:55 +08:00 committed by Teknium
parent a33c63b9f8
commit c705c7ac9b
2 changed files with 104 additions and 0 deletions

View file

@ -886,6 +886,65 @@ class DingTalkAdapter(BasePlatformAdapter):
"""DingTalk does not support typing indicators."""
pass
async def send_image(
self,
chat_id: str,
image_url: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> SendResult:
"""Send an image via DingTalk markdown.
DingTalk's session webhook only supports text/markdown payloads, not
native image/file attachments. For remote image URLs, render the image
inline with markdown so the user still sees the image. Local files need
OpenAPI media upload and are handled separately.
"""
image_block = f"![image]({image_url})"
content = f"{caption}\n\n{image_block}" if caption else image_block
return await self.send(
chat_id=chat_id,
content=content,
reply_to=reply_to,
metadata=metadata,
)
async def send_image_file(
self,
chat_id: str,
image_path: str,
caption: Optional[str] = None,
reply_to: Optional[str] = None,
**kwargs,
) -> SendResult:
"""DingTalk webhook replies cannot send local image files directly."""
return SendResult(
success=False,
error=(
"DingTalk session webhook replies do not support local image uploads. "
"Only markdown/text replies are supported without OpenAPI media upload."
),
)
async def send_document(
self,
chat_id: str,
file_path: str,
caption: Optional[str] = None,
file_name: Optional[str] = None,
reply_to: Optional[str] = None,
**kwargs,
) -> SendResult:
"""DingTalk webhook replies cannot send local file attachments directly."""
return SendResult(
success=False,
error=(
"DingTalk session webhook replies do not support local file attachments. "
"Only markdown/text replies are supported without OpenAPI message send."
),
)
async def get_chat_info(self, chat_id: str) -> Dict[str, Any]:
"""Return basic info about a DingTalk conversation."""
return {

View file

@ -223,6 +223,51 @@ class TestSend:
assert result.success is False
assert "400" in result.error
@pytest.mark.asyncio
async def test_send_image_renders_markdown_image(self):
from gateway.platforms.dingtalk import DingTalkAdapter
adapter = DingTalkAdapter(PlatformConfig(enabled=True))
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = "OK"
mock_client = AsyncMock()
mock_client.post = AsyncMock(return_value=mock_response)
adapter._http_client = mock_client
result = await adapter.send_image(
"chat-123",
"https://example.com/demo.png",
caption="Screenshot",
metadata={"session_webhook": "https://dingtalk.example/webhook"},
)
assert result.success is True
payload = mock_client.post.call_args.kwargs["json"]
assert payload["msgtype"] == "markdown"
assert payload["markdown"]["text"] == "Screenshot\n\n![image](https://example.com/demo.png)"
@pytest.mark.asyncio
async def test_send_image_file_returns_explicit_unsupported_error(self):
from gateway.platforms.dingtalk import DingTalkAdapter
adapter = DingTalkAdapter(PlatformConfig(enabled=True))
result = await adapter.send_image_file("chat-123", "/tmp/demo.png")
assert result.success is False
assert "do not support local image uploads" in result.error
@pytest.mark.asyncio
async def test_send_document_returns_explicit_unsupported_error(self):
from gateway.platforms.dingtalk import DingTalkAdapter
adapter = DingTalkAdapter(PlatformConfig(enabled=True))
result = await adapter.send_document("chat-123", "/tmp/demo.pdf")
assert result.success is False
assert "do not support local file attachments" in result.error
# ---------------------------------------------------------------------------
# Connect / disconnect