@xmanrui/dsh-im 2.4.0 → 2.6.0

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.
Files changed (60) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/lib/client.js +322 -109
  4. package/lib/index.js +218 -211
  5. package/package.json +1 -1
  6. package/plugin-src/client/channel-card-meta.js +36 -1
  7. package/plugin-src/client/channels/dingtalk/api.js +2 -0
  8. package/plugin-src/client/channels/dingtalk/index.js +9 -1
  9. package/plugin-src/client/channels/feishu/api.js +3 -0
  10. package/plugin-src/client/channels/feishu/index.js +9 -1
  11. package/plugin-src/client/channels/qq/api.js +2 -0
  12. package/plugin-src/client/channels/qq/index.js +9 -1
  13. package/plugin-src/client/channels/shared/token-api.js +2 -0
  14. package/plugin-src/client/channels/shared/token-channel.js +9 -1
  15. package/plugin-src/client/channels/wecom/api.js +2 -0
  16. package/plugin-src/client/channels/wecom/index.js +9 -1
  17. package/plugin-src/client/channels/weixin/api.js +2 -10
  18. package/plugin-src/client/channels/weixin/index.js +9 -5
  19. package/plugin-src/client/channels/whatsapp/api.js +2 -0
  20. package/plugin-src/client/channels/whatsapp/index.js +9 -1
  21. package/plugin-src/client/i18n.js +4 -0
  22. package/plugin-src/client/index.js +16 -2
  23. package/plugin-src/client/last-message-error.js +17 -0
  24. package/plugin-src/client/styles.js +5 -1
  25. package/plugin-src/host/channels/feishu/rpc.mjs +3 -0
  26. package/src/channels/dingtalk/dingtalk-api.mjs +102 -6
  27. package/src/channels/dingtalk/dingtalk-bridge.mjs +247 -38
  28. package/src/channels/dingtalk/dingtalk-card-stream.mjs +2 -2
  29. package/src/channels/dingtalk/dingtalk-controller.mjs +2 -0
  30. package/src/channels/discord/discord-api.mjs +35 -0
  31. package/src/channels/discord/discord-bridge.mjs +1 -0
  32. package/src/channels/discord/discord-runtime.mjs +26 -0
  33. package/src/channels/feishu/bridge.mjs +115 -69
  34. package/src/channels/feishu/multi-bot-controller.mjs +2 -0
  35. package/src/channels/qq/qq-bridge.mjs +83 -28
  36. package/src/channels/qq/qq-controller.mjs +2 -0
  37. package/src/channels/shared/harness-client.mjs +40 -4
  38. package/src/channels/shared/i18n-en/shared-a.mjs +77 -0
  39. package/src/channels/shared/message-failure.mjs +244 -0
  40. package/src/channels/shared/semantic/artifact-delivery.mjs +9 -2
  41. package/src/channels/shared/status-reaction.mjs +107 -0
  42. package/src/channels/shared/text-harness-bridge.mjs +123 -66
  43. package/src/channels/shared/token-bot-controller.mjs +2 -0
  44. package/src/channels/slack/manifest.mjs +1 -0
  45. package/src/channels/slack/slack-api.mjs +28 -0
  46. package/src/channels/slack/slack-bridge.mjs +5 -0
  47. package/src/channels/slack/slack-controller.mjs +2 -0
  48. package/src/channels/slack/slack-runtime.mjs +24 -0
  49. package/src/channels/telegram/telegram-api.mjs +9 -0
  50. package/src/channels/telegram/telegram-bridge.mjs +1 -0
  51. package/src/channels/telegram/telegram-runtime.mjs +20 -0
  52. package/src/channels/wecom/wecom-bridge.mjs +49 -15
  53. package/src/channels/wecom/wecom-controller.mjs +2 -0
  54. package/src/channels/weixin/weixin-api.mjs +47 -0
  55. package/src/channels/weixin/weixin-bridge.mjs +261 -43
  56. package/src/channels/weixin/weixin-controller.mjs +2 -15
  57. package/src/channels/weixin/weixin-runtime.mjs +1 -0
  58. package/src/channels/whatsapp/whatsapp-bridge.mjs +1 -0
  59. package/src/channels/whatsapp/whatsapp-controller.mjs +2 -0
  60. package/src/channels/whatsapp/whatsapp-runtime.mjs +36 -0
