@soimy/dingtalk 3.6.11 → 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 +11684 -10583
- 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} +1 -1
- 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/{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} +74 -1
- 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} +139 -84
- 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/{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} +76 -1
- 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,5 +1,5 @@
|
|
|
1
|
-
import { normalizeAllowFrom, isSenderOwner } from "
|
|
2
|
-
import type { DingTalkConfig } from "
|
|
1
|
+
import { normalizeAllowFrom, isSenderOwner } from "../platform/access-control";
|
|
2
|
+
import type { DingTalkConfig } from "../platform/types";
|
|
3
3
|
|
|
4
4
|
export interface ParsedLearnCommand {
|
|
5
5
|
scope:
|
|
@@ -33,31 +33,29 @@ export function parseLearnCommand(text: string | undefined): ParsedLearnCommand
|
|
|
33
33
|
return { scope: "unknown" };
|
|
34
34
|
}
|
|
35
35
|
const normalized = raw.toLowerCase();
|
|
36
|
-
if (
|
|
37
|
-
normalized === "/learn help"
|
|
38
|
-
) {
|
|
36
|
+
if (normalized === "/learn help") {
|
|
39
37
|
return { scope: "help" };
|
|
40
38
|
}
|
|
41
39
|
if (
|
|
42
|
-
normalized === "/learn whoami"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
normalized === "/learn whoami" ||
|
|
41
|
+
normalized === "/whoami" ||
|
|
42
|
+
normalized === "我是谁" ||
|
|
43
|
+
normalized === "我的信息"
|
|
46
44
|
) {
|
|
47
45
|
return { scope: "whoami" };
|
|
48
46
|
}
|
|
49
47
|
if (
|
|
50
|
-
normalized === "/learn whereami"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
normalized === "/learn whereami" ||
|
|
49
|
+
normalized === "这里是谁" ||
|
|
50
|
+
normalized === "这个群是谁" ||
|
|
51
|
+
normalized === "这个会话是谁"
|
|
54
52
|
) {
|
|
55
53
|
return { scope: "whereami" };
|
|
56
54
|
}
|
|
57
55
|
if (
|
|
58
|
-
normalized === "/learn owner status"
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
normalized === "/learn owner status" ||
|
|
57
|
+
normalized === "/owner status" ||
|
|
58
|
+
normalized === "/owner-status"
|
|
61
59
|
) {
|
|
62
60
|
return { scope: "owner-status" };
|
|
63
61
|
}
|
|
@@ -184,7 +182,9 @@ export function formatWhereAmIReply(params: {
|
|
|
184
182
|
params.peerId ? `- peerId: \`${params.peerId}\`` : undefined,
|
|
185
183
|
"",
|
|
186
184
|
"后续如果要定向注入到这里,就使用这个 conversationId。",
|
|
187
|
-
]
|
|
185
|
+
]
|
|
186
|
+
.filter(Boolean)
|
|
187
|
+
.join("\n");
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
export function formatOwnerStatusReply(params: {
|
|
@@ -247,15 +247,16 @@ export function formatLearnAppliedReply(params: {
|
|
|
247
247
|
targetIds?: string[];
|
|
248
248
|
setName?: string;
|
|
249
249
|
}): string {
|
|
250
|
-
const scopeLine =
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
250
|
+
const scopeLine =
|
|
251
|
+
params.scope === "global"
|
|
252
|
+
? "- 生效范围:同一钉钉账号下所有会话,将在下一条消息进入时自动加载"
|
|
253
|
+
: params.scope === "session"
|
|
254
|
+
? "- 生效范围:当前会话,将在下一条消息进入时自动加载"
|
|
255
|
+
: params.scope === "target"
|
|
256
|
+
? `- 生效范围:指定目标 \`${params.targetId}\``
|
|
257
|
+
: params.scope === "targets"
|
|
258
|
+
? `- 生效范围:${params.targetIds?.length || 0} 个目标`
|
|
259
|
+
: `- 生效范围:目标组 \`${params.setName}\``;
|
|
259
260
|
return [
|
|
260
261
|
params.scope === "global"
|
|
261
262
|
? "已注入全局知识。"
|
|
@@ -270,7 +271,9 @@ export function formatLearnAppliedReply(params: {
|
|
|
270
271
|
params.ruleId ? `- ruleId: \`${params.ruleId}\`` : undefined,
|
|
271
272
|
`- instruction: ${params.instruction}`,
|
|
272
273
|
scopeLine,
|
|
273
|
-
]
|
|
274
|
+
]
|
|
275
|
+
.filter(Boolean)
|
|
276
|
+
.join("\n");
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
export function formatLearnListReply(lines: string[]): string {
|
|
@@ -298,7 +301,9 @@ export function formatLearnDisabledReply(params: {
|
|
|
298
301
|
"已停用规则。",
|
|
299
302
|
"",
|
|
300
303
|
`- ruleId: \`${params.ruleId}\``,
|
|
301
|
-
params.scope === "target" && params.targetId
|
|
304
|
+
params.scope === "target" && params.targetId
|
|
305
|
+
? `- scope: target (\`${params.targetId}\`)`
|
|
306
|
+
: "- scope: global",
|
|
302
307
|
"- 当前效果:这条规则会停止生效,但记录仍会保留,便于后续排查或恢复。",
|
|
303
308
|
].join("\n");
|
|
304
309
|
}
|
|
@@ -321,7 +326,9 @@ export function formatLearnDeletedReply(params: {
|
|
|
321
326
|
"已删除规则。",
|
|
322
327
|
"",
|
|
323
328
|
`- ruleId: \`${params.ruleId}\``,
|
|
324
|
-
params.scope === "target" && params.targetId
|
|
329
|
+
params.scope === "target" && params.targetId
|
|
330
|
+
? `- scope: target (\`${params.targetId}\`)`
|
|
331
|
+
: "- scope: global",
|
|
325
332
|
].join("\n");
|
|
326
333
|
}
|
|
327
334
|
|
|
@@ -18,7 +18,9 @@ export function parseSessionCommand(text: string | undefined): ParsedSessionComm
|
|
|
18
18
|
if (!raw) {
|
|
19
19
|
return { scope: "unknown" };
|
|
20
20
|
}
|
|
21
|
-
const sessionAliasBindMatch = raw.match(
|
|
21
|
+
const sessionAliasBindMatch = raw.match(
|
|
22
|
+
/^\/session-alias\s+(bind|unbind)\s+(direct|group)\s+(\S+)(?:\s+(.+))?$/i,
|
|
23
|
+
);
|
|
22
24
|
if (sessionAliasBindMatch) {
|
|
23
25
|
const action = sessionAliasBindMatch[1]?.toLowerCase();
|
|
24
26
|
const sourceKind = sessionAliasBindMatch[2]?.toLowerCase() as "direct" | "group";
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import { DWClient, TOPIC_CARD, TOPIC_ROBOT } from "dingtalk-stream";
|
|
2
2
|
import { listAgentIds } from "openclaw/plugin-sdk/agent-runtime";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
deliveryContextFromSession,
|
|
5
|
+
sessionDeliveryChannel,
|
|
6
|
+
sessionDeliveryOrigin,
|
|
7
|
+
sessionDeliveryRoute,
|
|
8
|
+
} from "openclaw/plugin-sdk/session-store-runtime";
|
|
9
|
+
import type { SessionEntry } from "openclaw/plugin-sdk/session-store-runtime";
|
|
5
10
|
import { recoverAskUserQuestionsForAccount } from "../card/ask-user-question";
|
|
6
11
|
import { handleCardAction } from "../card/card-action-handler";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { isMessageProcessed, markMessageProcessed } from "../dedup";
|
|
12
|
+
import { analyzeCardCallback } from "../card/card-callback-service";
|
|
13
|
+
import { finalizeActiveCardsForAccount, recoverPendingCardsForAccount } from "../card/card-service";
|
|
10
14
|
import {
|
|
11
15
|
isLearningAutoApplyEnabled,
|
|
12
16
|
isLearningEnabled,
|
|
13
17
|
recordExplicitFeedbackLearning,
|
|
14
|
-
} from "../feedback-learning-service";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { getDingTalkRuntime } from "../runtime";
|
|
19
|
-
import { sendProactiveTextOrMarkdown } from "../send-service";
|
|
20
|
-
import {
|
|
21
|
-
listKnownGroupTargets,
|
|
22
|
-
listKnownUserTargets,
|
|
23
|
-
upsertObservedGroupTarget,
|
|
24
|
-
} from "../targeting/target-directory-store";
|
|
18
|
+
} from "../command/feedback-learning-service";
|
|
19
|
+
import { sendProactiveTextOrMarkdown } from "../messaging/send-service";
|
|
20
|
+
import { resolveRobotCode, resolveRuntimeConfig } from "../platform/config";
|
|
21
|
+
import { setCurrentLogger } from "../platform/logger-context";
|
|
22
|
+
import { getDingTalkRuntime } from "../platform/runtime";
|
|
25
23
|
import type {
|
|
26
24
|
ConnectionManagerConfig,
|
|
27
25
|
DingTalkChannelPlugin,
|
|
@@ -29,8 +27,9 @@ import type {
|
|
|
29
27
|
GatewayStartContext,
|
|
30
28
|
GatewayStopResult,
|
|
31
29
|
StreamClientFactory,
|
|
32
|
-
} from "../types";
|
|
33
|
-
import { ConnectionState } from "../types";
|
|
30
|
+
} from "../platform/types";
|
|
31
|
+
import { ConnectionState } from "../platform/types";
|
|
32
|
+
import { isMessageProcessed, markMessageProcessed } from "../shared/dedup";
|
|
34
33
|
import {
|
|
35
34
|
closePluginDebugLog,
|
|
36
35
|
cleanupOrphanedTempFiles,
|
|
@@ -38,7 +37,15 @@ import {
|
|
|
38
37
|
formatDingTalkConnectionErrorLog,
|
|
39
38
|
getCurrentTimestamp,
|
|
40
39
|
resolvePluginDebugLog,
|
|
41
|
-
} from "../utils";
|
|
40
|
+
} from "../shared/utils";
|
|
41
|
+
import { registerPeerId } from "../targeting/peer-id-registry";
|
|
42
|
+
import {
|
|
43
|
+
listKnownGroupTargets,
|
|
44
|
+
listKnownUserTargets,
|
|
45
|
+
upsertObservedGroupTarget,
|
|
46
|
+
} from "../targeting/target-directory-store";
|
|
47
|
+
import { ConnectionManager } from "./connection-manager";
|
|
48
|
+
import { handleDingTalkMessage } from "./inbound-handler";
|
|
42
49
|
|
|
43
50
|
type InstrumentedDWClient = {
|
|
44
51
|
getEndpoint?: () => Promise<unknown>;
|
|
@@ -47,6 +54,82 @@ type InstrumentedDWClient = {
|
|
|
47
54
|
dw_url?: string;
|
|
48
55
|
};
|
|
49
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Runtime read of a session entry that may still carry pre-`delivery`
|
|
59
|
+
* delivery fields.
|
|
60
|
+
*
|
|
61
|
+
* Session entries persist `origin` / `lastChannel` / `lastTo` / `lastAccountId`
|
|
62
|
+
* only until `openclaw doctor --fix` migrates them into the canonical
|
|
63
|
+
* `delivery` state, so both shapes can appear when the plugin scans an existing
|
|
64
|
+
* store. The index signature keeps legacy reads without reintroducing the
|
|
65
|
+
* retired fields into typed code paths.
|
|
66
|
+
*/
|
|
67
|
+
type SessionEntryWithLegacyDelivery = SessionEntry & Record<string, unknown>;
|
|
68
|
+
|
|
69
|
+
function readLegacySessionString(
|
|
70
|
+
entry: SessionEntryWithLegacyDelivery,
|
|
71
|
+
key: string,
|
|
72
|
+
): string | undefined {
|
|
73
|
+
const value = entry[key];
|
|
74
|
+
return typeof value === "string" ? value : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeOptionalSessionString(value: string | undefined): string | undefined {
|
|
78
|
+
return value && value.length > 0 ? value : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readLegacySessionOrigin(
|
|
82
|
+
entry: SessionEntryWithLegacyDelivery,
|
|
83
|
+
key: "provider" | "surface" | "accountId" | "from" | "to",
|
|
84
|
+
): string | undefined {
|
|
85
|
+
const origin = entry.origin;
|
|
86
|
+
if (!origin || typeof origin !== "object") {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
const value = (origin as Record<string, unknown>)[key];
|
|
90
|
+
return typeof value === "string" ? value : undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Reads one session entry's delivery identity across canonical and legacy shapes. */
|
|
94
|
+
function readSessionDeliveryIdentity(entry: SessionEntry): {
|
|
95
|
+
channels: Array<string | undefined>;
|
|
96
|
+
accountId: string | undefined;
|
|
97
|
+
peerIds: Array<string | undefined>;
|
|
98
|
+
} {
|
|
99
|
+
const legacy = entry as SessionEntryWithLegacyDelivery;
|
|
100
|
+
const origin = sessionDeliveryOrigin(entry);
|
|
101
|
+
const context = deliveryContextFromSession(entry);
|
|
102
|
+
const route = sessionDeliveryRoute(entry);
|
|
103
|
+
return {
|
|
104
|
+
channels: [
|
|
105
|
+
sessionDeliveryChannel(entry),
|
|
106
|
+
route?.channel,
|
|
107
|
+
context?.channel,
|
|
108
|
+
readLegacySessionString(legacy, "lastChannel"),
|
|
109
|
+
readLegacySessionString(legacy, "channel"),
|
|
110
|
+
origin?.provider,
|
|
111
|
+
origin?.surface,
|
|
112
|
+
readLegacySessionOrigin(legacy, "provider"),
|
|
113
|
+
readLegacySessionOrigin(legacy, "surface"),
|
|
114
|
+
],
|
|
115
|
+
// Canonical `delivery.context` wins, then the pre-`delivery` fields. Empty
|
|
116
|
+
// strings fall through so the chain matches the original `||` semantics.
|
|
117
|
+
accountId:
|
|
118
|
+
normalizeOptionalSessionString(context?.accountId) ??
|
|
119
|
+
normalizeOptionalSessionString(readLegacySessionString(legacy, "lastAccountId")) ??
|
|
120
|
+
normalizeOptionalSessionString(origin?.accountId) ??
|
|
121
|
+
normalizeOptionalSessionString(readLegacySessionOrigin(legacy, "accountId")),
|
|
122
|
+
peerIds: [
|
|
123
|
+
context?.to,
|
|
124
|
+
readLegacySessionString(legacy, "lastTo"),
|
|
125
|
+
origin?.from,
|
|
126
|
+
origin?.to,
|
|
127
|
+
readLegacySessionOrigin(legacy, "from"),
|
|
128
|
+
readLegacySessionOrigin(legacy, "to"),
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
50
133
|
function attachConnectionErrorContext(
|
|
51
134
|
err: unknown,
|
|
52
135
|
stage: "connect.open" | "connect.websocket",
|
|
@@ -205,17 +288,12 @@ export function createDingTalkGateway(): NonNullable<DingTalkChannelPlugin["gate
|
|
|
205
288
|
storePath: sessionStorePath,
|
|
206
289
|
readConsistency: "latest",
|
|
207
290
|
})) {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
entry.origin?.provider,
|
|
212
|
-
entry.origin?.surface,
|
|
213
|
-
].some((value) => value === "dingtalk");
|
|
214
|
-
const sessionAccountId = entry.lastAccountId || entry.origin?.accountId;
|
|
215
|
-
if (!isDingTalkSession || sessionAccountId !== account.accountId) {
|
|
291
|
+
const identity = readSessionDeliveryIdentity(entry);
|
|
292
|
+
const isDingTalkSession = identity.channels.some((value) => value === "dingtalk");
|
|
293
|
+
if (!isDingTalkSession || identity.accountId !== account.accountId) {
|
|
216
294
|
continue;
|
|
217
295
|
}
|
|
218
|
-
for (const peerId of
|
|
296
|
+
for (const peerId of identity.peerIds) {
|
|
219
297
|
if (typeof peerId !== "string" || !peerId.startsWith("cid")) {
|
|
220
298
|
continue;
|
|
221
299
|
}
|
|
@@ -16,8 +16,8 @@ import type {
|
|
|
16
16
|
ConnectionAttemptResult,
|
|
17
17
|
Logger,
|
|
18
18
|
StreamClientFactory,
|
|
19
|
-
} from "
|
|
20
|
-
import { ConnectionState as ConnectionStateEnum } from "
|
|
19
|
+
} from "../platform/types";
|
|
20
|
+
import { ConnectionState as ConnectionStateEnum } from "../platform/types";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Thrown when a runtime reconnection cycle exceeds the configured deadline.
|
|
@@ -98,7 +98,13 @@ export class ConnectionManager {
|
|
|
98
98
|
// Old client pending cleanup after a warm reconnect swap.
|
|
99
99
|
private pendingOldClient?: DWClient;
|
|
100
100
|
|
|
101
|
-
constructor(
|
|
101
|
+
constructor(
|
|
102
|
+
client: DWClient,
|
|
103
|
+
accountId: string,
|
|
104
|
+
config: ConnectionManagerConfig,
|
|
105
|
+
log?: Logger,
|
|
106
|
+
clientFactory?: StreamClientFactory,
|
|
107
|
+
) {
|
|
102
108
|
this.client = client;
|
|
103
109
|
this.accountId = accountId;
|
|
104
110
|
this.config = config;
|
|
@@ -148,17 +154,17 @@ export class ConnectionManager {
|
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
const now = Date.now();
|
|
151
|
-
const idleMs =
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
const idleMs =
|
|
158
|
+
this.lastSocketActivityAt !== undefined
|
|
159
|
+
? now - this.lastSocketActivityAt
|
|
160
|
+
: ConnectionManager.HEARTBEAT_INTERVAL_MS;
|
|
154
161
|
if (idleMs >= ConnectionManager.HEARTBEAT_INTERVAL_MS) {
|
|
155
162
|
this.consecutiveHeartbeatMisses += 1;
|
|
156
163
|
this.runtimeCounters.heartbeatMisses += 1;
|
|
157
164
|
|
|
158
165
|
if (this.consecutiveHeartbeatMisses >= ConnectionManager.HEARTBEAT_MISS_THRESHOLD) {
|
|
159
|
-
const lastPingAgoMs =
|
|
160
|
-
? now - this.lastHeartbeatPingAt
|
|
161
|
-
: undefined;
|
|
166
|
+
const lastPingAgoMs =
|
|
167
|
+
this.lastHeartbeatPingAt !== undefined ? now - this.lastHeartbeatPingAt : undefined;
|
|
162
168
|
this.log?.warn?.(
|
|
163
169
|
`[${this.accountId}] Connection heartbeat missed ${this.consecutiveHeartbeatMisses}/${ConnectionManager.HEARTBEAT_MISS_THRESHOLD} checks, triggering reconnection (lastPingAgoMs=${lastPingAgoMs ?? "n/a"})`,
|
|
164
170
|
);
|
|
@@ -178,9 +184,7 @@ export class ConnectionManager {
|
|
|
178
184
|
try {
|
|
179
185
|
socket.ping("", true);
|
|
180
186
|
} catch (err: any) {
|
|
181
|
-
this.log?.warn?.(
|
|
182
|
-
`[${this.accountId}] Failed to send heartbeat ping: ${err.message}`,
|
|
183
|
-
);
|
|
187
|
+
this.log?.warn?.(`[${this.accountId}] Failed to send heartbeat ping: ${err.message}`);
|
|
184
188
|
}
|
|
185
189
|
}, ConnectionManager.HEARTBEAT_INTERVAL_MS);
|
|
186
190
|
}
|
|
@@ -211,9 +215,10 @@ export class ConnectionManager {
|
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
const defaultOpenTimeout = 10_000;
|
|
214
|
-
const openTimeout =
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
const openTimeout =
|
|
219
|
+
this.reconnectDeadline !== undefined
|
|
220
|
+
? Math.min(defaultOpenTimeout, Math.max(1000, this.reconnectDeadline - Date.now()))
|
|
221
|
+
: defaultOpenTimeout;
|
|
217
222
|
|
|
218
223
|
await new Promise<void>((resolve, reject) => {
|
|
219
224
|
const timeout = setTimeout(() => {
|
|
@@ -226,9 +231,18 @@ export class ConnectionManager {
|
|
|
226
231
|
socket.removeListener("error", onError);
|
|
227
232
|
socket.removeListener("close", onClose);
|
|
228
233
|
};
|
|
229
|
-
const onOpen = () => {
|
|
230
|
-
|
|
231
|
-
|
|
234
|
+
const onOpen = () => {
|
|
235
|
+
cleanup();
|
|
236
|
+
resolve();
|
|
237
|
+
};
|
|
238
|
+
const onError = (err: Error) => {
|
|
239
|
+
cleanup();
|
|
240
|
+
reject(err);
|
|
241
|
+
};
|
|
242
|
+
const onClose = () => {
|
|
243
|
+
cleanup();
|
|
244
|
+
reject(new Error("Socket closed before open"));
|
|
245
|
+
};
|
|
232
246
|
socket.once("open", onOpen);
|
|
233
247
|
socket.once("error", onError);
|
|
234
248
|
socket.once("close", onClose);
|
|
@@ -370,7 +384,11 @@ export class ConnectionManager {
|
|
|
370
384
|
// Warm-reconnect failed with new client: revert to old client so
|
|
371
385
|
// subsequent retry attempts don't keep creating throwaway instances.
|
|
372
386
|
if (this.pendingOldClient) {
|
|
373
|
-
try {
|
|
387
|
+
try {
|
|
388
|
+
this.client.disconnect();
|
|
389
|
+
} catch {
|
|
390
|
+
/* best-effort cleanup of failed new client */
|
|
391
|
+
}
|
|
374
392
|
this.client = this.pendingOldClient;
|
|
375
393
|
this.pendingOldClient = undefined;
|
|
376
394
|
this.log?.debug?.(
|
|
@@ -509,10 +527,7 @@ export class ConnectionManager {
|
|
|
509
527
|
|
|
510
528
|
this.consecutiveUnhealthyChecks += 1;
|
|
511
529
|
this.runtimeCounters.healthUnhealthyChecks += 1;
|
|
512
|
-
if (
|
|
513
|
-
this.consecutiveUnhealthyChecks <
|
|
514
|
-
ConnectionManager.HEALTH_CHECK_UNHEALTHY_THRESHOLD
|
|
515
|
-
) {
|
|
530
|
+
if (this.consecutiveUnhealthyChecks < ConnectionManager.HEALTH_CHECK_UNHEALTHY_THRESHOLD) {
|
|
516
531
|
this.log?.debug?.(
|
|
517
532
|
`[${this.accountId}] Connection health check unhealthy (${this.consecutiveUnhealthyChecks}/${ConnectionManager.HEALTH_CHECK_UNHEALTHY_THRESHOLD}) connected=${String(client.connected)} registered=${String(registered)} socketReadyState=${socketReadyState ?? "unknown"}`,
|
|
518
533
|
);
|
|
@@ -723,7 +738,9 @@ export class ConnectionManager {
|
|
|
723
738
|
if (err instanceof ReconnectDeadlineError) {
|
|
724
739
|
this.consecutiveDeadlineTimeouts += 1;
|
|
725
740
|
|
|
726
|
-
if (
|
|
741
|
+
if (
|
|
742
|
+
this.consecutiveDeadlineTimeouts >= ConnectionManager.MAX_CONSECUTIVE_DEADLINE_TIMEOUTS
|
|
743
|
+
) {
|
|
727
744
|
this.log?.error?.(
|
|
728
745
|
`[${this.accountId}] Max consecutive deadline timeouts (${ConnectionManager.MAX_CONSECUTIVE_DEADLINE_TIMEOUTS}) reached. Giving up.`,
|
|
729
746
|
);
|
|
@@ -740,10 +757,7 @@ export class ConnectionManager {
|
|
|
740
757
|
|
|
741
758
|
const deadlineMs = this.config.reconnectDeadlineMs ?? 50000;
|
|
742
759
|
this.reconnectDeadline = Date.now() + deadlineMs;
|
|
743
|
-
const delay = Math.min(
|
|
744
|
-
this.calculateNextDelay(0),
|
|
745
|
-
ConnectionManager.MAX_CYCLE_BACKOFF_MS,
|
|
746
|
-
);
|
|
760
|
+
const delay = Math.min(this.calculateNextDelay(0), ConnectionManager.MAX_CYCLE_BACKOFF_MS);
|
|
747
761
|
this.attemptCount = 0;
|
|
748
762
|
this.clearReconnectTimer();
|
|
749
763
|
this.log?.warn?.(
|
|
@@ -756,12 +770,13 @@ export class ConnectionManager {
|
|
|
756
770
|
}
|
|
757
771
|
|
|
758
772
|
this.runtimeReconnectCycles += 1;
|
|
759
|
-
const maxCycles =
|
|
773
|
+
const maxCycles =
|
|
774
|
+
this.config.maxReconnectCycles ?? ConnectionManager.DEFAULT_MAX_RECONNECT_CYCLES;
|
|
760
775
|
|
|
761
776
|
if (this.runtimeReconnectCycles >= maxCycles) {
|
|
762
777
|
this.log?.error?.(
|
|
763
778
|
`[${this.accountId}] Max runtime reconnect cycles (${maxCycles}) reached. Giving up. ` +
|
|
764
|
-
|
|
779
|
+
`Please check network connectivity or restart the gateway.`,
|
|
765
780
|
);
|
|
766
781
|
this.state = ConnectionStateEnum.FAILED;
|
|
767
782
|
this.connectedAt = undefined;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
1
|
+
import { getAccessToken } from "../platform/auth";
|
|
2
|
+
import { getLogger } from "../platform/logger-context";
|
|
3
|
+
import type { DingTalkConfig, DocInfo, Logger } from "../platform/types";
|
|
4
|
+
import axios, { DEFAULT_HTTP_TIMEOUT_MS } from "../shared/http-client";
|
|
5
|
+
import { getProxyBypassOption } from "../shared/utils";
|
|
6
6
|
|
|
7
7
|
const DINGTALK_API = "https://api.dingtalk.com";
|
|
8
8
|
|