feat(dingtalk): prefix group replies with sender mention
In group chats, prefix the outbound markdown reply with an H1 naming the sender (# @<nick>) so the addressed user can spot it in a busy group. Private replies are sent verbatim. Visual only: DingTalk markdown robot messages do not push real @ notifications (that would require staffId plumbing and a different message type). sender_name is read from OutboundMessage.metadata, which the agent loop already propagates from inbound metadata. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
Xubin Ren
co-authored by
Claude
parent
14e692e40d
commit
205889f9e0
@@ -713,8 +713,15 @@ class DingTalkChannel(BaseChannel):
|
||||
if not token:
|
||||
raise RuntimeError("DingTalk access token unavailable")
|
||||
|
||||
if msg.content and msg.content.strip():
|
||||
if not await self._send_markdown_text(token, msg.chat_id, msg.content.strip()):
|
||||
content = msg.content.strip() if msg.content else ""
|
||||
if content:
|
||||
# In group chats, prefix the reply with a markdown header naming the
|
||||
# sender so the addressed user can spot the reply. Visual only —
|
||||
# DingTalk's markdown robot messages do not push real @ notifications.
|
||||
sender_name = msg.metadata.get("sender_name") if msg.metadata else None
|
||||
if msg.chat_id.startswith("group:") and sender_name:
|
||||
content = f"# @{sender_name}\n\n{content}"
|
||||
if not await self._send_markdown_text(token, msg.chat_id, content):
|
||||
raise RuntimeError("DingTalk text message was not delivered")
|
||||
|
||||
for media_ref in msg.media or []:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import json
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
from types import SimpleNamespace
|
||||
@@ -252,6 +253,56 @@ async def test_group_send_uses_group_messages_api() -> None:
|
||||
assert call["json"]["msgKey"] == "sampleMarkdown"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_group_send_prepends_sender_mention(monkeypatch) -> None:
|
||||
"""Group replies are prefixed with a markdown header naming the sender."""
|
||||
config = DingTalkConfig(client_id="app", client_secret="secret", allow_from=["*"])
|
||||
channel = DingTalkChannel(config, MessageBus())
|
||||
channel._http = _FakeHttp()
|
||||
|
||||
async def _fake_token() -> str:
|
||||
return "token"
|
||||
|
||||
monkeypatch.setattr(channel, "_get_access_token", _fake_token)
|
||||
|
||||
await channel.send(
|
||||
OutboundMessage(
|
||||
channel="dingtalk",
|
||||
chat_id="group:conv123",
|
||||
content="hello",
|
||||
metadata={"sender_name": "Alice"},
|
||||
)
|
||||
)
|
||||
|
||||
sent_text = json.loads(channel._http.calls[0]["json"]["msgParam"])["text"]
|
||||
assert sent_text == "# @Alice\n\nhello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_private_send_does_not_prepend_mention(monkeypatch) -> None:
|
||||
"""Private replies are sent verbatim, without the sender header."""
|
||||
config = DingTalkConfig(client_id="app", client_secret="secret", allow_from=["*"])
|
||||
channel = DingTalkChannel(config, MessageBus())
|
||||
channel._http = _FakeHttp()
|
||||
|
||||
async def _fake_token() -> str:
|
||||
return "token"
|
||||
|
||||
monkeypatch.setattr(channel, "_get_access_token", _fake_token)
|
||||
|
||||
await channel.send(
|
||||
OutboundMessage(
|
||||
channel="dingtalk",
|
||||
chat_id="user1", # private chat: no "group:" prefix
|
||||
content="hello",
|
||||
metadata={"sender_name": "Alice"},
|
||||
)
|
||||
)
|
||||
|
||||
sent_text = json.loads(channel._http.calls[0]["json"]["msgParam"])["text"]
|
||||
assert sent_text == "hello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handler_uses_voice_recognition_text_when_text_is_empty(monkeypatch) -> None:
|
||||
bus = MessageBus()
|
||||
|
||||
Reference in New Issue
Block a user