@@ -9,6 +9,7 @@ import {
9
9
  } from './message-utils.mjs';
10
10
  import {
11
11
  hasInboundImages,
12
+ imagePromptDiagnostic,
12
13
  imagePromptUserMessage,
13
14
  promptContentForMessage,
14
15
  } from '../shared/image-prompt.mjs';
@@ -48,6 +49,13 @@ import { deliverOutboundArtifacts } from '../shared/semantic/artifact-delivery.m
48
49
  import {
49
50
  createDeliveryReceipt,
50
51
  } from '../shared/semantic/delivery.mjs';
52
+ import {
53
+ channelDeliveryFailure,
54
+ clearLastMessageFailure,
55
+ messageFailureText,
56
+ setLastMessageFailure,
57
+ } from '../shared/message-failure.mjs';
58
+ import { beginStatusReaction } from '../shared/status-reaction.mjs';
51
59
  import {
52
60
  MENU_PAGE_SIZE,
53
61
  PRESET_FOLLOW_DEFAULT_SENTINEL,
@@ -365,6 +373,7 @@ function ensureStatus(status) {
365
373
  status.lastReplyAt ??= null;
366
374
  status.lastRejectedAt ??= null;
367
375
  status.lastError ??= null;
376
+ status.lastMessageError ??= null;
368
377
  }
369
378
 
370
379
  export class FeishuHarnessBridge {
@@ -541,7 +550,7 @@ export class FeishuHarnessBridge {
541
550
  }
542
551
 
543
552
  this.#acceptedMessageIds.add(messageId);
544
- const processingReaction = this.#addReaction(messageId, 'OnIt');
553
+ const processingReaction = this.#beginReaction(messageId);
545
554
  const commandMessage = extractInboundMessage(event, this.#client);
546
555
  const commandText = nonEmptyString(commandMessage.content) ?? '';
547
556
  const batchText = event.message.message_type === 'text'
@@ -801,6 +810,30 @@ export class FeishuHarnessBridge {
801
810
  return current;
802
811
  }
803
812
 
813
+ #recordFailure(error, {
814
+ logLabel = 'operation',
815
+ logLevel = 'warn',
816
+ userMessage,
817
+ reason,
818
+ } = {}) {
819
+ this.#status.lastError = error?.message ?? String(error);
820
+ const failure = setLastMessageFailure(this.#status, error, { userMessage, reason });
821
+ this.#logger?.[logLevel]?.(
822
+ `[dsh-feishu] ${logLabel} failed [${failure.referenceId}]:`,
823
+ error?.message ?? String(error),
824
+ );
825
+ return failure;
826
+ }
827
+
828
+ async #sendFailure(chatId, error, options = {}) {
829
+ const failure = this.#recordFailure(error, options);
830
+ const text = options.appendMessage
831
+ ? `${messageFailureText(failure)}\n\n${options.appendMessage}`
832
+ : messageFailureText(failure);
833
+ await this.#send(chatId, text).catch(() => undefined);
834
+ return failure;
835
+ }
836
+
804
837
  async #handleMessageFailure(event, messageId, processingReaction, error) {
805
838
  if (error?.code === 'turn-stopped') {
806
839
  await this.#removeProcessingReaction(messageId, processingReaction);
@@ -813,15 +846,21 @@ export class FeishuHarnessBridge {
813
846
  await this.#removeProcessingReaction(messageId, processingReaction);
814
847
  return;
815
848
  }
816
- this.#logger.error?.('[dsh-feishu] message handling failed:', error?.message ?? String(error));
817
- this.#status.lastError = error?.message ?? String(error);
849
+ const userMessage = inboundFileUserMessage(error)
850
+ ?? imagePromptUserMessage(error);
851
+ const failure = this.#recordFailure(error, {
852
+ logLabel: 'message handling',
853
+ logLevel: 'error',
854
+ userMessage,
855
+ reason: imagePromptDiagnostic(error)?.reason,
856
+ });
818
857
  await this.#finishReaction(messageId, processingReaction, 'ERROR');
