@yaoyuanchao/dingtalk 1.7.7 → 1.7.8

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.7.7",
3
+ "version": "1.7.8",
4
4
  "type": "module",
5
5
  "description": "DingTalk channel plugin for ClawdBot/OpenClaw with Stream Mode support",
6
6
  "license": "MIT",
package/src/api.ts CHANGED
@@ -116,11 +116,16 @@ export async function getDingTalkAccessToken(clientId: string, clientSecret: str
116
116
  export async function sendViaSessionWebhook(
117
117
  sessionWebhook: string,
118
118
  text: string,
119
+ atUserIds?: string[],
119
120
  ): Promise<{ ok: boolean; errcode?: number; errmsg?: string; processQueryKey?: string }> {
120
- const res = await jsonPost(sessionWebhook, {
121
+ const body: any = {
121
122
  msgtype: "text",
122
123
  text: { content: text },
123
- });
124
+ };
125
+ if (atUserIds?.length) {
126
+ body.at = { atUserIds, isAtAll: false };
127
+ }
128
+ const res = await jsonPost(sessionWebhook, body);
124
129
  const ok = res?.errcode === 0 || !res?.errcode;
125
130
  if (!ok) {
126
131
  console.warn(`[dingtalk] SessionWebhook text error: errcode=${res?.errcode}, errmsg=${res?.errmsg}`);
@@ -133,11 +138,16 @@ export async function sendMarkdownViaSessionWebhook(
133
138
  sessionWebhook: string,
134
139
  title: string,
135
140
  text: string,
141
+ atUserIds?: string[],
136
142
  ): Promise<{ ok: boolean; errcode?: number; errmsg?: string; processQueryKey?: string }> {
137
- const res = await jsonPost(sessionWebhook, {
143
+ const body: any = {
138
144
  msgtype: "markdown",
139
145
  markdown: { title, text },
140
- });
146
+ };
147
+ if (atUserIds?.length) {
148
+ body.at = { atUserIds, isAtAll: false };
149
+ }
150
+ const res = await jsonPost(sessionWebhook, body);
141
151
  const ok = res?.errcode === 0 || !res?.errcode;
142
152
  if (!ok) {
143
153
  console.warn(`[dingtalk] SessionWebhook markdown error: errcode=${res?.errcode}, errmsg=${res?.errmsg}`);
package/src/monitor.ts CHANGED
@@ -369,8 +369,8 @@ export async function startDingTalkMonitor(ctx: DingTalkMonitorContext): Promise
369
369
  });
370
370
 
371
371
  // Reconnection configuration
372
- const HEARTBEAT_CHECK_MS = 15_000; // Check connectivity every 15s
373
- const HEARTBEAT_TIMEOUT_MS = 90_000; // 90s no activity = force reconnect
372
+ const HEARTBEAT_CHECK_MS = 30_000; // Check connectivity every 30s
373
+ const HEARTBEAT_TIMEOUT_MS = 5 * 60 * 1000; // 5 min no activity = force reconnect (safety net only)
374
374
  const RECONNECT_BASE_MS = 1_000; // 1s initial backoff
375
375
  const RECONNECT_CAP_MS = 30_000; // 30s max backoff
376
376
  let reconnectAttempt = 0;
@@ -421,6 +421,7 @@ export async function startDingTalkMonitor(ctx: DingTalkMonitorContext): Promise
421
421
  });
422
422
 
423
423
  client.registerAllEventListener((msg: any) => {
424
+ touchActivity(); // SDK events (including ping/pong) count as activity
424
425
  return { status: "SUCCESS", message: "OK" };
425
426
  });
426
427
 
@@ -437,7 +438,7 @@ export async function startDingTalkMonitor(ctx: DingTalkMonitorContext): Promise
437
438
  log?.info?.("[dingtalk:" + account.accountId + "] Stream connected");
438
439
  setStatus?.({ running: true, lastStartAt: connectTime });
439
440
 
440
- // Start heartbeat monitor: if no activity for 90s, force disconnect to trigger reconnect.
441
+ // Start heartbeat monitor: if no activity for 5 min, force disconnect to trigger reconnect.
441
442
  // The SDK's keepAlive ping/pong (8s interval) handles socket-level liveness and sets
442
443
  // client.connected=false on missed pongs, which our poll loop below detects.
443
444
  // This heartbeat is a secondary safety net for higher-level silent failures where
@@ -2025,6 +2026,10 @@ async function deliverReply(target: any, text: string, log?: any): Promise<void>
2025
2026
  }
2026
2027
  }
2027
2028
 
2029
+ // Auto @mention sender in group chats (only on first chunk to avoid spam)
2030
+ const atUserIds = (!target.isDm && target.senderId) ? [target.senderId] : undefined;
2031
+ let atApplied = false; // Only @ on the first chunk
2032
+
2028
2033
  for (const chunk of chunks) {
2029
2034
  let webhookSuccess = false;
2030
2035
  const maxRetries = 2;
@@ -2036,12 +2041,13 @@ async function deliverReply(target: any, text: string, log?: any): Promise<void>
2036
2041
  await throttleSend();
2037
2042
  log?.info?.("[dingtalk] Using sessionWebhook (attempt " + attempt + "/" + maxRetries + "), format=" + messageFormat);
2038
2043
  log?.info?.("[dingtalk] Sending text (" + chunk.length + " chars): " + chunk.substring(0, 200));
2044
+ const currentAt = (!atApplied && atUserIds) ? atUserIds : undefined;
2039
2045
  let sendResult: { ok: boolean; errcode?: number; errmsg?: string; processQueryKey?: string };
2040
2046
  if (isMarkdown) {
2041
2047
  const markdownTitle = buildMarkdownPreviewTitle(chunk, "Jax");
2042
- sendResult = await sendMarkdownViaSessionWebhook(target.sessionWebhook, markdownTitle, chunk);
2048
+ sendResult = await sendMarkdownViaSessionWebhook(target.sessionWebhook, markdownTitle, chunk, currentAt);
2043
2049
  } else {
2044
- sendResult = await sendViaSessionWebhook(target.sessionWebhook, chunk);
2050
+ sendResult = await sendViaSessionWebhook(target.sessionWebhook, chunk, currentAt);
2045
2051
  }
2046
2052
  if (!sendResult.ok) {
2047
2053
  throw new Error(`SessionWebhook rejected: errcode=${sendResult.errcode}, errmsg=${sendResult.errmsg}`);
@@ -2055,6 +2061,7 @@ async function deliverReply(target: any, text: string, log?: any): Promise<void>
2055
2061
  // so interactiveCard quotes can resolve via repliedMsg.createdAt
2056
2062
  cacheOutboundMessageByTime(chunk);
2057
2063
  }
2064
+ if (currentAt) atApplied = true;
2058
2065
  webhookSuccess = true;
2059
2066
  break;
2060
2067
  } catch (err) {