@krischoichoi/channel-dingtalk 0.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.
- package/README.md +139 -0
- package/lib/adapter.d.ts +123 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +355 -0
- package/lib/adapter.js.map +1 -0
- package/lib/ai-card.d.ts +64 -0
- package/lib/ai-card.d.ts.map +1 -0
- package/lib/ai-card.js +150 -0
- package/lib/ai-card.js.map +1 -0
- package/lib/auth/device-registration.d.ts +18 -0
- package/lib/auth/device-registration.d.ts.map +1 -0
- package/lib/auth/device-registration.js +147 -0
- package/lib/auth/device-registration.js.map +1 -0
- package/lib/config.d.ts +119 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +75 -0
- package/lib/config.js.map +1 -0
- package/lib/definition.d.ts +53 -0
- package/lib/definition.d.ts.map +1 -0
- package/lib/definition.js +227 -0
- package/lib/definition.js.map +1 -0
- package/lib/image-hydrator.d.ts +213 -0
- package/lib/image-hydrator.d.ts.map +1 -0
- package/lib/image-hydrator.js +244 -0
- package/lib/image-hydrator.js.map +1 -0
- package/lib/inbound.d.ts +38 -0
- package/lib/inbound.d.ts.map +1 -0
- package/lib/inbound.js +158 -0
- package/lib/inbound.js.map +1 -0
- package/lib/index.d.ts +49 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +123 -0
- package/lib/index.js.map +1 -0
- package/lib/manifest.d.ts +23 -0
- package/lib/manifest.d.ts.map +1 -0
- package/lib/manifest.js +34 -0
- package/lib/manifest.js.map +1 -0
- package/lib/mapper.d.ts +36 -0
- package/lib/mapper.d.ts.map +1 -0
- package/lib/mapper.js +175 -0
- package/lib/mapper.js.map +1 -0
- package/lib/media-mime.d.ts +14 -0
- package/lib/media-mime.d.ts.map +1 -0
- package/lib/media-mime.js +32 -0
- package/lib/media-mime.js.map +1 -0
- package/lib/official-upstream.d.ts +143 -0
- package/lib/official-upstream.d.ts.map +1 -0
- package/lib/official-upstream.js +425 -0
- package/lib/official-upstream.js.map +1 -0
- package/lib/openapi-port.d.ts +190 -0
- package/lib/openapi-port.d.ts.map +1 -0
- package/lib/openapi-port.js +2 -0
- package/lib/openapi-port.js.map +1 -0
- package/lib/outbound.d.ts +69 -0
- package/lib/outbound.d.ts.map +1 -0
- package/lib/outbound.js +148 -0
- package/lib/outbound.js.map +1 -0
- package/lib/stream-upstream.d.ts +77 -0
- package/lib/stream-upstream.d.ts.map +1 -0
- package/lib/stream-upstream.js +303 -0
- package/lib/stream-upstream.js.map +1 -0
- package/lib/transport.d.ts +37 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +73 -0
- package/lib/transport.js.map +1 -0
- package/lib/upstream.d.ts +60 -0
- package/lib/upstream.d.ts.map +1 -0
- package/lib/upstream.js +61 -0
- package/lib/upstream.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/***
|
|
2
|
+
* Outbound sending: channel message -> dingtalk payload -> upstream.
|
|
3
|
+
*
|
|
4
|
+
* Two explicit send paths:
|
|
5
|
+
* - REPLY: the target carries an inbound `sessionWebhook` (or the upstream is
|
|
6
|
+
* the legacy gateway, which has no proactive path). sessionWebhook belongs
|
|
7
|
+
* ONLY to the current inbound reply — never a durable outbox route.
|
|
8
|
+
* - PROACTIVE: a target with no reply context / an arbitrary conversation id
|
|
9
|
+
* is sent through the official proactive OpenAPI port (`sendProactiveText`,
|
|
10
|
+
* and proactive media via `uploadMedia` + `sendMedia`) — never via a stale
|
|
11
|
+
* sessionWebhook.
|
|
12
|
+
*
|
|
13
|
+
* Capability flags (`outboxCapabilities`) fail CLOSED: they only read
|
|
14
|
+
* `true` when the matching official path is actually wired. Streaming replies go
|
|
15
|
+
* through `DingTalkCardReply`; this sender is the buffered fallback path
|
|
16
|
+
* (`adapter.send`) used when no card was created.
|
|
17
|
+
*/
|
|
18
|
+
import type { ChannelLogger, ChannelTarget, OutboundMessage, SendResult } from '@krischoichoi/channel-core';
|
|
19
|
+
import type { DingTalkUpstream } from './upstream.js';
|
|
20
|
+
import type { DingTalkOpenApiPort, OutboxCapabilities } from './openapi-port.js';
|
|
21
|
+
export interface OutboundSenderOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Reply-path upstream (sessionWebhook for official mode, legacy /message/send
|
|
24
|
+
* for gateway mode). Used for replies and for the no-proactive fallback.
|
|
25
|
+
*/
|
|
26
|
+
reply: DingTalkUpstream;
|
|
27
|
+
logger: ChannelLogger;
|
|
28
|
+
/**
|
|
29
|
+
* Proactive OpenAPI port (official proactive path). Absent in gateway mode ->
|
|
30
|
+
* proactive capabilities are false (fail closed).
|
|
31
|
+
*/
|
|
32
|
+
proactive?: DingTalkOpenApiPort;
|
|
33
|
+
/**
|
|
34
|
+
* Resolved proactive capability flags. Provided by the adapter;
|
|
35
|
+
* defaults to all-false (fail closed) when not supplied.
|
|
36
|
+
*/
|
|
37
|
+
capabilities?: OutboxCapabilities;
|
|
38
|
+
interactiveTemplateId?: string;
|
|
39
|
+
interactiveTextParam?: string;
|
|
40
|
+
interactiveActionsParam?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare class OutboundSender {
|
|
43
|
+
private readonly reply;
|
|
44
|
+
private readonly logger;
|
|
45
|
+
private readonly proactive?;
|
|
46
|
+
private readonly capabilities;
|
|
47
|
+
private readonly interactiveTemplateId?;
|
|
48
|
+
private readonly interactiveTextParam;
|
|
49
|
+
private readonly interactiveActionsParam;
|
|
50
|
+
private readonly interactiveCards;
|
|
51
|
+
constructor(options: OutboundSenderOptions);
|
|
52
|
+
/** Proactive capability flags the harness outbox reads. */
|
|
53
|
+
get outboxCapabilities(): OutboxCapabilities;
|
|
54
|
+
/**
|
|
55
|
+
* Send one channel message. REPLIES (sessionWebhook present) go through the
|
|
56
|
+
* reply upstream; PROACTIVE targets use the official proactive API; gateway
|
|
57
|
+
* mode (no proactive port) falls back to the reply upstream /message/send.
|
|
58
|
+
* Media-carrying proactive messages go through upload + sendMedia.
|
|
59
|
+
*/
|
|
60
|
+
send(target: ChannelTarget, message: OutboundMessage): Promise<SendResult>;
|
|
61
|
+
edit(target: ChannelTarget, messageId: string, message: OutboundMessage): Promise<SendResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Inspect an outbound message for a single sendable binary (file/image/image-like)
|
|
64
|
+
* part already carrying trusted bytes (`localData`). Returns undefined for pure
|
|
65
|
+
* text messages.
|
|
66
|
+
*/
|
|
67
|
+
private proactiveMediaPart;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=outbound.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound.d.ts","sourceRoot":"","sources":["../src/outbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EAEb,eAAe,EACf,UAAU,EACX,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAQ3B,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqE;gBAE1F,OAAO,EAAE,qBAAqB;IAU1C,2DAA2D;IAC3D,IAAI,kBAAkB,IAAI,kBAAkB,CAE3C;IAED;;;;;OAKG;IACG,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IAuF1E,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IAiBnG;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;CAiB3B"}
|
package/lib/outbound.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ChannelSendError } from '@krischoichoi/channel-core';
|
|
2
|
+
import { toTextPayload } from './mapper.js';
|
|
3
|
+
export class OutboundSender {
|
|
4
|
+
reply;
|
|
5
|
+
logger;
|
|
6
|
+
proactive;
|
|
7
|
+
capabilities;
|
|
8
|
+
interactiveTemplateId;
|
|
9
|
+
interactiveTextParam;
|
|
10
|
+
interactiveActionsParam;
|
|
11
|
+
interactiveCards = new Map();
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.reply = options.reply;
|
|
14
|
+
this.logger = options.logger;
|
|
15
|
+
this.proactive = options.proactive;
|
|
16
|
+
this.capabilities = options.capabilities ?? { proactiveText: false, proactiveMedia: false };
|
|
17
|
+
this.interactiveTemplateId = options.interactiveTemplateId?.trim() || undefined;
|
|
18
|
+
this.interactiveTextParam = options.interactiveTextParam ?? 'text';
|
|
19
|
+
this.interactiveActionsParam = options.interactiveActionsParam ?? 'actions';
|
|
20
|
+
}
|
|
21
|
+
/** Proactive capability flags the harness outbox reads. */
|
|
22
|
+
get outboxCapabilities() {
|
|
23
|
+
return { ...this.capabilities };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Send one channel message. REPLIES (sessionWebhook present) go through the
|
|
27
|
+
* reply upstream; PROACTIVE targets use the official proactive API; gateway
|
|
28
|
+
* mode (no proactive port) falls back to the reply upstream /message/send.
|
|
29
|
+
* Media-carrying proactive messages go through upload + sendMedia.
|
|
30
|
+
*/
|
|
31
|
+
async send(target, message) {
|
|
32
|
+
const probe = (target.raw ?? {});
|
|
33
|
+
const isReply = typeof probe.sessionWebhook === 'string' && probe.sessionWebhook !== '';
|
|
34
|
+
try {
|
|
35
|
+
if (message.actions?.length) {
|
|
36
|
+
if (!this.proactive || !this.interactiveTemplateId || !this.capabilities.proactiveText) {
|
|
37
|
+
throw new ChannelSendError('dingtalk interactive card template is not configured');
|
|
38
|
+
}
|
|
39
|
+
const text = toTextPayload(target, message).content;
|
|
40
|
+
const result = await this.proactive.sendInteractiveCard({
|
|
41
|
+
target,
|
|
42
|
+
templateId: this.interactiveTemplateId,
|
|
43
|
+
text,
|
|
44
|
+
actions: message.actions,
|
|
45
|
+
textParam: this.interactiveTextParam,
|
|
46
|
+
actionsParam: this.interactiveActionsParam,
|
|
47
|
+
});
|
|
48
|
+
this.interactiveCards.set(result.outTrackId, { text, actions: message.actions });
|
|
49
|
+
// `outTrackId` is the stable identifier accepted by the official PUT
|
|
50
|
+
// update API; keep it as the contract messageId for later edits.
|
|
51
|
+
return { delivered: true, messageId: result.outTrackId, raw: result.raw };
|
|
52
|
+
}
|
|
53
|
+
if (isReply || !this.proactive) {
|
|
54
|
+
// REPLY path (sessionWebhook) — or gateway fallback (no proactive port).
|
|
55
|
+
const payload = toTextPayload(target, message);
|
|
56
|
+
const response = await this.reply.sendText(target, payload.content);
|
|
57
|
+
return { delivered: true, raw: response };
|
|
58
|
+
}
|
|
59
|
+
// PROACTIVE path: official proactive API.
|
|
60
|
+
const robotCode = (probe.robotCode ?? '').trim() || undefined;
|
|
61
|
+
const conversationType = target.conversationType;
|
|
62
|
+
const media = this.proactiveMediaPart(message);
|
|
63
|
+
if (media) {
|
|
64
|
+
if (!this.capabilities.proactiveMedia) {
|
|
65
|
+
throw new ChannelSendError('dingtalk proactive media is not enabled; message carries binary content with no reply context');
|
|
66
|
+
}
|
|
67
|
+
const agentId = robotCode;
|
|
68
|
+
if (!agentId) {
|
|
69
|
+
throw new ChannelSendError('dingtalk proactive send requires a robotCode in target.raw');
|
|
70
|
+
}
|
|
71
|
+
const uploaded = await this.proactive.uploadMedia({
|
|
72
|
+
robotCode: agentId,
|
|
73
|
+
fileName: media.fileName,
|
|
74
|
+
mimeType: media.mimeType,
|
|
75
|
+
data: media.localData,
|
|
76
|
+
mediaType: media.msgtype,
|
|
77
|
+
target,
|
|
78
|
+
});
|
|
79
|
+
const result = await this.proactive.sendMedia({
|
|
80
|
+
conversationId: String(target.conversationId),
|
|
81
|
+
robotCode: agentId,
|
|
82
|
+
mediaId: uploaded.mediaId,
|
|
83
|
+
msgtype: media.msgtype,
|
|
84
|
+
name: media.fileName,
|
|
85
|
+
conversationType,
|
|
86
|
+
});
|
|
87
|
+
return { delivered: true, raw: result.raw };
|
|
88
|
+
}
|
|
89
|
+
if (!this.capabilities.proactiveText) {
|
|
90
|
+
throw new ChannelSendError('dingtalk proactive text is not enabled');
|
|
91
|
+
}
|
|
92
|
+
if (!robotCode) {
|
|
93
|
+
throw new ChannelSendError('dingtalk proactive send requires a robotCode in target.raw');
|
|
94
|
+
}
|
|
95
|
+
const text = toTextPayload(target, message).content;
|
|
96
|
+
const result = await this.proactive.sendProactiveText({
|
|
97
|
+
conversationId: String(target.conversationId),
|
|
98
|
+
robotCode,
|
|
99
|
+
text,
|
|
100
|
+
conversationType,
|
|
101
|
+
});
|
|
102
|
+
return { delivered: true, raw: result.raw };
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
this.logger.error(`[channel-dingtalk] send failed to '${target.conversationId}'`, error instanceof Error ? error.message : error);
|
|
106
|
+
throw new ChannelSendError(`dingtalk send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async edit(target, messageId, message) {
|
|
110
|
+
if (!this.proactive)
|
|
111
|
+
throw new ChannelSendError('dingtalk interactive card update is unavailable');
|
|
112
|
+
const known = this.interactiveCards.get(messageId);
|
|
113
|
+
if (!known)
|
|
114
|
+
throw new ChannelSendError('dingtalk cannot edit an unknown interactive card');
|
|
115
|
+
const text = message.text ?? known.text;
|
|
116
|
+
const actions = message.actions ?? [];
|
|
117
|
+
const raw = await this.proactive.updateInteractiveCard({
|
|
118
|
+
outTrackId: messageId,
|
|
119
|
+
text,
|
|
120
|
+
actions,
|
|
121
|
+
textParam: this.interactiveTextParam,
|
|
122
|
+
actionsParam: this.interactiveActionsParam,
|
|
123
|
+
});
|
|
124
|
+
this.interactiveCards.set(messageId, { text, actions });
|
|
125
|
+
return { delivered: true, messageId, raw };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Inspect an outbound message for a single sendable binary (file/image/image-like)
|
|
129
|
+
* part already carrying trusted bytes (`localData`). Returns undefined for pure
|
|
130
|
+
* text messages.
|
|
131
|
+
*/
|
|
132
|
+
proactiveMediaPart(message) {
|
|
133
|
+
if (!message.parts || message.parts.length !== 1)
|
|
134
|
+
return undefined;
|
|
135
|
+
const part = message.parts[0];
|
|
136
|
+
if (!part || (part.type !== 'file' && part.type !== 'image'))
|
|
137
|
+
return undefined;
|
|
138
|
+
if (!part.localData)
|
|
139
|
+
return undefined;
|
|
140
|
+
return {
|
|
141
|
+
msgtype: part.type === 'image' ? 'image' : 'file',
|
|
142
|
+
localData: part.localData,
|
|
143
|
+
mimeType: part.mimeType,
|
|
144
|
+
fileName: part.name ?? (part.type === 'file' ? 'file' : 'image'),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=outbound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound.js","sourceRoot":"","sources":["../src/outbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ChannelTarget } from '@krischoichoi/channel-core';
|
|
2
|
+
import type { CardCreateResult, DingTalkUpstream } from './upstream.js';
|
|
3
|
+
/** Downstream headers of a stream message (subset of the SDK shape). */
|
|
4
|
+
export interface DingTalkStreamHeaders {
|
|
5
|
+
/** Subscription topic (e.g. TOPIC_ROBOT for robot messages). */
|
|
6
|
+
topic?: string;
|
|
7
|
+
/** Event id; dedup fallback when the payload omits a msgId. */
|
|
8
|
+
eventId?: string;
|
|
9
|
+
/** Server-side message id of the downstream frame. */
|
|
10
|
+
messageId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** The downstream message shape the stream client delivers to listeners. */
|
|
13
|
+
export interface DingTalkStreamMessage {
|
|
14
|
+
headers?: DingTalkStreamHeaders;
|
|
15
|
+
/** JSON-encoded payload (the SDK delivers `data` as a string). */
|
|
16
|
+
data?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Minimal structural client surface consumed by the driver. The real SDK
|
|
20
|
+
* `DWClient` satisfies it (connect / disconnect / registerCallbackListener);
|
|
21
|
+
* tests inject a fake without a WebSocket.
|
|
22
|
+
*/
|
|
23
|
+
export interface DingTalkStreamClient {
|
|
24
|
+
/** Open the stream WebSocket and register subscriptions with the server. */
|
|
25
|
+
connect(): Promise<void>;
|
|
26
|
+
/** Close the stream WebSocket (sync in the real SDK). */
|
|
27
|
+
disconnect(): void;
|
|
28
|
+
/** Subscribe to one downstream topic (robot messages by default). */
|
|
29
|
+
registerCallbackListener(topic: string, callback: (message: DingTalkStreamMessage) => void | Promise<void>): unknown;
|
|
30
|
+
/**
|
|
31
|
+
* Acknowledge one downstream frame so the stream server does not retry it
|
|
32
|
+
* (~60s retry window). The real SDK's `DWClient` implements this as
|
|
33
|
+
* `socketCallBackResponse(messageId, result)`.
|
|
34
|
+
*/
|
|
35
|
+
socketCallBackResponse(messageId: string, response: unknown): void;
|
|
36
|
+
}
|
|
37
|
+
export interface DingTalkStreamUpstreamOptions {
|
|
38
|
+
/** The stream-mode client (real DWClient or injected fake). */
|
|
39
|
+
client: DingTalkStreamClient;
|
|
40
|
+
/** Outbound delegate selected by the adapter (official API or legacy gateway). */
|
|
41
|
+
outbound: DingTalkUpstream;
|
|
42
|
+
/** Invoked after the stream connection is established (connection state). */
|
|
43
|
+
onConnected?: () => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Map one SDK downstream message into the gateway raw shape consumed by the
|
|
47
|
+
* inbound mapper (`{ type, msgId, eventId, senderId, conversationId, content,
|
|
48
|
+
* ... }`). Returns `undefined` when the payload is absent or not JSON.
|
|
49
|
+
*/
|
|
50
|
+
export declare function toGatewayRaw(message: DingTalkStreamMessage): Record<string, unknown> | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Acknowledge one robot callback frame. ACK means "the message was reliably
|
|
53
|
+
* received and submitted to the inbound pipeline" — not "the LLM finished
|
|
54
|
+
* answering". A frame without a server `messageId` cannot be acked.
|
|
55
|
+
*/
|
|
56
|
+
export declare function ackRobotMessage(client: DingTalkStreamClient, message: DingTalkStreamMessage): void;
|
|
57
|
+
export declare function toCardInteractionRaw(message: DingTalkStreamMessage): Record<string, unknown> | undefined;
|
|
58
|
+
/** Stream-mode implementation of `DingTalkUpstream` (inbound via SDK). */
|
|
59
|
+
export declare class DingTalkStreamUpstream implements DingTalkUpstream {
|
|
60
|
+
private readonly options;
|
|
61
|
+
/** The listener is registered once per client; reconnect reuses it. */
|
|
62
|
+
private listenerRegistered;
|
|
63
|
+
private onMessage?;
|
|
64
|
+
constructor(options: DingTalkStreamUpstreamOptions);
|
|
65
|
+
/**
|
|
66
|
+
* Connect the stream client, route inbound robot messages into the gateway
|
|
67
|
+
* raw shape, and keep the stream open until `signal` aborts.
|
|
68
|
+
*/
|
|
69
|
+
receive(signal: AbortSignal, onMessage: (raw: unknown) => void): Promise<void>;
|
|
70
|
+
private registerListenerOnce;
|
|
71
|
+
sendText(target: ChannelTarget, text: string): Promise<unknown>;
|
|
72
|
+
createCard(target: ChannelTarget, text: string): Promise<CardCreateResult>;
|
|
73
|
+
updateCard(cardId: string, text: string): Promise<unknown>;
|
|
74
|
+
finishCard(cardId: string, text?: string): Promise<unknown>;
|
|
75
|
+
failCard(cardId: string, reason?: string): Promise<unknown>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=stream-upstream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-upstream.d.ts","sourceRoot":"","sources":["../src/stream-upstream.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGxE,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,yDAAyD;IACzD,UAAU,IAAI,IAAI,CAAC;IACnB,qEAAqE;IACrE,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;IACrH;;;;OAIG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;CACpE;AAED,MAAM,WAAW,6BAA6B;IAC5C,+DAA+D;IAC/D,MAAM,EAAE,oBAAoB,CAAC;IAC7B,kFAAkF;IAClF,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAqFD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgFhG;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,qBAAqB,GAC7B,IAAI,CAIN;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CA4BxG;AAED,0EAA0E;AAC1E,qBAAa,sBAAuB,YAAW,gBAAgB;IAKjD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,uEAAuE;IACvE,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,SAAS,CAAC,CAAyB;gBAEd,OAAO,EAAE,6BAA6B;IAEnE;;;OAGG;IACG,OAAO,CACX,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC;IA0BhB,OAAO,CAAC,oBAAoB;IAsB5B,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/D,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI1E,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAG5D"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DingTalk stream-mode upstream driver (official `dingtalk-stream` SDK).
|
|
3
|
+
*
|
|
4
|
+
* This driver replaces only the INBOUND leg of the legacy self-hosted gateway
|
|
5
|
+
* integration: robot messages arrive over the DingTalk Stream Mode WebSocket
|
|
6
|
+
* (CALLBACK downstream messages on `TOPIC_ROBOT`, payload JSON-encoded in
|
|
7
|
+
* `message.data`) and are mapped into the SAME raw shape the gateway
|
|
8
|
+
* long-poll driver produces (`{ type, msgId, senderId, conversationId,
|
|
9
|
+
* content, ... }`), so the existing mapper + dedup pipeline is untouched.
|
|
10
|
+
*
|
|
11
|
+
* OUTBOUND is deliberately delegated: message send / AI Card create/update are
|
|
12
|
+
* HTTP calls, not part of the stream SDK. `DingTalkStreamUpstream` forwards
|
|
13
|
+
* every outbound method to an injected `DingTalkUpstream`; SDK mode injects
|
|
14
|
+
* the official `sessionWebhook` / OpenAPI driver, while gateway mode uses its
|
|
15
|
+
* legacy HTTP driver.
|
|
16
|
+
*
|
|
17
|
+
* Credentials never appear in this module and are never logged. Live
|
|
18
|
+
* verification against a real DingTalk app (AppKey/AppSecret) is a manual
|
|
19
|
+
* step — the offline tests inject a fake stream client.
|
|
20
|
+
*
|
|
21
|
+
* Ack note: the stream server may retry a callback after ~60s without a
|
|
22
|
+
* response. The SDK's `DWClient` exposes `socketCallBackResponse(messageId,
|
|
23
|
+
* result)` for exactly this purpose; this driver ACKs each successfully
|
|
24
|
+
* parsed + submitted robot message (message "reliably received", NOT "LLM
|
|
25
|
+
* turn finished") so the platform does not redeliver it. Malformed payloads
|
|
26
|
+
* are deliberately NOT acked, so the platform can retry / surface the error.
|
|
27
|
+
*/
|
|
28
|
+
import { TOPIC_CARD, TOPIC_ROBOT } from 'dingtalk-stream';
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
// Validate the untrusted JSON frame before any field access. The schema only
|
|
31
|
+
// covers fields consumed by this adapter; `.passthrough()` preserves the
|
|
32
|
+
// platform's additional media fields for the existing mapper logic.
|
|
33
|
+
const dingtalkStreamRobotMessageSchema = z.object({
|
|
34
|
+
msgId: z.string().optional(),
|
|
35
|
+
senderStaffId: z.string().optional(),
|
|
36
|
+
senderId: z.string().optional(),
|
|
37
|
+
conversationId: z.string().optional(),
|
|
38
|
+
conversationType: z.string().optional(),
|
|
39
|
+
sessionWebhook: z.string().optional(),
|
|
40
|
+
robotCode: z.string().optional(),
|
|
41
|
+
isInAtList: z.boolean().optional(),
|
|
42
|
+
msgtype: z.string().optional(),
|
|
43
|
+
text: z.object({ content: z.string().optional() }).optional(),
|
|
44
|
+
content: z.unknown().optional(),
|
|
45
|
+
richText: z.object({ richTextList: z.array(z.unknown()).optional() }).optional(),
|
|
46
|
+
picture: z.object({ url: z.string().optional(), picMediaId: z.string().optional(), downloadCode: z.string().optional() }).optional(),
|
|
47
|
+
audio: z.object({ duration: z.number().optional(), url: z.string().optional(), downloadCode: z.string().optional() }).optional(),
|
|
48
|
+
video: z.object({ duration: z.number().optional(), url: z.string().optional(), downloadCode: z.string().optional() }).optional(),
|
|
49
|
+
file: z.object({ fileName: z.string().optional(), url: z.string().optional(), downloadCode: z.string().optional() }).optional(),
|
|
50
|
+
link: z.object({ title: z.string().optional(), text: z.string().optional(), picUrl: z.string().optional() }).optional(),
|
|
51
|
+
}).passthrough();
|
|
52
|
+
/** Resolve the media content container (mirrors the official connector's
|
|
53
|
+
* `resolveContent`): `data.content` as an object, or a parsed JSON string. */
|
|
54
|
+
function resolveContent(data) {
|
|
55
|
+
const raw = data.content;
|
|
56
|
+
if (raw == null)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (typeof raw === 'object')
|
|
59
|
+
return raw;
|
|
60
|
+
if (typeof raw === 'string') {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(raw);
|
|
63
|
+
if (parsed && typeof parsed === 'object')
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// fall through
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
function richTextList(data, content) {
|
|
73
|
+
const current = content?.richText;
|
|
74
|
+
const legacy = data.richText?.richTextList;
|
|
75
|
+
const list = Array.isArray(current) ? current : Array.isArray(legacy) ? legacy : [];
|
|
76
|
+
return list.filter((item) => !!item && typeof item === 'object');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Map one SDK downstream message into the gateway raw shape consumed by the
|
|
80
|
+
* inbound mapper (`{ type, msgId, eventId, senderId, conversationId, content,
|
|
81
|
+
* ... }`). Returns `undefined` when the payload is absent or not JSON.
|
|
82
|
+
*/
|
|
83
|
+
export function toGatewayRaw(message) {
|
|
84
|
+
if (typeof message.data !== 'string')
|
|
85
|
+
return undefined;
|
|
86
|
+
let parsed;
|
|
87
|
+
try {
|
|
88
|
+
parsed = JSON.parse(message.data);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const result = dingtalkStreamRobotMessageSchema.safeParse(parsed);
|
|
94
|
+
if (!result.success)
|
|
95
|
+
return undefined;
|
|
96
|
+
const data = result.data;
|
|
97
|
+
const raw = {
|
|
98
|
+
type: data.msgtype,
|
|
99
|
+
msgId: data.msgId,
|
|
100
|
+
eventId: message.headers?.eventId,
|
|
101
|
+
senderId: data.senderStaffId ?? data.senderId,
|
|
102
|
+
conversationId: data.conversationId,
|
|
103
|
+
conversationType: data.conversationType,
|
|
104
|
+
sessionWebhook: data.sessionWebhook,
|
|
105
|
+
robotCode: data.robotCode,
|
|
106
|
+
// Preserve the official activation fact under the channel-neutral name
|
|
107
|
+
// consumed by the mapper. Do not infer it from message text.
|
|
108
|
+
...(typeof data.isInAtList === 'boolean' ? { mentionedBot: data.isInAtList } : {}),
|
|
109
|
+
};
|
|
110
|
+
// Media fields follow the documented robot-message schema (oracle:
|
|
111
|
+
// @dingtalk-real-ai/dingtalk-connector@0.8.24). Media messages carry their
|
|
112
|
+
// content in `data.content` (object or JSON string) whose actionable field
|
|
113
|
+
// is `downloadCode`; picture may also carry `pictureUrl` / `picMediaId`.
|
|
114
|
+
// The `data.<type>?.url` shapes are kept for gateway-mode compatibility.
|
|
115
|
+
// The mapper turns these into image/audio/video/file parts.
|
|
116
|
+
const content = resolveContent(data);
|
|
117
|
+
switch (data.msgtype) {
|
|
118
|
+
case 'text':
|
|
119
|
+
raw.content = data.text?.content;
|
|
120
|
+
break;
|
|
121
|
+
case 'picture': {
|
|
122
|
+
raw.picUrl = content?.pictureUrl ?? data.picture?.url;
|
|
123
|
+
raw.picMediaId = content?.picMediaId ?? data.picture?.picMediaId;
|
|
124
|
+
raw.picDownloadCode = content?.downloadCode ?? data.picture?.downloadCode;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case 'richText': {
|
|
128
|
+
const items = richTextList(data, content);
|
|
129
|
+
raw.content = items
|
|
130
|
+
.filter((item) => item.type !== 'skill' && !item.skillData)
|
|
131
|
+
.map((item) => typeof item.text === 'string' ? item.text : '')
|
|
132
|
+
.join('');
|
|
133
|
+
raw.richTextImages = items.flatMap((item) => {
|
|
134
|
+
const pictureUrl = typeof item.pictureUrl === 'string' ? item.pictureUrl : undefined;
|
|
135
|
+
const downloadCode = typeof item.downloadCode === 'string' && (item.type === 'picture' || !item.type)
|
|
136
|
+
? item.downloadCode
|
|
137
|
+
: undefined;
|
|
138
|
+
return pictureUrl || downloadCode ? [{ pictureUrl, downloadCode }] : [];
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'audio':
|
|
143
|
+
raw.mediaUrl = content?.url ?? data.audio?.url;
|
|
144
|
+
raw.downloadCode = content?.downloadCode ?? data.audio?.downloadCode;
|
|
145
|
+
raw.durationMs = content?.duration ?? data.audio?.duration;
|
|
146
|
+
break;
|
|
147
|
+
case 'video':
|
|
148
|
+
raw.mediaUrl = content?.url ?? data.video?.url;
|
|
149
|
+
raw.downloadCode = content?.downloadCode ?? data.video?.downloadCode;
|
|
150
|
+
raw.durationMs = content?.duration ?? data.video?.duration;
|
|
151
|
+
break;
|
|
152
|
+
case 'file':
|
|
153
|
+
raw.mediaUrl = content?.url ?? data.file?.url;
|
|
154
|
+
raw.downloadCode = content?.downloadCode ?? data.file?.downloadCode;
|
|
155
|
+
raw.title = content?.fileName ?? data.file?.fileName;
|
|
156
|
+
break;
|
|
157
|
+
case 'link':
|
|
158
|
+
raw.title = data.link?.title;
|
|
159
|
+
raw.content = data.link?.text;
|
|
160
|
+
break;
|
|
161
|
+
default:
|
|
162
|
+
// Keep the SDK msgtype; the mapper reports unknown types as unsupported.
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
return raw;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Acknowledge one robot callback frame. ACK means "the message was reliably
|
|
169
|
+
* received and submitted to the inbound pipeline" — not "the LLM finished
|
|
170
|
+
* answering". A frame without a server `messageId` cannot be acked.
|
|
171
|
+
*/
|
|
172
|
+
export function ackRobotMessage(client, message) {
|
|
173
|
+
const messageId = message.headers?.messageId;
|
|
174
|
+
if (!messageId)
|
|
175
|
+
return;
|
|
176
|
+
client.socketCallBackResponse(messageId, { success: true });
|
|
177
|
+
}
|
|
178
|
+
export function toCardInteractionRaw(message) {
|
|
179
|
+
if (typeof message.data !== 'string')
|
|
180
|
+
return undefined;
|
|
181
|
+
let parsed;
|
|
182
|
+
try {
|
|
183
|
+
parsed = JSON.parse(message.data);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
const schema = z.object({
|
|
189
|
+
outTrackId: z.string().min(1),
|
|
190
|
+
userId: z.string().min(1),
|
|
191
|
+
openConversationId: z.string().min(1).optional(),
|
|
192
|
+
cardActionData: z.object({
|
|
193
|
+
cardPrivateData: z.object({ params: z.record(z.string(), z.unknown()).optional() }).optional(),
|
|
194
|
+
}).default({}),
|
|
195
|
+
}).passthrough();
|
|
196
|
+
const result = schema.safeParse(parsed);
|
|
197
|
+
if (!result.success)
|
|
198
|
+
return undefined;
|
|
199
|
+
const params = result.data.cardActionData.cardPrivateData?.params ?? {};
|
|
200
|
+
const action = typeof params.action === 'string' ? params.action : typeof params.actionId === 'string' ? params.actionId : undefined;
|
|
201
|
+
if (!action)
|
|
202
|
+
return undefined;
|
|
203
|
+
return {
|
|
204
|
+
type: 'interaction',
|
|
205
|
+
msgId: message.headers?.messageId ?? result.data.outTrackId,
|
|
206
|
+
eventId: message.headers?.eventId,
|
|
207
|
+
senderId: result.data.userId,
|
|
208
|
+
conversationId: result.data.openConversationId ?? result.data.userId,
|
|
209
|
+
conversationType: result.data.openConversationId ? '2' : '1',
|
|
210
|
+
interactionId: result.data.outTrackId,
|
|
211
|
+
action,
|
|
212
|
+
value: params,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/** Stream-mode implementation of `DingTalkUpstream` (inbound via SDK). */
|
|
216
|
+
export class DingTalkStreamUpstream {
|
|
217
|
+
options;
|
|
218
|
+
/** The listener is registered once per client; reconnect reuses it. */
|
|
219
|
+
listenerRegistered = false;
|
|
220
|
+
onMessage;
|
|
221
|
+
constructor(options) {
|
|
222
|
+
this.options = options;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Connect the stream client, route inbound robot messages into the gateway
|
|
226
|
+
* raw shape, and keep the stream open until `signal` aborts.
|
|
227
|
+
*/
|
|
228
|
+
async receive(signal, onMessage) {
|
|
229
|
+
this.onMessage = onMessage;
|
|
230
|
+
this.registerListenerOnce();
|
|
231
|
+
try {
|
|
232
|
+
await this.options.client.connect();
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
// Abort-driven teardown exits gracefully; other failures propagate to
|
|
236
|
+
// the adapter, which owns reconnect/backoff.
|
|
237
|
+
if (signal.aborted)
|
|
238
|
+
return;
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
if (signal.aborted) {
|
|
242
|
+
this.options.client.disconnect();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// dingtalk-stream v2 resolves connect() after a failed connection attempt
|
|
246
|
+
// and schedules its own retry. Do not report a healthy channel until its
|
|
247
|
+
// actual socket is open.
|
|
248
|
+
if (this.options.client.connected === false) {
|
|
249
|
+
throw new Error('dingtalk stream connection was not established');
|
|
250
|
+
}
|
|
251
|
+
this.options.onConnected?.();
|
|
252
|
+
await waitForAbort(signal);
|
|
253
|
+
this.options.client.disconnect();
|
|
254
|
+
}
|
|
255
|
+
registerListenerOnce() {
|
|
256
|
+
if (this.listenerRegistered)
|
|
257
|
+
return;
|
|
258
|
+
this.options.client.registerCallbackListener(TOPIC_ROBOT, (message) => {
|
|
259
|
+
if (!this.onMessage)
|
|
260
|
+
return;
|
|
261
|
+
const raw = toGatewayRaw(message);
|
|
262
|
+
if (raw === undefined)
|
|
263
|
+
return; // malformed payload: do not ack (platform retries / error observation)
|
|
264
|
+
this.onMessage(raw);
|
|
265
|
+
// ACK after submitting to the inbound pipeline ("reliably received"),
|
|
266
|
+
// never after the LLM turn completes.
|
|
267
|
+
ackRobotMessage(this.options.client, message);
|
|
268
|
+
});
|
|
269
|
+
this.options.client.registerCallbackListener(TOPIC_CARD, (message) => {
|
|
270
|
+
const raw = toCardInteractionRaw(message);
|
|
271
|
+
if (raw === undefined)
|
|
272
|
+
return;
|
|
273
|
+
this.onMessage?.(raw);
|
|
274
|
+
if (message.headers?.messageId) {
|
|
275
|
+
this.options.client.socketCallBackResponse(message.headers.messageId, { success: true });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
this.listenerRegistered = true;
|
|
279
|
+
}
|
|
280
|
+
sendText(target, text) {
|
|
281
|
+
return this.options.outbound.sendText(target, text);
|
|
282
|
+
}
|
|
283
|
+
createCard(target, text) {
|
|
284
|
+
return this.options.outbound.createCard(target, text);
|
|
285
|
+
}
|
|
286
|
+
updateCard(cardId, text) {
|
|
287
|
+
return this.options.outbound.updateCard(cardId, text);
|
|
288
|
+
}
|
|
289
|
+
finishCard(cardId, text) {
|
|
290
|
+
return this.options.outbound.finishCard(cardId, text);
|
|
291
|
+
}
|
|
292
|
+
failCard(cardId, reason) {
|
|
293
|
+
return this.options.outbound.failCard(cardId, reason);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function waitForAbort(signal) {
|
|
297
|
+
if (signal.aborted)
|
|
298
|
+
return Promise.resolve();
|
|
299
|
+
return new Promise((resolve) => {
|
|
300
|
+
signal.addEventListener('abort', () => resolve(), { once: true });
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=stream-upstream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-upstream.js","sourceRoot":"","sources":["../src/stream-upstream.ts"],"names":[],"mappings":"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"}
|