858
+ const failureText = error?.batchInputMessage
859
+ ? `${messageFailureText(failure)}\n\n${error.batchInputMessage}`
860
+ : messageFailureText(failure);
819
861
  await this.#send(
820
862
  event.message.chat_id,
821
- error?.batchInputMessage
822
- ?? inboundFileUserMessage(error)
823
- ?? imagePromptUserMessage(error)
824
- ?? t('处理失败,请稍后重试。如果问题持续,请在 DeepSeek Harness 的飞书插件页面检查连接状态。'),
863
+ failureText,
825
864
  ).catch(() => undefined);
826
865
  }
827
866
 
@@ -1025,7 +1064,11 @@ export class FeishuHarnessBridge {
1025
1064
  const batchSubmission = event.batchSubmission ?? null;
1026
1065
  let batchAskCompleted = false;
1027
1066
  try {
1028
- const receipt = await this.#answerWithStream(event, key, message, {
1067
+ const {
1068
+ receipt,
1069
+ artifactSendErrors,
1070
+ textDeliveryErrors = 0,
1071
+ } = await this.#answerWithStream(event, key, message, {
1029
1072
  onAskComplete: batchSubmission
1030
1073
  ? () => {
1031
1074
  batchAskCompleted = this.#batchInputs.complete(
@@ -1038,6 +1081,9 @@ export class FeishuHarnessBridge {
1038
1081
  this.#status.messagesReplied += 1;
1039
1082
  this.#status.lastReplyAt = new Date().toISOString();
1040
1083
  this.#status.lastError = null;
1084
+ if (textDeliveryErrors === 0 && artifactSendErrors === 0) {
1085
+ clearLastMessageFailure(this.#status);
1086
+ }
1041
1087
  return receipt;
1042
1088
  } catch (error) {
1043
1089
  if (batchSubmission && !batchAskCompleted) {
@@ -1049,6 +1095,9 @@ export class FeishuHarnessBridge {
1049
1095
  if (failed.retained) {
1050
1096
  const batchError = new Error(error?.message ?? String(error), { cause: error });
1051
1097
  batchError.code = error?.code;
1098
+ batchError.providerCode = error?.providerCode;
1099
+ batchError.method = error?.method;
1100
+ if (Number.isInteger(error?.status)) batchError.status = error.status;
1052
1101
  batchError.batchInputMessage = failed.message;
1053
1102
  throw batchError;
1054
1103
  }
@@ -1643,9 +1692,7 @@ export class FeishuHarnessBridge {
1643
1692
  })
1644
1693
  .catch(async (error) => {
1645
1694
  if (this.#signal?.aborted) return;
1646
- this.#logger.warn?.('[dsh-feishu] card action failed:', error?.message ?? String(error));
1647
- this.#status.lastError = error?.message ?? String(error);
1648
- await this.#send(entry.chatId, t('卡片操作失败,请稍后重试。')).catch(() => undefined);
1695
+ await this.#sendFailure(entry.chatId, error, { logLabel: 'card action' });
1649
1696
  })
1650
1697
  .finally(() => {
1651
1698
  if (this.#cardActionInFlight.get(dedupeKey) === tracked) {
@@ -2008,8 +2055,7 @@ export class FeishuHarnessBridge {
2008
2055
  },
2009
2056
  );
2010
2057
  } catch (error) {
2011
- this.#logger.warn?.('[dsh-feishu] session list failed:', error.message);
2012
- await this.#send(chatId, t('暂时无法获取会话列表,请稍后重试。'));
2058
+ await this.#sendFailure(chatId, error, { logLabel: 'session list' });
2013
2059
  }
2014
2060
  }
2015
2061
 
@@ -2026,8 +2072,7 @@ export class FeishuHarnessBridge {
2026
2072
  { key, updateMessageId },
2027
2073
  );
2028
2074
  } catch (error) {
2029
- this.#logger.warn?.('[dsh-feishu] workspace list failed:', error.message);
2030
- await this.#send(chatId, t('暂时无法获取工作区列表,请稍后重试。'));
2075
+ await this.#sendFailure(chatId, error, { logLabel: 'workspace list' });
2031
2076
  }
2032
2077
  }
2033
2078
 
@@ -2038,7 +2083,10 @@ export class FeishuHarnessBridge {
2038
2083
  await this.#send(chatId, t('已绑定会话「{title}」\nID:{id}', { title, id: bound?.sessionId ?? sessionId }));
2039
2084
  await this.#sendMenuCard(key, chatId, { updateMessageId });
2040
2085
  } catch (error) {
2041
- await this.#send(chatId, t('绑定失败:{message}', { message: safeErrorText(error) }));
2086
+ await this.#sendFailure(chatId, error, {
2087
+ logLabel: 'session binding',
2088
+ userMessage: t('绑定失败:{message}', { message: safeErrorText(error) }),
2089
+ });
2042
2090
  }
2043
2091
  }
2044
2092
 
@@ -2048,7 +2096,10 @@ export class FeishuHarnessBridge {
2048
2096
  await this.#send(chatId, t('工作区已切换为:{workspace}', { workspace: current }));
2049
2097
  await this.#sendMenuCard(key, chatId, { updateMessageId });
2050
2098
  } catch (error) {
2051
- await this.#send(chatId, t('切换失败:{message}', { message: safeErrorText(error) }));
2099
+ await this.#sendFailure(chatId, error, {
2100
+ logLabel: 'workspace switch',
2101
+ userMessage: t('切换失败:{message}', { message: safeErrorText(error) }),
2102
+ });
2052
2103
  }
