@yaoyuanchao/dingtalk 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yaoyuanchao/dingtalk",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "type": "module",
5
5
  "description": "DingTalk channel plugin for Clawdbot with Stream Mode support",
6
6
  "license": "MIT",
package/src/channel.ts CHANGED
@@ -206,10 +206,11 @@ export const dingtalkPlugin = {
206
206
  const account = resolveDingTalkAccount({ cfg, accountId });
207
207
  const [type, id] = to.split(':');
208
208
 
209
- // Build text message with image URL
209
+ // Build text message with image URL as markdown image
210
+ const imageMarkdown = `![image](${mediaUrl})`;
210
211
  const textMessage = text
211
- ? `${text}\n\n图片链接: ${mediaUrl}`
212
- : `图片链接: ${mediaUrl}`;
212
+ ? `${text}\n\n${imageMarkdown}`
213
+ : imageMarkdown;
213
214
 
214
215
  if (type === 'dm') {
215
216
  await sendDingTalkRestMessage({
package/src/monitor.ts CHANGED
@@ -221,6 +221,9 @@ async function extractRichTextContent(
221
221
  for (const item of content.richText) {
222
222
  if (item.msgType === "text" && item.content) {
223
223
  parts.push(item.content);
224
+ } else if (item.text) {
225
+ // DingTalk sometimes sends richText items as {text: "..."} without msgType wrapper
226
+ parts.push(item.text);
224
227
  } else if ((item.msgType === "picture" || item.pictureDownloadCode || item.downloadCode) && (item.downloadCode || item.pictureDownloadCode)) {
225
228
  const downloadCode = item.downloadCode || item.pictureDownloadCode;
226
229
  try {
@@ -425,8 +428,8 @@ async function processInboundMessage(
425
428
 
426
429
  if (userIds.length > 0 && account.clientId && account.clientSecret) {
427
430
  try {
428
- // Batch query user info with 500ms timeout
429
- const userInfoMap = await batchGetUserInfo(account.clientId, account.clientSecret, userIds, 500);
431
+ // Batch query user info (3s timeout — needs token fetch + API call)
432
+ const userInfoMap = await batchGetUserInfo(account.clientId, account.clientSecret, userIds, 3000);
430
433
 
431
434
  if (userInfoMap.size > 0) {
432
435
  // Build mention list: [@张三 @李四]
@@ -574,8 +577,9 @@ async function processInboundMessage(
574
577
  cfg: actualCfg,
575
578
  dispatcherOptions: {
576
579
  deliver: async (payload: any) => {
577
- if (payload.text) {
578
- await deliverReply(replyTarget, payload.text, log);
580
+ const textToSend = resolveDeliverText(payload, log);
581
+ if (textToSend) {
582
+ await deliverReply(replyTarget, textToSend, log);
579
583
  setStatus?.({ lastOutboundAt: Date.now() });
580
584
  }
581
585
  },
@@ -676,7 +680,7 @@ async function dispatchWithFullPipeline(params: {
676
680
  responsePrefix: '',
677
681
  deliver: async (payload: any) => {
678
682
  try {
679
- const textToSend = payload.markdown || payload.text;
683
+ const textToSend = resolveDeliverText(payload, log);
680
684
  if (!textToSend) return { ok: true };
681
685
  await deliverReply(replyTarget, textToSend, log);
682
686
  setStatus?.({ lastOutboundAt: Date.now() });
@@ -699,6 +703,24 @@ async function dispatchWithFullPipeline(params: {
699
703
  rt.channel?.activity?.record?.('dingtalk', account.accountId, 'message');
700
704
  }
701
705
 
706
+ /**
707
+ * Extract text + media URL from a deliver payload.
708
+ * The Clawdbot platform may send media URLs in separate fields (e.g. from the `message` tool).
709
+ * We merge them into the text as markdown image syntax so DingTalk can render them.
710
+ */
711
+ function resolveDeliverText(payload: any, log?: any): string | undefined {
712
+ let text = payload.markdown || payload.text;
713
+ const mediaUrl = payload.mediaUrl || payload.media || payload.imageUrl || payload.image;
714
+
715
+ if (mediaUrl && typeof mediaUrl === 'string' && mediaUrl.startsWith('http')) {
716
+ log?.info?.("[dingtalk] Deliver payload includes media URL: " + mediaUrl);
717
+ const imageMarkdown = `![image](${mediaUrl})`;
718
+ text = text ? `${text}\n\n${imageMarkdown}` : imageMarkdown;
719
+ }
720
+
721
+ return text || undefined;
722
+ }
723
+
702
724
  async function deliverReply(target: any, text: string, log?: any): Promise<void> {
703
725
  const now = Date.now();
704
726
  const chunkLimit = 2000;