@soimy/dingtalk 3.6.10 → 3.7.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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11655 -10324
- package/dist/src/{ack-reaction-service.d.ts → ack-reaction/ack-reaction-service.d.ts} +1 -1
- package/dist/src/ack-reaction/dynamic-ack-reaction-controller.d.ts +3 -3
- package/dist/src/card/ask-user-question-context.d.ts +1 -1
- package/dist/src/card/ask-user-question-store.d.ts +1 -1
- package/dist/src/card/ask-user-question.d.ts +1 -1
- package/dist/src/card/card-action-handler.d.ts +2 -2
- package/dist/src/{card-draft-controller.d.ts → card/card-draft-controller.d.ts} +5 -1
- package/dist/src/card/card-run-registry.d.ts +2 -2
- package/dist/src/{card-service.d.ts → card/card-service.d.ts} +1 -1
- package/dist/src/card/card-stop-handler.d.ts +1 -1
- package/dist/src/card/card-streaming-mode.d.ts +1 -1
- package/dist/src/card/card-task-progress.d.ts +60 -0
- package/dist/src/channel.d.ts +6 -6
- package/dist/src/command/card-stop-command.d.ts +1 -1
- package/dist/src/{feedback-learning-service.d.ts → command/feedback-learning-service.d.ts} +1 -1
- package/dist/src/command/inbound-command-dispatch-service.d.ts +2 -2
- package/dist/src/{learning-command-service.d.ts → command/learning-command-service.d.ts} +1 -1
- package/dist/src/gateway/channel-gateway.d.ts +1 -1
- package/dist/src/{connection-manager.d.ts → gateway/connection-manager.d.ts} +1 -1
- package/dist/src/{docs-service.d.ts → gateway/docs-service.d.ts} +5 -5
- package/dist/src/{inbound-handler.d.ts → gateway/inbound-handler.d.ts} +1 -1
- package/dist/src/gateway/inbound-session-queue-dispatcher.d.ts +1 -1
- package/dist/src/messaging/attachment-text-extractor.d.ts +1 -1
- package/dist/src/messaging/btw-deliver.d.ts +1 -1
- package/dist/src/messaging/channel-outbound.d.ts +1 -1
- package/dist/src/messaging/inline-directives.d.ts +42 -0
- package/dist/src/{media-utils.d.ts → messaging/media-utils.d.ts} +1 -1
- package/dist/src/{message-context-store.d.ts → messaging/message-context-store.d.ts} +7 -1
- package/dist/src/{message-utils.d.ts → messaging/message-utils.d.ts} +1 -1
- package/dist/src/messaging/quoted-context.d.ts +2 -2
- package/dist/src/messaging/quoted-file-service.d.ts +1 -1
- package/dist/src/messaging/quoted-ref.d.ts +2 -2
- package/dist/src/{reply-strategy-card.d.ts → messaging/reply-strategy-card.d.ts} +1 -1
- package/dist/src/{reply-strategy-types.d.ts → messaging/reply-strategy-types.d.ts} +10 -1
- package/dist/src/{reply-strategy-with-reaction.d.ts → messaging/reply-strategy-with-reaction.d.ts} +1 -1
- package/dist/src/{reply-strategy.d.ts → messaging/reply-strategy.d.ts} +1 -1
- package/dist/src/{send-service.d.ts → messaging/send-service.d.ts} +1 -1
- package/dist/src/platform/channel-status.d.ts +1 -1
- package/dist/src/platform/config.d.ts +110 -0
- package/dist/src/platform/runtime-events.d.ts +67 -0
- package/dist/src/{types.d.ts → platform/types.d.ts} +75 -2
- package/dist/src/{persistence-store.d.ts → shared/persistence-store.d.ts} +1 -1
- package/dist/src/{utils.d.ts → shared/utils.d.ts} +1 -1
- package/dist/src/targeting/agent-name-matcher.d.ts +1 -1
- package/dist/src/targeting/agent-routing.d.ts +3 -3
- package/dist/src/{session-routing.d.ts → targeting/session-routing.d.ts} +1 -1
- package/index.ts +193 -69
- package/openclaw.plugin.json +158 -2
- package/package.json +16 -16
- package/src/{ack-reaction-classifier.ts → ack-reaction/ack-reaction-classifier.ts} +85 -13
- package/src/{ack-reaction-service.ts → ack-reaction/ack-reaction-service.ts} +13 -7
- package/src/ack-reaction/dynamic-ack-reaction-controller.ts +46 -31
- package/src/ack-reaction/dynamic-ack-reaction-progress.ts +4 -1
- package/src/card/ask-user-question-context.ts +1 -1
- package/src/card/ask-user-question-store.ts +2 -2
- package/src/card/ask-user-question.ts +11 -7
- package/src/card/card-action-handler.ts +2 -2
- package/src/{card-callback-service.ts → card/card-callback-service.ts} +7 -9
- package/src/card/card-draft-controller.ts +770 -0
- package/src/card/card-markdown-image-reroute.ts +8 -2
- package/src/card/card-run-registry.ts +17 -7
- package/src/{card-service.ts → card/card-service.ts} +91 -61
- package/src/card/card-stop-handler.ts +12 -8
- package/src/card/card-streaming-mode.ts +1 -1
- package/src/card/card-task-progress.ts +330 -0
- package/src/card/draft-stream-loop.ts +119 -0
- package/src/card/reasoning-answer-split.ts +125 -121
- package/src/card/reasoning-block-assembler.ts +122 -123
- package/src/{run-usage-store.ts → card/run-usage-store.ts} +18 -6
- package/src/card/statusline-renderer.ts +14 -5
- package/src/card/task-model-metadata.ts +8 -3
- package/src/channel.ts +14 -16
- package/src/command/card-stop-command.ts +2 -2
- package/src/{feedback-learning-service.ts → command/feedback-learning-service.ts} +36 -16
- package/src/{feedback-learning-store.ts → command/feedback-learning-store.ts} +133 -81
- package/src/command/inbound-command-dispatch-service.ts +12 -6
- package/src/{learning-command-service.ts → command/learning-command-service.ts} +36 -29
- package/src/{session-command-service.ts → command/session-command-service.ts} +3 -1
- package/src/gateway/channel-gateway.ts +106 -28
- package/src/{connection-manager.ts → gateway/connection-manager.ts} +45 -30
- package/src/{docs-service.ts → gateway/docs-service.ts} +5 -5
- package/src/{inbound-handler.ts → gateway/inbound-handler.ts} +144 -85
- package/src/gateway/inbound-session-queue-dispatcher.ts +18 -14
- package/src/{session-lock.ts → gateway/session-lock.ts} +14 -14
- package/src/messaging/attachment-text-extractor.ts +21 -7
- package/src/messaging/btw-deliver.ts +2 -2
- package/src/messaging/channel-actions.ts +15 -7
- package/src/messaging/channel-outbound.ts +16 -11
- package/src/messaging/inline-directives.ts +342 -0
- package/src/{media-utils.ts → messaging/media-utils.ts} +276 -72
- package/src/{message-context-store.ts → messaging/message-context-store.ts} +11 -2
- package/src/{message-utils.ts → messaging/message-utils.ts} +46 -11
- package/src/messaging/quoted-context.ts +2 -2
- package/src/messaging/quoted-file-service.ts +311 -294
- package/src/messaging/quoted-ref.ts +11 -7
- package/src/{reply-strategy-card.ts → messaging/reply-strategy-card.ts} +178 -101
- package/src/{reply-strategy-markdown.ts → messaging/reply-strategy-markdown.ts} +8 -2
- package/src/{reply-strategy-types.ts → messaging/reply-strategy-types.ts} +11 -1
- package/src/{reply-strategy-with-reaction.ts → messaging/reply-strategy-with-reaction.ts} +6 -1
- package/src/{reply-strategy.ts → messaging/reply-strategy.ts} +2 -2
- package/src/{send-service.ts → messaging/send-service.ts} +100 -65
- package/src/{auth.ts → platform/auth.ts} +2 -2
- package/src/platform/channel-status.ts +3 -3
- package/src/{config-schema.ts → platform/config-schema.ts} +81 -4
- package/src/{config.ts → platform/config.ts} +167 -19
- package/src/{device-registration.ts → platform/device-registration.ts} +5 -4
- package/src/{onboarding.ts → platform/onboarding.ts} +1 -4
- package/src/{plugin-sdk-channel-actions-augment.ts → platform/plugin-sdk-channel-actions-augment.ts} +6 -5
- package/src/platform/runtime-events.ts +202 -0
- package/src/{secret-input.ts → platform/secret-input.ts} +117 -11
- package/src/{types.ts → platform/types.ts} +77 -2
- package/src/{http-client.ts → shared/http-client.ts} +1 -3
- package/src/{persistence-store.ts → shared/persistence-store.ts} +6 -6
- package/src/{utils.ts → shared/utils.ts} +30 -13
- package/src/targeting/agent-name-matcher.ts +1 -1
- package/src/targeting/agent-routing.ts +7 -7
- package/src/targeting/group-members-store.ts +1 -1
- package/src/{session-peer-store.ts → targeting/session-peer-store.ts} +9 -4
- package/src/{session-routing.ts → targeting/session-routing.ts} +1 -1
- package/src/targeting/target-directory-adapter.ts +3 -3
- package/src/targeting/target-directory-store.ts +1 -1
- package/src/targeting/target-input.ts +1 -1
- package/dist/src/ack-reaction/dynamic-ack-reaction-events.d.ts +0 -35
- package/dist/src/config.d.ts +0 -59
- package/src/ack-reaction/dynamic-ack-reaction-events.ts +0 -123
- package/src/card-draft-controller.ts +0 -637
- package/src/draft-stream-loop.ts +0 -119
- /package/dist/src/{ack-reaction-classifier.d.ts → ack-reaction/ack-reaction-classifier.d.ts} +0 -0
- /package/dist/src/{card-callback-service.d.ts → card/card-callback-service.d.ts} +0 -0
- /package/dist/src/{draft-stream-loop.d.ts → card/draft-stream-loop.d.ts} +0 -0
- /package/dist/src/{run-usage-store.d.ts → card/run-usage-store.d.ts} +0 -0
- /package/dist/src/{feedback-learning-store.d.ts → command/feedback-learning-store.d.ts} +0 -0
- /package/dist/src/{session-command-service.d.ts → command/session-command-service.d.ts} +0 -0
- /package/dist/src/{session-lock.d.ts → gateway/session-lock.d.ts} +0 -0
- /package/dist/src/{proactive-risk-registry.d.ts → messaging/proactive-risk-registry.d.ts} +0 -0
- /package/dist/src/{reply-strategy-markdown.d.ts → messaging/reply-strategy-markdown.d.ts} +0 -0
- /package/dist/src/{access-control.d.ts → platform/access-control.d.ts} +0 -0
- /package/dist/src/{auth.d.ts → platform/auth.d.ts} +0 -0
- /package/dist/src/{config-schema.d.ts → platform/config-schema.d.ts} +0 -0
- /package/dist/src/{device-registration.d.ts → platform/device-registration.d.ts} +0 -0
- /package/dist/src/{logger-context.d.ts → platform/logger-context.d.ts} +0 -0
- /package/dist/src/{onboarding.d.ts → platform/onboarding.d.ts} +0 -0
- /package/dist/src/{plugin-sdk-channel-actions-augment.d.ts → platform/plugin-sdk-channel-actions-augment.d.ts} +0 -0
- /package/dist/src/{runtime.d.ts → platform/runtime.d.ts} +0 -0
- /package/dist/src/{secret-input.d.ts → platform/secret-input.d.ts} +0 -0
- /package/dist/src/{session-state.d.ts → platform/session-state.d.ts} +0 -0
- /package/dist/src/{signature.d.ts → platform/signature.d.ts} +0 -0
- /package/dist/src/{dedup.d.ts → shared/dedup.d.ts} +0 -0
- /package/dist/src/{http-client.d.ts → shared/http-client.d.ts} +0 -0
- /package/dist/src/{path-utils.d.ts → shared/path-utils.d.ts} +0 -0
- /package/dist/src/{peer-id-registry.d.ts → targeting/peer-id-registry.d.ts} +0 -0
- /package/dist/src/{session-peer-store.d.ts → targeting/session-peer-store.d.ts} +0 -0
- /package/src/{proactive-risk-registry.ts → messaging/proactive-risk-registry.ts} +0 -0
- /package/src/{access-control.ts → platform/access-control.ts} +0 -0
- /package/src/{logger-context.ts → platform/logger-context.ts} +0 -0
- /package/src/{runtime.ts → platform/runtime.ts} +0 -0
- /package/src/{session-state.ts → platform/session-state.ts} +0 -0
- /package/src/{signature.ts → platform/signature.ts} +0 -0
- /package/src/{dedup.ts → shared/dedup.ts} +0 -0
- /package/src/{path-utils.ts → shared/path-utils.ts} +0 -0
- /package/src/{peer-id-registry.ts → targeting/peer-id-registry.ts} +0 -0
|
@@ -1,109 +1,115 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { formatInboundEnvelope } from "openclaw/plugin-sdk/channel-inbound";
|
|
3
4
|
import { isAbortRequestText, isBtwRequestText } from "openclaw/plugin-sdk/reply-runtime";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { attachNativeAckReaction } from "./ack-reaction-service";
|
|
8
|
-
import { createDynamicAckReactionController } from "./ack-reaction/dynamic-ack-reaction-controller";
|
|
9
|
-
import { getAccessToken } from "./auth";
|
|
10
|
-
import {
|
|
11
|
-
createAICard,
|
|
12
|
-
commitAICardBlocks,
|
|
13
|
-
isCardInTerminalState,
|
|
14
|
-
recallAICardMessage,
|
|
15
|
-
} from "./card-service";
|
|
5
|
+
import { classifyAckReactionEmoji } from "../ack-reaction/ack-reaction-classifier";
|
|
6
|
+
import { attachNativeAckReaction } from "../ack-reaction/ack-reaction-service";
|
|
7
|
+
import { createDynamicAckReactionController } from "../ack-reaction/dynamic-ack-reaction-controller";
|
|
16
8
|
import {
|
|
17
9
|
invalidateAskUserQuestionsForScope,
|
|
18
10
|
syncInvalidatedAskUserQuestionCards,
|
|
19
|
-
} from "
|
|
11
|
+
} from "../card/ask-user-question";
|
|
20
12
|
import {
|
|
21
13
|
getDingTalkQuestionContext,
|
|
22
14
|
withDingTalkQuestionContext,
|
|
23
|
-
} from "
|
|
24
|
-
import { isCardRunStopRequested, registerCardRun, removeCardRun } from "
|
|
25
|
-
import { renderStatusLine } from "./card/statusline-renderer";
|
|
26
|
-
import { resolveConfiguredTaskModelMetadata } from "./card/task-model-metadata";
|
|
27
|
-
import { dispatchDingTalkCardStopCommand } from "./command/card-stop-command";
|
|
28
|
-
import { handleInboundCommandDispatch } from "./command/inbound-command-dispatch-service";
|
|
15
|
+
} from "../card/ask-user-question-context";
|
|
16
|
+
import { isCardRunStopRequested, registerCardRun, removeCardRun } from "../card/card-run-registry";
|
|
29
17
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from "
|
|
35
|
-
import {
|
|
36
|
-
import
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
18
|
+
createAICard,
|
|
19
|
+
commitAICardBlocks,
|
|
20
|
+
isCardInTerminalState,
|
|
21
|
+
recallAICardMessage,
|
|
22
|
+
} from "../card/card-service";
|
|
23
|
+
import { renderStatusLine } from "../card/statusline-renderer";
|
|
24
|
+
import { resolveConfiguredTaskModelMetadata } from "../card/task-model-metadata";
|
|
25
|
+
import { dispatchDingTalkCardStopCommand } from "../command/card-stop-command";
|
|
26
|
+
import { buildLearningContextBlock, isLearningEnabled } from "../command/feedback-learning-service";
|
|
27
|
+
import { handleInboundCommandDispatch } from "../command/inbound-command-dispatch-service";
|
|
28
|
+
import { extractAttachmentText } from "../messaging/attachment-text-extractor";
|
|
29
|
+
import { deliverBtwReply, stripLeadingMentions } from "../messaging/btw-deliver";
|
|
30
|
+
import { parseInlineDirectives } from "../messaging/inline-directives";
|
|
31
|
+
import { prepareMediaInput, resolveOutboundMediaType } from "../messaging/media-utils";
|
|
40
32
|
import {
|
|
41
33
|
DEFAULT_MEDIA_CONTEXT_TTL_MS,
|
|
42
34
|
DEFAULT_MESSAGE_CONTEXT_TTL_DAYS,
|
|
43
35
|
upsertInboundMessageContext,
|
|
44
|
-
} from "
|
|
45
|
-
import { extractMessageContent } from "
|
|
46
|
-
import {
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
} from "../messaging/message-context-store";
|
|
37
|
+
import { extractMessageContent } from "../messaging/message-utils";
|
|
38
|
+
import {
|
|
39
|
+
clearProactiveRiskObservationsForTest,
|
|
40
|
+
getProactiveRiskObservationForAny,
|
|
41
|
+
} from "../messaging/proactive-risk-registry";
|
|
42
|
+
import { resolveQuotedRuntimeContext } from "../messaging/quoted-context";
|
|
49
43
|
import {
|
|
50
44
|
downloadGroupFile,
|
|
51
45
|
getUnionIdByStaffId,
|
|
52
46
|
resolveQuotedFile,
|
|
53
|
-
} from "
|
|
47
|
+
} from "../messaging/quoted-file-service";
|
|
54
48
|
import {
|
|
55
49
|
buildInboundQuotedRef,
|
|
56
50
|
createReplyQuotedRef,
|
|
57
51
|
resolveQuotedRecord,
|
|
58
|
-
} from "
|
|
59
|
-
import {
|
|
52
|
+
} from "../messaging/quoted-ref";
|
|
53
|
+
import { createReplyStrategy } from "../messaging/reply-strategy";
|
|
54
|
+
import type { DeliverPayload, ReplyStrategy } from "../messaging/reply-strategy-types";
|
|
55
|
+
import { sendBySession, sendMessage, sendProactiveMedia } from "../messaging/send-service";
|
|
60
56
|
import {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
withReplySessionConflictRetry,
|
|
67
|
-
} from "./gateway/reply-session-conflict";
|
|
68
|
-
import { createReplyStrategy } from "./reply-strategy";
|
|
69
|
-
import type { DeliverPayload } from "./reply-strategy-types";
|
|
70
|
-
import { getDingTalkRuntime } from "./runtime";
|
|
71
|
-
import { sendBySession, sendMessage, sendProactiveMedia } from "./send-service";
|
|
72
|
-
import { acquireSessionLock } from "./session-lock";
|
|
73
|
-
import {
|
|
74
|
-
clearSessionPeerOverride,
|
|
75
|
-
getSessionPeerOverride,
|
|
76
|
-
setSessionPeerOverride,
|
|
77
|
-
} from "./session-peer-store";
|
|
78
|
-
import { resolveDingTalkSessionPeer } from "./session-routing";
|
|
79
|
-
import { getSessionState, initSessionState } from "./session-state";
|
|
80
|
-
import { getAgentDisplayName } from "./targeting/agent-name-matcher";
|
|
81
|
-
import {
|
|
82
|
-
buildAgentSessionKey,
|
|
83
|
-
dispatchSubAgents,
|
|
84
|
-
resolveMessageTarget,
|
|
85
|
-
sendUnmatchedAgentNotice,
|
|
86
|
-
} from "./targeting/agent-routing";
|
|
87
|
-
import { formatGroupMembers, noteGroupMember } from "./targeting/group-members-store";
|
|
57
|
+
normalizeAllowFrom,
|
|
58
|
+
isSenderAllowed,
|
|
59
|
+
resolveGroupAccess,
|
|
60
|
+
} from "../platform/access-control";
|
|
61
|
+
import { getAccessToken } from "../platform/auth";
|
|
88
62
|
import {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
63
|
+
resolveAckReactionSetting,
|
|
64
|
+
resolveGroupConfig,
|
|
65
|
+
resolveRelativePath,
|
|
66
|
+
resolveRobotCode,
|
|
67
|
+
} from "../platform/config";
|
|
68
|
+
import { setCurrentLogger } from "../platform/logger-context";
|
|
69
|
+
import { getDingTalkRuntime } from "../platform/runtime";
|
|
70
|
+
import { createRuntimeEventsFanout } from "../platform/runtime-events";
|
|
71
|
+
import { getSessionState, initSessionState } from "../platform/session-state";
|
|
72
|
+
import { AICardStatus } from "../platform/types";
|
|
93
73
|
import type {
|
|
94
74
|
DingTalkConfig,
|
|
95
75
|
HandleDingTalkMessageParams,
|
|
96
76
|
Logger,
|
|
97
77
|
MediaFile,
|
|
98
78
|
ResolvedDingTalkRoute,
|
|
99
|
-
} from "
|
|
79
|
+
} from "../platform/types";
|
|
80
|
+
import axios from "../shared/http-client";
|
|
100
81
|
import {
|
|
101
82
|
formatDingTalkErrorPayloadLog,
|
|
102
83
|
getErrorMessage,
|
|
103
84
|
getErrorResponseData,
|
|
104
85
|
maskSensitiveData,
|
|
105
86
|
parseBooleanLike,
|
|
106
|
-
} from "
|
|
87
|
+
} from "../shared/utils";
|
|
88
|
+
import { getAgentDisplayName } from "../targeting/agent-name-matcher";
|
|
89
|
+
import {
|
|
90
|
+
buildAgentSessionKey,
|
|
91
|
+
dispatchSubAgents,
|
|
92
|
+
resolveMessageTarget,
|
|
93
|
+
sendUnmatchedAgentNotice,
|
|
94
|
+
} from "../targeting/agent-routing";
|
|
95
|
+
import { formatGroupMembers, noteGroupMember } from "../targeting/group-members-store";
|
|
96
|
+
import { registerPeerId } from "../targeting/peer-id-registry";
|
|
97
|
+
import {
|
|
98
|
+
clearSessionPeerOverride,
|
|
99
|
+
getSessionPeerOverride,
|
|
100
|
+
setSessionPeerOverride,
|
|
101
|
+
} from "../targeting/session-peer-store";
|
|
102
|
+
import { resolveDingTalkSessionPeer } from "../targeting/session-routing";
|
|
103
|
+
import {
|
|
104
|
+
upsertObservedGroupTarget,
|
|
105
|
+
upsertObservedUserTarget,
|
|
106
|
+
} from "../targeting/target-directory-store";
|
|
107
|
+
import { dispatchInboundViaSessionQueue } from "./inbound-session-queue-dispatcher";
|
|
108
|
+
import {
|
|
109
|
+
isReplySessionConflictError,
|
|
110
|
+
withReplySessionConflictRetry,
|
|
111
|
+
} from "./reply-session-conflict";
|
|
112
|
+
import { acquireSessionLock } from "./session-lock";
|
|
107
113
|
|
|
108
114
|
const DEFAULT_PROACTIVE_HINT_COOLDOWN_HOURS = 24;
|
|
109
115
|
const MIN_THINKING_REACTION_VISIBLE_MS = 1200;
|
|
@@ -431,14 +437,25 @@ function buildGroupTurnContextPrompt(params: {
|
|
|
431
437
|
conversationId: string;
|
|
432
438
|
senderDingtalkId: string;
|
|
433
439
|
senderName?: string;
|
|
440
|
+
mentionedDingtalkIds?: readonly string[];
|
|
434
441
|
}): string {
|
|
435
442
|
const sanitizedSenderName = sanitizeGroupPromptName(params.senderName) || "Unknown";
|
|
443
|
+
const mentionedDingtalkIds = [
|
|
444
|
+
...new Set(
|
|
445
|
+
(params.mentionedDingtalkIds || [])
|
|
446
|
+
.map((value) => value.replace(/[\r\n,=]/g, " ").trim())
|
|
447
|
+
.filter(Boolean),
|
|
448
|
+
),
|
|
449
|
+
];
|
|
436
450
|
return [
|
|
437
451
|
"Current DingTalk group turn context:",
|
|
438
452
|
`- conversationId: ${params.conversationId}`,
|
|
439
453
|
`- senderDingtalkId: ${params.senderDingtalkId}`,
|
|
440
454
|
`- senderName: ${sanitizedSenderName}`,
|
|
455
|
+
`- mentionedDingtalkIds: ${mentionedDingtalkIds.length > 0 ? mentionedDingtalkIds.join(", ") : "(none provided)"}`,
|
|
441
456
|
"Treat senderDingtalkId and senderName as the authoritative sender for this turn. Do not guess the current sender from GroupMembers.",
|
|
457
|
+
"Treat mentionedDingtalkIds as raw structured IDs only; do not infer a display name or map them to text mentions without payload evidence.",
|
|
458
|
+
"mentionedDingtalkIds are webhook atUsers/richText IDs (for example $:LWCP_v1:$...), not staff IDs; they are not comparable with senderDingtalkId or GroupMembers ids and must not be used as send targets.",
|
|
442
459
|
].join("\n");
|
|
443
460
|
}
|
|
444
461
|
|
|
@@ -1007,6 +1024,17 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
1007
1024
|
const quotedRef = buildInboundQuotedRef(data, extractedContent);
|
|
1008
1025
|
const replyQuotedRef = createReplyQuotedRef(data.msgId);
|
|
1009
1026
|
const content = extractedContent;
|
|
1027
|
+
const mentionUserIds = [
|
|
1028
|
+
...new Set(
|
|
1029
|
+
[
|
|
1030
|
+
...(content.atUserDingtalkIds || []),
|
|
1031
|
+
...(content.atMentions || []).map((mention) => mention.userId),
|
|
1032
|
+
]
|
|
1033
|
+
.filter((value): value is string => typeof value === "string")
|
|
1034
|
+
.map((value) => value.trim())
|
|
1035
|
+
.filter(Boolean),
|
|
1036
|
+
),
|
|
1037
|
+
];
|
|
1010
1038
|
// Decide control-message bypasses once from the user-authored text. Reusing
|
|
1011
1039
|
// the result below keeps /stop out of the FIFO queue without calling the SDK
|
|
1012
1040
|
// classifier a second time after attachment/OCR enrichment.
|
|
@@ -1081,7 +1109,7 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
1081
1109
|
// abort branch finalizes the card with the abort confirmation text instead of
|
|
1082
1110
|
// sending a separate plain-text message.
|
|
1083
1111
|
let useCardMode = dingtalkConfig.messageType === "card";
|
|
1084
|
-
let currentAICard: import("
|
|
1112
|
+
let currentAICard: import("../platform/types").AICardInstance | undefined;
|
|
1085
1113
|
let questionCardTookOver = false;
|
|
1086
1114
|
|
|
1087
1115
|
let cardFlightKey: string | undefined;
|
|
@@ -1247,6 +1275,9 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
1247
1275
|
quotedRef,
|
|
1248
1276
|
senderId,
|
|
1249
1277
|
senderName,
|
|
1278
|
+
mentions: content.atMentions?.map((mention) => mention.name),
|
|
1279
|
+
mentionUserIds,
|
|
1280
|
+
chatType: isDirect ? "direct" : "group",
|
|
1250
1281
|
createdAt: data.createAt,
|
|
1251
1282
|
ttlMs: ttlDaysToMs(journalTTLDays),
|
|
1252
1283
|
ttlReferenceMs: data.createAt,
|
|
@@ -1779,6 +1810,7 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
1779
1810
|
conversationId: groupId,
|
|
1780
1811
|
senderDingtalkId: senderId,
|
|
1781
1812
|
senderName,
|
|
1813
|
+
mentionedDingtalkIds: mentionUserIds,
|
|
1782
1814
|
}),
|
|
1783
1815
|
groupConfig?.systemPrompt?.trim(),
|
|
1784
1816
|
]
|
|
@@ -1792,7 +1824,10 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
1792
1824
|
const groupMembers = !isDirect ? formatGroupMembers(storePath, groupId) : undefined;
|
|
1793
1825
|
|
|
1794
1826
|
const fromLabel = isDirect ? `${senderName} (${senderId})` : `${groupName} - ${senderName}`;
|
|
1795
|
-
|
|
1827
|
+
// `rt.channel.reply.formatInboundEnvelope` was removed from the runtime
|
|
1828
|
+
// facade in OpenClaw 2026.8.x; the helper is still exported from the
|
|
1829
|
+
// `channel-inbound` SDK subpath.
|
|
1830
|
+
const body = formatInboundEnvelope({
|
|
1796
1831
|
channel: "DingTalk",
|
|
1797
1832
|
from: fromLabel,
|
|
1798
1833
|
timestamp: data.createAt,
|
|
@@ -2260,7 +2295,12 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
2260
2295
|
};
|
|
2261
2296
|
}
|
|
2262
2297
|
).events;
|
|
2298
|
+
const replyRuntimeEvents = createRuntimeEventsFanout(runtimeEvents, { log });
|
|
2263
2299
|
const releaseSessionLock = await acquireSessionLock(route.sessionKey);
|
|
2300
|
+
// Declared outside the dispatch try-block so the finally below can always
|
|
2301
|
+
// release strategy-owned resources, including early returns that neither
|
|
2302
|
+
// finalize nor abort (ask-user question-card takeover).
|
|
2303
|
+
let strategyForCleanup: ReplyStrategy | undefined;
|
|
2264
2304
|
const dynamicAckReactionController = createDynamicAckReactionController({
|
|
2265
2305
|
enabled: shouldTrackDynamicAckReaction,
|
|
2266
2306
|
initialReaction: resolvedAckReaction || "",
|
|
@@ -2271,7 +2311,7 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
2271
2311
|
conversationId: groupId,
|
|
2272
2312
|
sessionKey: route.sessionKey,
|
|
2273
2313
|
log,
|
|
2274
|
-
runtimeEvents,
|
|
2314
|
+
runtimeEvents: replyRuntimeEvents,
|
|
2275
2315
|
onReactionDisposed: () => {
|
|
2276
2316
|
ackReactionAttached = false;
|
|
2277
2317
|
},
|
|
@@ -2346,7 +2386,9 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
2346
2386
|
isStopRequested: isCurrentCardStopRequested,
|
|
2347
2387
|
inboundText: rawInboundText,
|
|
2348
2388
|
taskMeta,
|
|
2389
|
+
runtimeEvents: replyRuntimeEvents,
|
|
2349
2390
|
});
|
|
2391
|
+
strategyForCleanup = strategy;
|
|
2350
2392
|
|
|
2351
2393
|
let deliveredFinalCount = 0;
|
|
2352
2394
|
// Extracted as a thunk so reply-session init conflicts (raised by the
|
|
@@ -2530,19 +2572,36 @@ async function handleDingTalkMessageInner(params: HandleDingTalkMessageParams):
|
|
|
2530
2572
|
}
|
|
2531
2573
|
await strategy.finalize();
|
|
2532
2574
|
} finally {
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2575
|
+
try {
|
|
2576
|
+
// Only remove the registry entry if no stop was requested. When a stop is
|
|
2577
|
+
// in progress, card-stop-handler may still be running async operations
|
|
2578
|
+
// (finalize card, hide button, gateway abort) that read the record.
|
|
2579
|
+
// In that case, let the 30-minute TTL sweep handle cleanup.
|
|
2580
|
+
if (currentOutTrackId && !isCardRunStopRequested(currentOutTrackId)) {
|
|
2581
|
+
removeCardRun(currentOutTrackId);
|
|
2582
|
+
}
|
|
2583
|
+
// Guarantee strategy-owned cleanup on every exit path, including the
|
|
2584
|
+
// question-card takeover return above. `dispose()` is idempotent, so a
|
|
2585
|
+
// prior finalize()/abort() already having disposed is harmless. A
|
|
2586
|
+
// failing dispose is logged and swallowed: it must not skip the
|
|
2587
|
+
// remaining cleanup below.
|
|
2588
|
+
if (strategyForCleanup) {
|
|
2589
|
+
await strategyForCleanup.dispose().catch((disposeErr: unknown) => {
|
|
2590
|
+
log?.warn?.(
|
|
2591
|
+
`[DingTalk] Reply strategy cleanup failed for session ${route.sessionKey}: ${getErrorMessage(disposeErr)}`,
|
|
2592
|
+
);
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
await waitForDynamicAckDispose({
|
|
2596
|
+
dispose: () => dynamicAckReactionController.dispose(MIN_THINKING_REACTION_VISIBLE_MS),
|
|
2597
|
+
log,
|
|
2598
|
+
sessionKey: route.sessionKey,
|
|
2599
|
+
});
|
|
2600
|
+
} finally {
|
|
2601
|
+
// Unconditional: a session lock that never releases blocks every later
|
|
2602
|
+
// message on this session, so nothing above may prevent it.
|
|
2603
|
+
releaseSessionLock();
|
|
2539
2604
|
}
|
|
2540
|
-
await waitForDynamicAckDispose({
|
|
2541
|
-
dispose: () => dynamicAckReactionController.dispose(MIN_THINKING_REACTION_VISIBLE_MS),
|
|
2542
|
-
log,
|
|
2543
|
-
sessionKey: route.sessionKey,
|
|
2544
|
-
});
|
|
2545
|
-
releaseSessionLock();
|
|
2546
2605
|
}
|
|
2547
2606
|
} finally {
|
|
2548
2607
|
if (cardFlightKey) {
|
|
@@ -21,13 +21,20 @@
|
|
|
21
21
|
// orchestrator, adapted to soimy's blocking gateway contract (we await each
|
|
22
22
|
// task so the gateway's per-message dedup stays correct).
|
|
23
23
|
|
|
24
|
-
import { attachNativeAckReaction } from "../ack-reaction-service";
|
|
24
|
+
import { attachNativeAckReaction } from "../ack-reaction/ack-reaction-service";
|
|
25
25
|
import {
|
|
26
26
|
createAICard,
|
|
27
27
|
isCardInTerminalState,
|
|
28
28
|
recallAICardMessage,
|
|
29
29
|
streamAICard,
|
|
30
|
-
} from "../card-service";
|
|
30
|
+
} from "../card/card-service";
|
|
31
|
+
import { sendMessage } from "../messaging/send-service";
|
|
32
|
+
import type {
|
|
33
|
+
AICardInstance,
|
|
34
|
+
DingTalkConfig,
|
|
35
|
+
DingTalkInboundMessage,
|
|
36
|
+
Logger,
|
|
37
|
+
} from "../platform/types";
|
|
31
38
|
import {
|
|
32
39
|
chainInboundSessionTask,
|
|
33
40
|
getInboundSessionQueueDepth,
|
|
@@ -37,8 +44,6 @@ import {
|
|
|
37
44
|
MAX_INBOUND_SESSION_QUEUE_WAIT_MS,
|
|
38
45
|
pickQueueBusyAckPhrase,
|
|
39
46
|
} from "./inbound-session-queue";
|
|
40
|
-
import { sendMessage } from "../send-service";
|
|
41
|
-
import type { AICardInstance, DingTalkConfig, DingTalkInboundMessage, Logger } from "../types";
|
|
42
47
|
|
|
43
48
|
export interface InboundQueueDispatchInput {
|
|
44
49
|
accountId: string;
|
|
@@ -138,13 +143,14 @@ export async function dispatchInboundViaSessionQueue<T>(
|
|
|
138
143
|
// slot below. Otherwise a burst of inbound messages can all observe the
|
|
139
144
|
// same pre-await depth and each pass the cap check.
|
|
140
145
|
let queuedAckState: "queued" | "timed-out" = "queued";
|
|
141
|
-
const preCreatedCardPromise =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
const preCreatedCardPromise =
|
|
147
|
+
wasBusy && shouldPrepareQueueAckCard(input)
|
|
148
|
+
? tryPrepareQueueAckCard(input, () =>
|
|
149
|
+
queuedAckState === "timed-out"
|
|
150
|
+
? { content: QUEUE_WAIT_TIMEOUT_ACK, finished: true }
|
|
151
|
+
: { content: pickQueueBusyAckPhrase(), finished: false },
|
|
152
|
+
)
|
|
153
|
+
: undefined;
|
|
148
154
|
// Chain onto the prior task for this conversation and AWAIT. Awaiting (rather
|
|
149
155
|
// than fire-and-forget) preserves the gateway's per-message dedup:
|
|
150
156
|
// `markMessageProcessed` runs only after this message truly completes, so a
|
|
@@ -153,9 +159,7 @@ export async function dispatchInboundViaSessionQueue<T>(
|
|
|
153
159
|
return await chainInboundSessionTask(
|
|
154
160
|
queueKey,
|
|
155
161
|
async () => {
|
|
156
|
-
const preCreatedCard = preCreatedCardPromise
|
|
157
|
-
? await preCreatedCardPromise
|
|
158
|
-
: undefined;
|
|
162
|
+
const preCreatedCard = preCreatedCardPromise ? await preCreatedCardPromise : undefined;
|
|
159
163
|
if (!preCreatedCard) {
|
|
160
164
|
return handler(undefined);
|
|
161
165
|
}
|
|
@@ -13,22 +13,22 @@ export const SESSION_LOCK_NAMESPACE_POLICY = "memory-only" as const;
|
|
|
13
13
|
* Different session keys run in parallel; same key runs serially.
|
|
14
14
|
*/
|
|
15
15
|
export async function acquireSessionLock(sessionKey: string): Promise<() => void> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
let release!: () => void;
|
|
17
|
+
const gate = new Promise<void>((r) => {
|
|
18
|
+
release = r;
|
|
19
|
+
});
|
|
20
|
+
const prev = locks.get(sessionKey) ?? Promise.resolve();
|
|
21
|
+
locks.set(sessionKey, gate);
|
|
22
|
+
await prev;
|
|
23
|
+
return () => {
|
|
24
|
+
if (locks.get(sessionKey) === gate) {
|
|
25
|
+
locks.delete(sessionKey);
|
|
26
|
+
}
|
|
27
|
+
release();
|
|
28
|
+
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** Visible for testing only. */
|
|
32
32
|
export function _getLocksMapForTest(): Map<string, Promise<void>> {
|
|
33
|
-
|
|
33
|
+
return locks;
|
|
34
34
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import type { AttachmentTextSource } from "../types";
|
|
3
|
+
import type { AttachmentTextSource } from "../platform/types";
|
|
4
4
|
|
|
5
5
|
const MAX_EXTRACTED_TEXT_CHARS = 6000;
|
|
6
6
|
const MAX_ATTACHMENT_EXTRACT_BYTES = 2 * 1024 * 1024;
|
|
@@ -37,7 +37,8 @@ function isTextLikeMimeType(mimeType: string | undefined): boolean {
|
|
|
37
37
|
function normalizeWhitespace(text: string): string {
|
|
38
38
|
return text
|
|
39
39
|
.replace(/\r\n/g, "\n")
|
|
40
|
-
.split("\u0000")
|
|
40
|
+
.split("\u0000")
|
|
41
|
+
.join("")
|
|
41
42
|
.replace(/[ \t]{2,}/g, " ")
|
|
42
43
|
.replace(/[ \t]+\n/g, "\n")
|
|
43
44
|
.replace(/\n{3,}/g, "\n\n")
|
|
@@ -50,7 +51,9 @@ function limitExtractedText(text: string): AttachmentTextExtractionResult | null
|
|
|
50
51
|
return null;
|
|
51
52
|
}
|
|
52
53
|
const truncated = normalized.length > MAX_EXTRACTED_TEXT_CHARS;
|
|
53
|
-
const limited = truncated
|
|
54
|
+
const limited = truncated
|
|
55
|
+
? `${normalized.slice(0, MAX_EXTRACTED_TEXT_CHARS)}\n\n[内容已截断]`
|
|
56
|
+
: normalized;
|
|
54
57
|
return {
|
|
55
58
|
text: limited,
|
|
56
59
|
truncated,
|
|
@@ -68,10 +71,12 @@ function stripHtml(html: string): string {
|
|
|
68
71
|
.replace(/>/gi, ">")
|
|
69
72
|
.replace(/&/gi, "&")
|
|
70
73
|
.replace(/'/gi, "'")
|
|
71
|
-
.replace(/"/gi, "
|
|
74
|
+
.replace(/"/gi, '"');
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
async function extractTextLikeFile(
|
|
77
|
+
async function extractTextLikeFile(
|
|
78
|
+
filePath: string,
|
|
79
|
+
): Promise<AttachmentTextExtractionResult | null> {
|
|
75
80
|
const raw = await fs.readFile(filePath, "utf8");
|
|
76
81
|
return limitExtractedText(raw);
|
|
77
82
|
}
|
|
@@ -108,7 +113,11 @@ export async function extractAttachmentText(
|
|
|
108
113
|
const mimeType = input.mimeType?.toLowerCase();
|
|
109
114
|
const fileName = (input.fileName || path.basename(input.path)).toLowerCase();
|
|
110
115
|
|
|
111
|
-
if (
|
|
116
|
+
if (
|
|
117
|
+
mimeType?.startsWith("image/") ||
|
|
118
|
+
mimeType?.startsWith("audio/") ||
|
|
119
|
+
mimeType?.startsWith("video/")
|
|
120
|
+
) {
|
|
112
121
|
return null;
|
|
113
122
|
}
|
|
114
123
|
|
|
@@ -127,7 +136,12 @@ export async function extractAttachmentText(
|
|
|
127
136
|
return extractPdf(input.path);
|
|
128
137
|
}
|
|
129
138
|
|
|
130
|
-
if (
|
|
139
|
+
if (
|
|
140
|
+
mimeType === "text/html" ||
|
|
141
|
+
mimeType === "application/xhtml+xml" ||
|
|
142
|
+
fileName.endsWith(".html") ||
|
|
143
|
+
fileName.endsWith(".htm")
|
|
144
|
+
) {
|
|
131
145
|
return extractHtml(input.path);
|
|
132
146
|
}
|
|
133
147
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { DingTalkConfig, Logger } from "../platform/types";
|
|
2
|
+
import { sendMessage } from "./send-service";
|
|
3
3
|
|
|
4
4
|
const MAX_QUESTION_LENGTH = 80;
|
|
5
5
|
const LEADING_MENTIONS_RE = /^(?:@\S+\s+)*/u;
|
|
@@ -3,12 +3,12 @@ import type { ChannelMessageActionAdapter } from "openclaw/plugin-sdk/channel-co
|
|
|
3
3
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
4
4
|
import { readStringParam } from "openclaw/plugin-sdk/param-readers";
|
|
5
5
|
import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
|
|
6
|
-
import { getConfig, stripTargetPrefix } from "../config";
|
|
7
|
-
import { getLogger } from "../logger-context";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { getConfig, stripTargetPrefix } from "../platform/config";
|
|
7
|
+
import { getLogger } from "../platform/logger-context";
|
|
8
|
+
import { hasConfiguredSecretInput } from "../platform/secret-input";
|
|
9
|
+
import { parseBooleanLike } from "../shared/utils";
|
|
10
|
+
import { resolveOriginalPeerId } from "../targeting/peer-id-registry";
|
|
11
|
+
import { sendMedia, sendMessage } from "./send-service";
|
|
12
12
|
|
|
13
13
|
function readBooleanLikeParam(params: Record<string, unknown>, key: string): boolean | undefined {
|
|
14
14
|
return parseBooleanLike(params[key]);
|
|
@@ -67,7 +67,15 @@ export function createDingTalkMessageActions(): ChannelMessageActionAdapter {
|
|
|
67
67
|
describeMessageTool: ({ cfg }) => describeDingTalkMessageTool(cfg),
|
|
68
68
|
supportsAction: ({ action }) => action === "send",
|
|
69
69
|
extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
|
|
70
|
-
handleAction: async ({
|
|
70
|
+
handleAction: async ({
|
|
71
|
+
action,
|
|
72
|
+
params,
|
|
73
|
+
cfg,
|
|
74
|
+
accountId,
|
|
75
|
+
dryRun,
|
|
76
|
+
mediaLocalRoots,
|
|
77
|
+
sessionKey,
|
|
78
|
+
}) => {
|
|
71
79
|
if (action !== "send") {
|
|
72
80
|
throw new Error(`Action ${action} is not supported for provider dingtalk.`);
|
|
73
81
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { getConfig } from "../config";
|
|
3
|
-
import { getLogger } from "../logger-context";
|
|
4
|
-
import { getDingTalkRuntime } from "../runtime";
|
|
5
|
-
import {
|
|
2
|
+
import { getConfig } from "../platform/config";
|
|
3
|
+
import { getLogger } from "../platform/logger-context";
|
|
4
|
+
import { getDingTalkRuntime } from "../platform/runtime";
|
|
5
|
+
import type { DingTalkChannelPlugin } from "../platform/types";
|
|
6
|
+
import { formatDingTalkErrorPayloadLog, parseBooleanLike } from "../shared/utils";
|
|
6
7
|
import { normalizeResolvedDingTalkTarget } from "../targeting/target-directory-adapter";
|
|
7
|
-
import
|
|
8
|
-
import { formatDingTalkErrorPayloadLog, parseBooleanLike } from "../utils";
|
|
8
|
+
import { sendMedia, sendMessage } from "./send-service";
|
|
9
9
|
|
|
10
10
|
function readBooleanLikeParam(params: Record<string, unknown>, key: string): boolean | undefined {
|
|
11
11
|
return parseBooleanLike(params[key]);
|
|
@@ -66,7 +66,9 @@ export function createDingTalkOutbound(): NonNullable<DingTalkChannelPlugin["out
|
|
|
66
66
|
};
|
|
67
67
|
} catch (err: any) {
|
|
68
68
|
if (err?.response?.data !== undefined) {
|
|
69
|
-
effectiveLog?.error?.(
|
|
69
|
+
effectiveLog?.error?.(
|
|
70
|
+
formatDingTalkErrorPayloadLog("outbound.sendText", err.response.data),
|
|
71
|
+
);
|
|
70
72
|
}
|
|
71
73
|
throw new Error(
|
|
72
74
|
typeof err?.response?.data === "string"
|
|
@@ -112,9 +114,10 @@ export function createDingTalkOutbound(): NonNullable<DingTalkChannelPlugin["out
|
|
|
112
114
|
);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
const requestedMediaType =
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
const requestedMediaType =
|
|
118
|
+
typeof providedMediaType === "string"
|
|
119
|
+
? (providedMediaType as "image" | "voice" | "video" | "file")
|
|
120
|
+
: undefined;
|
|
118
121
|
|
|
119
122
|
try {
|
|
120
123
|
const result = await sendMedia(config, to, rawMediaPath, {
|
|
@@ -144,7 +147,9 @@ export function createDingTalkOutbound(): NonNullable<DingTalkChannelPlugin["out
|
|
|
144
147
|
};
|
|
145
148
|
} catch (err: any) {
|
|
146
149
|
if (err?.response?.data !== undefined) {
|
|
147
|
-
effectiveLog?.error?.(
|
|
150
|
+
effectiveLog?.error?.(
|
|
151
|
+
formatDingTalkErrorPayloadLog("outbound.sendMedia", err.response.data),
|
|
152
|
+
);
|
|
148
153
|
}
|
|
149
154
|
throw new Error(
|
|
150
155
|
typeof err?.response?.data === "string"
|