2053
2104
  }
2054
2105
 
@@ -2247,8 +2298,7 @@ export class FeishuHarnessBridge {
2247
2298
  catalog._currentId = settings.agentPreset;
2248
2299
  await this.#sendCard(chatId, presetCard(catalog), { key, updateMessageId });
2249
2300
  } catch (error) {
2250
- this.#logger.warn?.('[dsh-feishu] preset card failed:', error.message);
2251
- await this.#send(chatId, t('暂时无法获取预设列表,请稍后重试。'));
2301
+ await this.#sendFailure(chatId, error, { logLabel: 'preset card' });
2252
2302
  }
2253
2303
  }
2254
2304
 
@@ -2273,8 +2323,7 @@ export class FeishuHarnessBridge {
2273
2323
  }
2274
2324
  await this.#sendCard(chatId, modelCard(catalog), { key, updateMessageId });
2275
2325
  } catch (error) {
2276
- this.#logger.warn?.('[dsh-feishu] model card failed:', error.message);
2277
- await this.#send(chatId, t('暂时无法获取模型列表,请稍后重试。'));
2326
+ await this.#sendFailure(chatId, error, { logLabel: 'model card' });
2278
2327
  }
2279
2328
  }
2280
2329
 
@@ -2302,8 +2351,7 @@ export class FeishuHarnessBridge {
2302
2351
  }
2303
2352
  await this.#send(chatId, lines.join('\n'));
2304
2353
  } catch (error) {
2305
- this.#logger.warn?.('[dsh-feishu] status text failed:', error.message);
2306
- await this.#send(chatId, t('暂时无法获取系统状态,请稍后重试。'));
2354
+ await this.#sendFailure(chatId, error, { logLabel: 'status text' });
2307
2355
  }
2308
2356
  }
2309
2357
 
@@ -2355,8 +2403,7 @@ export class FeishuHarnessBridge {
2355
2403
 
2356
2404
  await this.#sendCard(chatId, statusCard(info), { key, updateMessageId });
2357
2405
  } catch (error) {
2358
- this.#logger.warn?.('[dsh-feishu] status card failed:', error.message);
2359
- await this.#send(chatId, t('暂时无法获取系统状态,请稍后重试。'));
2406
+ await this.#sendFailure(chatId, error, { logLabel: 'status card' });
2360
2407
  }
2361
2408
  }
2362
2409
 
@@ -2381,8 +2428,7 @@ export class FeishuHarnessBridge {
2381
2428
  );
2382
2429
  await this.#send(chatId, result?.message || t('上下文压缩失败。'));
2383
2430
  } catch (error) {
2384
- this.#logger.warn?.('[dsh-feishu] compact failed:', error.message, error.code);
2385
- await this.#send(chatId, t('上下文压缩失败,请稍后重试。'));
2431
+ await this.#sendFailure(chatId, error, { logLabel: 'compact' });
2386
2432
  }
2387
2433
  }
2388
2434
 
@@ -2405,8 +2451,7 @@ export class FeishuHarnessBridge {
2405
2451
  }
2406
2452
  await this.#send(chatId, result?.message || t('/stop 执行完成。'));
2407
2453
  } catch (error) {
2408
- this.#logger.warn?.('[dsh-feishu] stop failed:', error.message);
2409
- await this.#send(chatId, t('停止任务失败,请稍后重试。'));
2454
+ await this.#sendFailure(chatId, error, { logLabel: 'stop' });
2410
2455
  }
2411
2456
  }
2412
2457
 
@@ -2446,8 +2491,7 @@ export class FeishuHarnessBridge {
2446
2491
  if (reply) await this.#send(chatId, reply);
2447
2492
  }
2448
2493
  } catch (error) {
2449
- this.#logger.warn?.('[dsh-feishu] preset default failed:', error.message);
2450
- await this.#send(chatId, t('预设重置失败,请稍后重试。'));
2494
+ await this.#sendFailure(chatId, error, { logLabel: 'preset reset' });
2451
2495
  return;
2452
2496
  }
2453
2497
  try {
@@ -2470,8 +2514,7 @@ export class FeishuHarnessBridge {
2470
2514
  if (reply) await this.#send(chatId, reply);
2471
2515
  }
2472
2516
  } catch (error) {
2473
- this.#logger.warn?.('[dsh-feishu] preset select failed:', error.message);
2474
- await this.#send(chatId, t('预设切换失败,请稍后重试。'));
2517
+ await this.#sendFailure(chatId, error, { logLabel: 'preset selection' });
2475
2518
  return;
2476
2519
  }
2477
2520
  try {
@@ -2503,8 +2546,7 @@ export class FeishuHarnessBridge {
2503
2546
  if (reply) await this.#send(chatId, reply);
2504
2547
  }
2505
2548
  } catch (error) {
2506
- this.#logger.warn?.('[dsh-feishu] model select failed:', error.message);
2507
- await this.#send(chatId, t('模型切换失败,请稍后重试。'));
2549
+ await this.#sendFailure(chatId, error, { logLabel: 'model selection' });
2508
2550
  return;
2509
2551
  }
2510
2552
  try {
@@ -3046,10 +3088,14 @@ export class FeishuHarnessBridge {
3046
3088
  signal: this.#signal,
3047
3089
  })
3048
3090
  : undefined,
3049
- sendFailureNotice: async (artifact, error) => ({
3091
+ onFailure: (artifact, error) => setLastMessageFailure(this.#status, error, {
3092
+ userMessage: artifactFailureText(artifact?.fileName, error),
3093
+ reason: error?.code,
3094
+ }),
3095
+ sendFailureNotice: async (_artifact, _error, failure) => ({
3050
3096
  messageId: await this.#send(
3051
3097
  chatId,
3052
- artifactFailureText(artifact?.fileName, error),
3098
+ messageFailureText(failure),
3053
3099
  ),
3054
3100
  }),
3055
3101
  logger: this.#logger,
@@ -3065,11 +3111,13 @@ export class FeishuHarnessBridge {
3065
3111
  presentation: 'feishu-files',
3066
3112
  }),
3067
3113
  failureNoticeVisible: delivery.failureNoticeVisible,
3114
+ artifactSendErrors: delivery.artifactSendErrors,
3068
3115
  };
3069
3116
  }
3070
3117
  return {
3071
3118
  receipt: delivery.receipt,
3072
3119
  failureNoticeVisible: delivery.failureNoticeVisible,
3120
+ artifactSendErrors: delivery.artifactSendErrors,
3073
3121
  };
3074
3122
  }
3075
3123
 
@@ -3110,7 +3158,7 @@ export class FeishuHarnessBridge {
3110
3158
  },
3111
3159
  );
3112
3160
  } catch (error) {
3113
- textSendError = error;
3161
+ textSendError = channelDeliveryFailure(error);
3114
3162
  this.#logger.warn?.(
3115
3163
  '[dsh-feishu] final text delivery failed; continuing with result files:',
3116
3164
  error,
@@ -3123,8 +3171,11 @@ export class FeishuHarnessBridge {
3123
3171
  if (textSendError && !artifactDispatched && !delivery.failureNoticeVisible) {
3124
3172
  throw textSendError;
3125
3173
  }
3174
+ if (textSendError && delivery.artifactSendErrors === 0) {
3175
+ setLastMessageFailure(this.#status, textSendError);
3176
+ }
3126
3177
  this.#status.streamFallbacks = (this.#status.streamFallbacks ?? 0) + 1;
3127
- return delivery.receipt;
3178
+ return { ...delivery, textDeliveryErrors: textSendError ? 1 : 0 };
3128
3179
  }
3129
3180
 
3130
3181
  let promptStarted = false;
@@ -3177,7 +3228,7 @@ export class FeishuHarnessBridge {
3177
3228
  },
3178
3229
  );
3179
3230
  } catch (fallbackError) {
3180
- textSendError = fallbackError;
3231
+ textSendError = channelDeliveryFailure(fallbackError);
3181
3232
  this.#logger.warn?.(
3182
3233
  '[dsh-feishu] fallback text delivery failed; continuing with result files:',
3183
3234
  fallbackError,
@@ -3195,8 +3246,11 @@ export class FeishuHarnessBridge {
3195
3246
  if (textSendError && !artifactDispatched && !delivery.failureNoticeVisible) {
3196
3247
  throw textSendError;
3197
3248
  }
3249
+ if (textSendError && delivery.artifactSendErrors === 0) {
3250
+ setLastMessageFailure(this.#status, textSendError);
3251
+ }
3198
3252
  this.#status.streamFallbacks = (this.#status.streamFallbacks ?? 0) + 1;
3199
- return delivery.receipt;
3253
+ return { ...delivery, textDeliveryErrors: textSendError ? 1 : 0 };
3200
3254
  }
3201
3255
  if (promptStarted) throw error;
3202
3256
 
@@ -3224,7 +3278,7 @@ export class FeishuHarnessBridge {
3224
3278
  },
3225
3279
  );
3226
3280
  } catch (fallbackError) {
3227
- textSendError = fallbackError;
3281
+ textSendError = channelDeliveryFailure(fallbackError);
3228
3282
  this.#logger.warn?.(
3229
3283
  '[dsh-feishu] fallback text delivery failed; continuing with result files:',
3230
3284
  fallbackError,
@@ -3237,8 +3291,11 @@ export class FeishuHarnessBridge {
3237
3291
  if (textSendError && !artifactDispatched && !delivery.failureNoticeVisible) {
3238
3292
  throw textSendError;
3239
3293
  }
3294
+ if (textSendError && delivery.artifactSendErrors === 0) {
3295
+ setLastMessageFailure(this.#status, textSendError);
3296
+ }
3240
3297
  this.#status.streamFallbacks = (this.#status.streamFallbacks ?? 0) + 1;
3241
- return delivery.receipt;
3298
+ return { ...delivery, textDeliveryErrors: textSendError ? 1 : 0 };
3242
3299
  }
3243
3300
  const delivery = await this.#deliverArtifacts(
3244
3301
  chatId,
@@ -3251,7 +3308,7 @@ export class FeishuHarnessBridge {
3251
3308
  }),
3252
3309
  );
3253
3310
  this.#status.streamResponses = (this.#status.streamResponses ?? 0) + 1;
3254
- return delivery.receipt;
3311
+ return delivery;
3255
3312
  }
3256
3313
 
3257
3314
  async #processInteractionReply(event, messageId, key, expected, processingReaction) {
@@ -3547,35 +3604,24 @@ export class FeishuHarnessBridge {
3547
3604
  return t('_{text}_', { text: update.text || t('正在处理…') });
3548
3605
  }
3549
3606
 
3550
- async #addReaction(messageId, emojiType) {
3551
- if (!this.#channel?.addReaction) return null;
3552
- try {
3553
- const reactionId = await this.#channel.addReaction(messageId, emojiType);
3554
- this.#status.reactionsAdded = (this.#status.reactionsAdded ?? 0) + 1;
3555
- return reactionId;
3556
- } catch (error) {
3557
- this.#status.reactionErrors = (this.#status.reactionErrors ?? 0) + 1;
3558
- this.#logger.warn?.(`[dsh-feishu] unable to add ${emojiType} reaction:`, error.message);
3559
- return null;
3560
- }
3607
+ #beginReaction(messageId) {
3608
+ return beginStatusReaction({
3609
+ adapter: this.#channel,
3610
+ target: messageId,
3611
+ reactions: { processing: 'OnIt', success: 'DONE', error: 'ERROR' },
3612
+ status: this.#status,
3613
+ logger: this.#logger,
3614
+ label: 'feishu',
3615
+ });
3561
3616
  }
3562
3617
 
3563
- async #removeProcessingReaction(messageId, processingReaction) {
3564
- const reactionId = await processingReaction;
3565
- if (reactionId && this.#channel?.removeReaction) {
3566
- try {
3567
- await this.#channel.removeReaction(messageId, reactionId);
3568
- this.#status.reactionsRemoved = (this.#status.reactionsRemoved ?? 0) + 1;
3569
- } catch (error) {
3570
- this.#status.reactionErrors = (this.#status.reactionErrors ?? 0) + 1;
3571
- this.#logger.warn?.('[dsh-feishu] unable to remove processing reaction:', error.message);
3572
- }
3573
- }
3618
+ #removeProcessingReaction(_messageId, processingReaction) {
3619
+ processingReaction.clear();
3574
3620
  }
3575
3621
 
3576
- async #finishReaction(messageId, processingReaction, finalEmojiType) {
3577
- await this.#removeProcessingReaction(messageId, processingReaction);
3578
- await this.#addReaction(messageId, finalEmojiType);
3622
+ #finishReaction(_messageId, processingReaction, finalEmojiType) {
3623
+ if (finalEmojiType === 'ERROR') processingReaction.error();
3624
+ else processingReaction.success();
3579
3625
  }
3580
3626
 
3581
3627
  async #send(chatId, text) {
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { connectionTestMessage } from '../shared/connection-test.mjs';
3
+ import { publicMessageFailure } from '../shared/message-failure.mjs';
3
4
  import { RegistrationManager } from './registration-manager.mjs';
4
5
  import {
5
6
  CALLBACK_REPAIR_OPERATION,
@@ -633,6 +634,7 @@ export class MultiBotDshFeishuController {
633
634
  groupMessagePermissionGranted: config.groupMessagePermissionGranted === true,
634
635
  bot: publicBot(config),
635
636
  connection,
637
+ lastMessageError: publicMessageFailure(connection.lastMessageError),
636
638
  error,
637
639
  };
638
640
  });